File size: 4,505 Bytes
d4abe4b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3e12ae4
d4abe4b
 
 
 
 
 
 
3e12ae4
d4abe4b
 
 
 
 
 
 
 
 
c869f17
d4abe4b
 
 
 
 
 
 
3e12ae4
d4abe4b
 
 
 
 
 
 
 
 
 
 
 
3e12ae4
d4abe4b
 
 
 
3e12ae4
d4abe4b
3e12ae4
 
d4abe4b
 
 
3e12ae4
d4abe4b
 
 
 
 
3e12ae4
 
d4abe4b
3e12ae4
d4abe4b
3e12ae4
d4abe4b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3e12ae4
d4abe4b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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 {
    // Validate configuration
    logger.info('Validating configuration...');
    validateConfig();

    // Create Fastify instance
    const fastify = Fastify({
      logger: false, // Use custom logger
      requestIdLogLabel: 'reqId',
      disableRequestLogging: false,
      requestIdHeader: 'x-request-id'
    });

    // Register CORS
    await fastify.register(cors, {
      origin: true, // Allow all origins (adjust for production)
      credentials: true
    });

    // Register WebSocket
    await fastify.register(websocket, {
      options: {
        maxPayload: 10 * 1024, // 10KB max message size
        verifyClient: (_info, callback) => {
          // Optional: Add custom verification logic here
          callback(true);
        }
      }
    });

    // Register Swagger
    await fastify.register(swagger as any, swaggerOptions);
    await fastify.register(swaggerUi, swaggerUiOptions);

    // Initialize services
    logger.info('Initializing services...');
    const supabaseService = new SupabaseService();
    const mapsService = new MapsService();
    
    // Initialize agent
    logger.info('Initializing Medagen Agent...');
    const agent = new MedagenAgent(supabaseService, mapsService);
    await agent.initialize();

    // Register routes
    logger.info('Registering routes...');
    await registerRoutes(fastify, agent, supabaseService, mapsService);

    // Add request logging
    fastify.addHook('onRequest', async (request) => {
      logger.info({
        method: request.method,
        url: request.url,
        ip: request.ip
      }, 'Incoming request');
    });

    // Add response logging
    fastify.addHook('onResponse', async (request, reply) => {
      logger.info({
        method: request.method,
        url: request.url,
        statusCode: reply.statusCode
      }, 'Request completed');
    });

    // Error handler
    fastify.setErrorHandler((error, _request, reply) => {
      // Handle validation errors (Fastify schema validation)
      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
        });
      }

      // Handle other errors
      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'
      });
    });

    // Start server
    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);
  }
}

// Handle graceful shutdown
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);
});

// Start the server
startServer();