Skip to content

Commit 1a6669a

Browse files
committed
Add endpoint support for decompressing files
1 parent 78c76d6 commit 1a6669a

File tree

8 files changed

+91
-4
lines changed

8 files changed

+91
-4
lines changed

app/Http/Controllers/Api/Client/Servers/FileController.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,24 @@ public function compress(CompressFilesRequest $request, Server $server): array
231231
->toArray();
232232
}
233233

234+
/**
235+
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\DecompressFilesRequest $request
236+
* @param \Pterodactyl\Models\Server $server
237+
* @return \Illuminate\Http\JsonResponse
238+
*
239+
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
240+
*/
241+
public function decompress(DecompressFilesRequest $request, Server $server): JsonResponse
242+
{
243+
// Allow up to five minutes for this request to process before timing out.
244+
set_time_limit(300);
245+
246+
$this->fileRepository->setServer($server)
247+
->decompressFile($request->input('root'), $request->input('file'));
248+
249+
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT);
250+
}
251+
234252
/**
235253
* Deletes files or folders for the server in the given root directory.
236254
*
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Files;
4+
5+
use Pterodactyl\Models\Permission;
6+
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
7+
8+
class DecompressFilesRequest extends ClientApiRequest
9+
{
10+
/**
11+
* Checks that the authenticated user is allowed to create new files for the server. We don't
12+
* rely on the archive permission here as it makes more sense to make sure the user can create
13+
* additional files rather than make an archive.
14+
*
15+
* @return string
16+
*/
17+
public function permission(): string
18+
{
19+
return Permission::ACTION_FILE_CREATE;
20+
}
21+
22+
/**
23+
* @return array
24+
*/
25+
public function rules(): array
26+
{
27+
return [
28+
'root' => 'sometimes|nullable|string',
29+
'file' => 'required|string',
30+
];
31+
}
32+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import http from '@/api/http';
2+
3+
export default async (uuid: string, directory: string, file: string): Promise<void> => {
4+
await http.post(`/api/client/servers/${uuid}/files/decompress`, { root: directory, file }, {
5+
timeout: 300000,
6+
timeoutErrorMessage: 'It looks like this archive is taking a long time to be unarchived. Once completed the unarchived files will appear.',
7+
});
8+
};

resources/scripts/api/server/files/loadDirectory.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export interface FileObject {
1212
mimetype: string;
1313
createdAt: Date;
1414
modifiedAt: Date;
15+
isArchiveType: () => boolean;
1516
}
1617

1718
export default async (uuid: string, directory?: string): Promise<FileObject[]> => {

resources/scripts/api/transformers.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,12 @@ export const rawDataToFileObject = (data: FractalResponseData): FileObject => ({
2323
mimetype: data.attributes.mimetype,
2424
createdAt: new Date(data.attributes.created_at),
2525
modifiedAt: new Date(data.attributes.modified_at),
26+
27+
isArchiveType: function () {
28+
return this.isFile && [
29+
'application/zip',
30+
'application/gzip',
31+
'application/x-tar',
32+
].indexOf(this.mimetype) >= 0;
33+
},
2634
});

resources/scripts/components/server/files/FileDropdownMenu.tsx

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { useRef, useState } from 'react';
22
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
33
import {
4+
faBoxOpen,
45
faCopy,
56
faEllipsisH,
67
faFileArchive,
@@ -27,6 +28,7 @@ import DropdownMenu from '@/components/elements/DropdownMenu';
2728
import styled from 'styled-components/macro';
2829
import useEventListener from '@/plugins/useEventListener';
2930
import compressFiles from '@/api/server/files/compressFiles';
31+
import decompressFiles from '@/api/server/files/decompressFiles';
3032

3133
type ModalType = 'rename' | 'move';
3234

@@ -43,7 +45,7 @@ interface RowProps extends React.HTMLAttributes<HTMLDivElement> {
4345

4446
const Row = ({ icon, title, ...props }: RowProps) => (
4547
<StyledRow {...props}>
46-
<FontAwesomeIcon icon={icon} css={tw`text-xs`}/>
48+
<FontAwesomeIcon icon={icon} css={tw`text-xs`} fixedWidth/>
4749
<span css={tw`ml-2`}>{title}</span>
4850
</StyledRow>
4951
);
@@ -110,6 +112,16 @@ export default ({ file }: { file: FileObject }) => {
110112
.then(() => setShowSpinner(false));
111113
};
112114

115+
const doUnarchive = () => {
116+
setShowSpinner(true);
117+
clearFlashes('files');
118+
119+
decompressFiles(uuid, directory, file.name)
120+
.then(() => mutate())
121+
.catch(error => clearAndAddHttpError({ key: 'files', error }))
122+
.then(() => setShowSpinner(false));
123+
};
124+
113125
return (
114126
<DropdownMenu
115127
ref={onClickRef}
@@ -138,9 +150,15 @@ export default ({ file }: { file: FileObject }) => {
138150
<Row onClick={doCopy} icon={faCopy} title={'Copy'}/>
139151
</Can>
140152
}
141-
<Can action={'file.archive'}>
142-
<Row onClick={doArchive} icon={faFileArchive} title={'Archive'}/>
143-
</Can>
153+
{file.isArchiveType() ?
154+
<Can action={'file.create'}>
155+
<Row onClick={doUnarchive} icon={faBoxOpen} title={'Unarchive'}/>
156+
</Can>
157+
:
158+
<Can action={'file.archive'}>
159+
<Row onClick={doArchive} icon={faFileArchive} title={'Archive'}/>
160+
</Can>
161+
}
144162
<Row onClick={doDownload} icon={faFileDownload} title={'Download'}/>
145163
<Can action={'file.delete'}>
146164
<Row onClick={doDeletion} icon={faTrashAlt} title={'Delete'} $danger/>

resources/scripts/components/server/files/NewDirectoryButton.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const generateDirectoryData = (name: string): FileObject => ({
3434
mimetype: '',
3535
createdAt: new Date(),
3636
modifiedAt: new Date(),
37+
isArchiveType: () => false,
3738
});
3839

3940
export default () => {

routes/api-client.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
Route::post('/copy', 'Servers\FileController@copy');
6161
Route::post('/write', 'Servers\FileController@write');
6262
Route::post('/compress', 'Servers\FileController@compress');
63+
Route::post('/decompress', 'Servers\FileController@decompress');
6364
Route::post('/delete', 'Servers\FileController@delete');
6465
Route::post('/create-folder', 'Servers\FileController@create');
6566
});

0 commit comments

Comments
 (0)