From df7ffad851c2ba18b58e7633bbf60eb61508944b Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Tue, 7 Nov 2023 18:35:07 +0100 Subject: [PATCH 01/23] Increase minified length if there is only one file --- packages/realtime-compiler/src/Http/DashboardController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index 9513a4b38ff..8c444e3cbbf 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -186,7 +186,7 @@ public static function highlightMediaLibraryCode(string $contents): HtmlString $contents = str_replace([''', '"'], ['%SQT%', '%DQT%'], $contents); // Temporarily replace escaped quotes if (static::isMediaFileProbablyMinified($contents)) { - return new HtmlString(substr($contents, 0, 800)); + return new HtmlString(substr($contents, 0, count(MediaFile::files()) === 1 ? 2000 : 800)); } $highlighted = str($contents)->explode("\n")->slice(0, 25)->map(function (string $line): string { From c4e436d7589544c332d44ef3a28eb1eab3b6757b Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 17:23:04 +0100 Subject: [PATCH 02/23] Remove function imports --- .../src/Http/DashboardController.php | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index 8c444e3cbbf..41931af0c04 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -28,31 +28,6 @@ use Hyde\Framework\Actions\CreatesNewMarkdownPostFile; use Symfony\Component\HttpKernel\Exception\HttpException; -use function e; -use function str; -use function time; -use function trim; -use function round; -use function rtrim; -use function strlen; -use function substr; -use function is_bool; -use function basename; -use function in_array; -use function json_decode; -use function json_encode; -use function substr_count; -use function array_combine; -use function trigger_error; -use function escapeshellarg; -use function file_get_contents; -use function str_starts_with; -use function str_replace; -use function array_merge; -use function sprintf; -use function config; -use function app; - /** * @internal This class is not intended to be edited outside the Hyde Realtime Compiler. */ From 1ef0268a7fa875470df1e696bf73c8e3278491dd Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 17:26:56 +0100 Subject: [PATCH 03/23] Move up method in class --- .../src/Http/DashboardController.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index 41931af0c04..fd97d987a03 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -75,6 +75,13 @@ public function __construct() } } + public function show(): string + { + return AnonymousViewCompiler::handle(__DIR__.'/../../resources/dashboard.blade.php', array_merge( + (array) $this, ['dashboard' => $this, 'request' => $this->request], + )); + } + protected function handlePostRequest(): void { $actions = array_combine($actions = [ @@ -108,13 +115,6 @@ protected function handlePostRequest(): void } } - public function show(): string - { - return AnonymousViewCompiler::handle(__DIR__.'/../../resources/dashboard.blade.php', array_merge( - (array) $this, ['dashboard' => $this, 'request' => $this->request], - )); - } - public function getVersion(): string { $version = InstalledVersions::getPrettyVersion('hyde/realtime-compiler'); From 9e0283b76efa401c4f361dfb28cc3eb9e4feed15 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 17:30:17 +0100 Subject: [PATCH 04/23] Move response creation from router to controller Will allow us to better return Json responses without having to use manual exits, etc. --- .../realtime-compiler/src/Http/DashboardController.php | 8 ++++++++ packages/realtime-compiler/src/Routing/PageRouter.php | 4 +--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index fd97d987a03..4d1f94619a4 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -11,6 +11,7 @@ use Illuminate\Support\Arr; use Hyde\Pages\MarkdownPage; use Hyde\Pages\MarkdownPost; +use Desilva\Microserve\Response; use Hyde\Pages\Concerns\HydePage; use Hyde\Pages\DocumentationPage; use Hyde\Support\Models\RouteKey; @@ -75,6 +76,13 @@ public function __construct() } } + public function handle(): Response + { + return new HtmlResponse(200, 'OK', [ + 'body' => $this->show(), + ]); + } + public function show(): string { return AnonymousViewCompiler::handle(__DIR__.'/../../resources/dashboard.blade.php', array_merge( diff --git a/packages/realtime-compiler/src/Routing/PageRouter.php b/packages/realtime-compiler/src/Routing/PageRouter.php index 55cc5dbc5d0..58e7b6f64c6 100644 --- a/packages/realtime-compiler/src/Routing/PageRouter.php +++ b/packages/realtime-compiler/src/Routing/PageRouter.php @@ -33,9 +33,7 @@ public function __construct(Request $request) protected function handlePageRequest(): Response { if ($this->request->path === '/dashboard' && DashboardController::enabled()) { - return new HtmlResponse(200, 'OK', [ - 'body' => (new DashboardController())->show(), - ]); + return (new DashboardController())->handle(); } return new HtmlResponse(200, 'OK', [ From 456053057ef66e30fbf58f50a07ab0802414f0d1 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 17:31:13 +0100 Subject: [PATCH 05/23] Protect the show method --- packages/realtime-compiler/src/Http/DashboardController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index 4d1f94619a4..cbc37cbadf0 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -83,7 +83,7 @@ public function handle(): Response ]); } - public function show(): string + protected function show(): string { return AnonymousViewCompiler::handle(__DIR__.'/../../resources/dashboard.blade.php', array_merge( (array) $this, ['dashboard' => $this, 'request' => $this->request], From d11fffe8d68bab72b45235f8679980c5bda31edd Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 17:33:36 +0100 Subject: [PATCH 06/23] Shift API handling from constructor to handle method --- .../realtime-compiler/src/Http/DashboardController.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index cbc37cbadf0..8ab8173aa4a 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -58,9 +58,14 @@ public function __construct() if ($this->request->method === 'POST') { $this->isAsync = (getallheaders()['X-RC-Handler'] ?? getallheaders()['x-rc-handler'] ?? null) === 'Async'; + } + } + public function handle(): Response + { + if ($this->request->method === 'POST') { if (! $this->isInteractive()) { - $this->abort(403, 'Enable `server.editor` in `config/hyde.php` to use interactive dashboard features.'); + $this->abort(403, 'Enable `server.editor` in `config/hyde.php` to use interactive dashboard features.'); } try { @@ -74,10 +79,7 @@ public function __construct() $this->sendJsonErrorResponse($exception); } } - } - public function handle(): Response - { return new HtmlResponse(200, 'OK', [ 'body' => $this->show(), ]); From f046d8e29becb577390b3f2c7909f0b6a1380550 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Wed, 8 Nov 2023 16:34:00 +0000 Subject: [PATCH 07/23] Apply fixes from StyleCI --- packages/realtime-compiler/src/Http/DashboardController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index 8ab8173aa4a..9f91c3c5e1c 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -65,7 +65,7 @@ public function handle(): Response { if ($this->request->method === 'POST') { if (! $this->isInteractive()) { - $this->abort(403, 'Enable `server.editor` in `config/hyde.php` to use interactive dashboard features.'); + $this->abort(403, 'Enable `server.editor` in `config/hyde.php` to use interactive dashboard features.'); } try { From ca4db1ee598bbaef2a3ae6a4317bcfdd78f733ad Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 17:37:21 +0100 Subject: [PATCH 08/23] Refactor void method to return control state --- .../src/Http/DashboardController.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index 8ab8173aa4a..5a633685eb3 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -68,8 +68,11 @@ public function handle(): Response $this->abort(403, 'Enable `server.editor` in `config/hyde.php` to use interactive dashboard features.'); } + if ($this->shouldUnsafeRequestBeBlocked()) { + $this->abort(403, "Refusing to serve request from address {$_SERVER['REMOTE_ADDR']} (must be on localhost)"); + } + try { - $this->blockUnsafeRequests(); $this->handlePostRequest(); } catch (HttpException $exception) { if (! $this->isAsync) { @@ -458,7 +461,7 @@ protected static function getPackageVersion(string $packageName): string return $prettyVersion ?? 'unreleased'; } - protected function blockUnsafeRequests(): void + protected function shouldUnsafeRequestBeBlocked(): bool { // As the dashboard is not password-protected, and it can make changes to the file system, // we block any requests that are not coming from the host machine. While we are clear @@ -468,9 +471,7 @@ protected function blockUnsafeRequests(): void $requestIp = $_SERVER['REMOTE_ADDR']; $allowedIps = ['::1', '127.0.0.1', 'localhost']; - if (! in_array($requestIp, $allowedIps, true)) { - $this->abort(403, "Refusing to serve request from address '$requestIp' (must be on localhost)"); - } + return in_array($requestIp, $allowedIps, true); } protected function sendJsonResponse(int $statusCode, string $body): never From d659601de70a468ea16d3b9ab6108eb804597873 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 17:40:27 +0100 Subject: [PATCH 09/23] Shift helper method parameters --- .../realtime-compiler/src/Http/DashboardController.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index 5a633685eb3..0ec9767fe07 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -79,7 +79,7 @@ public function handle(): Response throw $exception; } - $this->sendJsonErrorResponse($exception); + $this->sendJsonErrorResponse($exception->getStatusCode(), $exception->getMessage()); } } @@ -489,9 +489,9 @@ protected function sendJsonResponse(int $statusCode, string $body): never exit; } - protected function sendJsonErrorResponse(HttpException $exception): never + protected function sendJsonErrorResponse(int $statusCode, string $message): never { - $statusMessage = match ($exception->getStatusCode()) { + $statusMessage = match ($statusCode) { 400 => 'Bad Request', 403 => 'Forbidden', 404 => 'Not Found', @@ -499,8 +499,8 @@ protected function sendJsonErrorResponse(HttpException $exception): never default => 'Internal Server Error', }; - (new JsonResponse($exception->getStatusCode(), $statusMessage, [ - 'error' => $exception->getMessage(), + (new JsonResponse($statusCode, $statusMessage, [ + 'error' => $message, ]))->send(); exit; From 2d3f67f66a7a52103e601ee20464b85b5d8415dd Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 17:41:59 +0100 Subject: [PATCH 10/23] Invert faulty boolean logic --- packages/realtime-compiler/src/Http/DashboardController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index 0ec9767fe07..3d09a87fdba 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -471,7 +471,7 @@ protected function shouldUnsafeRequestBeBlocked(): bool $requestIp = $_SERVER['REMOTE_ADDR']; $allowedIps = ['::1', '127.0.0.1', 'localhost']; - return in_array($requestIp, $allowedIps, true); + return ! in_array($requestIp, $allowedIps, true); } protected function sendJsonResponse(int $statusCode, string $body): never From 3a7eca199da5a9003c324edca7911b0cafb2fc89 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 17:42:12 +0100 Subject: [PATCH 11/23] Refactor to return response instead of sending it --- .../src/Http/DashboardController.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index 3d09a87fdba..9e8ffa39c08 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -65,11 +65,11 @@ public function handle(): Response { if ($this->request->method === 'POST') { if (! $this->isInteractive()) { - $this->abort(403, 'Enable `server.editor` in `config/hyde.php` to use interactive dashboard features.'); + return $this->sendJsonErrorResponse(403, 'Enable `server.editor` in `config/hyde.php` to use interactive dashboard features.'); } if ($this->shouldUnsafeRequestBeBlocked()) { - $this->abort(403, "Refusing to serve request from address {$_SERVER['REMOTE_ADDR']} (must be on localhost)"); + return $this->sendJsonErrorResponse(403, "Refusing to serve request from address {$_SERVER['REMOTE_ADDR']} (must be on localhost)"); } try { @@ -79,7 +79,7 @@ public function handle(): Response throw $exception; } - $this->sendJsonErrorResponse($exception->getStatusCode(), $exception->getMessage()); + return $this->sendJsonErrorResponse($exception->getStatusCode(), $exception->getMessage()); } } @@ -489,7 +489,7 @@ protected function sendJsonResponse(int $statusCode, string $body): never exit; } - protected function sendJsonErrorResponse(int $statusCode, string $message): never + protected function sendJsonErrorResponse(int $statusCode, string $message): JsonResponse { $statusMessage = match ($statusCode) { 400 => 'Bad Request', @@ -499,11 +499,9 @@ protected function sendJsonErrorResponse(int $statusCode, string $message): neve default => 'Internal Server Error', }; - (new JsonResponse($statusCode, $statusMessage, [ + return new JsonResponse($statusCode, $statusMessage, [ 'error' => $message, - ]))->send(); - - exit; + ]); } protected function abort(int $code, string $message): never From 3c320009e2a45de93ab4b35f22f8b2e6993f6cbe Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 17:46:02 +0100 Subject: [PATCH 12/23] Extract helper for shared code --- .../src/Http/DashboardController.php | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index 9e8ffa39c08..c963c863a82 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -476,13 +476,7 @@ protected function shouldUnsafeRequestBeBlocked(): bool protected function sendJsonResponse(int $statusCode, string $body): never { - $statusMessage = match ($statusCode) { - 200 => 'OK', - 201 => 'Created', - default => 'Internal Server Error', - }; - - (new JsonResponse($statusCode, $statusMessage, [ + (new JsonResponse($statusCode, $this->matchStatusCode($statusCode), [ 'body' => $body, ]))->send(); @@ -491,15 +485,7 @@ protected function sendJsonResponse(int $statusCode, string $body): never protected function sendJsonErrorResponse(int $statusCode, string $message): JsonResponse { - $statusMessage = match ($statusCode) { - 400 => 'Bad Request', - 403 => 'Forbidden', - 404 => 'Not Found', - 409 => 'Conflict', - default => 'Internal Server Error', - }; - - return new JsonResponse($statusCode, $statusMessage, [ + return new JsonResponse($statusCode, $this->matchStatusCode($statusCode), [ 'error' => $message, ]); } @@ -521,4 +507,17 @@ protected function findGeneralOpenBinary(): string ) }; } + + protected function matchStatusCode(int $statusCode): string + { + return match ($statusCode) { + 200 => 'OK', + 201 => 'Created', + 400 => 'Bad Request', + 403 => 'Forbidden', + 404 => 'Not Found', + 409 => 'Conflict', + default => 'Internal Server Error', + }; + } } From 706922a429c136aedca5de0096fe3cb2425d9db9 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 17:51:37 +0100 Subject: [PATCH 13/23] Improve internal Json response handling --- .../src/Http/DashboardController.php | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index 96d7b5110c1..539049c5a4c 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -49,6 +49,8 @@ class DashboardController 'The dashboard update your project files. You can disable this by setting `server.dashboard.interactive` to `false` in `config/hyde.php`.', ]; + protected JsonResponse $response; + public function __construct() { $this->title = config('hyde.name').' - Dashboard'; @@ -73,7 +75,7 @@ public function handle(): Response } try { - $this->handlePostRequest(); + return $this->handlePostRequest(); } catch (HttpException $exception) { if (! $this->isAsync) { throw $exception; @@ -95,7 +97,7 @@ protected function show(): string )); } - protected function handlePostRequest(): void + protected function handlePostRequest(): JsonResponse { $actions = array_combine($actions = [ 'openInExplorer', @@ -126,6 +128,10 @@ protected function handlePostRequest(): void if ($action === 'createPage') { $this->createPage(); } + + return $this->response ?? new JsonResponse(200, 'OK', [ + 'message' => 'Action completed successfully', + ]); } public function getVersion(): string @@ -370,7 +376,7 @@ protected function createPage(): void } $this->flash('justCreatedPage', RouteKey::fromPage($pageClass, $pageClass::pathToIdentifier($path))->get()); - $this->sendJsonResponse(201, "Created file '$path'!"); + $this->setJsonResponse(201, "Created file '$path'!"); } } @@ -474,13 +480,11 @@ protected function shouldUnsafeRequestBeBlocked(): bool return ! in_array($requestIp, $allowedIps, true); } - protected function sendJsonResponse(int $statusCode, string $body): never + protected function setJsonResponse(int $statusCode, string $body): void { - (new JsonResponse($statusCode, $this->matchStatusCode($statusCode), [ + $this->response = new JsonResponse($statusCode, $this->matchStatusCode($statusCode), [ 'body' => $body, - ]))->send(); - - exit; + ]); } protected function sendJsonErrorResponse(int $statusCode, string $message): JsonResponse From 973bde9e6f19c6148af4b6de6e061c0ddb20bd9c Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 17:58:11 +0100 Subject: [PATCH 14/23] Change error message --- packages/realtime-compiler/src/Http/DashboardController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index 539049c5a4c..3bb41eb8ecf 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -361,7 +361,7 @@ protected function createPage(): void 'markdown-page' => MarkdownPage::class, 'markdown-post' => MarkdownPost::class, 'documentation-page' => DocumentationPage::class, - default => throw new HttpException(400, "Invalid page type '$pageType'"), + default => throw new HttpException(400, "Unsupported page type '$pageType'"), }; if ($pageClass === MarkdownPost::class) { From 3306e608872c86429fde7f3bc04a3e6d2eff4064 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 17:59:18 +0100 Subject: [PATCH 15/23] Update error message --- packages/realtime-compiler/src/Http/DashboardController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index 3bb41eb8ecf..4e209aa3d57 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -507,7 +507,7 @@ protected function findGeneralOpenBinary(): string 'Darwin' => 'open', 'Linux' => 'xdg-open', default => throw new HttpException(500, - sprintf("Unable to find a matching binary for OS family '%s'", PHP_OS_FAMILY) + sprintf("Unable to find a matching 'open' binary for OS family '%s'", PHP_OS_FAMILY) ) }; } From c6fd089d56c5df710ec3cabcfa6514aef3b2d620 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 17:59:53 +0100 Subject: [PATCH 16/23] Use helper instead of throwing directly --- packages/realtime-compiler/src/Http/DashboardController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index 4e209aa3d57..1e0328f015d 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -361,7 +361,7 @@ protected function createPage(): void 'markdown-page' => MarkdownPage::class, 'markdown-post' => MarkdownPost::class, 'documentation-page' => DocumentationPage::class, - default => throw new HttpException(400, "Unsupported page type '$pageType'"), + default => $this->abort(400, "Unsupported page type '$pageType'"), }; if ($pageClass === MarkdownPost::class) { @@ -506,7 +506,7 @@ protected function findGeneralOpenBinary(): string 'Windows' => 'powershell Start-Process', 'Darwin' => 'open', 'Linux' => 'xdg-open', - default => throw new HttpException(500, + default => $this->abort(500, sprintf("Unable to find a matching 'open' binary for OS family '%s'", PHP_OS_FAMILY) ) }; From f1b846172142b8b6f6a9303227ac64643a7b8fba Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 18:00:28 +0100 Subject: [PATCH 17/23] Formatting --- .../realtime-compiler/src/Http/DashboardController.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index 1e0328f015d..8f7f715cfd2 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -364,11 +364,10 @@ protected function createPage(): void default => $this->abort(400, "Unsupported page type '$pageType'"), }; - if ($pageClass === MarkdownPost::class) { - $creator = new CreatesNewMarkdownPostFile($title, $postDescription, $postCategory, $postAuthor, $postDate, $content); - } else { - $creator = new CreatesNewPageSourceFile($title, $pageClass, false, $content); - } + $creator = $pageClass === MarkdownPost::class + ? new CreatesNewMarkdownPostFile($title, $postDescription, $postCategory, $postAuthor, $postDate, $content) + : new CreatesNewPageSourceFile($title, $pageClass, false, $content); + try { $path = $creator->save(); } catch (FileConflictException $exception) { From 296977d647b97000966de0b25f6685a0f18731c3 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 18:01:54 +0100 Subject: [PATCH 18/23] Unwrap conditions already evaluated by caller --- .../src/Http/DashboardController.php | 96 +++++++++---------- 1 file changed, 44 insertions(+), 52 deletions(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index 8f7f715cfd2..cf50505d4f9 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -305,78 +305,70 @@ protected function loadFlashData(): void protected function openInExplorer(): void { - if ($this->isInteractive()) { - $binary = $this->findGeneralOpenBinary(); - $path = Hyde::path(); + $binary = $this->findGeneralOpenBinary(); + $path = Hyde::path(); - Process::run(sprintf('%s %s', $binary, escapeshellarg($path)))->throw(); - } + Process::run(sprintf('%s %s', $binary, escapeshellarg($path)))->throw(); } protected function openPageInEditor(HydePage $page): void { - if ($this->isInteractive()) { - $binary = $this->findGeneralOpenBinary(); - $path = Hyde::path($page->getSourcePath()); - - if (! (str_ends_with($path, '.md') || str_ends_with($path, '.blade.php'))) { - $this->abort(403, sprintf("Refusing to open unsafe file '%s'", basename($path))); - } + $binary = $this->findGeneralOpenBinary(); + $path = Hyde::path($page->getSourcePath()); - Process::run(sprintf('%s %s', $binary, escapeshellarg($path)))->throw(); + if (! (str_ends_with($path, '.md') || str_ends_with($path, '.blade.php'))) { + $this->abort(403, sprintf("Refusing to open unsafe file '%s'", basename($path))); } + + Process::run(sprintf('%s %s', $binary, escapeshellarg($path)))->throw(); } protected function openMediaFileInEditor(MediaFile $file): void { - if ($this->isInteractive()) { - $binary = $this->findGeneralOpenBinary(); - $path = $file->getAbsolutePath(); - - if (! in_array($file->getExtension(), ['png', 'svg', 'jpg', 'jpeg', 'gif', 'ico', 'css', 'js'])) { - $this->abort(403, sprintf("Refusing to open unsafe file '%s'", basename($path))); - } + $binary = $this->findGeneralOpenBinary(); + $path = $file->getAbsolutePath(); - Process::run(sprintf('%s %s', $binary, escapeshellarg($path)))->throw(); + if (! in_array($file->getExtension(), ['png', 'svg', 'jpg', 'jpeg', 'gif', 'ico', 'css', 'js'])) { + $this->abort(403, sprintf("Refusing to open unsafe file '%s'", basename($path))); } + + Process::run(sprintf('%s %s', $binary, escapeshellarg($path)))->throw(); } protected function createPage(): void { - if ($this->isInteractive()) { - // Required data - $title = $this->request->data['titleInput'] ?? $this->abort(400, 'Must provide title'); - $content = $this->request->data['contentInput'] ?? $this->abort(400, 'Must provide content'); - $pageType = $this->request->data['pageTypeSelection'] ?? $this->abort(400, 'Must provide page type'); - - // Optional data - $postDescription = $this->request->data['postDescription'] ?? null; - $postCategory = $this->request->data['postCategory'] ?? null; - $postAuthor = $this->request->data['postAuthor'] ?? null; - $postDate = $this->request->data['postDate'] ?? null; - - // Match page class - $pageClass = match ($pageType) { - 'blade-page' => BladePage::class, - 'markdown-page' => MarkdownPage::class, - 'markdown-post' => MarkdownPost::class, - 'documentation-page' => DocumentationPage::class, - default => $this->abort(400, "Unsupported page type '$pageType'"), - }; - - $creator = $pageClass === MarkdownPost::class - ? new CreatesNewMarkdownPostFile($title, $postDescription, $postCategory, $postAuthor, $postDate, $content) - : new CreatesNewPageSourceFile($title, $pageClass, false, $content); + // Required data + $title = $this->request->data['titleInput'] ?? $this->abort(400, 'Must provide title'); + $content = $this->request->data['contentInput'] ?? $this->abort(400, 'Must provide content'); + $pageType = $this->request->data['pageTypeSelection'] ?? $this->abort(400, 'Must provide page type'); + + // Optional data + $postDescription = $this->request->data['postDescription'] ?? null; + $postCategory = $this->request->data['postCategory'] ?? null; + $postAuthor = $this->request->data['postAuthor'] ?? null; + $postDate = $this->request->data['postDate'] ?? null; + + // Match page class + $pageClass = match ($pageType) { + 'blade-page' => BladePage::class, + 'markdown-page' => MarkdownPage::class, + 'markdown-post' => MarkdownPost::class, + 'documentation-page' => DocumentationPage::class, + default => $this->abort(400, "Unsupported page type '$pageType'"), + }; - try { - $path = $creator->save(); - } catch (FileConflictException $exception) { - $this->abort($exception->getCode(), $exception->getMessage()); - } + $creator = $pageClass === MarkdownPost::class + ? new CreatesNewMarkdownPostFile($title, $postDescription, $postCategory, $postAuthor, $postDate, $content) + : new CreatesNewPageSourceFile($title, $pageClass, false, $content); - $this->flash('justCreatedPage', RouteKey::fromPage($pageClass, $pageClass::pathToIdentifier($path))->get()); - $this->setJsonResponse(201, "Created file '$path'!"); + try { + $path = $creator->save(); + } catch (FileConflictException $exception) { + $this->abort($exception->getCode(), $exception->getMessage()); } + + $this->flash('justCreatedPage', RouteKey::fromPage($pageClass, $pageClass::pathToIdentifier($path))->get()); + $this->setJsonResponse(201, "Created file '$path'!"); } protected static function injectDashboardButton(string $contents): string From 482e1ac81a3878c122b3053c8b896792c870d3ab Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 18:06:15 +0100 Subject: [PATCH 19/23] Replace if blocks with switch block --- .../src/Http/DashboardController.php | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index cf50505d4f9..3cbdc5a6814 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -109,24 +109,23 @@ protected function handlePostRequest(): JsonResponse $action = $this->request->data['action'] ?? $this->abort(400, 'Must provide action'); $action = $actions[$action] ?? $this->abort(403, "Invalid action '$action'"); - if ($action === 'openInExplorer') { - $this->openInExplorer(); - } - - if ($action === 'openPageInEditor') { - $routeKey = $this->request->data['routeKey'] ?? $this->abort(400, 'Must provide routeKey'); - $page = Routes::getOrFail($routeKey)->getPage(); - $this->openPageInEditor($page); - } - - if ($action === 'openMediaFileInEditor') { - $identifier = $this->request->data['identifier'] ?? $this->abort(400, 'Must provide identifier'); - $asset = @MediaFile::all()[$identifier] ?? $this->abort(404, "Invalid media identifier '$identifier'"); - $this->openMediaFileInEditor($asset); - } - - if ($action === 'createPage') { - $this->createPage(); + switch ($action) { + case 'openInExplorer': + $this->openInExplorer(); + break; + case 'openPageInEditor': + $routeKey = $this->request->data['routeKey'] ?? $this->abort(400, 'Must provide routeKey'); + $page = Routes::getOrFail($routeKey)->getPage(); + $this->openPageInEditor($page); + break; + case 'openMediaFileInEditor': + $identifier = $this->request->data['identifier'] ?? $this->abort(400, 'Must provide identifier'); + $asset = @MediaFile::all()[$identifier] ?? $this->abort(404, "Invalid media identifier '$identifier'"); + $this->openMediaFileInEditor($asset); + break; + case 'createPage': + $this->createPage(); + break; } return $this->response ?? new JsonResponse(200, 'OK', [ From 9f4a3cb592a77095fee56d60777dd565ceca0cfb Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 18:07:49 +0100 Subject: [PATCH 20/23] Move data access and validation to handler methods --- .../src/Http/DashboardController.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index 3cbdc5a6814..a3a10863af7 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -114,14 +114,10 @@ protected function handlePostRequest(): JsonResponse $this->openInExplorer(); break; case 'openPageInEditor': - $routeKey = $this->request->data['routeKey'] ?? $this->abort(400, 'Must provide routeKey'); - $page = Routes::getOrFail($routeKey)->getPage(); - $this->openPageInEditor($page); + $this->openPageInEditor(); break; case 'openMediaFileInEditor': - $identifier = $this->request->data['identifier'] ?? $this->abort(400, 'Must provide identifier'); - $asset = @MediaFile::all()[$identifier] ?? $this->abort(404, "Invalid media identifier '$identifier'"); - $this->openMediaFileInEditor($asset); + $this->openMediaFileInEditor(); break; case 'createPage': $this->createPage(); @@ -310,8 +306,11 @@ protected function openInExplorer(): void Process::run(sprintf('%s %s', $binary, escapeshellarg($path)))->throw(); } - protected function openPageInEditor(HydePage $page): void + protected function openPageInEditor(): void { + $routeKey = $this->request->data['routeKey'] ?? $this->abort(400, 'Must provide routeKey'); + $page = Routes::getOrFail($routeKey)->getPage(); + $binary = $this->findGeneralOpenBinary(); $path = Hyde::path($page->getSourcePath()); @@ -322,8 +321,11 @@ protected function openPageInEditor(HydePage $page): void Process::run(sprintf('%s %s', $binary, escapeshellarg($path)))->throw(); } - protected function openMediaFileInEditor(MediaFile $file): void + protected function openMediaFileInEditor(): void { + $identifier = $this->request->data['identifier'] ?? $this->abort(400, 'Must provide identifier'); + $file = @MediaFile::all()[$identifier] ?? $this->abort(404, "Invalid media identifier '$identifier'"); + $binary = $this->findGeneralOpenBinary(); $path = $file->getAbsolutePath(); From ce681460e4c0e0ae4caf49d7248ba4ab0940834c Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 18:09:58 +0100 Subject: [PATCH 21/23] Replace 'switch' with 'match' expression --- .../src/Http/DashboardController.php | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index a3a10863af7..971b8a21824 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -109,20 +109,12 @@ protected function handlePostRequest(): JsonResponse $action = $this->request->data['action'] ?? $this->abort(400, 'Must provide action'); $action = $actions[$action] ?? $this->abort(403, "Invalid action '$action'"); - switch ($action) { - case 'openInExplorer': - $this->openInExplorer(); - break; - case 'openPageInEditor': - $this->openPageInEditor(); - break; - case 'openMediaFileInEditor': - $this->openMediaFileInEditor(); - break; - case 'createPage': - $this->createPage(); - break; - } + match ($action) { + 'openInExplorer' => $this->openInExplorer(), + 'openPageInEditor' => $this->openPageInEditor(), + 'openMediaFileInEditor' => $this->openMediaFileInEditor(), + 'createPage' => $this->createPage(), + }; return $this->response ?? new JsonResponse(200, 'OK', [ 'message' => 'Action completed successfully', From 9a62155184d7fb1bf25443149c7014e10c863182 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 19:17:40 +0100 Subject: [PATCH 22/23] Replace validation array with default match handler --- .../realtime-compiler/src/Http/DashboardController.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index 971b8a21824..f7d22f2afb9 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -99,21 +99,14 @@ protected function show(): string protected function handlePostRequest(): JsonResponse { - $actions = array_combine($actions = [ - 'openInExplorer', - 'openPageInEditor', - 'openMediaFileInEditor', - 'createPage', - ], $actions); - $action = $this->request->data['action'] ?? $this->abort(400, 'Must provide action'); - $action = $actions[$action] ?? $this->abort(403, "Invalid action '$action'"); match ($action) { 'openInExplorer' => $this->openInExplorer(), 'openPageInEditor' => $this->openPageInEditor(), 'openMediaFileInEditor' => $this->openMediaFileInEditor(), 'createPage' => $this->createPage(), + default => $this->abort(403, "Invalid action '$action'"), }; return $this->response ?? new JsonResponse(200, 'OK', [ From ec74ddad6643a1be7a6d5e40648cf0e59827b672 Mon Sep 17 00:00:00 2001 From: Caen De Silva Date: Wed, 8 Nov 2023 19:23:41 +0100 Subject: [PATCH 23/23] Mark local variable as deprecated --- packages/realtime-compiler/src/Http/DashboardController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/realtime-compiler/src/Http/DashboardController.php b/packages/realtime-compiler/src/Http/DashboardController.php index f7d22f2afb9..bb1a1ead4cc 100644 --- a/packages/realtime-compiler/src/Http/DashboardController.php +++ b/packages/realtime-compiler/src/Http/DashboardController.php @@ -214,7 +214,7 @@ public function getTip(): HtmlString public static function enabled(): bool { - // Previously, the setting was hyde.server.dashboard, so for backwards compatability we need this + /** @deprecated Previously, the setting was hyde.server.dashboard, so for backwards compatability we need this */ if (is_bool($oldConfig = config('hyde.server.dashboard'))) { trigger_error('Using `hyde.server.dashboard` as boolean is deprecated. Please use `hyde.server.dashboard.enabled` instead.', E_USER_DEPRECATED);