Add a navigable Obsidian vault as the project's first-class technical documentation. Notes cross-reference with [[wikilinks]]; docs/index.md is the Map of Content. New notes: index.md MOC, entry point architecture.md what the extension owns vs what lives outside data-model.md entity-by-entity schema reference menu-tree.md the arbitrary-depth tree concept order-flow.md state machine + invoice listener + print nostr-layer.md kinds 0/30402/5/1059, signing, t-tags api-reference.md endpoint catalog by audience cms.md Vue 3 + Quasar 2 UMD conventions, q-tree webapp-integration.md multi-restaurant cart pattern + atomicity glossary.md domain terms Existing notes (kept as-is): adr-0001-menu-tree.md the storage choice rationale design-conversation.md trimmed transcript README.md adds a Documentation section pointing at docs/index.md with the headline note list. Each note links to ~3-5 others; the vault forms a connected graph. A project-level memory rule (saved outside the repo) commits us to keeping these docs in sync as the code evolves: any commit that materially changes schema, API, order flow, Nostr surface, CMS conventions, or webapp integration must update the relevant note(s) in the same commit.
89 lines
4.2 KiB
Markdown
89 lines
4.2 KiB
Markdown
# Architecture
|
|
|
|
The restaurant extension is the **operator's CMS** for one or many
|
|
restaurants on a single LNbits account. Customer-facing UIs (kiosks,
|
|
mobile apps, the AIO webapp) live outside the extension and consume it
|
|
over REST + Nostr.
|
|
|
|
## What this extension owns
|
|
|
|
- Restaurant profile rows and per-restaurant Nostr identity.
|
|
- The [[menu-tree]] (`menu_nodes` + `menu_items` + `modifier_groups` +
|
|
`modifiers` + `availability_windows`).
|
|
- The [[order-flow|order pipeline]] (`orders`, `order_items`,
|
|
`print_jobs`).
|
|
- Publishing the [[nostr-layer|menu to Nostr]] as NIP-99 listings.
|
|
- The [[cms|operator console]] under `/restaurant/...` (Jinja +
|
|
Quasar 2 UMD).
|
|
- A REST [[api-reference|API]] under `/restaurant/api/v1/...`.
|
|
|
|
## What lives outside the extension
|
|
|
|
| Concern | Where |
|
|
|---|---|
|
|
| Customer kiosk / mobile / web | `~/dev/webapp` ([[webapp-integration]]) |
|
|
| Multi-restaurant aggregation (festivals, food courts, collective spaces) | NIP-51 lists, curated externally |
|
|
| Lightning wallet, payment routing, user auth | LNbits core |
|
|
| Nostr relay connection | `nostrclient` extension |
|
|
| Thermal printer | `printer-pi` (subscribes to a webhook or Nostr event) |
|
|
|
|
## High-level topology
|
|
|
|
```
|
|
LNbits instance
|
|
┌────────────────────────────────┐
|
|
│ Restaurant ext │
|
|
│ ├── REST /restaurant/api/v1│
|
|
│ ├── CMS /restaurant/... │
|
|
│ ├── Nostr publisher │──────┐
|
|
│ └── Invoice listener │ │
|
|
│ (settle, decrement, │ │
|
|
│ queue print) │ │
|
|
└─────────┬──────────────────────┘ │
|
|
│ ▼
|
|
│ ┌─────────────────────────┐
|
|
│ │ nostrclient ext │──→ relays
|
|
│ └─────────────────────────┘
|
|
▼
|
|
┌──────────────┐ ┌─────────────────────┐
|
|
│ printer-pi │ ◀──────│ webapp / AIO │
|
|
│ (subscribes) │ │ (customer, multi- │
|
|
└──────────────┘ │ restaurant cart) │
|
|
└─────────────────────┘
|
|
```
|
|
|
|
## Lifecycle
|
|
|
|
`__init__.py` registers three permanent tasks on extension start
|
|
(`create_permanent_unique_task`):
|
|
|
|
1. **Invoice listener** — `tasks.wait_for_paid_invoices` consumes
|
|
LNbits' global payment queue, filters on
|
|
`payment.extra.tag == "restaurant"`, and dispatches to
|
|
[[order-flow|services.mark_order_paid]].
|
|
2. **NostrClient bootstrap** — `nostr.nostr_client.NostrClient`
|
|
connects to the `nostrclient` extension's internal WebSocket
|
|
after a 10s grace.
|
|
3. **Nostr sync** — `nostr_sync.wait_for_nostr_events` subscribes
|
|
to the relevant filters once the client is up. NIP-17 unwrap is
|
|
stubbed.
|
|
|
|
`restaurant_stop` cancels the three tasks and closes the WebSocket.
|
|
|
|
If `nostrclient` isn't enabled, the publisher / sync no-op gracefully
|
|
and the extension still works — it just operates as REST-only without
|
|
the [[nostr-layer]].
|
|
|
|
## Boundaries we keep
|
|
|
|
The extension only ever knows about **its own restaurant's** data.
|
|
There is no global "festival" or "marketplace" entity stored anywhere
|
|
in `restaurant.*` tables. Cross-restaurant grouping is the customer
|
|
webapp's concern; see [[webapp-integration]].
|
|
|
|
## See also
|
|
|
|
- [[data-model]] — the entity catalog
|
|
- [[order-flow]] — payment lands → print job
|
|
- [[nostr-layer]] — what propagates outward
|
|
- [[adr-0001-menu-tree]] — why the menu storage choice
|