diff --git a/commands/utility/diagnostique-ws.js b/commands/utility/diagnostique-ws.js index 47fcf60..24004d1 100644 --- a/commands/utility/diagnostique-ws.js +++ b/commands/utility/diagnostique-ws.js @@ -38,6 +38,7 @@ module.exports = { { name: '📡 État Connexion', value: status.wsState || 'N/A', inline: true }, { name: '🔄 En cours de connexion', value: status.isConnecting ? '⏳ Oui' : '✅ Non', inline: true }, { name: '💓 Heartbeat Actif', value: status.hasHeartbeat ? '✅ Oui' : '❌ Non', inline: true }, + { name: '⏱️ Timeout Heartbeat', value: status.hasHeartbeatTimeout ? '✅ Oui' : '❌ Non', inline: true }, { name: '🔍 Vérification Auto', value: status.hasCheckInterval ? '✅ Oui' : '❌ Non', inline: true }, { name: '⏱️ Reconnexion Planifiée', value: status.hasPendingReconnect ? '⏳ Oui' : '❌ Non', inline: true }, { name: '🔁 Délai Reconnexion', value: `${Math.round(status.reconnectDelayMs / 1000)}s`, inline: true }, @@ -49,6 +50,11 @@ module.exports = { embed.addFields({ name: '🕐 Dernière Connexion', value: status.connectionTimestamp, inline: false }); } + if (status.lastHeartbeatResponse) { + const timeSinceResponse = status.timeSinceLastResponse ? `${status.timeSinceLastResponse}s` : 'N/A'; + embed.addFields({ name: '💓 Dernière Réponse Serveur', value: `${status.lastHeartbeatResponse}\n⏱️ Il y a ${timeSinceResponse}`, inline: false }); + } + if (status.monitoringStartTimestamp) { embed.addFields({ name: '🚀 Démarrage Monitoring', value: status.monitoringStartTimestamp, inline: false }); } @@ -64,6 +70,9 @@ module.exports = { if (status.hasPendingReconnect) { recommendations += `ℹ️ Une reconnexion est prévue dans ${Math.round(status.reconnectDelayMs / 1000)}s.\n`; } + if (status.timeSinceLastResponse && status.timeSinceLastResponse > 45) { + recommendations += `⚠️ Aucune réponse du serveur depuis ${status.timeSinceLastResponse}s (timeout dans ${60 - status.timeSinceLastResponse}s)\n`; + } if (recommendations) { embed.addFields({ name: '💡 Recommandations', value: recommendations, inline: false }); diff --git a/consoleMonitor.js b/consoleMonitor.js index c089e04..7d3bb93 100644 --- a/consoleMonitor.js +++ b/consoleMonitor.js @@ -7,6 +7,8 @@ let ws = null; let reconnectTimeout = null; let client = null; let heartbeatInterval = null; +let heartbeatTimeout = null; // Timeout pour détecter si le serveur ne répond plus +let lastHeartbeatResponse = null; // Timestamp de la dernière réponse du serveur let checkInterval = null; let refreshCredentialsInterval = null; // Interval pour rafraîchir les credentials périodiquement let isMonitoring = false; @@ -316,13 +318,43 @@ const connectWebSocket = async (pterodactylToken, serverId) => { args: [null] })); + // Configurer le heartbeat if (heartbeatInterval) clearInterval(heartbeatInterval); + if (heartbeatTimeout) clearTimeout(heartbeatTimeout); + + lastHeartbeatResponse = Date.now(); + heartbeatInterval = setInterval(() => { if (ws && ws.readyState === WebSocket.OPEN) { - ws.send(JSON.stringify({ event: 'send heartbeat', args: [] })); + try { + ws.send(JSON.stringify({ event: 'send heartbeat', args: [] })); + console.log('💓 [WEBSOCKET] Heartbeat envoyé'); + + // Configurer un timeout pour détecter si le serveur ne répond plus + if (heartbeatTimeout) clearTimeout(heartbeatTimeout); + heartbeatTimeout = setTimeout(() => { + const timeSinceLastResponse = Date.now() - lastHeartbeatResponse; + if (timeSinceLastResponse > 60000) { // 1 minute sans réponse + console.error('💔 [WEBSOCKET] Timeout: aucune réponse du serveur depuis 1 minute'); + console.log('🔄 [WEBSOCKET] Fermeture et reconnexion du WebSocket...'); + if (ws) { + ws.close(); + } + } + }, 60000); + } catch (error) { + console.error('❌ [WEBSOCKET] Erreur lors de l\'envoi du heartbeat:', error.message); + } + } else { + console.warn('⚠️ [WEBSOCKET] Heartbeat impossible: WebSocket non ouvert (état:', ws ? ws.readyState : 'null', ')'); } }, 30000); - console.log('💓 [WEBSOCKET] Heartbeat configuré (30s)'); + console.log('💓 [WEBSOCKET] Heartbeat configuré (30s avec timeout de 60s)'); + } + + // Détecter les réponses du serveur (console output, status, etc) pour mettre à jour le lastHeartbeatResponse + if (message.event === 'console output' || message.event === 'status' || message.event === 'stats') { + lastHeartbeatResponse = Date.now(); } if (message.event === 'console output') { const log = message.args[0]; @@ -430,6 +462,11 @@ const connectWebSocket = async (pterodactylToken, serverId) => { console.log('💔 [WEBSOCKET] Heartbeat arrêté'); } + if (heartbeatTimeout) { + clearTimeout(heartbeatTimeout); + heartbeatTimeout = null; + } + // Planifier une reconnexion avec backoff (utile lors des redémarrages quotidiens du serveur) if (isMonitoring) { scheduleReconnect(); @@ -458,6 +495,11 @@ const stopWebSocketOnly = () => { heartbeatInterval = null; } + if (heartbeatTimeout) { + clearTimeout(heartbeatTimeout); + heartbeatTimeout = null; + } + if (refreshCredentialsInterval) { clearInterval(refreshCredentialsInterval); refreshCredentialsInterval = null; @@ -469,6 +511,7 @@ const stopWebSocketOnly = () => { } isConnecting = false; + lastHeartbeatResponse = null; }; const forceReconnectToRefreshCredentials = async () => { @@ -569,8 +612,11 @@ const getWebSocketStatus = () => { wsStateRaw: ws ? ws.readyState : null, connectionTimestamp: connectionTimestamp ? new Date(connectionTimestamp).toISOString() : null, monitoringStartTimestamp: monitoringStartTimestamp ? new Date(monitoringStartTimestamp).toISOString() : null, + lastHeartbeatResponse: lastHeartbeatResponse ? new Date(lastHeartbeatResponse).toISOString() : null, + timeSinceLastResponse: lastHeartbeatResponse ? Math.round((Date.now() - lastHeartbeatResponse) / 1000) : null, reconnectDelayMs, hasHeartbeat: !!heartbeatInterval, + hasHeartbeatTimeout: !!heartbeatTimeout, hasCheckInterval: !!checkInterval, hasRefreshInterval: !!refreshCredentialsInterval, hasPendingReconnect: !!reconnectTimeout