4e2e4feedb
A shift becomes a SITE-WIDE accountability period — at most one open at a
time — so every taking is unambiguously attributed to one operator. Login
stays decoupled from shifts (an operator can log in off-shift to review).
Backend:
- ShiftService.currentOpenShift()/requireOpenShift(); open() refuses when ANY
shift is open and throws ShiftAlreadyOpenError{heldBy} (self vs. other).
- requireShift preHandler gates /api/pay, /api/exit, /api/voucher,
/api/barrier/reopen → 409 {code:"no_shift"}; read-only lookups stay open.
- GET /api/shift/current returns site-wide {open:{startedAt,operator},isMine}.
- GET /api/events?since=<iso> for per-shift log scoping (db: re-export gte).
Frontend:
- Header shift button: open / close-mine / disabled-when-another-holds-it.
- Pay/exit modal gate banner (one-click open; "held by X" when another's);
pay/exit/voucher disabled until this operator's shift is open.
- Active-Sessions barrier re-open gated the same way.
- Live feed scoped to the open shift's window; shared useShift() Query
invalidated over the WS on shift_open/shift_z_report/cash_movement.
- sq/en strings for the control + gate.
Wiki: shift.md (site-wide single-open + gate; superseded per-operator note),
booth-console.md (header control + gate), log entry.
Verified: site-wide invariant + heldBy + handover + chain integrity on a
fresh migrated DB (11/11); db/server/web build clean.
23 lines
854 B
TypeScript
23 lines
854 B
TypeScript
import Database from "better-sqlite3";
|
|
import { drizzle } from "drizzle-orm/better-sqlite3";
|
|
import * as schema from "./schema.js";
|
|
|
|
export * from "./schema.js";
|
|
// Re-export the query helpers consumers need, so they don't depend on
|
|
// drizzle-orm directly (it's an implementation detail of this package).
|
|
export { eq, and, desc, gte, sql } from "drizzle-orm";
|
|
|
|
/**
|
|
* Open the local SQLite database in WAL mode. WAL allows many concurrent readers
|
|
* plus one writer — a parking workload never approaches SQLite's single-writer
|
|
* limit. See wiki/entities/sqlite.md.
|
|
*/
|
|
export function createDb(url = process.env.DATABASE_URL ?? "./parking.sqlite") {
|
|
const sqlite = new Database(url);
|
|
sqlite.pragma("journal_mode = WAL");
|
|
sqlite.pragma("foreign_keys = ON");
|
|
return drizzle(sqlite, { schema });
|
|
}
|
|
|
|
export type Db = ReturnType<typeof createDb>;
|