import { wsManager } from "../utils/websocket.js"; import { authenticate } from "../middleware/auth.js"; /** * WebSocket routes and handlers * @param {FastifyInstance} fastify - Fastify instance */ export default async function websocketRoutes(fastify, options) { // WebSocket endpoint fastify.get("/ws", { websocket: true }, (connection, request) => { // In @fastify/websocket, the connection parameter IS the WebSocket directly // It has properties like _socket, _readyState, etc. wsManager.handleConnection(connection, request.raw || request); }); // Get WebSocket stats (authenticated endpoint) fastify.get( "/ws/stats", { preHandler: authenticate, }, async (request, reply) => { const stats = { totalConnections: wsManager.getTotalSocketCount(), authenticatedUsers: wsManager.getAuthenticatedUserCount(), userConnected: wsManager.isUserConnected(request.user._id.toString()), }; return reply.send({ success: true, stats: stats, }); } ); // Broadcast to all users (admin only - requires staff level 3+) fastify.post( "/ws/broadcast", { preHandler: [ authenticate, async (request, reply) => { if (request.user.staffLevel < 3) { return reply.status(403).send({ error: "Forbidden", message: "Insufficient permissions", }); } }, ], schema: { body: { type: "object", required: ["type", "data"], properties: { type: { type: "string" }, data: { type: "object" }, excludeUsers: { type: "array", items: { type: "string" } }, }, }, }, }, async (request, reply) => { try { const { type, data, excludeUsers = [] } = request.body; const count = wsManager.broadcastToAll( { type, data, timestamp: Date.now(), }, excludeUsers ); return reply.send({ success: true, message: `Broadcast sent to ${count} clients`, recipientCount: count, }); } catch (error) { console.error("Error broadcasting:", error); return reply.status(500).send({ error: "InternalServerError", message: "Failed to broadcast message", }); } } ); // Send message to specific user (admin only) fastify.post( "/ws/send/:userId", { preHandler: [ authenticate, async (request, reply) => { if (request.user.staffLevel < 2) { return reply.status(403).send({ error: "Forbidden", message: "Insufficient permissions", }); } }, ], schema: { body: { type: "object", required: ["type", "data"], properties: { type: { type: "string" }, data: { type: "object" }, }, }, }, }, async (request, reply) => { try { const { userId } = request.params; const { type, data } = request.body; const sent = wsManager.sendToUser(userId, { type, data, timestamp: Date.now(), }); if (!sent) { return reply.status(404).send({ error: "NotFound", message: "User not connected", }); } return reply.send({ success: true, message: "Message sent to user", }); } catch (error) { console.error("Error sending message:", error); return reply.status(500).send({ error: "InternalServerError", message: "Failed to send message", }); } } ); // Check if user is online fastify.get( "/ws/status/:userId", { preHandler: authenticate, }, async (request, reply) => { try { const { userId } = request.params; const isConnected = wsManager.isUserConnected(userId); const metadata = wsManager.getUserMetadata(userId); return reply.send({ success: true, userId: userId, online: isConnected, metadata: metadata || null, }); } catch (error) { console.error("Error checking user status:", error); return reply.status(500).send({ error: "InternalServerError", message: "Failed to check user status", }); } } ); }