Skip to content

Commit

Permalink
Improve asynchronous dashboard API handling
Browse files Browse the repository at this point in the history
  • Loading branch information
caendesilva committed Oct 21, 2023
1 parent e617125 commit 0940736
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 11 deletions.
3 changes: 3 additions & 0 deletions packages/realtime-compiler/resources/dashboard.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@
fetch("", {
method: "POST",
body: new FormData(event.target),
headers: new Headers({
"X-RC-Handler": "Async",
}),
}).then(response => {
if (response.ok) {
// Request was successful, no need to do anything.
Expand Down
60 changes: 49 additions & 11 deletions packages/realtime-compiler/src/Http/DashboardController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@
use Hyde\Pages\DocumentationPage;
use Illuminate\Support\HtmlString;
use Hyde\Foundation\Facades\Routes;
use Desilva\Microserve\JsonResponse;
use Illuminate\Support\Facades\Process;
use Hyde\Framework\Actions\StaticPageBuilder;
use Hyde\Framework\Actions\AnonymousViewCompiler;
use Desilva\Microserve\Request;
use Composer\InstalledVersions;
use Hyde\Framework\Actions\CreatesNewPageSourceFile;
use Hyde\Framework\Exceptions\FileConflictException;
use Hyde\Framework\Actions\CreatesNewMarkdownPostFile;
use Symfony\Component\HttpKernel\Exception\HttpException;

use function abort;
use function basename;
use function array_combine;
use function escapeshellarg;
Expand All @@ -41,6 +42,7 @@ class DashboardController
public string $title;

protected Request $request;
protected bool $isAsync = false;

protected static array $tips = [
'This dashboard won\'t be saved to your static site.',
Expand All @@ -56,11 +58,21 @@ public function __construct()
$this->request = Request::capture();

if ($this->request->method === 'POST') {
$this->isAsync = (getallheaders()['X-RC-Handler'] ?? getallheaders()['x-rc-handler'] ?? null) === 'Async';

if (! $this->enableEditor()) {
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.');
}

$this->handlePostRequest();
try {
$this->handlePostRequest();
} catch (HttpException $exception) {
if (! $this->isAsync) {
throw $exception;
}

$this->sendJsonResponse($exception);
}
}
}

Expand All @@ -71,11 +83,11 @@ protected function handlePostRequest(): void
'createPage',
], $actions);

$action = $this->request->data['action'] ?? abort(400, 'Must provide action');
$action = $actions[$action] ?? abort(403, 'Invalid action');
$action = $this->request->data['action'] ?? $this->abort(400, 'Must provide action');
$action = $actions[$action] ?? $this->abort(403, "Invalid action '$action'");

if ($action === 'openInEditor') {
$routeKey = $this->request->data['routeKey'] ?? abort(400, 'Must provide routeKey');
$routeKey = $this->request->data['routeKey'] ?? $this->abort(400, 'Must provide routeKey');
$page = Routes::getOrFail($routeKey)->getPage();
$this->openInEditor($page);
}
Expand Down Expand Up @@ -178,7 +190,7 @@ protected function openInEditor(HydePage $page): void
$path = Hyde::path($page->getSourcePath());

if (! (str_ends_with($path, '.md') || str_ends_with($path, '.blade.php'))) {
abort(403, sprintf("Refusing to open unsafe file '%s'", basename($path)));
$this->abort(403, sprintf("Refusing to open unsafe file '%s'", basename($path)));
}

Process::run(sprintf('%s %s', $binary, escapeshellarg($path)))->throw();
Expand All @@ -189,9 +201,9 @@ protected function createPage(): void
{
if ($this->enableEditor()) {
// Required data
$title = $this->request->data['titleInput'] ?? abort(400, 'Must provide title');
$content = $this->request->data['contentInput'] ?? abort(400, 'Must provide content');
$pageType = $this->request->data['pageTypeSelection'] ?? abort(400, 'Must provide page type');
$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;
Expand All @@ -213,7 +225,11 @@ protected function createPage(): void
} else {
$creator = new CreatesNewPageSourceFile($title, $pageClass, false, $content);
}
$creator->save();
try {
$creator->save();
} catch (FileConflictException $exception) {
$this->abort($exception->getCode(), $exception->getMessage());
}
}
}

Expand Down Expand Up @@ -303,4 +319,26 @@ protected static function getPackageVersion(string $packageName): string

return $prettyVersion ?? 'unreleased';
}

protected function sendJsonResponse(HttpException $exception): never
{
$statusMessage = match ($exception->getStatusCode()) {
200 => 'OK',
201 => 'Created',
400 => 'Bad Request',
403 => 'Forbidden',
409 => 'Conflict',
default => 'Internal Server Error',
};
(new JsonResponse($exception->getStatusCode(), $statusMessage, [
'error' => $exception->getMessage(),
]))->send();

exit;
}

protected function abort(int $code, string $message): never
{
throw new HttpException($code, $message);
}
}

0 comments on commit 0940736

Please sign in to comment.