diff --git a/README.md b/README.md index 77260c3..ca51ae2 100644 --- a/README.md +++ b/README.md @@ -170,3 +170,7 @@ the service user owns the database directory automatically. - `WUTZ_SERVER_PORT` (default `3000`, **client dev only** — not read by the server) — the port `dev:client`'s Vite proxy targets; set it to match `dev:server`'s `PORT` when running the server on something other than the default +- `STATS_PUBLIC` (default off, set to `1` to enable) — makes `/stats` viewable without the admin + login normally required (e.g. a screen permanently mounted at a festival infopoint). Doesn't + affect `/admin` itself, or the "Statistik zurücksetzen" reset button, which stays gated on a real + admin session either way. diff --git a/server/src/index.ts b/server/src/index.ts index 5b22df9..1a302bc 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -90,31 +90,43 @@ app.get('/healthz', async () => ({ ok: true })); // Static client (built by Vite into client/dist) const clientDist = join(__dirname, '..', '..', 'client', 'dist'); -if (existsSync(clientDist)) { +const hasClientDist = existsSync(clientDist); +if (hasClientDist) { await app.register(fastifyStatic, { root: clientDist, prefix: '/', wildcard: false, }); - app.setNotFoundHandler((req, reply) => { - if (req.url.startsWith('/api') || req.url.startsWith('/admin/api')) { - sendProblem(req, reply, 404, 'Not Found', 'not found'); - return; - } - if (req.url.startsWith('/admin')) { - reply.sendFile('admin.html'); - return; - } - if (req.url.startsWith('/stats')) { - reply.sendFile('stats.html'); - return; - } - reply.sendFile('index.html'); - }); } else { app.log.warn(`client dist not found at ${clientDist} — run \`pnpm --filter client build\``); } +// Registered unconditionally — previously nested inside the `hasClientDist` +// branch above, so a server-only deploy (or any run before the client is +// built) fell through to Fastify's default 404 shape on /api and +// /admin/api instead of RFC 7807, quietly opting API routes out of the +// one-error-shape guarantee the RFC 7807 conversion was for. The +// SPA-fallback file sends below still need the client build to exist. +app.setNotFoundHandler((req, reply) => { + if (req.url.startsWith('/api') || req.url.startsWith('/admin/api')) { + sendProblem(req, reply, 404, 'Not Found', 'not found'); + return; + } + if (!hasClientDist) { + sendProblem(req, reply, 404, 'Not Found', 'not found'); + return; + } + if (req.url.startsWith('/admin')) { + reply.sendFile('admin.html'); + return; + } + if (req.url.startsWith('/stats')) { + reply.sendFile('stats.html'); + return; + } + reply.sendFile('index.html'); +}); + try { await app.listen({ port: PORT, host: HOST }); app.log.info(`listening on http://${HOST}:${PORT}`);