From 1c8e2456033803d2c257f6b2d65e0f66564390a7 Mon Sep 17 00:00:00 2001 From: bonamin Date: Sun, 27 Sep 2026 17:52:55 +0300 Subject: [PATCH] docs: add pack README with on-site rules, service map and commands Documents the request path through proxy -> waiter nginx -> backend, the _run_migrations requirement, real-time event handling, offline expectations and the duplicated nginx config in install.sh. Co-Authored-By: Claude Opus 5.5 --- CLAUDE.md | 4 +++ docs/README.md | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 CLAUDE.md create mode 100644 docs/README.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..e41f107 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,4 @@ +# client-services + +Read [docs/README.md](docs/README.md) (pack rules) and the global [../docs/README.md](../docs/README.md) (Working Rules) before making changes. +This is its own git repo. Commit here first, then bump the pointer in the parent `xenia-pos` repo. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..86f7e9e --- /dev/null +++ b/docs/README.md @@ -0,0 +1,98 @@ +# client-services — On-site Pack + +Everything that runs **inside the restaurant**, on the local server box. +This folder is its **own git repo**, nested inside the `xenia-pos` parent repo. + +> **Read the global rules first:** [../../docs/README.md](../../docs/README.md). This file adds only the rules specific to this pack. +> The feature catalog, the cloud contract, known issues and site records all live in the parent's `docs/`. + +--- + +## Services + +| Service | Stack | Dev port | Prod exposure | Image | +|---|---|---|---|---| +| `local_backend` | FastAPI + SQLAlchemy + SQLite (`/app/data/pos.db`) | 8000 | only through proxy / inner nginx | `pos-backend` | +| `waiter_pwa` | React + Vite + vite-plugin-pwa, axios, zustand, react-query, Dexie (IndexedDB) | 5173 | `https://` (443) | `pos-waiter` | +| `manager_dashboard` | React + Vite | 5174 | `https://:4443` | `pos-manager` | +| `proxy` | nginx:alpine, TLS termination | — | 80 → 443 redirect, 443, 4443 | stock | + +### Request path in production +``` +phone ──https──▶ proxy:443 ──http──▶ waiter_pwa nginx:80 ──┬─ / → static SPA + ├─ /api/ → backend:8000 + ├─ /api/ws/ → backend:8000 (WebSocket upgrade) + └─ /static/ → backend:8000/static/ +``` +Every hop must forward WebSocket upgrades: `proxy_http_version 1.1`, plus `Upgrade` and `Connection` headers, plus a long `proxy_read_timeout`. +The frontends call the API **same-origin, with relative paths** (`baseURL: ''`). Don't hardcode hosts. + +## Key files + +| File | Why it matters | +|---|---| +| `local_backend/main.py` | App setup, router registration, CORS, **`_run_migrations()`** | +| `local_backend/services/cloud_sync.py` | Every call to the cloud (see the parent's `docs/reference/cloud-contract.md`) | +| `local_backend/roles.py`, `routers/deps.py` | Roles, permission checks, auth dependencies | +| `local_backend/routers/ws.py` | Real-time event stream (seq + cursor replay) | +| `waiter_pwa/src/api/client.js` | axios instance: auth header, 401 → logout, network error → offline | +| `waiter_pwa/src/context/SSEContext.jsx` | Real-time lifecycle, event → store/cache updates, visibility refresh | +| `waiter_pwa/src/db/posdb`, `src/services/offlineOrders` | Offline cache and queued orders | +| `nginx-proxy/nginx.conf` | Proxy config used in dev / by hand | +| `install.sh` | Site installer. **Writes its own copy of the proxy config.** | +| `docker-compose.yml` / `docker-compose.dev.yml` | Prod (pull images) / dev override (build + expose ports) | + +--- + +## Pack-specific rules + +**CS-1. Schema changes go through `_run_migrations()`.** +A new table: `create_all` handles it. A new column on an existing table: append an `ALTER TABLE ADD COLUMN ...` to the `migrations` list in `local_backend/main.py`, with a SQLite-safe default for `NOT NULL`. +Add the column to the SQLAlchemy model **and** the Pydantic schema in the same commit. Never drop or rename columns. + +**CS-2. New real-time events need both ends.** +If the backend emits a new event type, handle it in `waiter_pwa/src/context/SSEContext.jsx` (and in the manager, if relevant), or state explicitly that it's ignored. +Events carry IDs; clients re-fetch the full object (`/api/orders/{id}`) instead of trusting partial payloads. + +**CS-3. The waiter app must survive bad networks.** +Waiters walk in and out of WiFi range and phones sleep. Any new waiter flow must: +- work from the IndexedDB cache when offline, or clearly block with an offline state +- never lose an order that was entered (queue it) +- tolerate duplicate or replayed events + +**CS-4. Money and prices are snapshotted.** +Prices, costs and discounts are copied onto order items and logs when the action happens (snapshot pattern, `PriceEventLog`). Reports read the snapshots, never the live catalogue. + +**CS-5. The proxy config lives in two places.** +Edit `nginx-proxy/nginx.conf` **and** the heredoc in `install.sh` together (global Rule 9). + +**CS-6. Permissions are checked on the backend.** +Hiding a button in the UI is not access control. Every new endpoint declares its role or `perm_*` requirement through `routers/deps.py`. + +**CS-7. Greek-market realities.** +Staff UIs are used by Greek staff and must render Greek correctly, including on thermal printers (codepage). Money uses €. + +## Common commands + +```powershell +# Dev: backend with reload +cd local_backend; uvicorn main:app --reload --port 8000 + +# Dev: frontends (Vite proxies /api and /api/ws to :8000) +cd waiter_pwa; npm run dev # :5173 +cd manager_dashboard; npm run dev # :5174 +cd waiter_pwa; npm run lint + +# Build release images (VERSION comes from .env). See the parent's DEPLOYMENT_GUIDE.md. +docker compose -f docker-compose.yml -f docker-compose.dev.yml build +docker push registry.bonamin.gr/pos-backend: # + pos-waiter, pos-manager +``` + +## Testing +There is no automated test suite yet. Before committing: +- the backend starts cleanly against an **existing** `pos.db` (this catches missing migrations) +- `npm run build` passes for any frontend you touched +- the flow was exercised by hand (the parent's `PLANS AND STRATEGIES/TESTING_CHECKLIST.md`) +- printing was checked against real hardware or `esc-pos-emulator` + +Say in the commit or hand-off what was and wasn't verified.