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 @@