feat(admin): step 6 — admin console (dashboard, users, events, badges)
- port internal/admincrypto (ed25519 Signer + Sign/Verify, key resolution
via ADMIN_SIGNING_KEY env or data/admin_signing.key file)
- port internal/badge (Design + canonical signing payload + RenderSVG with
shield/circle/star/hexagon shapes)
- features/admin/handlers.go: AdminDashboard, AdminUsers, AdminUsersPromote,
AdminEvents, AdminEventNew, AdminEventCreate, AdminEventBadge,
AdminEventBadgeSave; RequireAdmin middleware
- features/admin/pages/{dashboard,users,events}.templ split from the old
single admin_events.templ — same window-chrome + nav shell
- main.go loads the signer (allowGenerate=true only in dev) and threads it
through router.SetupRoutes -> admin.SetupRoutes
routes (all behind RequireAdmin → 303 /signin?next=… for anon):
- GET /admin dashboard
- GET /admin/users (?q= ?admins= ?page=) user list
- POST /admin/users/{did}/promote toggle admin
- GET /admin/events event list
- GET /admin/events/new new-event form
- POST /admin/events create + redirect to badge
- GET /admin/events/{token}/badge badge designer
- POST /admin/events/{token}/badge save signed badge design
verified: all 9 unauthed routes 303 to /signin with correct next=path.
build clean. all tests pass.