|
|
import Fastify from 'fastify'; |
|
|
import cors from '@fastify/cors'; |
|
|
import websocket from '@fastify/websocket'; |
|
|
import swagger from '@fastify/swagger'; |
|
|
import swaggerUi from '@fastify/swagger-ui'; |
|
|
import { config, validateConfig } from './utils/config.js'; |
|
|
import { logger } from './utils/logger.js'; |
|
|
import { swaggerOptions, swaggerUiOptions } from './utils/swagger.js'; |
|
|
import { MedagenAgent } from './agent/agent-executor.js'; |
|
|
import { SupabaseService } from './services/supabase.service.js'; |
|
|
import { MapsService } from './services/maps.service.js'; |
|
|
import { registerRoutes } from './routes/index.js'; |
|
|
import { wsConnectionManager } from './services/websocket.service.js'; |
|
|
|
|
|
async function startServer() { |
|
|
try { |
|
|
|
|
|
logger.info('Validating configuration...'); |
|
|
validateConfig(); |
|
|
|
|
|
|
|
|
const fastify = Fastify({ |
|
|
logger: false, |
|
|
requestIdLogLabel: 'reqId', |
|
|
disableRequestLogging: false, |
|
|
requestIdHeader: 'x-request-id' |
|
|
}); |
|
|
|
|
|
|
|
|
await fastify.register(cors, { |
|
|
origin: true, |
|
|
credentials: true |
|
|
}); |
|
|
|
|
|
|
|
|
await fastify.register(websocket, { |
|
|
options: { |
|
|
maxPayload: 10 * 1024, |
|
|
verifyClient: (_info, callback) => { |
|
|
|
|
|
callback(true); |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
await fastify.register(swagger as any, swaggerOptions); |
|
|
await fastify.register(swaggerUi, swaggerUiOptions); |
|
|
|
|
|
|
|
|
logger.info('Initializing services...'); |
|
|
const supabaseService = new SupabaseService(); |
|
|
const mapsService = new MapsService(); |
|
|
|
|
|
|
|
|
logger.info('Initializing Medagen Agent...'); |
|
|
const agent = new MedagenAgent(supabaseService, mapsService); |
|
|
await agent.initialize(); |
|
|
|
|
|
|
|
|
logger.info('Registering routes...'); |
|
|
await registerRoutes(fastify, agent, supabaseService, mapsService); |
|
|
|
|
|
|
|
|
fastify.addHook('onRequest', async (request) => { |
|
|
logger.info({ |
|
|
method: request.method, |
|
|
url: request.url, |
|
|
ip: request.ip |
|
|
}, 'Incoming request'); |
|
|
}); |
|
|
|
|
|
|
|
|
fastify.addHook('onResponse', async (request, reply) => { |
|
|
logger.info({ |
|
|
method: request.method, |
|
|
url: request.url, |
|
|
statusCode: reply.statusCode |
|
|
}, 'Request completed'); |
|
|
}); |
|
|
|
|
|
|
|
|
fastify.setErrorHandler((error, _request, reply) => { |
|
|
|
|
|
if (error && typeof error === 'object' && 'validation' in error && error.validation) { |
|
|
const validationError = error as { validation?: unknown; message?: string }; |
|
|
logger.warn({ validation: error.validation }, 'Validation error'); |
|
|
return reply.status(400).send({ |
|
|
error: 'Validation Error', |
|
|
message: validationError.message || 'Validation failed', |
|
|
details: error.validation |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
const errorMessage = error instanceof Error ? error.message : String(error); |
|
|
logger.error({ error }, 'Unhandled error'); |
|
|
|
|
|
return reply.status(500).send({ |
|
|
error: 'Internal Server Error', |
|
|
message: process.env.NODE_ENV === 'development' ? errorMessage : 'An error occurred' |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
const port = config.port; |
|
|
const host = process.env.HOST || '0.0.0.0'; |
|
|
|
|
|
await fastify.listen({ port, host }); |
|
|
|
|
|
logger.info(`π Medagen Backend is running on http://${host}:${port}`); |
|
|
logger.info(`π Health check: http://${host}:${port}/health`); |
|
|
logger.info(`π₯ Triage endpoint: http://${host}:${port}/api/health-check`); |
|
|
logger.info(`π WebSocket endpoint: ws://${host}:${port}/ws/chat`); |
|
|
logger.info(`π Swagger docs: http://${host}:${port}/docs`); |
|
|
} catch (error) { |
|
|
logger.error({ error }, 'Failed to start server'); |
|
|
process.exit(1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
process.on('SIGINT', () => { |
|
|
logger.info('Received SIGINT, shutting down gracefully...'); |
|
|
wsConnectionManager.destroy(); |
|
|
process.exit(0); |
|
|
}); |
|
|
|
|
|
process.on('SIGTERM', () => { |
|
|
logger.info('Received SIGTERM, shutting down gracefully...'); |
|
|
wsConnectionManager.destroy(); |
|
|
process.exit(0); |
|
|
}); |
|
|
|
|
|
|
|
|
startServer(); |
|
|
|
|
|
|