/** * WebSocket сервис для отслеживания деплоя модулей * Обеспечивает реальное время обновления статуса деплоя */ const WebSocket = require('ws'); class DeploymentWebSocketService { constructor() { this.wss = null; this.clients = new Map(); // Map для хранения клиентов по dleAddress this.deploymentSessions = new Map(); // Map для хранения сессий деплоя } /** * Инициализация WebSocket сервера */ initialize(server) { // Теперь мы не создаем отдельный WebSocket сервер, // а работаем с основным WebSocket сервером через wsHub console.log('[DeploymentWS] WebSocket сервис для деплоя инициализирован'); } /** * Обработка входящих сообщений */ handleMessage(ws, data) { switch (data.type) { case 'subscribe': this.subscribeToDeployment(ws, data.dleAddress); break; case 'unsubscribe': this.unsubscribeFromDeployment(ws, data.dleAddress); break; default: this.sendError(ws, 'Неизвестный тип сообщения'); } } /** * Подписка на деплой для конкретного DLE */ subscribeToDeployment(ws, dleAddress) { if (!dleAddress) { this.sendError(ws, 'Адрес DLE обязателен'); return; } console.log(`[DeploymentWS] Подписка на деплой DLE: ${dleAddress}`); // Сохраняем клиента ws.dleAddress = dleAddress; if (!this.clients.has(dleAddress)) { this.clients.set(dleAddress, new Set()); } this.clients.get(dleAddress).add(ws); // Отправляем подтверждение подписки this.sendToClient(ws, { type: 'subscribed', dleAddress: dleAddress, message: 'Подписка на деплой активирована' }); // Если есть активная сессия деплоя, отправляем текущий статус if (this.deploymentSessions.has(dleAddress)) { const session = this.deploymentSessions.get(dleAddress); this.sendToClient(ws, { type: 'deployment_status', dleAddress: dleAddress, ...session }); } } /** * Отписка от деплоя */ unsubscribeFromDeployment(ws, dleAddress) { if (ws.dleAddress === dleAddress) { this.removeClient(ws); } } /** * Удаление клиента из всех подписок */ removeClient(ws) { if (ws.dleAddress && this.clients.has(ws.dleAddress)) { this.clients.get(ws.dleAddress).delete(ws); if (this.clients.get(ws.dleAddress).size === 0) { this.clients.delete(ws.dleAddress); // Очищаем сессию деплоя если нет активных клиентов if (this.deploymentSessions.has(ws.dleAddress)) { console.log(`[DeploymentWS] Очистка сессии деплоя для DLE: ${ws.dleAddress}`); this.deploymentSessions.delete(ws.dleAddress); } } } } /** * Начало сессии деплоя */ startDeploymentSession(dleAddress, moduleType) { const session = { dleAddress: dleAddress, moduleType: moduleType, status: 'starting', progress: 0, step: 0, message: 'Инициализация деплоя...', logs: [], startTime: new Date().toISOString() }; this.deploymentSessions.set(dleAddress, session); console.log(`[DeploymentWS] Начало деплоя: ${moduleType} для DLE ${dleAddress}`); this.broadcastToDLE(dleAddress, { type: 'deployment_started', ...session }); return session; } /** * Обновление статуса деплоя */ updateDeploymentStatus(dleAddress, updates) { const session = this.deploymentSessions.get(dleAddress); if (!session) { console.warn(`[DeploymentWS] Сессия деплоя не найдена для DLE: ${dleAddress}`); return; } // Обновляем сессию Object.assign(session, updates); session.lastUpdate = new Date().toISOString(); console.log(`[DeploymentWS] Обновление статуса деплоя DLE ${dleAddress}:`, updates); this.broadcastToDLE(dleAddress, { type: 'deployment_status', ...session }); } /** * Добавление лога в сессию деплоя */ addDeploymentLog(dleAddress, logType, message) { const session = this.deploymentSessions.get(dleAddress); if (!session) { console.warn(`[DeploymentWS] Сессия деплоя не найдена для DLE: ${dleAddress}`); return; } const logEntry = { type: logType, message: message, timestamp: new Date().toISOString() }; session.logs.push(logEntry); console.log(`[DeploymentWS] Лог деплоя DLE ${dleAddress}:`, logEntry); this.broadcastToDLE(dleAddress, { type: 'deployment_log', dleAddress: dleAddress, log: logEntry }); } /** * Завершение сессии деплоя */ finishDeploymentSession(dleAddress, success, message = null) { const session = this.deploymentSessions.get(dleAddress); if (!session) { console.warn(`[DeploymentWS] Сессия деплоя не найдена для DLE: ${dleAddress}`); return; } session.status = success ? 'completed' : 'failed'; session.progress = success ? 100 : session.progress; session.endTime = new Date().toISOString(); session.message = message || (success ? 'Деплой завершен успешно' : 'Деплой завершен с ошибкой'); console.log(`[DeploymentWS] Завершение деплоя DLE ${dleAddress}: ${session.status}`); this.broadcastToDLE(dleAddress, { type: 'deployment_finished', ...session }); // Удаляем сессию через 30 секунд setTimeout(() => { this.deploymentSessions.delete(dleAddress); }, 30000); } /** * Отправка сообщения конкретному клиенту */ sendToClient(ws, message) { if (ws && ws.readyState === WebSocket.OPEN) { try { ws.send(JSON.stringify(message)); } catch (error) { console.error('[DeploymentWS] Ошибка отправки сообщения клиенту:', error); } } } /** * Отправка сообщения всем клиентам конкретного DLE */ broadcastToDLE(dleAddress, message) { const clients = this.clients.get(dleAddress); if (clients) { clients.forEach(ws => { this.sendToClient(ws, message); }); } } /** * Отправка ошибки клиенту */ sendError(ws, errorMessage) { this.sendToClient(ws, { type: 'error', message: errorMessage }); } /** * Уведомление об обновлении модулей */ notifyModulesUpdated(dleAddress) { console.log(`[DeploymentWS] Уведомление об обновлении модулей для DLE: ${dleAddress}`); this.broadcastToDLE(dleAddress, { type: 'modules_updated', dleAddress: dleAddress, timestamp: new Date().toISOString() }); } /** * Уведомление о верификации модуля */ notifyModuleVerified(dleAddress, moduleType, networkName) { console.log(`[DeploymentWS] Уведомление о верификации модуля ${moduleType} в сети ${networkName} для DLE: ${dleAddress}`); this.broadcastToDLE(dleAddress, { type: 'module_verified', dleAddress: dleAddress, moduleType: moduleType, networkName: networkName, timestamp: new Date().toISOString() }); } /** * Уведомление об изменении статуса модуля */ notifyModuleStatusChanged(dleAddress, moduleType, status) { console.log(`[DeploymentWS] Уведомление об изменении статуса модуля ${moduleType} на ${status} для DLE: ${dleAddress}`); this.broadcastToDLE(dleAddress, { type: 'module_status_changed', dleAddress: dleAddress, moduleType: moduleType, status: status, timestamp: new Date().toISOString() }); } /** * Получение статистики */ getStats() { const totalClients = Array.from(this.clients.values()).reduce((sum, clients) => sum + clients.size, 0); return { activeConnections: totalClients, activeSessions: this.deploymentSessions.size, subscriptions: this.clients.size }; } } // Создаем единственный экземпляр сервиса const deploymentWebSocketService = new DeploymentWebSocketService(); module.exports = deploymentWebSocketService;