Add endpoint support for decompressing files
This commit is contained in:
parent
78c76d6df4
commit
1a6669aa5c
8 changed files with 91 additions and 4 deletions
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
8
resources/scripts/api/server/files/decompressFiles.ts
Normal file
8
resources/scripts/api/server/files/decompressFiles.ts
Normal 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.',
|
||||||
|
});
|
||||||
|
};
|
|
@ -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[]> => {
|
||||||
|
|
|
@ -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;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -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/>
|
||||||
|
|
|
@ -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 () => {
|
||||||
|
|
|
@ -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');
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue