diff --git a/packages/backend/src/main.rs b/packages/backend/src/main.rs index e738eea..3fd0071 100644 --- a/packages/backend/src/main.rs +++ b/packages/backend/src/main.rs @@ -51,8 +51,11 @@ async fn main() { .merge(status_routes); let index = format!("{}{}", config::FRONTEND_PATH.to_string(), "/index.html"); + // SPA fallback: serve `index.html` for client side routes. + // `fallback` instead of `not_found_service`, as the latter forces a `404` status code, + // while the document itself is served successfully. A missing note is signalled by the API. let serve_dir = - ServeDir::new(config::FRONTEND_PATH.to_string()).not_found_service(ServeFile::new(index)); + ServeDir::new(config::FRONTEND_PATH.to_string()).fallback(ServeFile::new(index)); let app = Router::new() .nest("/api", api_routes) .fallback_service(serve_dir) diff --git a/test/web/spa-fallback.spec.ts b/test/web/spa-fallback.spec.ts new file mode 100644 index 0000000..012cbd1 --- /dev/null +++ b/test/web/spa-fallback.spec.ts @@ -0,0 +1,19 @@ +import { expect, test } from '@playwright/test' + +// The SPA fallback serves index.html for client side routes. The document is +// served successfully, so it must not be reported as 404. +// A missing note is signalled by the API on /api/notes/ instead. +// https://github.com/cupcakearmy/cryptgeon/issues/217 +test.describe('@web', () => { + for (const path of ['/', '/about', '/note/does-not-exist']) { + test(`serves ${path} with status 200`, async ({ request }) => { + const response = await request.get(path) + expect(response.status()).toBe(200) + }) + } + + test('api still reports a missing note as 404', async ({ request }) => { + const response = await request.get('/api/notes/does-not-exist') + expect(response.status()).toBe(404) + }) +})