Add endpoint support for decompressing files

This commit is contained in:
Dane Everitt 2020-07-14 21:16:49 -07:00
parent 78c76d6df4
commit 1a6669aa5c
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
8 changed files with 91 additions and 4 deletions

View file

@ -231,6 +231,24 @@ class FileController extends ClientApiController
->toArray(); ->toArray();
} }
/**
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\DecompressFilesRequest $request
* @param \Pterodactyl\Models\Server $server
* @return \Illuminate\Http\JsonResponse
*
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
*/
public function decompress(DecompressFilesRequest $request, Server $server): JsonResponse
{
// Allow up to five minutes for this request to process before timing out.
set_time_limit(300);
$this->fileRepository->setServer($server)
->decompressFile($request->input('root'), $request->input('file'));
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT);
}
/** /**
* Deletes files or folders for the server in the given root directory. * Deletes files or folders for the server in the given root directory.
* *

View file

@ -0,0 +1,32 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Files;
use Pterodactyl\Models\Permission;
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
class DecompressFilesRequest extends ClientApiRequest
{
/**
* Checks that the authenticated user is allowed to create new files for the server. We don't
* rely on the archive permission here as it makes more sense to make sure the user can create
* additional files rather than make an archive.
*
* @return string
*/
public function permission(): string
{
return Permission::ACTION_FILE_CREATE;
}
/**
* @return array
*/
public function rules(): array
{
return [
'root' => 'sometimes|nullable|string',
'file' => 'required|string',
];
}
}

View file

@ -0,0 +1,8 @@
import http from '@/api/http';
export default async (uuid: string, directory: string, file: string): Promise<void> => {
await http.post(`/api/client/servers/${uuid}/files/decompress`, { root: directory, file }, {
timeout: 300000,
timeoutErrorMessage: 'It looks like this archive is taking a long time to be unarchived. Once completed the unarchived files will appear.',
});
};

View file

@ -12,6 +12,7 @@ export interface FileObject {
mimetype: string; mimetype: string;
createdAt: Date; createdAt: Date;
modifiedAt: Date; modifiedAt: Date;
isArchiveType: () => boolean;
} }
export default async (uuid: string, directory?: string): Promise<FileObject[]> => { export default async (uuid: string, directory?: string): Promise<FileObject[]> => {

View file

@ -23,4 +23,12 @@ export const rawDataToFileObject = (data: FractalResponseData): FileObject => ({
mimetype: data.attributes.mimetype, mimetype: data.attributes.mimetype,
createdAt: new Date(data.attributes.created_at), createdAt: new Date(data.attributes.created_at),
modifiedAt: new Date(data.attributes.modified_at), modifiedAt: new Date(data.attributes.modified_at),
isArchiveType: function () {
return this.isFile && [
'application/zip',
'application/gzip',
'application/x-tar',
].indexOf(this.mimetype) >= 0;
},
}); });

View file

@ -1,6 +1,7 @@
import React, { useRef, useState } from 'react'; import React, { useRef, useState } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { import {
faBoxOpen,
faCopy, faCopy,
faEllipsisH, faEllipsisH,
faFileArchive, faFileArchive,
@ -27,6 +28,7 @@ import DropdownMenu from '@/components/elements/DropdownMenu';
import styled from 'styled-components/macro'; import styled from 'styled-components/macro';
import useEventListener from '@/plugins/useEventListener'; import useEventListener from '@/plugins/useEventListener';
import compressFiles from '@/api/server/files/compressFiles'; import compressFiles from '@/api/server/files/compressFiles';
import decompressFiles from '@/api/server/files/decompressFiles';
type ModalType = 'rename' | 'move'; type ModalType = 'rename' | 'move';
@ -43,7 +45,7 @@ interface RowProps extends React.HTMLAttributes<HTMLDivElement> {
const Row = ({ icon, title, ...props }: RowProps) => ( const Row = ({ icon, title, ...props }: RowProps) => (
<StyledRow {...props}> <StyledRow {...props}>
<FontAwesomeIcon icon={icon} css={tw`text-xs`}/> <FontAwesomeIcon icon={icon} css={tw`text-xs`} fixedWidth/>
<span css={tw`ml-2`}>{title}</span> <span css={tw`ml-2`}>{title}</span>
</StyledRow> </StyledRow>
); );
@ -110,6 +112,16 @@ export default ({ file }: { file: FileObject }) => {
.then(() => setShowSpinner(false)); .then(() => setShowSpinner(false));
}; };
const doUnarchive = () => {
setShowSpinner(true);
clearFlashes('files');
decompressFiles(uuid, directory, file.name)
.then(() => mutate())
.catch(error => clearAndAddHttpError({ key: 'files', error }))
.then(() => setShowSpinner(false));
};
return ( return (
<DropdownMenu <DropdownMenu
ref={onClickRef} ref={onClickRef}
@ -138,9 +150,15 @@ export default ({ file }: { file: FileObject }) => {
<Row onClick={doCopy} icon={faCopy} title={'Copy'}/> <Row onClick={doCopy} icon={faCopy} title={'Copy'}/>
</Can> </Can>
} }
<Can action={'file.archive'}> {file.isArchiveType() ?
<Row onClick={doArchive} icon={faFileArchive} title={'Archive'}/> <Can action={'file.create'}>
</Can> <Row onClick={doUnarchive} icon={faBoxOpen} title={'Unarchive'}/>
</Can>
:
<Can action={'file.archive'}>
<Row onClick={doArchive} icon={faFileArchive} title={'Archive'}/>
</Can>
}
<Row onClick={doDownload} icon={faFileDownload} title={'Download'}/> <Row onClick={doDownload} icon={faFileDownload} title={'Download'}/>
<Can action={'file.delete'}> <Can action={'file.delete'}>
<Row onClick={doDeletion} icon={faTrashAlt} title={'Delete'} $danger/> <Row onClick={doDeletion} icon={faTrashAlt} title={'Delete'} $danger/>

View file

@ -34,6 +34,7 @@ const generateDirectoryData = (name: string): FileObject => ({
mimetype: '', mimetype: '',
createdAt: new Date(), createdAt: new Date(),
modifiedAt: new Date(), modifiedAt: new Date(),
isArchiveType: () => false,
}); });
export default () => { export default () => {

View file

@ -60,6 +60,7 @@ Route::group(['prefix' => '/servers/{server}', 'middleware' => [AuthenticateServ
Route::post('/copy', 'Servers\FileController@copy'); Route::post('/copy', 'Servers\FileController@copy');
Route::post('/write', 'Servers\FileController@write'); Route::post('/write', 'Servers\FileController@write');
Route::post('/compress', 'Servers\FileController@compress'); Route::post('/compress', 'Servers\FileController@compress');
Route::post('/decompress', 'Servers\FileController@decompress');
Route::post('/delete', 'Servers\FileController@delete'); Route::post('/delete', 'Servers\FileController@delete');
Route::post('/create-folder', 'Servers\FileController@create'); Route::post('/create-folder', 'Servers\FileController@create');
}); });