This commit is contained in:
Louis Mazin 2026-02-01 19:50:27 +01:00
parent fc7542db6b
commit a86f14fce8
5 changed files with 418 additions and 2 deletions

146
RAM_MONITOR_README.md Normal file
View File

@ -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);
```

View File

@ -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.');
}
},
};

View File

@ -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 };

View File

@ -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é');

199
ramMonitor.js Normal file
View File

@ -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
};