From a86f14fce8b8bfd648c2b94a197722d23dc460e9 Mon Sep 17 00:00:00 2001 From: Louis Mazin Date: Sun, 1 Feb 2026 19:50:27 +0100 Subject: [PATCH] test --- RAM_MONITOR_README.md | 146 +++++++++++++++++++++++++ commands/server/ram-status.js | 60 ++++++++++ consoleMonitor.js | 8 +- index.js | 7 +- ramMonitor.js | 199 ++++++++++++++++++++++++++++++++++ 5 files changed, 418 insertions(+), 2 deletions(-) create mode 100644 RAM_MONITOR_README.md create mode 100644 commands/server/ram-status.js create mode 100644 ramMonitor.js diff --git a/RAM_MONITOR_README.md b/RAM_MONITOR_README.md new file mode 100644 index 0000000..f9ace98 --- /dev/null +++ b/RAM_MONITOR_README.md @@ -0,0 +1,146 @@ +# 🔍 Moniteur de RAM - Palworld Server + +## Description + +Le moniteur de RAM surveille automatiquement l'utilisation de la mémoire du serveur Palworld. Lorsque le seuil de **19 Go** est dépassé, le serveur est automatiquement redémarré pour éviter les problèmes de performance ou de crash. + +## Fonctionnalités + +### Surveillance automatique +- ✅ Vérification de la RAM toutes les 60 secondes +- ✅ Seuil de déclenchement : **19 Go** +- ✅ Redémarrage automatique si le seuil est dépassé +- ✅ Resynchronisation automatique du WebSocket après redémarrage + +### Processus de redémarrage +Lorsque le seuil est atteint, le système effectue les étapes suivantes : + +1. 💾 **Sauvegarde** du serveur +2. 📢 **Annonce** aux joueurs (broadcast : "Redémarrage automatique - RAM élevée - 30 secondes") +3. ⏹️ **Arrêt** du serveur (signal stop) +4. ⏳ **Attente** de 25 secondes +5. 🚀 **Redémarrage** du serveur (signal start) +6. ⏳ **Vérification** jusqu'à ce que le serveur soit opérationnel +7. 🔄 **Resynchronisation** du WebSocket (après 5 secondes supplémentaires) + +### Commande Discord + +#### `/ram-status` +Affiche l'état actuel de la RAM du serveur. + +**Permissions requises :** Rôle Rygainland + +**Informations affichées :** +- 📊 Utilisation actuelle (en Go et pourcentage) +- ⚠️ Seuil de redémarrage configuré +- 🔄 État du serveur (running, stopped, etc.) +- 💡 Niveau d'alerte (Normal 🟢 / Élevé 🟡 / Critique 🔴) +- ✅ État de la surveillance automatique + +**Exemple d'affichage :** +``` +🟢 État de la RAM du serveur Palworld + +📊 Utilisation actuelle: 15.32 Go / 19.00 Go (80.6%) +⚠️ Seuil de redémarrage: 19.00 Go +🔄 État du serveur: running +💡 Niveau: Élevée + +✅ Surveillance automatique active +``` + +## Configuration + +### Variables d'environnement requises + +Le moniteur utilise les variables d'environnement suivantes (déjà configurées) : + +```env +PTERODACTYL_API_URL=https://panel.louismazin.ovh +PTERODACTYL_API_TOKEN=votre_token +PTERODACTYL_SERVER_ID=ae4a628f +``` + +### Paramètres modifiables + +Dans le fichier `ramMonitor.js` : + +```javascript +const RAM_THRESHOLD_MB = 19 * 1024; // Seuil en MB (19 Go par défaut) +const CHECK_INTERVAL_MS = 60 * 1000; // Intervalle de vérification (60 secondes) +``` + +## Intégration + +### Fichiers modifiés/créés + +1. **`ramMonitor.js`** (nouveau) + - Module principal de surveillance + - Gestion du redémarrage automatique + +2. **`index.js`** (modifié) + - Ajout de `startRAMMonitoring(forceWebSocketReconnect)` + - Import du module ramMonitor + +3. **`consoleMonitor.js`** (modifié) + - Ajout de la fonction `forceWebSocketReconnect()` + - Export de la fonction pour utilisation par ramMonitor + +4. **`commands/server/ram-status.js`** (nouveau) + - Commande Discord pour consulter l'état de la RAM + +## Logs + +Le moniteur affiche des logs détaillés dans la console : + +``` +🚀 [RAM Monitor] Démarrage de la surveillance RAM (seuil: 19 Go) +⏱️ [RAM Monitor] Intervalle de vérification: 60s +🔍 [RAM Monitor] Utilisation RAM: 15.32 Go / État: running +⚠️ [RAM Monitor] SEUIL DÉPASSÉ ! 19.54 Go > 19 Go +🔄 [RAM Monitor] Déclenchement du redémarrage automatique... +💾 [RAM Monitor] Sauvegarde du serveur... +📢 [RAM Monitor] Annonce du redémarrage... +⏹️ [RAM Monitor] Arrêt du serveur... +⏳ [RAM Monitor] Attente de 25 secondes... +🚀 [RAM Monitor] Redémarrage du serveur... +✅ [RAM Monitor] Serveur redémarré avec succès ! +🔄 [RAM Monitor] Resynchronisation du WebSocket... +``` + +## Sécurité + +- 🔒 Un seul redémarrage ne peut être en cours à la fois (protection via flag `isRebooting`) +- 🔒 Timeout maximum de 2.5 minutes pour le redémarrage +- 🔒 Vérification de l'état du serveur avant déclenchement (doit être `running`) +- 🔒 Commande Discord protégée par rôle + +## Notes techniques + +- Le serveur Pterodactyl fournit `memory_bytes` qui est converti en Mo puis Go +- La surveillance ne se déclenche que si le serveur est en état `running` +- Le WebSocket est resynchronisé après 5 secondes de stabilisation post-redémarrage +- Les websockets existants sont proprement fermés avant reconnexion + +## Dépannage + +### La surveillance ne démarre pas +- Vérifier les variables d'environnement `PTERODACTYL_API_TOKEN` et `PTERODACTYL_SERVER_ID` +- Vérifier les permissions API Pterodactyl + +### Le redémarrage ne se déclenche pas +- Vérifier que le seuil est correctement configuré +- Consulter les logs pour voir l'utilisation actuelle +- Utiliser `/ram-status` pour diagnostiquer + +### Le WebSocket ne se reconnecte pas +- Vérifier que `consoleMonitor.js` est bien démarré +- Les logs indiqueront les tentatives de reconnexion + +## Désactivation + +Pour désactiver temporairement la surveillance, commenter dans `index.js` : + +```javascript +// startRAMMonitoring(forceWebSocketReconnect); +``` diff --git a/commands/server/ram-status.js b/commands/server/ram-status.js new file mode 100644 index 0000000..3f9eb91 --- /dev/null +++ b/commands/server/ram-status.js @@ -0,0 +1,60 @@ +const { SlashCommandBuilder } = require('discord.js'); +const { checkRAMUsage, getMonitoringStatus } = require('../../ramMonitor.js'); + +module.exports = { + data: new SlashCommandBuilder() + .setName('ram-status') + .setDescription('Affiche l\'utilisation actuelle de la RAM du serveur Palworld'), + async execute(interaction) { + + if (!interaction.member.roles.cache.has('1444684935632912394')) { + await interaction.reply({ content: '❌ Il faut avoir le rôle Rygainland pour pouvoir utiliser cette commande.', flags: 64 }); + return; + } + + await interaction.deferReply(); + + try { + // Récupérer les informations sur la RAM + const ramInfo = await checkRAMUsage(); + const monitorStatus = getMonitoringStatus(); + + if (!ramInfo) { + await interaction.editReply('❌ Impossible de récupérer les informations de RAM du serveur.'); + return; + } + + const { ramUsedGB, currentState } = ramInfo; + const thresholdGB = (monitorStatus.threshold / 1024).toFixed(2); + const percentUsed = ((ramInfo.ramUsedMB / monitorStatus.threshold) * 100).toFixed(1); + + // Construire le message avec indicateur visuel + let indicator; + let color; + if (percentUsed < 70) { + indicator = '🟢'; + color = 'Normale'; + } else if (percentUsed < 90) { + indicator = '🟡'; + color = 'Élevée'; + } else { + indicator = '🔴'; + color = 'Critique'; + } + + const message = `${indicator} **État de la RAM du serveur Palworld**\n\n` + + `📊 **Utilisation actuelle:** ${ramUsedGB} Go / ${thresholdGB} Go (${percentUsed}%)\n` + + `⚠️ **Seuil de redémarrage:** ${thresholdGB} Go\n` + + `🔄 **État du serveur:** ${currentState}\n` + + `💡 **Niveau:** ${color}\n\n` + + `${monitorStatus.isMonitoring ? '✅ Surveillance automatique active' : '⚠️ Surveillance automatique inactive'}\n` + + `${monitorStatus.isRebooting ? '🔄 Redémarrage en cours...' : ''}`; + + await interaction.editReply(message); + + } catch (error) { + console.error('Erreur lors de la récupération de l\'état RAM:', error); + await interaction.editReply('❌ Erreur lors de la récupération des informations du serveur.'); + } + }, +}; diff --git a/consoleMonitor.js b/consoleMonitor.js index 3fed9fd..452f874 100644 --- a/consoleMonitor.js +++ b/consoleMonitor.js @@ -522,4 +522,10 @@ const stopConsoleMonitoring = () => { console.log('🔌 Surveillance de la console arrêtée'); }; -module.exports = { startConsoleMonitoring, stopConsoleMonitoring }; +// Fonction pour forcer la reconnexion du WebSocket (utilisée par ramMonitor) +const forceWebSocketReconnect = async () => { + console.log('🔄 [CONSOLE] Reconnexion forcée du WebSocket...'); + await forceReconnectToRefreshCredentials(); +}; + +module.exports = { startConsoleMonitoring, stopConsoleMonitoring, forceWebSocketReconnect }; diff --git a/index.js b/index.js index bacc13b..28bd5ec 100644 --- a/index.js +++ b/index.js @@ -7,8 +7,9 @@ const update = require('./displayer.js'); const clean = require('./cleaner.js'); const { Client, GatewayIntentBits, Collection, Events, Partials } = require('discord.js'); const { initDatabase, createTables, cleanExpiredCodes } = require('./database.js'); -const { startConsoleMonitoring } = require('./consoleMonitor.js'); +const { startConsoleMonitoring, forceWebSocketReconnect } = require('./consoleMonitor.js'); const { initPalworldBridge } = require('./palworld-bridge.js'); +const { startRAMMonitoring } = require('./ramMonitor.js'); const client = new Client({ intents: [ @@ -82,6 +83,10 @@ client.once('ready', async () => { } startConsoleMonitoring(client, process.env.PTERODACTYL_API_TOKEN); + + // Démarrer la surveillance RAM avec callback de reconnexion WebSocket + startRAMMonitoring(forceWebSocketReconnect); + console.log('✅ Surveillance RAM activée (seuil: 19 Go)'); } catch (error) { console.error('⚠️ Erreur lors de l\'initialisation de la base de données'); console.error('⚠️ Le système de liaison est désactivé'); diff --git a/ramMonitor.js b/ramMonitor.js new file mode 100644 index 0000000..3cdda0d --- /dev/null +++ b/ramMonitor.js @@ -0,0 +1,199 @@ +const axios = require('axios'); + +let isMonitoring = false; +let checkInterval = null; +let isRebooting = false; +let reconnectCallback = null; // Callback pour resync le WebSocket + +const RAM_THRESHOLD_MB = 19 * 1024; // 19 Go en MB +const CHECK_INTERVAL_MS = 60 * 1000; // Vérifier toutes les 60 secondes + +/** + * Vérifie l'utilisation actuelle de la RAM du serveur + */ +const checkRAMUsage = async () => { + try { + const response = await axios.get( + `${process.env.PTERODACTYL_API_URL}/api/client/servers/${process.env.PTERODACTYL_SERVER_ID}/resources`, + { + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${process.env.PTERODACTYL_API_TOKEN}` + } + } + ); + + const resources = response.data.attributes.resources; + const ramUsedMB = resources.memory_bytes / (1024 * 1024); // Convertir en MB + const ramUsedGB = (ramUsedMB / 1024).toFixed(2); // Convertir en GB pour l'affichage + const currentState = response.data.attributes.current_state; + + console.log(`🔍 [RAM Monitor] Utilisation RAM: ${ramUsedGB} Go / État: ${currentState}`); + + // Vérifier si on dépasse le seuil ET que le serveur est en cours d'exécution + if (ramUsedMB > RAM_THRESHOLD_MB && currentState === 'running' && !isRebooting) { + console.log(`⚠️ [RAM Monitor] SEUIL DÉPASSÉ ! ${ramUsedGB} Go > 19 Go`); + console.log(`🔄 [RAM Monitor] Déclenchement du redémarrage automatique...`); + + await rebootServer(); + } + + return { ramUsedMB, ramUsedGB, currentState }; + } catch (error) { + console.error('❌ [RAM Monitor] Erreur lors de la vérification RAM:', error.message); + return null; + } +}; + +/** + * Redémarre le serveur automatiquement + */ +const rebootServer = async () => { + if (isRebooting) { + console.log('⚠️ [RAM Monitor] Redémarrage déjà en cours, annulation...'); + return; + } + + isRebooting = true; + + try { + const headers = { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${process.env.PTERODACTYL_API_TOKEN}` + }; + + const serverUrl = `${process.env.PTERODACTYL_API_URL}/api/client/servers/${process.env.PTERODACTYL_SERVER_ID}`; + + console.log('💾 [RAM Monitor] Sauvegarde du serveur...'); + await axios.post(`${serverUrl}/command`, { + command: 'save' + }, { headers }); + + // Annoncer le redémarrage aux joueurs + console.log('📢 [RAM Monitor] Annonce du redémarrage...'); + await axios.post(`${serverUrl}/command`, { + command: "broadcast 'Redémarrage automatique (RAM élevée - 30 secondes)'" + }, { headers }); + + // Attendre la sauvegarde + await new Promise(resolve => setTimeout(resolve, 3000)); + + console.log('⏹️ [RAM Monitor] Arrêt du serveur...'); + await axios.post(`${serverUrl}/power`, { + signal: 'stop' + }, { headers }); + + // Attendre 25 secondes + console.log('⏳ [RAM Monitor] Attente de 25 secondes...'); + await new Promise(resolve => setTimeout(resolve, 25000)); + + console.log('🚀 [RAM Monitor] Redémarrage du serveur...'); + await axios.post(`${serverUrl}/power`, { + signal: 'start' + }, { headers }); + + // Attendre que le serveur soit opérationnel + let isRunning = false; + let attempts = 0; + const maxAttempts = 30; // 30 tentatives * 5 secondes = 2.5 minutes max + + while (!isRunning && attempts < maxAttempts) { + attempts++; + await new Promise(resolve => setTimeout(resolve, 5000)); + + try { + const checkResponse = await axios.get(`${serverUrl}/resources`, { headers }); + const state = checkResponse.data.attributes.current_state; + + if (state === 'running') { + isRunning = true; + console.log('✅ [RAM Monitor] Serveur redémarré avec succès !'); + + // Resynchroniser le WebSocket si un callback est défini + if (reconnectCallback) { + console.log('🔄 [RAM Monitor] Resynchronisation du WebSocket...'); + await new Promise(resolve => setTimeout(resolve, 5000)); // Attendre 5s de plus + reconnectCallback(); + } + } + } catch (error) { + console.error(`❌ [RAM Monitor] Tentative ${attempts}/${maxAttempts} échouée:`, error.message); + } + } + + if (!isRunning) { + console.error('❌ [RAM Monitor] Le serveur n\'a pas redémarré après le délai maximum'); + } + + } catch (error) { + console.error('❌ [RAM Monitor] Erreur lors du redémarrage:', error.message); + } finally { + isRebooting = false; + } +}; + +/** + * Démarre la surveillance de la RAM + */ +const startRAMMonitoring = (websocketReconnectCallback = null) => { + if (isMonitoring) { + console.log('⚠️ [RAM Monitor] Surveillance déjà active'); + return; + } + + reconnectCallback = websocketReconnectCallback; + + console.log(`🚀 [RAM Monitor] Démarrage de la surveillance RAM (seuil: 19 Go)`); + console.log(`⏱️ [RAM Monitor] Intervalle de vérification: ${CHECK_INTERVAL_MS / 1000}s`); + + isMonitoring = true; + + // Première vérification immédiate + checkRAMUsage(); + + // Vérifications périodiques + checkInterval = setInterval(async () => { + if (!isRebooting) { + await checkRAMUsage(); + } + }, CHECK_INTERVAL_MS); +}; + +/** + * Arrête la surveillance de la RAM + */ +const stopRAMMonitoring = () => { + if (!isMonitoring) { + console.log('⚠️ [RAM Monitor] Surveillance déjà inactive'); + return; + } + + console.log('⏹️ [RAM Monitor] Arrêt de la surveillance RAM'); + isMonitoring = false; + + if (checkInterval) { + clearInterval(checkInterval); + checkInterval = null; + } +}; + +/** + * Obtenir le statut de la surveillance + */ +const getMonitoringStatus = () => { + return { + isMonitoring, + isRebooting, + threshold: RAM_THRESHOLD_MB, + checkInterval: CHECK_INTERVAL_MS + }; +}; + +module.exports = { + startRAMMonitoring, + stopRAMMonitoring, + checkRAMUsage, + getMonitoringStatus +};