296 lines
8.7 KiB
JavaScript
296 lines
8.7 KiB
JavaScript
/**
|
||
* 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);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Начало сессии деплоя
|
||
*/
|
||
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;
|