All files / src/routes metrics.js

0% Statements 0/30
0% Branches 0/10
0% Functions 0/2
0% Lines 0/30

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;