108 lines
3.8 KiB
TypeScript
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;
|
|
}
|