test, docs: admin page gate tests and the page's written rules (#102)
- web_test.go walks web.AdminPatterns() rather than naming routes by hand, so a new administrative route that forgets requireOwner fails the gate test instead of shipping open. - The harnesses take a LaneReporter; a fake one keeps the page's tests free of a poller and a Site. - backend/AGENTS.md records the adminRoutes/requireOwner rule and the nil-poller trap; design-system.md records --patina and the admin page's shape.
This commit is contained in:
+13
-3
@@ -211,6 +211,16 @@ Guidance for OpenCode (and Claude Code) working under `backend/`. See root `AGEN
|
||||
`POST /rotate-token` (atomic epoch bump + hash
|
||||
rewrite; invalidates every installed copy, so the panel warns to reinstall
|
||||
on all devices).
|
||||
Owner-only `POST /readers/{id}/revoke` (drops one Reader's session rows and
|
||||
re-renders the `readers` panel; 404 for any non-owner) is the only route that
|
||||
reaches across Readers.
|
||||
- **Owner-only admin page (`internal/web/admin.go`, issue #102):** `GET /admin`
|
||||
carries the Reader roster (sessions, Sighting counters, `POST
|
||||
/readers/{id}/revoke` and `POST /readers/{id}/clear-marks`) and Poll Lane
|
||||
status (`GET /ui/admin/lanes`, self-refreshing every 30s). Every route that
|
||||
reaches past the acting Reader is listed in `adminRoutes()` and wrapped in
|
||||
`requireOwner` at registration — add a route there, not a check inside a
|
||||
handler; `web.AdminPatterns()` is what the gate test walks. A non-owner gets
|
||||
404, never 403. Lane figures come from the running poller through the
|
||||
`web.LaneReporter` seam (`latest.Poller.LaneStatus`), never from a table: a
|
||||
nil reporter or a Lane that has not finished a pass renders "no data yet"
|
||||
rather than zeroes. `main.newRouter` takes the reporter as an interface and
|
||||
converts a nil `*Poller` to a nil interface — a typed nil would make the page
|
||||
claim a poller exists.
|
||||
|
||||
Reference in New Issue
Block a user