import { randomUUID } from 'node:crypto'; import cors from '@fastify/cors'; import rateLimit from '@fastify/rate-limit'; import Fastify, { type FastifyInstance } from 'fastify'; import { loadConfig, type AppConfig } from './config.js'; import { registerHealthRoutes } from './routes/health.js'; import { registerPlatformBootstrapRoutes, type PlatformConfigResolver } from './routes/platform-bootstrap.js'; import { registerAuthRoutes, type AuthRouteOptions } from './routes/auth.js'; import { registerUserManagementRoutes, type UserManagementRouteOptions } from './routes/user-management.js'; export interface BuildAppOptions { config?: AppConfig; platformConfigRepository?: PlatformConfigResolver; auth?: AuthRouteOptions; userManagement?: UserManagementRouteOptions; } declare module 'fastify' { interface FastifyRequest { traceId: string; } } export async function buildApp(options: BuildAppOptions = {}): Promise { const config = options.config ?? loadConfig(); const app = Fastify({ logger: { level: config.nodeEnv === 'test' ? 'silent' : 'info' }, genReqId: () => randomUUID() }); app.decorateRequest('traceId', ''); app.addHook('onRequest', async (request, reply) => { const traceHeader = request.headers['x-trace-id']; request.traceId = Array.isArray(traceHeader) ? traceHeader[0] : traceHeader || request.id; reply.header('x-trace-id', request.traceId); }); await app.register(cors, { origin: config.corsOrigins }); await app.register(rateLimit, { max: 120, timeWindow: '1 minute' }); app.setErrorHandler((error, request, reply) => { request.log.error({ err: error, traceId: request.traceId }, 'request failed'); reply.status(500).send({ code: 'INTERNAL_ERROR', message: 'Internal server error', traceId: request.traceId }); }); await registerHealthRoutes(app, config); if (options.platformConfigRepository) { await registerPlatformBootstrapRoutes(app, options.platformConfigRepository); } if (options.auth) { await registerAuthRoutes(app, options.auth); } if (options.userManagement) { await registerUserManagementRoutes(app, options.userManagement); } return app; }