Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | /** * Routes Metriques — Historique des metriques serveur. * * Endpoints : * GET /servers/:serverId/metrics/history?range=1h|6h|24h|7d|30d * * Retourne les metriques historiques avec un down-sampling automatique * pour garder un nombre raisonnable de points (~100-200 par requete). * * @module routes/metrics */ const express = require('express'); const { pool: db } = require('../db'); const { requireAuth } = require('../middleware/auth'); const { requireGroupContext, requireGroupPermission, requireGroupServer } = require('../middleware/groupContext'); const router = express.Router({ mergeParams: true }); router.use(requireAuth); router.use(requireGroupContext()); // Mapping range → intervalle SQL + pas de groupement const RANGES = { '1h': { interval: '1 hour', bucket: '1 minute' }, '6h': { interval: '6 hours', bucket: '5 minutes' }, '24h': { interval: '24 hours', bucket: '15 minutes' }, '7d': { interval: '7 days', bucket: '1 hour' }, '30d': { interval: '30 days', bucket: '6 hours' }, }; // ─── GET /servers/:serverId/metrics/history ────────────────────────────────── router.get('/history', requireGroupServer(), requireGroupPermission('metrics', 'read'), async (req, res, next) => { try { const { serverId } = req.params; const range = req.query.range || '24h'; if (!RANGES[range]) { return res.status(400).json({ error: `Range invalide. Valeurs acceptees : ${Object.keys(RANGES).join(', ')}`, }); } // Verifier l'acces au serveur (owner ou membre du groupe) const hasAccess = req.access?.serverIds ? req.access.serverIds.includes(serverId) : (await db.query('SELECT id FROM servers WHERE id = $1 AND user_id = $2', [serverId, req.user.id])).rows.length > 0; if (!hasAccess) { return res.status(404).json({ error: 'Serveur introuvable.' }); } const { interval } = RANGES[range]; // Requete avec down-sampling via date_trunc pour grouper par bucket // On utilise AVG pour lisser les donnees sur chaque bucket const result = await db.query( `SELECT date_trunc('minute', created_at) AS ts, ROUND(AVG(cpu_percent))::int AS cpu, ROUND(AVG(mem_percent))::int AS mem, ROUND(AVG(mem_used_mb))::int AS mem_used, ROUND(AVG(disk_percent))::int AS disk, ROUND(AVG(load_1m)::numeric, 2) AS load FROM metrics_history WHERE server_id = $1 AND created_at > NOW() - INTERVAL '${interval}' GROUP BY date_trunc('minute', created_at) ORDER BY ts ASC`, [serverId], ); res.json({ range, count: result.rows.length, metrics: result.rows, }); } catch (err) { next(err); } }); // ─── GET /servers/:serverId/metrics/latest ─────────────────────────────────── // Retourne la derniere metrique enregistree (pour comparaison) router.get('/latest', requireGroupServer(), requireGroupPermission('metrics', 'read'), async (req, res, next) => { try { const { serverId } = req.params; const result = await db.query( `SELECT cpu_percent AS cpu, mem_percent AS mem, disk_percent AS disk, load_1m AS load, created_at AS ts FROM metrics_history WHERE server_id = $1 ORDER BY created_at DESC LIMIT 1`, [serverId], ); if (result.rows.length === 0) { return res.json({ collecting: true, message: 'Pas encore de donnees. Premiere collecte dans quelques minutes.' }); } res.json(result.rows[0]); } catch (err) { next(err); } }); module.exports = router; |