197 lines
4.6 KiB
JavaScript
197 lines
4.6 KiB
JavaScript
/**
|
|
* WebSocket client for real-time consensus updates
|
|
*/
|
|
|
|
class ConsensusWebSocket {
|
|
constructor() {
|
|
this.ws = null;
|
|
this.reconnectAttempts = 0;
|
|
this.maxReconnectAttempts = 10;
|
|
this.reconnectDelay = 1000;
|
|
this.listeners = new Map();
|
|
this.isConnected = false;
|
|
this.reconnectTimeout = null;
|
|
}
|
|
|
|
/**
|
|
* Connect to WebSocket server
|
|
*/
|
|
connect() {
|
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
const wsUrl = `${protocol}//${window.location.host}/ws`;
|
|
|
|
try {
|
|
this.ws = new WebSocket(wsUrl);
|
|
|
|
this.ws.onopen = () => {
|
|
this.isConnected = true;
|
|
this.reconnectAttempts = 0;
|
|
this.emit('connected');
|
|
this.updateStatus(true);
|
|
};
|
|
|
|
this.ws.onmessage = (event) => {
|
|
try {
|
|
const message = JSON.parse(event.data);
|
|
this.handleMessage(message);
|
|
} catch (err) {
|
|
console.error('Error parsing WebSocket message:', err);
|
|
}
|
|
};
|
|
|
|
this.ws.onerror = (error) => {
|
|
console.error('WebSocket error:', error);
|
|
this.emit('error', error);
|
|
};
|
|
|
|
this.ws.onclose = () => {
|
|
this.isConnected = false;
|
|
this.updateStatus(false);
|
|
this.emit('disconnected');
|
|
this.attemptReconnect();
|
|
};
|
|
} catch (err) {
|
|
console.error('Error connecting to WebSocket:', err);
|
|
this.attemptReconnect();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle incoming messages
|
|
*/
|
|
handleMessage(message) {
|
|
const { type, data } = message;
|
|
|
|
switch (type) {
|
|
case 'init':
|
|
this.emit('init', data);
|
|
break;
|
|
case 'update':
|
|
this.emit('update', data);
|
|
break;
|
|
case 'consensus-update':
|
|
// Real-time consensus change detected
|
|
this.emit('consensus-update', data);
|
|
break;
|
|
case 'domain-added':
|
|
this.emit('domain-added', data);
|
|
break;
|
|
case 'domain-removed':
|
|
this.emit('domain-removed', data);
|
|
break;
|
|
default:
|
|
// Unknown message type
|
|
console.debug('Unknown WebSocket message type:', type);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send message to server
|
|
*/
|
|
send(type, data = {}) {
|
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
this.ws.send(JSON.stringify({ type, ...data }));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attempt to reconnect
|
|
*/
|
|
attemptReconnect() {
|
|
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
console.error('Max reconnection attempts reached');
|
|
return;
|
|
}
|
|
|
|
if (this.reconnectTimeout) {
|
|
clearTimeout(this.reconnectTimeout);
|
|
}
|
|
|
|
this.reconnectAttempts++;
|
|
const delay = Math.min(this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1), 30000);
|
|
|
|
this.reconnectTimeout = setTimeout(() => {
|
|
console.log(`Attempting to reconnect (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
|
|
this.connect();
|
|
}, delay);
|
|
}
|
|
|
|
/**
|
|
* Update connection status indicator
|
|
*/
|
|
updateStatus(connected) {
|
|
const indicator = document.getElementById('statusIndicator');
|
|
const statusText = document.getElementById('statusText');
|
|
|
|
if (indicator && statusText) {
|
|
if (connected) {
|
|
indicator.classList.remove('disconnected');
|
|
statusText.textContent = 'Connected';
|
|
} else {
|
|
indicator.classList.add('disconnected');
|
|
statusText.textContent = 'Disconnected - Reconnecting...';
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add event listener
|
|
*/
|
|
on(event, callback) {
|
|
if (!this.listeners.has(event)) {
|
|
this.listeners.set(event, []);
|
|
}
|
|
this.listeners.get(event).push(callback);
|
|
}
|
|
|
|
/**
|
|
* Remove event listener
|
|
*/
|
|
off(event, callback) {
|
|
if (this.listeners.has(event)) {
|
|
const callbacks = this.listeners.get(event);
|
|
const index = callbacks.indexOf(callback);
|
|
if (index > -1) {
|
|
callbacks.splice(index, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Emit event to listeners
|
|
*/
|
|
emit(event, data) {
|
|
if (this.listeners.has(event)) {
|
|
this.listeners.get(event).forEach(callback => {
|
|
try {
|
|
callback(data);
|
|
} catch (err) {
|
|
console.error(`Error in WebSocket event listener for ${event}:`, err);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Disconnect
|
|
*/
|
|
disconnect() {
|
|
if (this.reconnectTimeout) {
|
|
clearTimeout(this.reconnectTimeout);
|
|
this.reconnectTimeout = null;
|
|
}
|
|
|
|
if (this.ws) {
|
|
this.ws.close();
|
|
this.ws = null;
|
|
}
|
|
|
|
this.isConnected = false;
|
|
this.listeners.clear();
|
|
}
|
|
}
|
|
|
|
// Create singleton instance
|
|
window.wsClient = new ConsensusWebSocket();
|
|
|