Add Phase 6: multi-server admin panel (server registry + settings)

Gate a new /api/admin/* route set (register/list/update/delete servers)
behind a single shared MCMAPPER_ADMIN_TOKEN header, and add a /admin
frontend page (Alpine) to unlock, register new servers, and edit
authMode/anonymousChatAllowed/waypointFormat per server — these columns
already existed but were only editable via direct DB edit until now.

Written test-first per the project's TDD workflow: admin.ts's domain
logic, the index.ts route wiring, and a new e2e/tests/admin.spec.ts
covering the token gate and register/edit/delete round trip through the
real browser UI.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015tKdPZt78zbPUZMXWzKEKt
This commit is contained in:
2026-08-09 13:06:37 +02:00
parent 7b85f4dff1
commit a134c47152
13 changed files with 634 additions and 16 deletions
+32 -13
View File
@@ -61,20 +61,37 @@ docker compose up
migrations on startup (see `api/src/db/migrate.ts`); `worker` consumes the `mcmapper:dirty-chunks`
Redis stream via a consumer group (`mcmapper-workers`) so multiple instances split work safely.
### Phase 1: connecting a mod instance
### Connecting a mod instance
There's no admin registration API yet (Phase 6) — seed one server row by hand, then point the
mod's `MapperConfig` at the same token:
Register a server via the admin panel at `/admin` (set `MCMAPPER_ADMIN_TOKEN` first — see
"Admin panel" below), or seed one row by hand for scripted/headless setup:
```
docker compose run --rm api bun run seed
```
reads `MCMAPPER_SEED_SERVER_NAME`/`MCMAPPER_SEED_SERVER_TOKEN` from `api/.env.example` (edit
those first, or override with `-e`). Once the mod connects and sends its initial chunk backfill,
tiles appear at `GET /api/tiles/:serverId/:dimension/:zoom/:tileX/:tileY.png` (zoom is always `0`
for now — see `worker/src/render/cpu.rs`) and the frontend's Leaflet viewer picks them up
automatically from `GET /api/servers`.
those first, or override with `-e`). Either way, point the mod's `MapperConfig#serverToken` at
the resulting token. Once the mod connects and sends its initial chunk backfill, tiles appear at
`GET /api/tiles/:serverId/:dimension/:zoom/:tileX/:tileY.png` (zoom is always `0` for now — see
`worker/src/render/cpu.rs`) and the frontend's Leaflet viewer picks them up automatically from
`GET /api/servers`.
### Admin panel
`/admin` (linked from the map page's header) manages the server registry: register new servers
(generates their token — never admin-supplied, so it can't collide with or be guessed from
anything else), and edit `authMode`, `anonymousChatAllowed`, and `waypointFormat` per server.
It's gated behind a single shared secret, not a per-account role (this is a single-operator
backend) — set `MCMAPPER_ADMIN_TOKEN` in `api/`'s untracked `.env` (see `api/.env.example`),
restart `api`, then enter that same value into the panel's unlock prompt. Leaving it unset
disables every `/api/admin/*` route (401), it does not default to open. The token is remembered
in the browser's `localStorage` after unlocking, same pattern as the player-facing session token
(see `api/src/link.ts`'s doc comment).
Settings not exposed here yet (which map types render, dimension filtering, player-position
visibility) don't have underlying features built for them either — no point in a knob nothing
reads. They'll gain admin UI alongside the feature that needs them.
## Running tests
@@ -121,12 +138,14 @@ bunx playwright test
(unlike the `bun test` section above): throwaway Postgres/Redis/MinIO containers
(`mcmapper-e2e-*`, distinct names/ports from the `bun test` ones so both can run at once),
migrations, a seeded server + linked account/session + a 5x5-chunk terrain footprint around the
world origin, then the `api`/`frontend`/proxy processes. `global-teardown.ts` kills every spawned
process and removes the containers afterward. Covers the two UI flows most worth a real
click-through: the marker click-to-place/edit popup (`tests/markers.spec.ts`, including that a
marker created while linked shows up in a second browser context with the same session — the
cross-device sync claim) and the region-select drag + glTF export (`tests/region-export.spec.ts`,
including a real triggered file download).
world origin, then the `api`/`frontend`/proxy processes (the `api` process is started with a
fixed `MCMAPPER_ADMIN_TOKEN` for `tests/admin.spec.ts` to use — see `config.ts`'s `ADMIN_TOKEN`).
`global-teardown.ts` kills every spawned process and removes the containers afterward. Covers the
UI flows most worth a real click-through: the marker click-to-place/edit popup
(`tests/markers.spec.ts`, including that a marker created while linked shows up in a second
browser context with the same session — the cross-device sync claim), the region-select drag +
glTF export (`tests/region-export.spec.ts`, including a real triggered file download), and the
admin panel's token gate + server register/edit/delete round trip (`tests/admin.spec.ts`).
## Attribution