Files
sencho/backend/src/middleware/errorHandler.ts
T
Anso 856a260a11 refactor(backend): extract rate limiters, body parsing, and request gates (phase 1) (#731)
Phase 1 of the index.ts monolith refactor. Extracts all non-auth middleware
into focused modules. Routes and authMiddleware itself stay in index.ts for
now; Phase 2 introduces createApp() and extracts authMiddleware.

New modules:
- middleware/rateLimiters.ts: globalApiLimiter, pollingLimiter,
  webhookTriggerLimiter, authRateLimiter, ssoRateLimiter, trivyInstallLimiter
  plus the hybrid rateLimitKeyGenerator and isNodeProxyRequest helper
- middleware/jsonParser.ts: conditionalJsonParser that preserves the raw
  stream for remote-proxy forwarding (via helpers/proxyExemptPaths)
- middleware/nodeContext.ts: nodeContextMiddleware
- middleware/apiTokenScope.ts: enforceApiTokenScope + DEPLOY_ALLOWED_PATTERNS
- middleware/authGate.ts: createAuthGate(authMiddleware) factory + auditLog.
  Factory takes authMiddleware as a dependency to avoid a circular import
  until Phase 2 extracts the auth module.
- middleware/errorHandler.ts: central error handler that preserves
  err.status / err.expose from body-parser and other HTTP errors
- helpers/routePatterns.ts: WEBHOOK_TRIGGER_RE shared by rateLimiters and
  authGate

index.ts drops ~290 lines. Middleware registration order is unchanged.
All 1278 tests pass.

Code review fixes: typed ApiTokenScope in apiTokenScope.ts; replaced 2
em dashes with colons (Directive 18); added local CachedProxyFlagReq
type alias for the node_proxy memoization cast; extracted deny() helper.
2026-04-23 18:22:26 -04:00

29 lines
1.0 KiB
TypeScript

import type { Request, Response, NextFunction, ErrorRequestHandler } from 'express';
import { getErrorMessage } from '../utils/errors';
interface HttpError {
status?: number;
statusCode?: number;
expose?: boolean;
message?: string;
}
/**
* Central Express error handler. Preserves the status + message of HTTP errors
* thrown by upstream middleware (body-parser's 413 `PayloadTooLargeError`,
* CORS 403s, etc.) when the error is safe to expose; otherwise returns a
* generic 500. Mounted last so Express recognises the 4-argument signature.
*/
export const errorHandler: ErrorRequestHandler = (err: unknown, _req: Request, res: Response, next: NextFunction): void => {
console.error('[Error]', err);
if (res.headersSent) {
next(err);
return;
}
const e = (err ?? {}) as HttpError;
const status = e.statusCode ?? e.status ?? 500;
const expose = e.expose === true || status < 500;
const message = expose ? getErrorMessage(err, 'Request failed') : 'Internal server error';
res.status(status).json({ error: message });
};