// /api/v1/player — the player self-service surface, assembled from // per-capability routers. // // This file owns exactly two things: the gate every player route shares, and the // mount table. No route is declared here. Each capability router mounts at the // prefix it already owned inside the old monolithic player.routes.js, so the // emitted URL set is byte-identical — proved by a zero-line diff in // server/routes.manifest.json (`npm run routes:manifest`). // // **Staff are a superset of players.** This group is open to any authenticated // account, not just role 'player': every read/write is self-scoped to req.user.id, // and a staff member has every player ability plus their staff tools on top. // Adding a requireRole('player') here would 403 an admin off their own characters // (it happened once — see docs/website/BACKEND_DESIGN.md). Staff also reach the // identical self-scoped handlers under /admin/shard and /auth/me/account; those // are alternative URLs onto the same controllers, not duplicated logic — and // both of those live in module-uo now, which changes where they are defined and // nothing about which URLs answer. // // See docs/website/API_V2_PLAN.md § Phase 2 for the split. const express = require('express') const { requireAuth } = require('../../../auth/session.middleware') const noindex = require('../../../middleware/noindex') const accountRouter = require('./account.router') const appealsRouter = require('./appeals.router') const teamsRouter = require('./teams.router') const teamForumRouter = require('./teamForum.router') const playerRouter = express.Router() // Group gate: authenticated only (no role restriction). Keep it out of search // indexes. requireAuth also enforces the account status check (a disabled/banned // account is rejected here with 403 before any handler runs). // // It lives here, ahead of every mount, so a capability router added later cannot // silently ship without it. playerRouter.use(noindex, requireAuth) playerRouter.use('/account', accountRouter) playerRouter.use('/appeals', appealsRouter) playerRouter.use('/teams', teamsRouter) // Same prefix, second router. The forum and the leader-exercised grant flow are a // different capability from "the caller's own Teams", and splitting them keeps // each file about one thing; no path in the two collides. playerRouter.use('/teams', teamForumRouter) module.exports = playerRouter