mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-28 12:49:03 +00:00
856a260a11
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.
36 lines
1.4 KiB
TypeScript
36 lines
1.4 KiB
TypeScript
import type { Request, Response, NextFunction, RequestHandler } from 'express';
|
|
import { NodeRegistry } from '../services/NodeRegistry';
|
|
import { DatabaseService } from '../services/DatabaseService';
|
|
import { isProxyExemptPath } from '../helpers/proxyExemptPaths';
|
|
|
|
/**
|
|
* Resolve `req.nodeId` from the `x-node-id` header, `?nodeId=` query param,
|
|
* or the default node. Returns 404 for requests targeting a deleted node so
|
|
* downstream handlers don't fail with obscure errors.
|
|
*
|
|
* `/api/nodes` is intentionally exempt so the frontend can re-sync after a
|
|
* node is deleted (otherwise a stale `x-node-id` in localStorage triggers an
|
|
* unrecoverable 404 loop).
|
|
*/
|
|
export const nodeContextMiddleware: RequestHandler = (req: Request, res: Response, next: NextFunction) => {
|
|
const nodeIdHeader = req.headers['x-node-id'] as string;
|
|
const nodeIdQuery = req.query.nodeId as string;
|
|
if (nodeIdHeader) {
|
|
req.nodeId = parseInt(nodeIdHeader, 10);
|
|
} else if (nodeIdQuery) {
|
|
req.nodeId = parseInt(nodeIdQuery, 10);
|
|
} else {
|
|
req.nodeId = NodeRegistry.getInstance().getDefaultNodeId();
|
|
}
|
|
|
|
if (req.path.startsWith('/api/') && !isProxyExemptPath(req.path)) {
|
|
const node = DatabaseService.getInstance().getNode(req.nodeId);
|
|
if (!node) {
|
|
res.status(404).json({ error: `Node with id ${req.nodeId} not found or was deleted.` });
|
|
return;
|
|
}
|
|
}
|
|
|
|
next();
|
|
};
|