SD-0001: Solution Design — MVP sign up and create a single storefront (Feature #1) #2
@@ -683,7 +683,128 @@ adding fields is free, renaming/removing is a spec change (this document,
|
|||||||
|
|
||||||
### 6.5 Per–Product-Use-Case design
|
### 6.5 Per–Product-Use-Case design
|
||||||
|
|
||||||
<!-- §6.5 pending -->
|
#### Entry routing (the shared spine: PUC-1, PUC-5, PUC-6)
|
||||||
|
|
||||||
|
One client-side rule, fed by one server answer, decides where any arrival
|
||||||
|
lands. The server's answer is `GET /api/auth/me` (or the `verify` response,
|
||||||
|
which carries the same shape):
|
||||||
|
|
||||||
|
| Session | Storefront | Lands on |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| none | — | Landing (PUC-1) |
|
||||||
|
| valid | `null` | Create storefront (PUC-5) |
|
||||||
|
| valid | present | Admin shell (PUC-6) |
|
||||||
|
|
||||||
|
The rule is exhaustive — no state lands nowhere (BUC-4's "never stranded";
|
||||||
|
OHM: Duty of Care). The SPA router applies it on load and after `verify`.
|
||||||
|
|
||||||
|
#### PUC-2 / PUC-3 — Sign up & log in (one flow; realizes BUC-1, BUC-2)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
actor V as Visitor
|
||||||
|
participant SPA as Admin SPA
|
||||||
|
participant BFF as REST BFF
|
||||||
|
participant ACC as accounts
|
||||||
|
participant M as mailer (port)
|
||||||
|
participant DB as SQLite
|
||||||
|
|
||||||
|
V->>SPA: enter email, "Send code"
|
||||||
|
SPA->>BFF: POST /api/auth/request-code {email}
|
||||||
|
BFF->>ACC: request_code(email)
|
||||||
|
ACC->>DB: upsert auth_code (hash, expiry; cooldown check)
|
||||||
|
ACC->>M: send(email, "Your ecomm code: …")
|
||||||
|
ACC-->>BFF: ok (uniform, new-or-known)
|
||||||
|
BFF-->>SPA: 204 → code-entry step
|
||||||
|
V->>SPA: enter code, "Continue"
|
||||||
|
SPA->>BFF: POST /api/auth/verify {email, code}
|
||||||
|
BFF->>ACC: verify(email, code)
|
||||||
|
ACC->>DB: check hash, expiry, attempts; consume (INV-3)
|
||||||
|
ACC->>DB: get-or-create account by normalized email (INV-2)
|
||||||
|
ACC-->>BFF: account, created?
|
||||||
|
BFF->>SF: storefront_for(account) — via domains, for the routing answer
|
||||||
|
BFF-->>SPA: 200 {account, storefront|null, created} + session cookie
|
||||||
|
SPA-->>V: honest welcome → entry routing
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Implementation:** `request_code` is uniform for new vs known emails (no
|
||||||
|
enumeration, §6.6) and enforces the 60s cooldown (INV-3 → PUC-2c).
|
||||||
|
`verify` consumes the code transactionally — increment attempts on
|
||||||
|
mismatch (PUC-2a), refuse expired (PUC-2b), invalidate at 5 (INV-3) — and
|
||||||
|
only then gets-or-creates the account (INV-2). Account creation *is* email
|
||||||
|
verification: the address provably received the code. Honors INV-1 (works
|
||||||
|
on an empty DB: the first verify creates account #1), INV-6, INV-9.
|
||||||
|
|
||||||
|
#### PUC-4 / PUC-7 — Create the storefront (realizes BUC-3, BUC-3a)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
actor Mer as Merchant
|
||||||
|
participant SPA as Admin SPA
|
||||||
|
participant BFF as REST BFF
|
||||||
|
participant SF as storefronts
|
||||||
|
participant DB as SQLite
|
||||||
|
|
||||||
|
Mer->>SPA: name (optional), "Create storefront"
|
||||||
|
SPA->>BFF: POST /api/storefronts {name?}
|
||||||
|
BFF->>SF: create_storefront(account, name?)
|
||||||
|
SF->>DB: any membership for account? (INV-4 guard)
|
||||||
|
alt already owns one
|
||||||
|
SF-->>BFF: refuse
|
||||||
|
BFF-->>SPA: 409 already_owns_storefront (PUC-7, honest copy)
|
||||||
|
else none yet
|
||||||
|
SF->>DB: insert storefront (name or generated default) + membership(owner)
|
||||||
|
SF-->>BFF: storefront
|
||||||
|
BFF-->>SPA: 201 {id, name}
|
||||||
|
SPA-->>Mer: → admin shell (PUC-6)
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Implementation:** guard + insert run in one transaction (two concurrent
|
||||||
|
creates cannot both pass the guard). Default naming per §6.3. No payment,
|
||||||
|
plan, or extra field exists to ask for (corpus 14.01.0015/0016). Honors
|
||||||
|
INV-4, INV-5, INV-6.
|
||||||
|
|
||||||
|
#### PUC-8 — Admin shell (realizes BUC-4)
|
||||||
|
|
||||||
|
No new backend surface: the shell renders from `GET /api/auth/me`
|
||||||
|
(storefront name + account email) and is otherwise static honest-empty copy
|
||||||
|
(§5.4). Honors INV-9 — it claims nothing that doesn't exist.
|
||||||
|
|
||||||
|
#### PUC-9 — Sign out
|
||||||
|
|
||||||
|
`POST /api/auth/logout` clears the session cookie; SPA returns to Landing.
|
||||||
|
Idempotent; no server state to destroy (sessions are cookie-borne, §6.2).
|
||||||
|
|
||||||
|
#### PUC-10 / PUC-11 — Bootstrap (realizes BUC-5, BUC-5a)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Op as Operator / Developer
|
||||||
|
participant App as ecomm process
|
||||||
|
participant DB as SQLite (empty file or absent)
|
||||||
|
actor Mer as First merchant
|
||||||
|
|
||||||
|
Op->>App: start (dev script · flotilla deploy)
|
||||||
|
App->>DB: open/create; apply pending migrations in order (INV-7)
|
||||||
|
App-->>Op: /healthz 200 (migrations current)
|
||||||
|
Mer->>App: sign up → create storefront → admin (the ordinary PUC-2/4/6 flows)
|
||||||
|
Note over App,Mer: first rows created through the product alone (INV-1)
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Implementation:** identical in all three environments; only configuration
|
||||||
|
differs (INV-8): **localhost** — `scripts/dev.sh` from a clean checkout
|
||||||
|
(venv + deps, backend on :8000, Vite on :5173 proxying `/api`); mailer is
|
||||||
|
`LogMailer`, so the one-time code appears in the backend log (the "local
|
||||||
|
dev channel" of PUC-10 — auth itself is not stubbed; the same issue/verify
|
||||||
|
path runs). **PPE/Prod** — provisioned and deployed through
|
||||||
|
flotilla/launch-app (operator gesture, handbook §8.5); mailer is
|
||||||
|
`SmtpMailer` with relay coordinates and secrets resolved from Secret
|
||||||
|
Manager via deployment config; migrations apply at startup under
|
||||||
|
flotilla's fail-stop deploy, gated by `/healthz`. The bring-up runbook
|
||||||
|
(per-environment: prerequisites, the one gesture, how to watch `/healthz`)
|
||||||
|
ships in-repo as `docs/BOOTSTRAP.md` — the documented gesture BUC-5
|
||||||
|
demands.
|
||||||
|
|
||||||
### 6.6 Non-functional requirements & cross-cutting concerns
|
### 6.6 Non-functional requirements & cross-cutting concerns
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user