import type { FastifyInstance } from "fastify"; import { desc, ledgerEvents, type Db } from "@parking/db"; import { requireRole } from "../auth.js"; import type { EventLog } from "../event-log.js"; // Read access to the append-only signed event log. NO write/update/delete routes // exist by design — events are only ever appended internally (entry flow, device // pushes). Corrections are new appended events, never edits. See // wiki/concepts/append-only-event-chain.md. export async function eventRoutes( app: FastifyInstance, db: Db, eventLog: EventLog, ): Promise { // Any authenticated role may read the log (it's the audit trail). const guard = requireRole("admin", "operator", "cashier", "readonly"); // Recent events, newest first. `limit` caps the page (default 100, max 1000). app.get<{ Querystring: { limit?: string } }>( "/api/events", { preHandler: guard }, async (req) => { const limit = Math.min(Math.max(Number(req.query.limit) || 100, 1), 1000); const rows = db.select().from(ledgerEvents).orderBy(desc(ledgerEvents.index)).limit(limit).all(); return { events: rows }; }, ); // Integrity self-check: walk the chain and verify hashes + signatures. Admin- // only (it's an audit action). Returns the first break, or ok. This is what a // reconciliation job / "is the log intact?" check calls. app.get( "/api/events/verify", { preHandler: requireRole("admin") }, async () => eventLog.verifyChain(), ); }