fase(7): api server refactor with composition root

This commit is contained in:
debian
2026-03-05 09:36:28 -05:00
parent e746dc0497
commit f01acfe985
20 changed files with 861 additions and 2 deletions

64
dist/api/server.js vendored Normal file
View File

@@ -0,0 +1,64 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createServer = createServer;
/**
* ABE API Server — Express app factory.
* Middleware order matters: requestId → helmet → cors → rateLimit → body → routes → notFound → errorHandler
*/
const express_1 = __importDefault(require("express"));
const cors_1 = __importDefault(require("cors"));
const helmet_1 = __importDefault(require("helmet"));
const express_rate_limit_1 = __importDefault(require("express-rate-limit"));
const requestId_1 = require("./middleware/requestId");
const notFound_1 = require("./middleware/notFound");
const errorHandler_1 = require("./middleware/errorHandler");
const router_1 = require("./router");
function createServer(deps) {
const app = (0, express_1.default)();
// 1. Request ID — must be first so all logs have requestId
app.use((0, requestId_1.createRequestIdMiddleware)(deps.logger));
// 2. Security headers
app.use((0, helmet_1.default)({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
connectSrc: ["'self'", 'ws:', 'wss:'],
scriptSrc: ["'self'", "'unsafe-inline'"],
},
},
}));
// 3. CORS
app.use((0, cors_1.default)({ origin: deps.config.cors.origin, credentials: true }));
// 4. Rate limiting
app.use((0, express_rate_limit_1.default)({
windowMs: deps.config.api.rateLimitWindowMs,
max: deps.config.api.rateLimitMax,
standardHeaders: true,
legacyHeaders: false,
}));
// 5. Body parsing
app.use(express_1.default.json({ limit: '10mb' }));
// 6. Health endpoints — no auth required
app.get('/health/live', (_req, res) => {
res.json({ status: 'ok', uptime: process.uptime() });
});
app.get('/health/ready', async (_req, res) => {
try {
await deps.db.selectFrom('sessions').select('id').limit(1).execute();
res.json({ status: 'ready', db: 'connected' });
}
catch (err) {
res.status(503).json({ status: 'not_ready', db: 'disconnected', error: String(err) });
}
});
// 7. Module routes
app.use('/api', (0, router_1.createRouter)(deps));
// 8. 404 handler
app.use(notFound_1.notFoundMiddleware);
// 9. Global error handler — always last
app.use(errorHandler_1.globalErrorHandler);
return app;
}