public.ts: convert error responses to RFC 7807, matching admin.ts

Follow-up from #25's review feedback — that PR converted admin.ts + the global
error handler to application/problem+json (sendProblem/ProblemDetails), left
public.ts (the tablet-facing /api/* routes) on the old ad hoc { error } shape to
stay scoped to what #25 was actually fixing. Same conversion here, no functional
changes — every reply.code(N).send({ error }) becomes sendProblem(req, reply, N,
title, detail). Client already degrades gracefully either way (errText() in
Admin.tsx checks detail/title first, falls back to .error), so this was purely
consistency cleanup, not urgent.

typecheck+build clean.
This commit is contained in:
iris 2026-07-30 01:16:00 +02:00
commit bec4a0080c

View file

@ -7,6 +7,7 @@ import type {
CreateTransactionResponse,
Drink,
} from '@wutzcalc/shared';
import { sendProblem } from '../problem-details.js';
export function registerPublicRoutes(app: FastifyInstance, db: DB) {
app.get('/api/bars', async () => {
@ -16,12 +17,12 @@ export function registerPublicRoutes(app: FastifyInstance, db: DB) {
app.get<{ Querystring: { bar: string } }>('/api/config', async (req, reply) => {
const barId = Number(req.query.bar);
if (!Number.isInteger(barId)) return reply.code(400).send({ error: 'bar required' });
if (!Number.isInteger(barId)) return sendProblem(req, reply, 400, 'Bad Request', 'bar required');
const bar = db
.prepare('SELECT id, name, pfand_cents FROM bars WHERE id = ?')
.get(barId) as Bar | undefined;
if (!bar) return reply.code(404).send({ error: 'bar not found' });
if (!bar) return sendProblem(req, reply, 404, 'Not Found', 'bar not found');
const drinks = db
.prepare(
@ -49,29 +50,29 @@ export function registerPublicRoutes(app: FastifyInstance, db: DB) {
const { client_uuid, bar_id, crew, items } = body;
if (!client_uuid || typeof client_uuid !== 'string') {
return reply.code(400).send({ error: 'client_uuid required' });
return sendProblem(req, reply, 400, 'Bad Request', 'client_uuid required');
}
if (!Number.isInteger(bar_id)) {
return reply.code(400).send({ error: 'bar_id required' });
return sendProblem(req, reply, 400, 'Bad Request', 'bar_id required');
}
if (!Array.isArray(items)) {
return reply.code(400).send({ error: 'items required' });
return sendProblem(req, reply, 400, 'Bad Request', 'items required');
}
if (items.length > MAX_ITEMS_PER_TRANSACTION) {
return reply.code(400).send({ error: 'too many items' });
return sendProblem(req, reply, 400, 'Bad Request', 'too many items');
}
const rawPfandReturns = body.pfand_returns ?? 0;
if (!Number.isInteger(rawPfandReturns) || rawPfandReturns < 0) {
return reply.code(400).send({ error: 'pfand_returns must be a non-negative integer' });
return sendProblem(req, reply, 400, 'Bad Request', 'pfand_returns must be a non-negative integer');
}
if (rawPfandReturns > MAX_PFAND_RETURNS) {
return reply.code(400).send({ error: 'pfand_returns too large' });
return sendProblem(req, reply, 400, 'Bad Request', 'pfand_returns too large');
}
const pfand_returns = rawPfandReturns;
if (items.length === 0 && pfand_returns === 0) {
return reply.code(400).send({ error: 'empty transaction' });
return sendProblem(req, reply, 400, 'Bad Request', 'empty transaction');
}
const findByUuid = () =>
@ -88,7 +89,7 @@ export function registerPublicRoutes(app: FastifyInstance, db: DB) {
const bar = db
.prepare('SELECT id, pfand_cents FROM bars WHERE id = ?')
.get(bar_id) as { id: number; pfand_cents: number } | undefined;
if (!bar) return reply.code(404).send({ error: 'bar not found' });
if (!bar) return sendProblem(req, reply, 404, 'Not Found', 'bar not found');
const drinkStmt = db.prepare(
`SELECT d.id, d.price_cents
@ -112,12 +113,14 @@ export function registerPublicRoutes(app: FastifyInstance, db: DB) {
item.qty <= 0 ||
item.qty > MAX_QTY_PER_LINE
) {
return reply.code(400).send({ error: 'invalid item' });
return sendProblem(req, reply, 400, 'Bad Request', 'invalid item');
}
const drink = drinkStmt.get(bar_id, item.drink_id) as
| { id: number; price_cents: number }
| undefined;
if (!drink) return reply.code(400).send({ error: `drink ${item.drink_id} not at bar` });
if (!drink) {
return sendProblem(req, reply, 400, 'Bad Request', `drink ${item.drink_id} not at bar`);
}
const lineUnit = drink.price_cents + bar.pfand_cents;
total += lineUnit * item.qty;