import express, { Express, Request, Response, NextFunction } from 'express'; import { config } from '../config'; import { logger } from '../utils/logger'; import healthRouter from './routes/health'; import submitRouter from './routes/submit'; import internalRouter from './routes/internal'; import reportsRouter from './routes/reports'; import accountsRouter from './routes/accounts'; import adminUsersRouter from './routes/admin-users'; /** * Create and configure the Express application with all routes and middleware. */ export function createApp(): Express { const app = express(); app.disable('x-powered-by'); app.use((req: Request, res: Response, next: NextFunction) => { const origin = req.headers.origin; const allowOrigin = origin && (config.allowedOrigins.length === 0 || config.allowedOrigins.includes(origin)); if (allowOrigin) { res.setHeader('Access-Control-Allow-Origin', origin); res.setHeader('Vary', 'Origin'); } res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Authorization,Content-Type,Idempotency-Key,X-Admin-Secret'); res.setHeader('Access-Control-Max-Age', '86400'); res.setHeader('X-Content-Type-Options', 'nosniff'); res.setHeader('Referrer-Policy', 'no-referrer'); if (req.method === 'OPTIONS') { res.status(204).end(); return; } next(); }); // --- Body parsers --- app.use(express.json({ limit: '1mb' })); // --- Routes --- app.use(healthRouter); app.use(submitRouter); app.use(reportsRouter); app.use(accountsRouter); app.use(adminUsersRouter); app.use(internalRouter); app.use((req: Request, res: Response) => { res.status(404).json({ error: 'Not found' }); }); // --- Global error handler --- app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => { logger.error('Unhandled server error', { error: err.message, stack: err.stack, }); res.status(500).json({ error: 'Internal server error' }); }); return app; }