import Fastify, { type FastifyInstance } from 'fastify'; import type { AppConfig } from './config.js'; import { pool } from './db/pool.js'; import { registerSecurityPlugins } from './lib/security.js'; import { registerInstrumentation } from './lib/instrumentation.js'; import { registerAgentRoutes } from './routes/agents.js'; import { registerTokenRoutes } from './routes/tokens.js'; import { registerSessionRoutes } from './routes/sessions.js'; import { registerRoomRoutes } from './routes/rooms.js'; import { registerSocialRoutes } from './routes/social.js'; import { registerDirectoryRoutes } from './routes/directory.js'; import { setupSocketIO } from './socket/index.js'; import { register as metricsRegister } from './lib/metrics.js'; import { startMetricsCollector } from './services/metrics-collector.js'; export interface BuildAppOptions { config: AppConfig; } export async function buildApp({ config }: BuildAppOptions): Promise { const app = Fastify({ logger: { level: config.LOG_LEVEL }, disableRequestLogging: config.NODE_ENV === 'test', }); // Register security plugins first await registerSecurityPlugins(app, config); // Register instrumentation for metrics await registerInstrumentation(app); app.get('/healthz', async () => { return { status: 'ok', uptime: process.uptime() }; }); app.get('/readyz', async (_req, reply) => { const start = Date.now(); try { // Check DB connectivity await pool.query('SELECT 1'); const elapsed = Date.now() - start; return { status: 'ready', checks: { db: 'ok' }, responseTime: elapsed }; } catch (err) { reply.status(503); return { status: 'not_ready', checks: { db: 'failed' }, error: err instanceof Error ? err.message : 'unknown', }; } }); app.get('/metrics', async (_req, reply) => { reply.header('Content-Type', metricsRegister.contentType); return metricsRegister.metrics(); }); // Register API routes await registerAgentRoutes(app, pool); await registerTokenRoutes(app, pool); await registerSessionRoutes(app, pool, config); await registerRoomRoutes(app, pool); await registerSocialRoutes(app, pool); await registerDirectoryRoutes(app, pool); // Setup socket.io after app is ready (if feature enabled) await app.ready(); if (config.FEATURE_MESSAGING_ENABLED) { setupSocketIO(app.server, pool, config); app.log.info('✅ Socket.IO messaging enabled'); } else { app.log.warn('⚠️ Socket.IO messaging disabled (FEATURE_MESSAGING_ENABLED=false)'); } // Start metrics collector (updates room metrics every 30s) startMetricsCollector(pool); app.log.info('✅ Metrics collector started'); return app; }