Ajout de la persistance des pics de connexions et configuration via JSON

This commit is contained in:
2025-11-02 11:16:56 +01:00
parent bc27296ecb
commit 31175af43c
2 changed files with 95 additions and 5 deletions

View File

@@ -1,3 +1,32 @@
# UmbraMonitor # UmbraMonitor
Bot Discord de monitoring pour l'infrastructure Umbra. Il vérifie périodiquement le serveur de synchronisation, le serveur de fichiers, le serveur d'identification ainsi que les endpoints externes (dépôt Dalamud, serveur Git) et publie un message « sticky » dans un canal Discord dédié. Bot Discord de monitoring pour l'infrastructure Umbra. Il vérifie périodiquement le serveur de synchronisation, le serveur de fichiers, le serveur d'identification ainsi que les endpoints externes (dépôt Dalamud, serveur Git) et publie un message « sticky » dans un canal Discord dédié.
## Persistance du « pic de connexions »
Le bot mémorise désormais le « Pic de connexions » même après un redémarrage.
- Stockage: un fichier JSON `peaks.json` à la racine de l'application (chemin par défaut). Vous pouvez changer l'emplacement via la variable d'environnement `PEAKS_PATH`.
- Démarrage: le pic du service "Serveur d'identification" est amorcé à 70 si aucune valeur n'est connue, comme demandé.
- Écriture: les mises à jour sont écrites de façon groupée (debounce) afin de limiter les I/O disque.
### Exemple de configuration
Variables d'environnement:
```
# Emplacement personnalisé pour la persistance (optionnel)
PEAKS_PATH=./data/peaks.json
```
docker-compose.yml (conseillé pour garder la persistance à travers les redéploiements):
```
services:
umbra-monitor-bot-js:
volumes:
- ./config.yml:/app/config.yml:ro
- ./data:/app/data
```
Créez le dossier `data/` localement si vous utilisez `PEAKS_PATH=./data/peaks.json`.

View File

@@ -8,7 +8,6 @@ const yaml = require('js-yaml');
const envPath = path.resolve(__dirname, '..', '.env'); const envPath = path.resolve(__dirname, '..', '.env');
const envResult = dotenv.config({ path: envPath }); const envResult = dotenv.config({ path: envPath });
if (envResult.error) { if (envResult.error) {
// Fallback to default resolution when the project root already matches process.cwd().
dotenv.config(); dotenv.config();
} }
@@ -23,6 +22,11 @@ const RESOLVED_CONFIG_PATH = path.isAbsolute(CONFIG_PATH)
const REFRESH_INTERVAL = parseInt(process.env.REFRESH_INTERVAL || '60', 10); const REFRESH_INTERVAL = parseInt(process.env.REFRESH_INTERVAL || '60', 10);
const STICKY_MESSAGE = String(process.env.STICKY_MESSAGE || 'true').toLowerCase() === 'true'; const STICKY_MESSAGE = String(process.env.STICKY_MESSAGE || 'true').toLowerCase() === 'true';
const PEAKS_PATH = process.env.PEAKS_PATH || './peaks.json';
const RESOLVED_PEAKS_PATH = path.isAbsolute(PEAKS_PATH)
? PEAKS_PATH
: path.resolve(__dirname, '..', PEAKS_PATH);
const requiredEnv = { const requiredEnv = {
DISCORD_TOKEN, DISCORD_TOKEN,
STATUS_GUILD_ID, STATUS_GUILD_ID,
@@ -660,6 +664,58 @@ const metricNameOverrides = {
const connectionPeaks = new Map(); const connectionPeaks = new Map();
function loadConnectionPeaks() {
try {
if (!fs.existsSync(RESOLVED_PEAKS_PATH)) return;
const text = fs.readFileSync(RESOLVED_PEAKS_PATH, 'utf8');
if (!text.trim()) return;
const data = JSON.parse(text);
if (data && typeof data === 'object') {
for (const [k, v] of Object.entries(data)) {
const n = asFiniteNumber(v);
if (n !== null) connectionPeaks.set(k, n);
}
}
} catch (e) {
console.warn('Unable to load peaks file:', e.message || e);
}
}
let peaksSaveTimer = null;
function scheduleSaveConnectionPeaks() {
if (peaksSaveTimer) clearTimeout(peaksSaveTimer);
peaksSaveTimer = setTimeout(() => {
try {
const obj = Object.fromEntries(connectionPeaks.entries());
fs.writeFileSync(RESOLVED_PEAKS_PATH, JSON.stringify(obj, null, 2), 'utf8');
} catch (e) {
console.warn('Unable to write peaks file:', e.message || e);
}
}, 300);
}
function updateConnectionPeak(name, value) {
const n = asFiniteNumber(value);
if (n === null) return;
const prev = connectionPeaks.get(name);
const next = prev !== undefined ? Math.max(prev, n) : n;
if (prev !== next) {
connectionPeaks.set(name, next);
scheduleSaveConnectionPeaks();
}
}
loadConnectionPeaks();
{
const key = "Serveur d'identification";
const prev = connectionPeaks.get(key);
const seeded = prev !== undefined ? Math.max(prev, 70) : 70;
if (prev !== seeded) {
connectionPeaks.set(key, seeded);
scheduleSaveConnectionPeaks();
}
}
function pickKeyMetrics(name, all) { function pickKeyMetrics(name, all) {
const perService = { const perService = {
'Serveur de synchronisation': [ 'Serveur de synchronisation': [
@@ -749,14 +805,19 @@ function formatMetrics(name, allMetrics, metricsByService, peakStore = null) {
if (peakStore) { if (peakStore) {
if (peakEntry) { if (peakEntry) {
peakStore.set(name, peakEntry.value); updateConnectionPeak(name, peakEntry.value);
} else { } else {
const numericConnected = asFiniteNumber(usersConnectedRaw); const numericConnected = asFiniteNumber(usersConnectedRaw);
if (numericConnected !== null) { if (numericConnected !== null) {
const prev = peakStore.get(name); const prev = peakStore.get ? peakStore.get(name) : undefined;
const nextPeak = prev !== undefined ? Math.max(prev, numericConnected) : numericConnected; const nextPeak = prev !== undefined ? Math.max(prev, numericConnected) : numericConnected;
peakStore.set(name, nextPeak); updateConnectionPeak(name, nextPeak);
peakEntry = { key: '_peak_authorized_connections', value: nextPeak }; peakEntry = { key: '_peak_authorized_connections', value: nextPeak };
} else {
const stored = peakStore.get ? peakStore.get(name) : undefined;
if (stored !== undefined && stored !== null) {
peakEntry = { key: '_peak_authorized_connections', value: stored };
}
} }
} }
} }