import { FastifyPluginAsync } from 'fastify'; import { verifyAccessToken } from '../../plugins/auth.js'; import { ChatManager } from '../../services/chat-manager.service.js'; interface ChatWsMessage { type: 'subscribe' | 'unsubscribe' | 'send_message'; planId?: string; destinationId?: string; text?: string; } export function createChatRoutes(chatManager: ChatManager): FastifyPluginAsync { const chatRoutes: FastifyPluginAsync = async (fastify) => { fastify.get('/chat/ws', { websocket: true }, async (socket, request) => { // Authenticate via query param const url = new URL(request.url, `http://${request.headers.host}`); const token = url.searchParams.get('token'); if (!token) { socket.send(JSON.stringify({ type: 'error', error: 'Missing token' })); socket.close(); return; } let userId: string; try { userId = await verifyAccessToken(token); } catch { socket.send(JSON.stringify({ type: 'error', error: 'Invalid or expired token' })); socket.close(); return; } request.log.info({ userId }, 'Chat WebSocket connected'); socket.on('message', async (data: Buffer) => { try { const raw = data.toString(); request.log.info({ userId, raw }, 'Chat WS message received'); const msg: ChatWsMessage = JSON.parse(raw); switch (msg.type) { case 'subscribe': if (msg.planId) { await chatManager.startChat(msg.planId, userId, socket); } break; case 'unsubscribe': if (msg.planId) { await chatManager.stopChat(msg.planId, userId); } break; case 'send_message': if (msg.planId && msg.destinationId && msg.text) { await chatManager.handleSendMessage( msg.planId, userId, msg.destinationId, msg.text, ); } break; default: socket.send(JSON.stringify({ type: 'error', error: `Unknown message type: ${msg.type}` })); } } catch (err) { request.log.error({ err }, 'Chat WebSocket message handling error'); socket.send(JSON.stringify({ type: 'error', error: 'Internal error' })); } }); socket.on('close', () => { request.log.info({ userId }, 'Chat WebSocket disconnected'); chatManager.stopAllForSocket(socket); }); socket.on('error', (err: Error) => { request.log.error({ err, userId }, 'Chat WebSocket error'); chatManager.stopAllForSocket(socket); }); }); }; return chatRoutes; }