import { io } from 'socket.io-client'; class WebSocketManager { constructor() { this.socket = null; this.subscribers = new Map(); this.connectionStatus = 'disconnected'; this.connectionCallbacks = new Set(); this.connecting = false; } connect() { if (this.socket?.connected || this.connecting) { return this.socket; } this.connecting = true; this.socket = io(`${import.meta.env.VITE_BACK_WS_URL}/api/metrics-ws`, { transports: ['websocket'], reconnection: true, reconnectionAttempts: Infinity, reconnectionDelay: 1000, reconnectionDelayMax: 5000, }); this.socket.on('connect', () => { this.connectionStatus = 'connected'; this.connecting = false; this.notifyConnectionStatus(); }); this.socket.on('disconnect', (reason) => { this.connectionStatus = 'disconnected'; this.connecting = false; this.notifyConnectionStatus(); if (reason === 'io server disconnect') this.socket.connect(); }); this.socket.on('connect_error', (error) => { this.connectionStatus = 'error'; this.notifyConnectionStatus(); setTimeout(() => this.socket.connect(), 1000); }); this.socket.on('metrics-data', (response) => { const callbacks = this.subscribers.get(response.metric); if (callbacks) { callbacks.forEach(callback => callback(response.data)); } }); return this.socket; } subscribe(metricName, callback) { if (!this.socket?.connected) { this.connect(); } if (!this.subscribers.has(metricName)) { this.subscribers.set(metricName, new Set()); this.socket.emit('subscribe-metric', { metric: metricName, isSubscription: true // Флаг для подписки }); } this.subscribers.get(metricName).add(callback); return () => this.unsubscribe(metricName, callback); } unsubscribe(metricName, callback) { const callbacks = this.subscribers.get(metricName); if (callbacks) { callbacks.delete(callback); if (callbacks.size === 0) { this.subscribers.delete(metricName); this.socket.emit('unsubscribe-metric', { metric: metricName }); } } } getMetricsRange(metricName, start, end, step) { return new Promise((resolve, reject) => { const timer = setTimeout(() => { reject(new Error('Timeout while waiting for metrics data')); }, 10000); // Временный обработчик для разового запроса const tempHandler = (data) => { clearTimeout(timer); this.socket.off(`metrics-range-${metricName}`, tempHandler); resolve(data); }; this.socket.on(`metrics-range-${metricName}`, tempHandler); this.socket.emit('get-metrics', { metric: metricName, start, end, step, isRangeQuery: true // Флаг для разового запроса }); }); } onConnectionStatusChange(callback) { this.connectionCallbacks.add(callback); callback(this.connectionStatus); return () => this.connectionCallbacks.delete(callback); } notifyConnectionStatus() { this.connectionCallbacks.forEach(callback => callback(this.connectionStatus)); } } export const webSocketManager = new WebSocketManager();