refactor(server): split admin moderation, bot-activity and activity into capability routers (PR 2) #103
Reference in New Issue
Block a user
No description provided.
Delete Branch "refactor/admin-router-split-2"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What & why
PR 2 of 5 of the admin router domain split (
docs/website/API_V2_PLAN.md§ Phase 2). Carves 18 more routes out ofadmin.routes.jsinto one router file per business capability — in place, with every URL unchanged.moderation.router.js/admin/moderationmodAccess(admin + moderator) at router levelbotActivity.router.js/admin/bot-activityadminOnlyper routeactivity.router.js/admin/activityadmin.routes.js(residual)Two decisions worth a reviewer's eye:
modAccessmoved to a router-leveluse; bot-activity'sadminOnlydeliberately did not. Moderation was already gated by a prefix mount (adminRouter.use('/moderation', modAccess)), somoderationRouter.use(modAccess)is the exact equivalent now that the router is mounted at a prefix (the PR 1userscase). Bot-activity's gate was per-route, and keeping it per-route is what preserves the per-route handler count inroutes.guards.json— the only signal that would catch a dropped gate, sincerequireRole(...)returns an anonymous arrow and never appears by name./activitygets its own file, deviating from the plan's target tree, which parked it as a singleton insidedashboard.router.js(PR 4). Honouring the tree would have meant leaving one route in the residual file for two more PRs to satisfy a filename, and it is a genuinely separate capability:/activityis the staff audit log,/dashboardis a stats overview,/bot-activityis the botScore middleware's in-memory ban state. PR 4 now mountsdashboard+site-modeonly; the plan doc is updated to match.modAccessstays in the residual file — the/shard/*in-game staff operations still use it and do not move until PR 4.No controller logic changed. No client, bot or Android change is needed: every URL is byte-identical.
How it was tested
Four zero-diff gates, all clean:
npm run routes:manifest→routes.manifest.jsonzero-diff (200 public + 2 internal)routes.guards.jsonzero-diff — no route lost or gained a gatenpm run swagger→swagger-output.jsonzero-diff (198 operations, all 18 moved ones present at unchanged paths)docs/website/api-route-inventory.jsonalready in sync with the manifestPlus:
cd website/server && npm test→ 434 passing, 0 failingmain.routes.guards.jsoncannot see this on its own:requireRole('admin')andrequireRole('admin','moderator')both return an anonymous arrow, so attaching a gate with the wrong role set would be a zero-diff change there. So I patchedrequireRoleto tag each arrow it returns with its role set, walked the live Express stack, and dumped the full middleware chain (mount-chainusegates included) for every moved route plus untouched controls (/admin/users,/admin/dashboard,/admin/shard/kick,/admin/posts). Ran it against this branch and against amainworktree — the two tables are identical, same handler counts and same role sets.Docs PR: RunicGateway/docs#54.
Checklist
AI-assisted contributions (required)
Claude Code. I have reviewed and understandevery change, and take responsibility for it. AI-authored commits are
marked with a
Co-Authored-By/Assisted-Bytrailer.License
(GNU GPL v3.0 or later), and I have the right to contribute it.
PR 2 of the domain split (docs/website/API_V2_PLAN.md § Phase 2). Carves 18 more routes out of admin.routes.js into one router file per business capability, in place, with every URL unchanged: moderation.router.js (15) /admin/moderation modAccess at router level botActivity.router.js (2) /admin/bot-activity adminOnly per route activity.router.js (1) /admin/activity staff-wide, no extra gate The residual admin.routes.js drops from 82 routes to 64. Moderation was already gated by a prefix mount (adminRouter.use('/moderation', modAccess)), so moderationRouter.use(modAccess) is the exact equivalent now that the router is mounted at a prefix. Bot-activity's adminOnly was per-route and is deliberately kept per-route: that is what holds the per-route handler count in routes.guards.json, the only signal that would catch a dropped gate, since requireRole(...) returns an anonymous arrow and never appears by name. /activity gets its own file rather than waiting for dashboard.router.js in PR 4 — it is the staff audit log, a different capability from the dashboard's stats overview and from the botScore middleware's in-memory ban state. Acceptance: - routes.manifest.json zero-diff (200 public + 2 internal) - routes.guards.json zero-diff - swagger-output.json zero-diff (198 operations) - api-route-inventory.json already in sync - 434 server tests green - role gates verified identical to main by reading the requireRole role sets off the live Express stack for every moved route plus untouched controls Co-Authored-By: Claude <noreply@anthropic.com>