mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-23 19:57:09 +00:00
393 lines
12 KiB
JavaScript
393 lines
12 KiB
JavaScript
PulseApp.socketHandler = (() => {
|
|
let socket = null;
|
|
let isConnected = false;
|
|
let reconnectAttempts = 0;
|
|
const maxReconnectAttempts = 10;
|
|
const reconnectDelay = 2000; // 2 seconds
|
|
|
|
function init() {
|
|
console.log('[Socket] Initializing socket connection...');
|
|
createSocket();
|
|
}
|
|
|
|
function createSocket() {
|
|
if (socket) {
|
|
socket.removeAllListeners();
|
|
socket.disconnect();
|
|
}
|
|
|
|
socket = io();
|
|
window.socket = socket; // Make socket available globally for alerts
|
|
|
|
setupEventListeners();
|
|
}
|
|
|
|
function setupEventListeners() {
|
|
socket.on('connect', handleConnect);
|
|
socket.on('disconnect', handleDisconnect);
|
|
socket.on('rawData', handleRawData);
|
|
socket.on('initialState', handleInitialState);
|
|
socket.on('requestError', handleRequestError);
|
|
|
|
// Enhanced monitoring events
|
|
socket.on('alert', handleAlert);
|
|
socket.on('alertResolved', handleAlertResolved);
|
|
|
|
// Development features
|
|
socket.on('hotReload', handleHotReload);
|
|
|
|
// Handle connection errors
|
|
socket.on('connect_error', handleConnectError);
|
|
socket.on('reconnect', handleReconnect);
|
|
socket.on('reconnect_error', handleReconnectError);
|
|
socket.on('reconnect_failed', handleReconnectFailed);
|
|
}
|
|
|
|
function handleConnect() {
|
|
console.log('[Socket] Connected to server');
|
|
isConnected = true;
|
|
reconnectAttempts = 0;
|
|
updateConnectionStatus('connected');
|
|
|
|
// Request initial data
|
|
socket.emit('requestData');
|
|
}
|
|
|
|
function handleDisconnect(reason) {
|
|
console.log('[Socket] Disconnected from server:', reason);
|
|
isConnected = false;
|
|
updateConnectionStatus('disconnected');
|
|
|
|
// If it's not a planned disconnect, try to reconnect
|
|
if (reason !== 'io client disconnect') {
|
|
attemptReconnect();
|
|
}
|
|
}
|
|
|
|
function handleRawData(data) {
|
|
try {
|
|
// Update main application state
|
|
if (PulseApp.state) {
|
|
PulseApp.state.updateState(data);
|
|
}
|
|
|
|
// Update alerts system with new state
|
|
if (PulseApp.alerts && data.alerts) {
|
|
PulseApp.alerts.updateAlertsFromState(data);
|
|
}
|
|
|
|
// Process UI updates based on tab
|
|
updateUIFromData(data);
|
|
|
|
} catch (error) {
|
|
console.error('[Socket] Error processing raw data:', error);
|
|
}
|
|
}
|
|
|
|
function handleInitialState(data) {
|
|
console.log('[Socket] Received initial state:', data);
|
|
|
|
try {
|
|
if (PulseApp.state) {
|
|
PulseApp.state.updateState(data);
|
|
}
|
|
|
|
updateUIFromData(data);
|
|
|
|
} catch (error) {
|
|
console.error('[Socket] Error processing initial state:', error);
|
|
}
|
|
}
|
|
|
|
function handleRequestError(error) {
|
|
console.error('[Socket] Request error:', error);
|
|
updateConnectionStatus('error');
|
|
}
|
|
|
|
function handleAlert(alert) {
|
|
console.log('[Socket] Received alert:', alert);
|
|
|
|
// Forward to alerts handler
|
|
if (PulseApp.alerts) {
|
|
// The alerts handler will be called directly from its socket listeners
|
|
// This is just for any additional processing
|
|
}
|
|
}
|
|
|
|
function handleAlertResolved(alert) {
|
|
console.log('[Socket] Alert resolved:', alert);
|
|
|
|
// Forward to alerts handler
|
|
if (PulseApp.alerts) {
|
|
// The alerts handler will be called directly from its socket listeners
|
|
// This is just for any additional processing
|
|
}
|
|
}
|
|
|
|
function handleHotReload() {
|
|
console.log('[Socket] Hot reload triggered');
|
|
if (process.env.NODE_ENV === 'development') {
|
|
window.location.reload();
|
|
}
|
|
}
|
|
|
|
function handleConnectError(error) {
|
|
console.error('[Socket] Connection error:', error);
|
|
updateConnectionStatus('error');
|
|
}
|
|
|
|
function handleReconnect() {
|
|
console.log('[Socket] Reconnected successfully');
|
|
reconnectAttempts = 0;
|
|
updateConnectionStatus('connected');
|
|
}
|
|
|
|
function handleReconnectError(error) {
|
|
console.error('[Socket] Reconnection error:', error);
|
|
reconnectAttempts++;
|
|
updateConnectionStatus('reconnecting');
|
|
}
|
|
|
|
function handleReconnectFailed() {
|
|
console.error('[Socket] Reconnection failed - max attempts reached');
|
|
updateConnectionStatus('failed');
|
|
}
|
|
|
|
function attemptReconnect() {
|
|
if (reconnectAttempts < maxReconnectAttempts) {
|
|
reconnectAttempts++;
|
|
updateConnectionStatus('reconnecting');
|
|
|
|
setTimeout(() => {
|
|
console.log(`[Socket] Attempting to reconnect... (${reconnectAttempts}/${maxReconnectAttempts})`);
|
|
socket.connect();
|
|
}, reconnectDelay * reconnectAttempts); // Exponential backoff
|
|
} else {
|
|
updateConnectionStatus('failed');
|
|
}
|
|
}
|
|
|
|
function updateConnectionStatus(status) {
|
|
const statusElement = document.getElementById('connection-status');
|
|
if (!statusElement) return;
|
|
|
|
// Clear previous classes
|
|
statusElement.className = statusElement.className
|
|
.replace(/\b(connected|disconnected|reconnecting|error|failed)\b/g, '')
|
|
.trim();
|
|
|
|
let statusText, statusClass;
|
|
|
|
switch (status) {
|
|
case 'connected':
|
|
statusText = 'Connected';
|
|
statusClass = 'connected text-xs px-2 py-1 rounded-full bg-green-100 dark:bg-green-900 text-green-600 dark:text-green-400';
|
|
break;
|
|
case 'disconnected':
|
|
statusText = 'Disconnected';
|
|
statusClass = 'disconnected text-xs px-2 py-1 rounded-full bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-400';
|
|
break;
|
|
case 'reconnecting':
|
|
statusText = `Reconnecting... (${reconnectAttempts}/${maxReconnectAttempts})`;
|
|
statusClass = 'reconnecting text-xs px-2 py-1 rounded-full bg-yellow-100 dark:bg-yellow-900 text-yellow-600 dark:text-yellow-400 animate-pulse';
|
|
break;
|
|
case 'error':
|
|
statusText = 'Connection Error';
|
|
statusClass = 'error text-xs px-2 py-1 rounded-full bg-red-100 dark:bg-red-900 text-red-600 dark:text-red-400';
|
|
break;
|
|
case 'failed':
|
|
statusText = 'Connection Failed';
|
|
statusClass = 'failed text-xs px-2 py-1 rounded-full bg-red-100 dark:bg-red-900 text-red-600 dark:text-red-400';
|
|
break;
|
|
default:
|
|
statusText = 'Unknown';
|
|
statusClass = 'text-xs px-2 py-1 rounded-full bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-400';
|
|
}
|
|
|
|
statusElement.textContent = statusText;
|
|
statusElement.className = statusClass;
|
|
}
|
|
|
|
function updateUIFromData(data) {
|
|
try {
|
|
// Hide loading overlay when we receive data
|
|
const loadingOverlay = document.getElementById('loading-overlay');
|
|
if (loadingOverlay && (data.nodes || data.vms || data.containers || data.pbs)) {
|
|
loadingOverlay.style.display = 'none';
|
|
}
|
|
|
|
// Update different UI sections based on current tab
|
|
const activeTab = document.querySelector('.tab.active');
|
|
if (!activeTab) return;
|
|
|
|
const tabName = activeTab.getAttribute('data-tab');
|
|
|
|
switch (tabName) {
|
|
case 'main':
|
|
updateMainTab(data);
|
|
break;
|
|
case 'storage':
|
|
updateStorageTab(data);
|
|
break;
|
|
case 'pbs':
|
|
updatePbsTab(data);
|
|
break;
|
|
case 'backups':
|
|
updateBackupsTab(data);
|
|
break;
|
|
}
|
|
|
|
// Update performance indicators if available
|
|
updatePerformanceIndicators(data);
|
|
|
|
} catch (error) {
|
|
console.error('[Socket] Error updating UI from data:', error);
|
|
}
|
|
}
|
|
|
|
function updateMainTab(data) {
|
|
try {
|
|
// Update node summary cards
|
|
if (PulseApp.ui && PulseApp.ui.nodes && data.nodes) {
|
|
PulseApp.ui.nodes.updateNodeSummaryCards(data.nodes);
|
|
}
|
|
|
|
// Update main dashboard
|
|
if (PulseApp.ui && PulseApp.ui.dashboard) {
|
|
PulseApp.ui.dashboard.updateDashboardTable();
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('[Socket] Error updating main tab:', error);
|
|
}
|
|
}
|
|
|
|
function updateStorageTab(data) {
|
|
try {
|
|
if (PulseApp.ui && PulseApp.ui.storage && data.nodes) {
|
|
PulseApp.ui.storage.updateStorageInfo();
|
|
}
|
|
} catch (error) {
|
|
console.error('[Socket] Error updating storage tab:', error);
|
|
}
|
|
}
|
|
|
|
function updatePbsTab(data) {
|
|
try {
|
|
if (PulseApp.ui && PulseApp.ui.pbs && data.pbs) {
|
|
PulseApp.ui.pbs.updatePbsInfo(data.pbs);
|
|
}
|
|
} catch (error) {
|
|
console.error('[Socket] Error updating PBS tab:', error);
|
|
}
|
|
}
|
|
|
|
function updateBackupsTab(data) {
|
|
try {
|
|
if (PulseApp.ui && PulseApp.ui.backups && data.pbs) {
|
|
PulseApp.ui.backups.updateBackupsTab();
|
|
}
|
|
} catch (error) {
|
|
console.error('[Socket] Error updating backups tab:', error);
|
|
}
|
|
}
|
|
|
|
function updatePerformanceIndicators(data) {
|
|
try {
|
|
// Update performance stats if available
|
|
if (data.stats) {
|
|
updateStatsDisplay(data.stats);
|
|
}
|
|
|
|
// Update any health indicators
|
|
if (data.performance) {
|
|
updateHealthDisplay(data.performance);
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('[Socket] Error updating performance indicators:', error);
|
|
}
|
|
}
|
|
|
|
function updateStatsDisplay(stats) {
|
|
// Update various stats in the UI
|
|
try {
|
|
const statusText = document.getElementById('dashboard-status-text');
|
|
if (statusText && stats.totalGuests !== undefined) {
|
|
const runningText = stats.runningGuests ? `${stats.runningGuests} running` : '0 running';
|
|
const stoppedText = stats.stoppedGuests ? `${stats.stoppedGuests} stopped` : '0 stopped';
|
|
statusText.textContent = `${stats.totalGuests} total guests (${runningText}, ${stoppedText})`;
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('[Socket] Error updating stats display:', error);
|
|
}
|
|
}
|
|
|
|
function updateHealthDisplay(performance) {
|
|
// Update health indicators in the UI
|
|
try {
|
|
// Could add health indicators to the header or other parts of the UI
|
|
// For now, this is just a placeholder for future enhancements
|
|
|
|
} catch (error) {
|
|
console.error('[Socket] Error updating health display:', error);
|
|
}
|
|
}
|
|
|
|
// Manual data request
|
|
function requestData() {
|
|
if (socket && isConnected) {
|
|
socket.emit('requestData');
|
|
} else {
|
|
console.warn('[Socket] Cannot request data - not connected');
|
|
}
|
|
}
|
|
|
|
// Get connection status
|
|
function getConnectionStatus() {
|
|
return {
|
|
connected: isConnected,
|
|
reconnectAttempts,
|
|
socket: socket ? socket.id : null
|
|
};
|
|
}
|
|
|
|
// Manual reconnect
|
|
function reconnect() {
|
|
if (socket) {
|
|
reconnectAttempts = 0;
|
|
socket.disconnect();
|
|
socket.connect();
|
|
}
|
|
}
|
|
|
|
// Cleanup
|
|
function destroy() {
|
|
if (socket) {
|
|
socket.removeAllListeners();
|
|
socket.disconnect();
|
|
socket = null;
|
|
}
|
|
isConnected = false;
|
|
window.socket = null;
|
|
}
|
|
|
|
// Public API
|
|
return {
|
|
init,
|
|
requestData,
|
|
getConnectionStatus,
|
|
reconnect,
|
|
destroy
|
|
};
|
|
})();
|
|
|
|
// Auto-initialize when DOM is ready
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', PulseApp.socketHandler.init);
|
|
} else {
|
|
PulseApp.socketHandler.init();
|
|
}
|
|
|