Files
Autonomous-Bug-Explorer/src/api/server.ts
debian 30f293fbf8 fase(21): openapi documentation with scalar
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 06:06:44 -04:00

108 lines
3.8 KiB
TypeScript

/**
* ABE API Server — Express app factory.
* Middleware order matters: requestId → helmet → cors → rateLimit → body → routes → notFound → errorHandler
*/
import express, { Express, Request, Response } from 'express';
import cors from 'cors';
import helmet from 'helmet';
import rateLimit from 'express-rate-limit';
import cookieParser from 'cookie-parser';
import { Kysely } from 'kysely';
import { AppConfig } from '../shared/infrastructure/Config';
import { Logger } from '../shared/infrastructure/Logger';
import { Database } from '../shared/infrastructure/DatabaseConnection';
import { createRequestIdMiddleware } from './middleware/requestId';
import { notFoundMiddleware } from './middleware/notFound';
import { globalErrorHandler } from './middleware/errorHandler';
import { createRouter } from './router';
import { createApiDocsRouter } from './openapi';
import { CrawlingControllerDeps } from '../modules/crawling/infrastructure/http/CrawlingController';
import { FindingsControllerDeps } from '../modules/findings/infrastructure/http/FindingsController';
import { FuzzingControllerDeps } from '../modules/fuzzing/infrastructure/http/FuzzingController';
import { ReportingControllerDeps } from '../modules/reporting/infrastructure/http/ReportingController';
import { IntegrationsDeps } from '../modules/integrations/infrastructure/http/IntegrationsController';
import { AuthControllerDeps } from './router';
import { LicenseService } from '../modules/licensing/application/LicenseService';
import { SchedulingControllerDeps } from '../modules/scheduling/infrastructure/http/SchedulingController';
import { VisualRegressionControllerDeps } from '../modules/visual-regression/infrastructure/http/VisualRegressionController';
export interface ServerDependencies {
config: AppConfig;
logger: Logger;
db: Kysely<Database>;
crawlingDeps: CrawlingControllerDeps;
findingsDeps: FindingsControllerDeps;
fuzzingDeps: FuzzingControllerDeps;
reportingDeps: ReportingControllerDeps;
integrationsDeps: IntegrationsDeps;
schedulingDeps: SchedulingControllerDeps;
visualRegressionDeps: VisualRegressionControllerDeps;
authDeps: AuthControllerDeps;
licenseService: LicenseService;
}
export function createServer(deps: ServerDependencies): Express {
const app = express();
// 1. Request ID — must be first so all logs have requestId
app.use(createRequestIdMiddleware(deps.logger));
// 2. Security headers
app.use(
helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
connectSrc: ["'self'", 'ws:', 'wss:'],
scriptSrc: ["'self'", "'unsafe-inline'"],
},
},
}),
);
// 3. CORS
app.use(cors({ origin: deps.config.cors.origin, credentials: true }));
// 4. Rate limiting
app.use(
rateLimit({
windowMs: deps.config.api.rateLimitWindowMs,
max: deps.config.api.rateLimitMax,
standardHeaders: true,
legacyHeaders: false,
}),
);
// 5. Body parsing + cookies
app.use(express.json({ limit: '10mb' }));
app.use(cookieParser());
// 6. Health endpoints — no auth required
app.get('/health/live', (_req: Request, res: Response) => {
res.json({ status: 'ok', uptime: process.uptime() });
});
app.get('/health/ready', async (_req: Request, res: Response) => {
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', createRouter(deps));
// 7b. API documentation (no auth required)
app.use('/api-docs', createApiDocsRouter());
// 8. 404 handler
app.use(notFoundMiddleware);
// 9. Global error handler — always last
app.use(globalErrorHandler);
return app;
}