"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const Logger_1 = __importDefault(require("../utils/Logger")); /** * Web客户端管理器 */ class WebClientManager { constructor(databaseService) { this.clients = new Map(); this.socketToClient = new Map(); this.deviceControllers = new Map(); // deviceId -> clientId // 🔧 添加请求速率限制 - 防止频繁重复请求 this.requestTimestamps = new Map(); // "clientId:deviceId" -> timestamp this.REQUEST_COOLDOWN = 2000; // 2秒内不允许重复请求(增加冷却时间) this.logger = new Logger_1.default('WebClientManager'); this.databaseService = databaseService; } /** * ✅ 清理所有客户端记录(服务器重启时调用) */ clearAllClients() { const clientCount = this.clients.size; this.clients.clear(); this.socketToClient.clear(); this.deviceControllers.clear(); this.requestTimestamps.clear(); this.logger.info(`🧹 已清理所有客户端记录: ${clientCount} 个客户端`); } /** * 设置Socket.IO实例 */ setSocketIO(io) { this.io = io; } /** * 添加Web客户端 */ addClient(clientInfo) { // 🔧 检查是否已有相同Socket ID的客户端记录 const existingClientId = this.socketToClient.get(clientInfo.socketId); if (existingClientId) { this.logger.warn(`⚠️ Socket ${clientInfo.socketId} 已有客户端记录 ${existingClientId},清理旧记录`); this.removeClient(existingClientId); } this.clients.set(clientInfo.id, clientInfo); this.socketToClient.set(clientInfo.socketId, clientInfo.id); this.logger.info(`Web客户端已添加: ${clientInfo.id} from ${clientInfo.ip}`); } /** * 移除Web客户端 */ removeClient(clientId) { const client = this.clients.get(clientId); if (client) { this.clients.delete(clientId); this.socketToClient.delete(client.socketId); // 如果客户端正在控制设备,释放控制权 if (client.controllingDeviceId) { this.logger.info(`🔓 客户端断开连接,自动释放设备控制权: ${clientId} -> ${client.controllingDeviceId}`); this.releaseDeviceControl(client.controllingDeviceId); } // 清理请求时间戳记录 const keysToDelete = Array.from(this.requestTimestamps.keys()).filter(key => key.startsWith(clientId + ':')); keysToDelete.forEach(key => this.requestTimestamps.delete(key)); this.logger.info(`Web客户端已移除: ${clientId}`); return true; } return false; } /** * 通过Socket ID移除客户端 */ removeClientBySocketId(socketId) { const clientId = this.socketToClient.get(socketId); if (clientId) { return this.removeClient(clientId); } return false; } /** * 获取客户端信息 */ getClient(clientId) { return this.clients.get(clientId); } /** * 通过Socket ID获取客户端 */ getClientBySocketId(socketId) { const clientId = this.socketToClient.get(socketId); return clientId ? this.clients.get(clientId) : undefined; } /** * 获取所有客户端 */ getAllClients() { return Array.from(this.clients.values()); } /** * 获取客户端数量 */ getClientCount() { return this.clients.size; } /** * 获取客户端Socket */ getClientSocket(clientId) { const client = this.clients.get(clientId); if (client && this.io) { return this.io.sockets.sockets.get(client.socketId); } return undefined; } /** * 请求控制设备 */ requestDeviceControl(clientId, deviceId) { // 🔧 防止频繁重复请求 const requestKey = `${clientId}:${deviceId}`; const now = Date.now(); const lastRequestTime = this.requestTimestamps.get(requestKey) || 0; if (now - lastRequestTime < this.REQUEST_COOLDOWN) { this.logger.debug(`🚫 请求过于频繁: ${clientId} -> ${deviceId} (间隔${now - lastRequestTime}ms < ${this.REQUEST_COOLDOWN}ms)`); return { success: false, message: '请求过于频繁,请稍后再试' }; } // 获取客户端信息 const client = this.clients.get(clientId); if (!client) { this.logger.error(`❌ 客户端不存在: ${clientId}`); return { success: false, message: '客户端不存在' }; } // ✅ 优化:先检查是否是重复请求(已经在控制此设备) const currentController = this.deviceControllers.get(deviceId); if (currentController === clientId) { this.logger.debug(`🔄 客户端 ${clientId} 重复请求控制设备 ${deviceId},已在控制中`); client.lastSeen = new Date(); // 更新请求时间戳,但返回成功(避免频繁日志) this.requestTimestamps.set(requestKey, now); return { success: true, message: '已在控制此设备' }; } // 记录请求时间戳(在检查重复控制后记录) this.requestTimestamps.set(requestKey, now); // 检查设备是否被其他客户端控制 if (currentController && currentController !== clientId) { const controllerClient = this.clients.get(currentController); this.logger.warn(`🚫 设备 ${deviceId} 控制权冲突: 当前控制者 ${currentController}, 请求者 ${clientId}`); return { success: false, message: `设备正在被其他客户端控制 (${controllerClient?.ip || 'unknown'})`, currentController }; } // 如果客户端已在控制其他设备,先释放 if (client.controllingDeviceId && client.controllingDeviceId !== deviceId) { this.logger.info(`🔄 客户端 ${clientId} 切换控制设备: ${client.controllingDeviceId} -> ${deviceId}`); this.releaseDeviceControl(client.controllingDeviceId); } // 建立控制关系 this.deviceControllers.set(deviceId, clientId); client.controllingDeviceId = deviceId; client.lastSeen = new Date(); // 🔐 如果客户端有用户ID,将权限持久化到数据库 if (client.userId && this.databaseService) { this.databaseService.grantUserDevicePermission(client.userId, deviceId, 'control'); this.logger.info(`🔐 用户 ${client.userId} 的设备 ${deviceId} 控制权限已持久化`); } this.logger.info(`🎮 客户端 ${clientId} 开始控制设备 ${deviceId}`); return { success: true, message: '控制权获取成功' }; } /** * 释放设备控制权 */ releaseDeviceControl(deviceId) { const controllerId = this.deviceControllers.get(deviceId); if (controllerId) { const client = this.clients.get(controllerId); if (client) { const previousDevice = client.controllingDeviceId; client.controllingDeviceId = undefined; this.logger.debug(`🔓 客户端 ${controllerId} 释放设备控制权: ${previousDevice}`); } else { this.logger.warn(`⚠️ 控制设备 ${deviceId} 的客户端 ${controllerId} 不存在,可能已断开`); } this.deviceControllers.delete(deviceId); this.logger.info(`🔓 设备 ${deviceId} 的控制权已释放 (之前控制者: ${controllerId})`); return true; } else { this.logger.debug(`🤷 设备 ${deviceId} 没有被控制,无需释放`); return false; } } /** * 获取设备控制者 */ getDeviceController(deviceId) { return this.deviceControllers.get(deviceId); } /** * 检查客户端是否有设备控制权 */ hasDeviceControl(clientId, deviceId) { // 🛡️ 记录权限检查审计日志 this.logPermissionOperation(clientId, deviceId, '权限检查'); // 🔐 获取客户端信息 const client = this.clients.get(clientId); // 🆕 超级管理员绕过权限检查 if (client?.username) { const superAdminUsername = process.env.SUPERADMIN_USERNAME || 'superadmin'; if (client.username === superAdminUsername) { this.logger.info(`🔐 超级管理员 ${client.username} 绕过设备控制权限检查`); return true; } } // 🔐 首先检查内存中的控制权 const memoryControl = this.deviceControllers.get(deviceId) === clientId; if (memoryControl) { return true; } // 🔐 如果内存中没有控制权,检查数据库中的用户权限 if (client?.userId && this.databaseService) { const hasPermission = this.databaseService.hasUserDevicePermission(client.userId, deviceId, 'control'); if (hasPermission) { // 🔐 如果用户有权限,自动建立控制关系(允许权限恢复) this.deviceControllers.set(deviceId, clientId); client.controllingDeviceId = deviceId; this.logger.info(`🔐 用户 ${client.userId} 基于数据库权限获得设备 ${deviceId} 控制权`); return true; } } return false; } /** * 向指定客户端发送消息 */ sendToClient(clientId, event, data) { const socket = this.getClientSocket(clientId); if (socket) { socket.emit(event, data); return true; } return false; } /** * 向所有客户端广播消息 */ broadcastToAll(event, data) { if (this.io) { let activeClients = 0; // 只向Web客户端广播,且过滤掉已断开的连接 for (const [socketId, clientId] of this.socketToClient.entries()) { const socket = this.io.sockets.sockets.get(socketId); if (socket && socket.connected) { socket.emit(event, data); activeClients++; } } this.logger.debug(`📡 广播消息到 ${activeClients} 个活跃Web客户端: ${event}`); } } /** * 向控制指定设备的客户端发送消息 */ sendToDeviceController(deviceId, event, data) { const controllerId = this.deviceControllers.get(deviceId); if (controllerId) { return this.sendToClient(controllerId, event, data); } return false; } /** * 更新客户端活跃时间 */ updateClientActivity(socketId) { const clientId = this.socketToClient.get(socketId); if (clientId) { const client = this.clients.get(clientId); if (client) { client.lastSeen = new Date(); } } } /** * 清理不活跃的客户端 */ cleanupInactiveClients(timeoutMs = 600000) { const now = Date.now(); const clientsToRemove = []; for (const [clientId, client] of this.clients.entries()) { if (now - client.lastSeen.getTime() > timeoutMs) { clientsToRemove.push(clientId); } } clientsToRemove.forEach(clientId => { this.removeClient(clientId); }); if (clientsToRemove.length > 0) { this.logger.info(`已清理 ${clientsToRemove.length} 个不活跃的Web客户端`); } } /** * 获取客户端统计信息 */ getClientStats() { const clients = Array.from(this.clients.values()); return { total: clients.length, controlling: clients.filter(c => c.controllingDeviceId).length, idle: clients.filter(c => !c.controllingDeviceId).length, }; } /** * 🔐 恢复用户的设备权限 */ restoreUserPermissions(userId, clientId) { if (!this.databaseService) { this.logger.warn('数据库服务未初始化,无法恢复用户权限'); return; } try { // 获取用户的所有设备权限 const permissions = this.databaseService.getUserDevicePermissions(userId); if (permissions.length > 0) { this.logger.info(`🔐 为用户 ${userId} 恢复 ${permissions.length} 个设备权限`); // 恢复第一个设备的控制权(优先恢复用户之前的权限) for (const permission of permissions) { if (permission.permissionType === 'control') { // 直接恢复权限,不检查冲突(因为这是用户自己的权限恢复) this.deviceControllers.set(permission.deviceId, clientId); const client = this.clients.get(clientId); if (client) { client.controllingDeviceId = permission.deviceId; this.logger.info(`🔐 用户 ${userId} 的设备 ${permission.deviceId} 控制权已恢复`); break; // 只恢复第一个设备 } } } } } catch (error) { this.logger.error('恢复用户权限失败:', error); } } /** * 🔐 设置客户端用户信息 */ setClientUserInfo(clientId, userId, username) { const client = this.clients.get(clientId); if (client) { client.userId = userId; client.username = username; this.logger.info(`🔐 客户端 ${clientId} 用户信息已设置: ${username} (${userId})`); // 🛡️ 记录安全审计日志 this.logger.info(`🛡️ 安全审计: 客户端 ${clientId} (IP: ${client.ip}) 绑定用户 ${username} (${userId})`); } } /** * 🛡️ 记录权限操作审计日志 */ logPermissionOperation(clientId, deviceId, operation) { const client = this.clients.get(clientId); if (client) { this.logger.info(`🛡️ 权限审计: 客户端 ${clientId} (用户: ${client.username || 'unknown'}, IP: ${client.ip}) 执行 ${operation} 操作,目标设备: ${deviceId}`); } } } exports.default = WebClientManager; //# sourceMappingURL=WebClientManager.js.map