const jwt = require('jsonwebtoken'); const logger = require('../utils/logger'); const redis = require('../config/redis'); const database = require('../config/database'); const socketHandler = (io) => { // Authentication middleware for Socket.IO io.use(async (socket, next) => { try { const token = socket.handshake.auth.token || socket.handshake.headers.authorization; if (!token) { return next(new Error('Authentication error: No token provided')); } const cleanToken = token.replace('Bearer ', ''); const decoded = jwt.verify(cleanToken, process.env.JWT_SECRET); // Verify user session const session = await redis.getUserSession(decoded.id); if (!session) { return next(new Error('Authentication error: Session expired')); } // Get user info const [users] = await database.query( 'SELECT id, username, email, role FROM users WHERE id = ? AND status = "active"', [decoded.id] ); if (users.length === 0) { return next(new Error('Authentication error: User not found')); } socket.user = users[0]; next(); } catch (error) { logger.error('Socket authentication failed:', error); next(new Error('Authentication error: Invalid token')); } }); io.on('connection', (socket) => { logger.info(`User connected: ${socket.user.username} (${socket.id})`); // Join user to their personal room socket.join(`user:${socket.user.id}`); // Join admin users to admin room if (socket.user.role === 'admin') { socket.join('admin'); } // Handle device data updates socket.on('subscribe_device', (deviceId) => { socket.join(`device:${deviceId}`); logger.info(`User ${socket.user.username} subscribed to device ${deviceId}`); }); socket.on('unsubscribe_device', (deviceId) => { socket.leave(`device:${deviceId}`); logger.info(`User ${socket.user.username} unsubscribed from device ${deviceId}`); }); // Handle alert subscriptions socket.on('subscribe_alerts', () => { socket.join('alerts'); logger.info(`User ${socket.user.username} subscribed to alerts`); }); socket.on('unsubscribe_alerts', () => { socket.leave('alerts'); logger.info(`User ${socket.user.username} unsubscribed from alerts`); }); // Handle disconnect socket.on('disconnect', () => { logger.info(`User disconnected: ${socket.user.username} (${socket.id})`); }); }); // Export socket functions for use in other modules return { // Emit device data updates emitDeviceData: (deviceId, data) => { io.to(`device:${deviceId}`).emit('device_data_update', { deviceId, data, timestamp: new Date().toISOString() }); }, // Emit alerts emitAlert: (alert) => { io.to('alerts').emit('new_alert', { ...alert, timestamp: new Date().toISOString() }); }, // Emit to admin users emitToAdmin: (event, data) => { io.to('admin').emit(event, { ...data, timestamp: new Date().toISOString() }); } }; }; module.exports = socketHandler;