Files
server/dist/managers/WebClientManager.js

385 lines
15 KiB
JavaScript
Raw Permalink Normal View History

2026-02-09 16:34:01 +08:00
"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