Files
brammie15 3caa6d6f0d
CI / build-and-test (push) Canceled after 0s
fix: auth + double sending etc
2026-10-30 00:41:58 +01:00

3.2 KiB

Architecture

KoolTap is a modular monolith: one NestJS API, one Next.js dashboard, one PostgreSQL database, and a shared package that holds the contract between them and the bar terminals.

The server holds the venue

The venue's open tabs, stock and catalog live on the server. A terminal records what happens on it as events (tab_opened, line_added, settled, stock_set, product_updated, …) in an outbox, and every sync:

  1. sends the outbox; the server stores each event once (by its id) and applies it to the venue,
  2. gets back the venue's complete state: the open tabs, the stock, the catalog (only when it changed) and the settlements since its cursor,
  3. replaces its own state with that and replays the events it still has to send on top.

Terminals apply the same rules the server does, so a terminal that is offline keeps working and lands in the same state once it syncs. The dashboard changes the same state directly (settling or deleting a tab, stock movements, products); terminals see that on their next sync. See terminal-protocol.md.

API (apps/api)

Module What it does
ledger/ The rules, pure and unit tested: tab-state (tabs, lines, drinks, settling), stock-rules, product-fields (per-field last-write-wins), and venue-ledger, which loads a venue under a row lock, applies changes and writes back only what changed
terminal/ Pairing, sync and picture uploads for terminals; device token auth; the minimum app version
auth/ Dashboard sign-in with httpOnly cookies, refresh token rotation, GET /auth/me
tabs/, customers/, stock/, reminders/, venues/ The dashboard API, one module per page
admin/ The superadmin page: venues, terminals, users, resets, consistency checks, purges; daily housekeeping
realtime/ Socket.IO: tells dashboards which data changed (topics only, never data)
email/ Templates in templates/emails and Resend
images/ Product pictures, processed with sharp and stored on disk by content hash

Every request body and query is validated with a zod schema from @kooltap/shared; responses are typed with the shared interfaces, so the dashboard and the API can't drift apart.

Data

  • TabUnit is one drink ever rung up. It belongs to an open Tab until a Settlement takes it; a drink taken off again keeps its removedAt and moves with the settlement. Lines are the units sharing a lineId. Sales reports are plain SQL over this table.
  • Product holds the catalog and the current stock; StockMovement is the dashboard's deliveries, breakage and counts.
  • TabEvent is what terminals reported, as they sent it: the record of which events were applied.
  • Customer is a regular: the next sync opens a tab for a customer without one, and their email is used for reminders. Tabs match customers by name.
  • Everything belongs to a Venue and is deleted with it.

Dashboard (apps/dashboard)

Next.js App Router, rendered on the client. TanStack Query for data, refetched when the realtime socket says a topic changed (polling only while the socket is down). Translations in src/locales/{en,nl}.json.