server: RFC7807 404s without a client build; document STATS_PUBLIC
Two of the three items from #54 (item 1, the dead is_return column, is a drop-or-use product call left for mara — see issue comment): 1. The RFC7807 404 handler for /api and /admin/api was only registered inside the 'if (existsSync(clientDist))' branch, so a server-only deploy (or any run before 'pnpm --filter client build') fell through to Fastify's default 404 shape instead — quietly opting API routes out of the one-error-shape guarantee the RFC7807 conversion was for. Split registration from the static-file serving: the not-found handler (and its API-vs-SPA-fallback branching) is now unconditional, only the SPA html sendFile calls stay gated on hasClientDist. 2. STATS_PUBLIC added to the README's env-var list (was only in deploy/wutzcalc.env.example) — an operator wanting the infopoint- screen setup wouldn't find the switch, and an operator auditing "what can expose data here" from the README wouldn't see it exists. Verified live: renamed client/dist away and confirmed /api, /admin/api, and a bare unmatched route all return RFC7807 problem+json (no crash); restored it and confirmed the SPA fallback (admin.html/stats.html/ index.html) still serves correctly. tsc --noEmit and server build both clean.
This commit is contained in:
parent
7a58a4a1bb
commit
5f0a7bf6cc
2 changed files with 32 additions and 16 deletions
|
|
@ -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}`);
|
||||
|
|
|
|||
Loading…
Reference in a new issue