From a8462bf109f8ae3f488f69ca3746d75cbdb3bf72 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 25 May 2019 16:24:13 -0700 Subject: [PATCH] Add initial support for opening a file in the file manager, still needs more work --- .../Daemon/FileRepositoryInterface.php | 7 ++- .../Api/Client/Servers/FileController.php | 27 ++++++++++- .../Servers/Files/GetFileContentsRequest.php | 31 ++++++++++++ app/Repositories/Wings/FileRepository.php | 48 +++++++++++++++---- .../api/server/files/getFileContents.ts | 11 +++++ .../filemanager/FileContextMenu.vue | 2 +- .../server/components/filemanager/FileRow.vue | 13 ++++- .../{NewFileModal.vue => EditFileModal.vue} | 35 ++++++++++++-- .../server/subpages/FileManager.vue | 8 ++-- routes/api-client.php | 1 + 10 files changed, 159 insertions(+), 24 deletions(-) create mode 100644 app/Http/Requests/Api/Client/Servers/Files/GetFileContentsRequest.php create mode 100644 resources/assets/scripts/api/server/files/getFileContents.ts rename resources/assets/scripts/components/server/components/filemanager/modals/{NewFileModal.vue => EditFileModal.vue} (81%) diff --git a/app/Contracts/Repository/Daemon/FileRepositoryInterface.php b/app/Contracts/Repository/Daemon/FileRepositoryInterface.php index 6ef25332e..0124fad38 100644 --- a/app/Contracts/Repository/Daemon/FileRepositoryInterface.php +++ b/app/Contracts/Repository/Daemon/FileRepositoryInterface.php @@ -20,12 +20,11 @@ interface FileRepositoryInterface extends BaseRepositoryInterface /** * Return the contents of a given file if it can be edited in the Panel. * - * @param string $path + * @param string $path + * @param int|null $notLargerThan * @return string - * - * @throws \GuzzleHttp\Exception\TransferException */ - public function getContent(string $path): string; + public function getContent(string $path, int $notLargerThan = null): string; /** * Save new contents to a given file. diff --git a/app/Http/Controllers/Api/Client/Servers/FileController.php b/app/Http/Controllers/Api/Client/Servers/FileController.php index d4479c087..689c26426 100644 --- a/app/Http/Controllers/Api/Client/Servers/FileController.php +++ b/app/Http/Controllers/Api/Client/Servers/FileController.php @@ -8,6 +8,7 @@ use Illuminate\Http\Response; use Pterodactyl\Models\Server; use Illuminate\Http\JsonResponse; use Illuminate\Contracts\Cache\Repository as CacheRepository; +use Illuminate\Contracts\Config\Repository as ConfigRepository; use Pterodactyl\Http\Controllers\Api\Client\ClientApiController; use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface; use Pterodactyl\Http\Requests\Api\Client\Servers\Files\CopyFileRequest; @@ -16,6 +17,7 @@ use Pterodactyl\Http\Requests\Api\Client\Servers\Files\DeleteFileRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Files\RenameFileRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Files\CreateFolderRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Files\DownloadFileRequest; +use Pterodactyl\Http\Requests\Api\Client\Servers\Files\GetFileContentsRequest; class FileController extends ClientApiController { @@ -24,6 +26,11 @@ class FileController extends ClientApiController */ private $cache; + /** + * @var \Illuminate\Contracts\Config\Repository + */ + private $config; + /** * @var \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface */ @@ -32,14 +39,16 @@ class FileController extends ClientApiController /** * FileController constructor. * + * @param \Illuminate\Contracts\Config\Repository $config * @param \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface $fileRepository * @param \Illuminate\Contracts\Cache\Repository $cache */ - public function __construct(FileRepositoryInterface $fileRepository, CacheRepository $cache) + public function __construct(ConfigRepository $config, FileRepositoryInterface $fileRepository, CacheRepository $cache) { parent::__construct(); $this->cache = $cache; + $this->config = $config; $this->fileRepository = $fileRepository; } @@ -55,9 +64,25 @@ class FileController extends ClientApiController 'contents' => $this->fileRepository->setServer($request->getModel(Server::class))->getDirectory( $request->get('directory') ?? '/' ), + 'editable' => $this->config->get('pterodactyl.files.editable', []), ]); } + /** + * Return the contents of a specified file for the user. + * + * @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\GetFileContentsRequest $request + * @return \Illuminate\Http\Response + */ + public function getFileContents(GetFileContentsRequest $request): Response + { + return Response::create( + $this->fileRepository->setServer($request->getModel(Server::class))->getContent( + $request->get('file'), $this->config->get('pterodactyl.files.max_edit_size') + ) + ); + } + /** * Creates a new folder on the server. * diff --git a/app/Http/Requests/Api/Client/Servers/Files/GetFileContentsRequest.php b/app/Http/Requests/Api/Client/Servers/Files/GetFileContentsRequest.php new file mode 100644 index 000000000..7c596c8d4 --- /dev/null +++ b/app/Http/Requests/Api/Client/Servers/Files/GetFileContentsRequest.php @@ -0,0 +1,31 @@ + 'required|string', + ]; + } +} diff --git a/app/Repositories/Wings/FileRepository.php b/app/Repositories/Wings/FileRepository.php index 2879ef95d..5b0a8dabe 100644 --- a/app/Repositories/Wings/FileRepository.php +++ b/app/Repositories/Wings/FileRepository.php @@ -3,7 +3,9 @@ namespace Pterodactyl\Repositories\Wings; use stdClass; +use Exception; use Psr\Http\Message\ResponseInterface; +use Pterodactyl\Exceptions\Http\Server\FileSizeTooLargeException; use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface; class FileRepository extends BaseWingsRepository implements FileRepositoryInterface @@ -14,28 +16,47 @@ class FileRepository extends BaseWingsRepository implements FileRepositoryInterf * @param string $path * @return \stdClass * + * @throws \Exception * @throws \GuzzleHttp\Exception\TransferException */ public function getFileStat(string $path): stdClass { - // TODO: Implement getFileStat() method. + throw new Exception('Function not implemented.'); } /** - * Return the contents of a given file if it can be edited in the Panel. + * Return the contents of a given file. * - * @param string $path + * @param string $path + * @param int|null $notLargerThan the maximum content length in bytes * @return string * * @throws \GuzzleHttp\Exception\TransferException + * @throws \Pterodactyl\Exceptions\Http\Server\FileSizeTooLargeException */ - public function getContent(string $path): string + public function getContent(string $path, int $notLargerThan = null): string { - // TODO: Implement getContent() method. + $response = $this->getHttpClient()->get( + sprintf('/api/servers/%s/files/contents', $this->getServer()->uuid), + [ + 'query' => ['file' => $path], + ] + ); + + $length = (int) $response->getHeader('Content-Length')[0] ?? 0; + + if ($notLargerThan && $length > $notLargerThan) { + throw new FileSizeTooLargeException( + trans('server.files.exceptions.max_size') + ); + } + + return $response->getBody()->__toString(); } /** - * Save new contents to a given file. + * Save new contents to a given file. This works for both creating and updating + * a file. * * @param string $path * @param string $content @@ -45,7 +66,13 @@ class FileRepository extends BaseWingsRepository implements FileRepositoryInterf */ public function putContent(string $path, string $content): ResponseInterface { - // TODO: Implement putContent() method. + return $this->getHttpClient()->post( + sprintf('/api/servers/%s/files/write', $this->getServer()->uuid), + [ + 'query' => ['file' => $path], + 'body' => $content, + ] + ); } /** @@ -59,9 +86,10 @@ class FileRepository extends BaseWingsRepository implements FileRepositoryInterf public function getDirectory(string $path): array { $response = $this->getHttpClient()->get( - // Reason for the path check is because it is unnecessary on the Daemon but we need - // to respect the interface. - sprintf('/api/servers/%s/files/list/%s', $this->getServer()->uuid, $path === '/' ? '' : $path) + sprintf('/api/servers/%s/files/list-directory', $this->getServer()->uuid), + [ + 'query' => ['directory' => $path], + ] ); return json_decode($response->getBody(), true); diff --git a/resources/assets/scripts/api/server/files/getFileContents.ts b/resources/assets/scripts/api/server/files/getFileContents.ts new file mode 100644 index 000000000..4074721eb --- /dev/null +++ b/resources/assets/scripts/api/server/files/getFileContents.ts @@ -0,0 +1,11 @@ +import http from "@/api/http"; + +export default (server: string, file: string): Promise => { + return new Promise((resolve, reject) => { + http.get(`/api/client/servers/${server}/files/contents`, { + params: { file } + }) + .then(response => resolve(response.data)) + .catch(reject); + }); +} diff --git a/resources/assets/scripts/components/server/components/filemanager/FileContextMenu.vue b/resources/assets/scripts/components/server/components/filemanager/FileContextMenu.vue index f808fc22e..af317622b 100644 --- a/resources/assets/scripts/components/server/components/filemanager/FileContextMenu.vue +++ b/resources/assets/scripts/components/server/components/filemanager/FileContextMenu.vue @@ -74,7 +74,7 @@ }, openNewFileModal: function () { - window.events.$emit('server:files:open-new-file-modal'); + window.events.$emit('server:files:open-edit-file-modal'); this.$emit('close'); }, diff --git a/resources/assets/scripts/components/server/components/filemanager/FileRow.vue b/resources/assets/scripts/components/server/components/filemanager/FileRow.vue index 6a069d7fd..8f7cf6eb0 100644 --- a/resources/assets/scripts/components/server/components/filemanager/FileRow.vue +++ b/resources/assets/scripts/components/server/components/filemanager/FileRow.vue @@ -1,7 +1,12 @@