refactor(backend): extract 8 low-blast-radius route groups into routers (phase 4a-1) (#734)

First slice of Phase 4 (route extraction). Pulls 8 well-tested, mostly
independent route groups out of index.ts into focused Router files. No
behavior change; every handler body moves verbatim.

New route files under backend/src/routes/:
- meta.ts            /api/health, /api/meta (mounted before authGate)
- license.ts         /api/license/* + /api/system/update,
                     exports scheduleLocalUpdate for the fleet route
- permissions.ts     /api/permissions/me
- convert.ts         POST /api/convert
- alerts.ts          /api/alerts/*
- labels.ts          /api/labels/* + PUT /api/stacks/:name/labels
                     (exported as stackLabelsRouter)
- apiTokens.ts       /api/api-tokens/*
- auditLog.ts        /api/audit-log/*

Shared helper lifts:
- helpers/cacheInvalidation.ts: invalidateNodeCaches()
- middleware/tierGates.ts: requireBody (was inline in index.ts)
- utils/errors.ts: isSqliteUniqueViolation (was inline in index.ts)
- middleware/apiTokenScope.ts: rejectApiTokenScope() helper (new)
- utils/csv.ts: escapeCsvField() (new)

index.ts drops from ~7520 to ~6775 lines and now mounts the routers right
after enforceApiTokenScope. The remote proxy and fleet/auth/webhooks/users
routes remain inline in index.ts pending later Phase 4 slices.

Code review fixes: rejectApiTokenScope helper replaces duplicated
`if (req.apiTokenScope) 403 SCOPE_DENIED` blocks in apiTokens.ts and
license.ts; escapeCsvField replaces the inline CSV escape in auditLog.ts.
This commit is contained in:
Anso
2026-04-23 20:24:37 -04:00
committed by GitHub
parent dc3699189d
commit 50e64b058b
14 changed files with 877 additions and 770 deletions
+15
View File
@@ -2,6 +2,21 @@ import type { Request, Response, NextFunction, RequestHandler } from 'express';
import { isDebugEnabled } from '../utils/debug';
import type { ApiTokenScope } from '../services/DatabaseService';
/**
* 403 the request if it is authenticated via an API token. Many admin and
* account-scoped endpoints reject API tokens outright; this helper
* centralises the message and `code: 'SCOPE_DENIED'` envelope. Returns true
* and writes the response when rejected, false otherwise; callers should
* early-return on true.
*/
export function rejectApiTokenScope(req: Request, res: Response, message: string): boolean {
if (req.apiTokenScope) {
res.status(403).json({ error: message, code: 'SCOPE_DENIED' });
return true;
}
return false;
}
// Scope enforcement for API tokens: restricts which endpoints a token can reach.
const DEPLOY_ALLOWED_PATTERNS: RegExp[] = [
/^\/api\/stacks\/[^/]+\/deploy$/,