diff --git a/config/inertia.php b/config/inertia.php index 326bdf4a..d79657b9 100644 --- a/config/inertia.php +++ b/config/inertia.php @@ -25,7 +25,7 @@ 'url' => env('INERTIA_SSR_URL', 'http://127.0.0.1:13714'), - 'dispatch_without_bundle' => false, + 'dispatch_without_bundle' => (bool) env('INERTIA_SSR_DISPATCH_WITHOUT_BUNDLE', false), // 'bundle' => base_path('bootstrap/ssr/ssr.mjs'), diff --git a/src/Commands/CheckSsr.php b/src/Commands/CheckSsr.php new file mode 100644 index 00000000..fa355b1d --- /dev/null +++ b/src/Commands/CheckSsr.php @@ -0,0 +1,44 @@ +error('The SSR gateway does not support health checks.'); + + return self::FAILURE; + } + + ($check = $gateway->isHealthy()) + ? $this->info('Inertia SSR server is running.') + : $this->error('Inertia SSR server is not running.'); + + return $check ? self::SUCCESS : self::FAILURE; + } +} diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index b0e0e0c8..e3c7bf44 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -68,6 +68,7 @@ protected function registerConsoleCommands(): void Commands\CreateMiddleware::class, Commands\StartSsr::class, Commands\StopSsr::class, + Commands\CheckSsr::class, ]); } diff --git a/src/Ssr/HasHealthCheck.php b/src/Ssr/HasHealthCheck.php new file mode 100644 index 00000000..2e3212b2 --- /dev/null +++ b/src/Ssr/HasHealthCheck.php @@ -0,0 +1,11 @@ +getHttpUrl()) { + if (! $url = $this->getUrl('/render')) { return null; } @@ -45,6 +46,14 @@ public function dispatch(array $page): ?Response ); } + /** + * Determine if the SSR server is healthy. + */ + public function isHealthy(): bool + { + return Http::get($this->getUrl('/health'))->successful(); + } + /** * Determine if dispatch should proceed even if no bundle is detected. */ @@ -62,10 +71,12 @@ protected function bundleExists(): bool } /** - * Get the SSR URL from the configuration, ensuring it ends with '/render'. + * Get the SSR URL from the configuration, ensuring it ends with '/{$path}'. */ - public function getHttpUrl(): ?string + public function getUrl(string $path): ?string { - return str_replace('/render', '', rtrim(config('inertia.ssr.url', 'http://127.0.0.1:13714'), '/')).'/render'; + $path = Str::start($path, '/'); + + return str_replace($path, '', rtrim(config('inertia.ssr.url', 'http://127.0.0.1:13714'), '/')).$path; } } diff --git a/tests/Commands/CheckSsrTest.php b/tests/Commands/CheckSsrTest.php new file mode 100644 index 00000000..cbf426c5 --- /dev/null +++ b/tests/Commands/CheckSsrTest.php @@ -0,0 +1,44 @@ +mock(HttpGateway::class, fn ($mock) => $mock + ->shouldReceive('isHealthy') + ->andReturnTrue() + ->getMock() + ); + + $this->artisan('inertia:check-ssr') + ->expectsOutput('Inertia SSR server is running.') + ->assertExitCode(0); + } + + public function test_failure_on_unhealthy_ssr_server() + { + $this->mock(HttpGateway::class, fn ($mock) => $mock + ->shouldReceive('isHealthy') + ->andReturnFalse() + ->getMock() + ); + + $this->artisan('inertia:check-ssr') + ->expectsOutput('Inertia SSR server is not running.') + ->assertExitCode(1); + } + + public function test_failure_on_unsupported_gateway() + { + $this->mock(Gateway::class); + + $this->artisan('inertia:check-ssr') + ->expectsOutput('The SSR gateway does not support health checks.') + ->assertExitCode(1); + } +} diff --git a/tests/HttpGatewayTest.php b/tests/HttpGatewayTest.php index a80af616..0d1ccafa 100644 --- a/tests/HttpGatewayTest.php +++ b/tests/HttpGatewayTest.php @@ -9,11 +9,14 @@ class HttpGatewayTest extends TestCase { protected HttpGateway $gateway; + protected string $renderUrl; + protected function setUp(): void { parent::setUp(); $this->gateway = new HttpGateway; + $this->renderUrl = $this->gateway->getUrl('render'); Http::preventStrayRequests(); } @@ -46,7 +49,7 @@ public function test_it_uses_the_configured_http_url_when_the_bundle_file_is_det ]); Http::fake([ - $this->gateway->getHttpUrl() => Http::response(json_encode([ + $this->renderUrl => Http::response(json_encode([ 'head' => ['SSR Test', ''], 'body' => '
SSR Response
', ])), @@ -69,7 +72,7 @@ public function test_it_uses_the_configured_http_url__when_bundle_file_detection ]); Http::fake([ - $this->gateway->getHttpUrl() => Http::response(json_encode([ + $this->renderUrl => Http::response(json_encode([ 'head' => ['SSR Test', ''], 'body' => '
SSR Response
', ])), @@ -91,7 +94,7 @@ public function test_it_returns_null_when_the_http_request_fails() ]); Http::fake([ - $this->gateway->getHttpUrl() => Http::response(null, 500), + $this->renderUrl => Http::response(null, 500), ]); $this->assertNull($this->gateway->dispatch(['page' => self::EXAMPLE_PAGE_OBJECT])); @@ -105,9 +108,21 @@ public function test_it_returns_null_when_invalid_json_is_returned() ]); Http::fake([ - $this->gateway->getHttpUrl() => Http::response('invalid json'), + $this->renderUrl => Http::response('invalid json'), ]); $this->assertNull($this->gateway->dispatch(['page' => self::EXAMPLE_PAGE_OBJECT])); } + + public function test_health_check_the_ssr_server() + { + Http::fake([ + $this->gateway->getUrl('health') => Http::sequence() + ->push(status: 200) + ->push(status: 500), + ]); + + $this->assertTrue($this->gateway->isHealthy()); + $this->assertFalse($this->gateway->isHealthy()); + } }