Add check-in audit logging + in-app history view
Every successful check-in/undo writes a row to the "2026 Ticket Audit Logs" NocoDB table (timestamp, people, code, action, name, remaining-after). Non-fatal: audit failures never block a gate check-in. New GET /api/audit endpoint (global or per-code). Admin panel gains a global "Recent check-ins" panel and per-ticket history. Audit table is optional via NOCODB_AUDIT_TABLE_ID. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
3397e3e3ec
commit
b36d63a6a4
11 changed files with 342 additions and 4 deletions
|
|
@ -66,11 +66,20 @@ export class FakeNocoDB {
|
|||
}
|
||||
|
||||
export function fakeContext(db: FakeNocoDB): AppContext {
|
||||
const auditEntries: any[] = [];
|
||||
return {
|
||||
config: {} as any,
|
||||
nocodb: db as any,
|
||||
mailer: { isBlockedRecipient: () => false, sendTicket: async () => {} } as any,
|
||||
queue: new RedeemQueue(2000),
|
||||
audit: {
|
||||
enabled: true,
|
||||
entries: auditEntries,
|
||||
log: async (e: any) => {
|
||||
auditEntries.push(e);
|
||||
},
|
||||
recent: async () => auditEntries,
|
||||
} as any,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,26 @@ describe("redeem", () => {
|
|||
if (r.ok) expect(r.ticket.redeemed).toBe(0);
|
||||
});
|
||||
|
||||
it("writes an audit entry on each successful check-in and undo", async () => {
|
||||
const db = new FakeNocoDB();
|
||||
await seedTicket(db, { code: "BC26-AUDT-0001", ages: { "Ages 26-45": 4 } });
|
||||
const ctx = fakeContext(db);
|
||||
await redeem(ctx, "BC26-AUDT-0001", 2);
|
||||
await redeem(ctx, "BC26-AUDT-0001", -1);
|
||||
const log = (ctx.audit as any).entries;
|
||||
expect(log).toHaveLength(2);
|
||||
expect(log[0]).toMatchObject({ code: "BC26-AUDT-0001", people: 2, action: "check-in", remainingAfter: 2 });
|
||||
expect(log[1]).toMatchObject({ people: -1, action: "undo", remainingAfter: 3 });
|
||||
});
|
||||
|
||||
it("does not audit a no-op (undo when nothing redeemed)", async () => {
|
||||
const db = new FakeNocoDB();
|
||||
await seedTicket(db, { code: "BC26-AUDT-0002", ages: { "Ages 26-45": 3 }, redeemed: 0 });
|
||||
const ctx = fakeContext(db);
|
||||
await redeem(ctx, "BC26-AUDT-0002", -2); // clamps to 0, delta 0
|
||||
expect((ctx.audit as any).entries).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("returns not_found for unknown codes", async () => {
|
||||
const ctx = fakeContext(new FakeNocoDB());
|
||||
const r = await redeem(ctx, "BC26-ZZZZ-9999", 1);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue