mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-10-23 21:22:16 +00:00
feat: Add WebSocket service with typed message handling, auto-reconnect, and subscription management
- Add WebSocketMessage, ServiceUpdate, SystemMetrics, AlertUpdate interfaces for typed message handling - Add WebSocketService class with connection management, 5 max reconnect attempts, exponential backoff (1s-30s delay) - Add subscribe/unsubscribe pattern with type-based message routing and listener cleanup - Add getWebSocketService singleton factory with auto-detect ws/wss protocol and disconn
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
export interface WebSocketMessage {
|
||||
type: string;
|
||||
data: any;
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
export interface ServiceUpdate {
|
||||
service: string;
|
||||
status: string;
|
||||
replicas: number;
|
||||
readyReplicas: number;
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
export interface SystemMetrics {
|
||||
cpu: number;
|
||||
memory: number;
|
||||
disk: number;
|
||||
network: number;
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
export interface AlertUpdate {
|
||||
id: string;
|
||||
severity: string;
|
||||
message: string;
|
||||
status: string;
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
export class WebSocketService {
|
||||
private ws: WebSocket | null = null;
|
||||
private reconnectAttempts = 0;
|
||||
private maxReconnectAttempts = 5;
|
||||
private reconnectDelay = 1000;
|
||||
private listeners: Map<string, ((data: any) => void)[]> = new Map();
|
||||
private isConnecting = false;
|
||||
|
||||
constructor(private url: string) {
|
||||
this.connect();
|
||||
}
|
||||
|
||||
private connect() {
|
||||
if (this.isConnecting || (this.ws && this.ws.readyState === WebSocket.OPEN)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isConnecting = true;
|
||||
|
||||
try {
|
||||
this.ws = new WebSocket(this.url);
|
||||
|
||||
this.ws.onopen = () => {
|
||||
console.log('WebSocket connected');
|
||||
this.isConnecting = false;
|
||||
this.reconnectAttempts = 0;
|
||||
this.reconnectDelay = 1000;
|
||||
};
|
||||
|
||||
this.ws.onmessage = (event) => {
|
||||
try {
|
||||
const message: WebSocketMessage = JSON.parse(event.data);
|
||||
this.handleMessage(message);
|
||||
} catch (error) {
|
||||
console.error('Failed to parse WebSocket message:', error);
|
||||
}
|
||||
};
|
||||
|
||||
this.ws.onclose = (event) => {
|
||||
console.log('WebSocket disconnected:', event.code, event.reason);
|
||||
this.isConnecting = false;
|
||||
this.handleReconnect();
|
||||
};
|
||||
|
||||
this.ws.onerror = (error) => {
|
||||
console.error('WebSocket error:', error);
|
||||
this.isConnecting = false;
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to create WebSocket connection:', error);
|
||||
this.isConnecting = false;
|
||||
this.handleReconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private handleReconnect() {
|
||||
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
||||
this.reconnectAttempts++;
|
||||
console.log(`Attempting to reconnect (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
|
||||
|
||||
setTimeout(() => {
|
||||
this.connect();
|
||||
}, this.reconnectDelay);
|
||||
|
||||
// Exponential backoff
|
||||
this.reconnectDelay = Math.min(this.reconnectDelay * 2, 30000);
|
||||
} else {
|
||||
console.error('Max reconnection attempts reached');
|
||||
}
|
||||
}
|
||||
|
||||
private handleMessage(message: WebSocketMessage) {
|
||||
const listeners = this.listeners.get(message.type);
|
||||
if (listeners) {
|
||||
listeners.forEach(listener => {
|
||||
try {
|
||||
listener(message.data);
|
||||
} catch (error) {
|
||||
console.error('Error in WebSocket listener:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public subscribe(type: string, callback: (data: any) => void) {
|
||||
if (!this.listeners.has(type)) {
|
||||
this.listeners.set(type, []);
|
||||
}
|
||||
this.listeners.get(type)!.push(callback);
|
||||
|
||||
// Return unsubscribe function
|
||||
return () => {
|
||||
const listeners = this.listeners.get(type);
|
||||
if (listeners) {
|
||||
const index = listeners.indexOf(callback);
|
||||
if (index > -1) {
|
||||
listeners.splice(index, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public send(message: WebSocketMessage) {
|
||||
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
||||
this.ws.send(JSON.stringify(message));
|
||||
} else {
|
||||
console.warn('WebSocket not connected, message not sent:', message);
|
||||
}
|
||||
}
|
||||
|
||||
public disconnect() {
|
||||
if (this.ws) {
|
||||
this.ws.close();
|
||||
this.ws = null;
|
||||
}
|
||||
this.listeners.clear();
|
||||
}
|
||||
|
||||
public isConnected(): boolean {
|
||||
return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
|
||||
}
|
||||
}
|
||||
|
||||
// Global WebSocket service instance
|
||||
let wsService: WebSocketService | null = null;
|
||||
|
||||
export function getWebSocketService(url?: string): WebSocketService {
|
||||
if (!wsService) {
|
||||
const wsUrl = url || `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws`;
|
||||
wsService = new WebSocketService(wsUrl);
|
||||
}
|
||||
return wsService;
|
||||
}
|
||||
|
||||
export function disconnectWebSocket() {
|
||||
if (wsService) {
|
||||
wsService.disconnect();
|
||||
wsService = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user