Stage 10: admin console & dictionary ops (complaint review, hot-reload, broadcasts) (#11)
This commit was merged in pull request #11.
This commit is contained in:
@@ -43,7 +43,7 @@ independent (see ARCHITECTURE §9.1).
|
||||
| 7 | UI — playable slice + UX polish (Svelte+Vite, board, lobby, chat, hint/word-check, i18n) | **done** |
|
||||
| 8 | UI — social/account/history (friends, blocks, invitations, profile edit, stats, history/GCG) | **done** |
|
||||
| 9 | Telegram integration (bot side-service, deep-link, push) | **done** |
|
||||
| 10 | Admin & dictionary ops (complaint review, version reload) | todo |
|
||||
| 10 | Admin & dictionary ops (complaint review, version reload) | **done** |
|
||||
| 11 | Account linking & merge | todo |
|
||||
| 12 | Polish (observability, perf with evidence, deploy) | todo |
|
||||
|
||||
@@ -693,6 +693,51 @@ Open details: deployment target/host; dashboards; load expectations.
|
||||
the sweeper skip when CI ran with `now-1h` inside it) was made deterministic by
|
||||
clearing the test account's away window.
|
||||
|
||||
- **Stage 10** (interview + implementation):
|
||||
- **Admin console = backend-rendered `/_gm`, gateway Basic-Auth** (interview, two
|
||||
rounds): the owner chose a dedicated web console but, pointing at `../galaxy-game`
|
||||
and asking to keep it simple, the deliverable is **server-rendered Go
|
||||
`html/template` + one embedded CSS** (`backend/internal/adminconsole`: a
|
||||
framework-agnostic renderer + page view-models, `//go:embed` templates/assets, zero
|
||||
JS, no build step), **not** a SPA. It lives **in the backend** on its own route
|
||||
`/_gm/*`; the **gateway** (the project's built-in reverse proxy) gates `/_gm/*` with
|
||||
the existing `GATEWAY_ADMIN_USER/PASSWORD` Basic-Auth on its **public** listener and
|
||||
proxies **verbatim** to backend `/_gm/*` (mounted on the edge mux below the h2c wrap
|
||||
so Connect keeps working). This **supersedes Stage 6's** gateway-fronts-
|
||||
`/api/v1/admin` model: the separate admin port `GATEWAY_ADMIN_ADDR` is dropped (only
|
||||
the port — user/password stay), the backend `/api/v1/admin` group + `ping` are
|
||||
removed, and `gateway/internal/admin` is repurposed to the verbatim proxy. The
|
||||
backend keeps **no operator identity** and no `admin_accounts` table; CSRF on the
|
||||
console's POSTs is a **same-origin** check (`Origin`/`Referer` vs `Host`, the gateway
|
||||
preserves the inbound Host) — no token. Discharges Stage 1's "admin bootstrap" (it is
|
||||
config, not a DB seed).
|
||||
- **Complaint resolution + dictionary pipeline** (interview): migration **00008**
|
||||
(+ jetgen) adds `disposition`/`resolution_note`/`resolved_at`/`applied_in_version`
|
||||
to `complaints` and the deferred `status` CHECK (`open|resolved`) — **discharges
|
||||
Stage 3's** deferral (no `resolved_by`: operator identity is not tracked). Resolution
|
||||
sets a disposition (`reject`/`accept_add`/`accept_remove`); accepted complaints are
|
||||
**derived by query** into a pending dictionary-change list (no new table), stamped
|
||||
`applied_in_version` once a rebuilt version is loaded. New `game` reads
|
||||
`ListComplaints`/`GetComplaint`/`CountComplaints`/`ResolveComplaint`/
|
||||
`DictionaryChanges`/`MarkChangesApplied`; admin list/count reads
|
||||
`account.ListAccounts/CountAccounts/Identities` and `game.ListGames/CountGames/
|
||||
GameByID`.
|
||||
- **Dictionary hot-reload = per-version subdir** (interview): the launch version stays
|
||||
in the flat `BACKEND_DICT_DIR` (CI/dev untouched); a reloaded version `X` loads from
|
||||
`BACKEND_DICT_DIR/X/` via the new `Registry.LoadAvailable` (present variants only),
|
||||
and boot re-loads every subdirectory via `engine.OpenWithVersions` so reloaded
|
||||
versions survive a restart. **Partially addresses TODO-2** (the runtime reload
|
||||
contract; the offline DAWG generator stays future work).
|
||||
- **Operator broadcasts** (discharges Stage 9's forward-note): the backend gains its
|
||||
own connector gRPC client (`backend/internal/connector`, `BACKEND_CONNECTOR_ADDR`,
|
||||
nil when unset) over the existing `pkg/proto/telegram/v1`; the console messages a
|
||||
user by `account_id` (backend resolves the Telegram `external_id`) and posts to the
|
||||
game channel via `SendToUser`/`SendToGameChannel`.
|
||||
- **Config/CI**: backend adds `BACKEND_CONNECTOR_ADDR`; gateway drops
|
||||
`GATEWAY_ADMIN_ADDR` (keeps user/password). No new module and no fbs/proto/UI codegen
|
||||
(the console is server-rendered Go). The Go workflows already span
|
||||
`./backend/... ./gateway/... ./pkg/...`; integration stays `./backend/...`.
|
||||
|
||||
## Deferred TODOs (cross-stage)
|
||||
|
||||
- **TODO-1 — publish & version the solver.** Once `scrabble-solver` is stable,
|
||||
@@ -710,7 +755,9 @@ Open details: deployment target/host; dashboards; load expectations.
|
||||
git submodule (the ~0.5–0.7 MB DAWGs are regenerated wholesale and bloat git
|
||||
history); pin by tag/hash for a reproducible startup set. A submodule/LFS pull
|
||||
is a **deploy-time** way to populate the directory, **not** the runtime
|
||||
dynamic-reload mechanism (Stage 10) — keep the `BACKEND_DICT_DIR` directory as
|
||||
dynamic-reload mechanism (**implemented in Stage 10**: a per-version subdirectory
|
||||
`BACKEND_DICT_DIR/<version>/` loaded via `Registry.LoadAvailable`, restart-restored by
|
||||
`engine.OpenWithVersions`) — keep the `BACKEND_DICT_DIR` directory as
|
||||
the runtime contract: a new `.dawg` appears in it and is loaded with
|
||||
`dawg.Load`.
|
||||
- **TODO-3 — garbage-collect abandoned guest accounts.** Stage 6 makes a guest a
|
||||
|
||||
Reference in New Issue
Block a user