Implemented phase 6 for the POC prototype in the Website service.
This commit is contained in:
1 parent
256d22df40
commit
f2f445827b
40 files changed
+1906
-37
No files matched your search
@@ -52,9 +52,9 @@ LogRequestsMiddleware
|
||||
→ FileMiddleware (serves Resources/Static)
|
||||
RootController (GET / → landing page, language negotiated; GET /<lang> → its other editions)
|
||||
CatalogueController (GET /search, GET /listing/{slug}, GET /shop/{slug}, GET /grading-guide → rendered per request)
|
||||
SessionController (GET /sign-in, POST /sign-in, POST /sign-out)
|
||||
SessionController (GET /sign-in, POST /sign-in, POST /sign-out, GET/POST /recover, GET /recover/{token})
|
||||
AccountController (GET/POST /welcome, GET /account, POST /account/…, GET/POST /sell/open-a-shop)
|
||||
CollectingController (GET/POST /wants…, GET /series, GET /series/{slug}, GET /compare)
|
||||
CollectingController (GET/POST /wants…, GET/POST /collection, GET /series…, GET /compare, GET /events)
|
||||
PasskeyController (POST /sign-in/passkey/options, POST /sign-in/passkey/finish → JSON)
|
||||
ListingFormController (GET /sell/new, POST /sell/new → multipart, for a shop's owner)
|
||||
TradeController (offer sheet, messages, sale records, seller dashboard → signed-in accounts only)
|
||||
@@ -91,7 +91,8 @@ How it holds together:
|
||||
- **Per-viewer pages**: the controllers read the viewer from the request and send those pages `Cache-Control: private, no-cache`. The landing page stays cached for signed-out visitors and is rendered per request for signed-in ones.
|
||||
- **Adding a passkey** happens from the account screen alone: the account's own session is the proof of who is asking.
|
||||
- **Sign-out** is a `POST` form on the account screen, so a link on another site cannot trigger it. The not-found page always renders signed out.
|
||||
- **Left out**: passkey recovery.
|
||||
- **Recovery**: `/recover` hands out a one-time link (`RecoveryLinks`: in memory, good once, for an hour); following it runs a WebAuthn registration that replaces every passkey on the account and signs in. **Stubbed**: with no mail to send, the link is shown on the screen — so anyone who knows an email can recover its account. The design's 24-hour hold, and the cancel from another device, are not built.
|
||||
- **States**: a cancelled passkey prompt says so, and a browser without WebAuthn — or without scripts — is told it can't use passkeys.
|
||||
|
||||
The signatures need a WebAuthn client, so the ceremonies are checked in a browser rather than in `swift test`: `Tests/Browser/passkeys.py` drives headless Chrome with a virtual authenticator over the DevTools protocol (Python standard library only; the script's header has the commands).
|
||||
|
||||
@@ -107,7 +108,7 @@ The signatures need a WebAuthn client, so the ceremonies are checked in a browse
|
||||
- **The rules live in `Persistence`**: `TradeRepository` decides who may offer, answer, or take a step; `TradeController` only maps a refusal to the sheet re-rendered with a `422`, or to a redirect that changes nothing.
|
||||
- **Privacy**: a conversation or sale the account is no party to is not found, rather than forbidden.
|
||||
- **Demo accounts**: every seeded shop has an owner; sign in as `joost@kestrelrow.example` to sell for Kestrel Row Comics.
|
||||
- **Left out**: counter-offers, problem reports and disputes, the PDF record, price guidance, and vacation mode.
|
||||
- **Left out**: counter-offers, problem reports and disputes, the PDF record, price guidance, and vacation mode. Terms, privacy, and seller rules have no screens: the design has none to build them from.
|
||||
|
||||
### Accounts and collecting
|
||||
- **First sign-in**: a new account — joined by passkey or through the demo sign-in — meets `/welcome` once, picks its display name, and goes on to what it came for.
|
||||
@@ -115,6 +116,8 @@ The signatures need a WebAuthn client, so the ceremonies are checked in a browse
|
||||
- **Open a shop**: one form — the shop's public identity, private or business seller (a business states its KvK number, as Dutch consumer law asks), and policies — then straight on to the first listing. A new shop starts unverified and unrated.
|
||||
- **Want list**: a want is words a listing's title must carry, within a grade and a price, among copies shipping from the EU. Matches listed since the list was last looked at are new, and ring the header's bell. Saving a copy, and a gap in a series, both fill the want form.
|
||||
- **Series**: `Persistence` seeds a small catalogue of runs, which is what lets a gap show: every issue of the run, the cheapest copy of each, and *alert me* where none is listed. A series sellers list but the catalogue lacks is made up from its listings, without gaps.
|
||||
- **Collection**: the issues an account owns of each series, added from a form (`1-6, 9, 12`) or a switch per issue on the series screen — which then dims what is owned and counts the gaps listed right now.
|
||||
- **Events**: a seeded calendar of cons and the shops trading at each. A seller picks theirs on the dashboard; a listing and its shop say where a copy can be picked up; and `pickup=` narrows a search to the shops at one event, or at any.
|
||||
- **Compare**: up to three copies of an issue, ordered by what each costs delivered to the Netherlands, flagging a customs border.
|
||||
|
||||
Every link on the site leads somewhere: `Tests/Browser/links.py` crawls them all — signed out, as a buyer, and as a seller — against a running service.
|
||||
|
||||
Reference in new issue
Block a user