""" Endpoints pour les métriques du dashboard """ from fastapi import APIRouter, HTTPException from ..database import db from ..models import MetricsResponse, MetricsSummary, TimeSeriesPoint router = APIRouter(prefix="/api/metrics", tags=["metrics"]) @router.get("", response_model=MetricsResponse) async def get_metrics(): """ Récupère les métriques globales du dashboard """ try: # Résumé des métriques summary_query = """ SELECT count() AS total_detections, countIf(threat_level = 'CRITICAL') AS critical_count, countIf(threat_level = 'HIGH') AS high_count, countIf(threat_level = 'MEDIUM') AS medium_count, countIf(threat_level = 'LOW') AS low_count, countIf(bot_name != '') AS known_bots_count, countIf(bot_name = '') AS anomalies_count, uniq(src_ip) AS unique_ips FROM ml_detected_anomalies WHERE detected_at >= now() - INTERVAL 24 HOUR """ summary_result = db.query(summary_query) summary_row = summary_result.result_rows[0] if summary_result.result_rows else None if not summary_row: raise HTTPException(status_code=404, detail="Aucune donnée disponible") summary = MetricsSummary( total_detections=summary_row[0], critical_count=summary_row[1], high_count=summary_row[2], medium_count=summary_row[3], low_count=summary_row[4], known_bots_count=summary_row[5], anomalies_count=summary_row[6], unique_ips=summary_row[7] ) # Série temporelle (par heure) timeseries_query = """ SELECT toStartOfHour(detected_at) AS hour, count() AS total, countIf(threat_level = 'CRITICAL') AS critical, countIf(threat_level = 'HIGH') AS high, countIf(threat_level = 'MEDIUM') AS medium, countIf(threat_level = 'LOW') AS low FROM ml_detected_anomalies WHERE detected_at >= now() - INTERVAL 24 HOUR GROUP BY hour ORDER BY hour """ timeseries_result = db.query(timeseries_query) timeseries = [ TimeSeriesPoint( hour=row[0], total=row[1], critical=row[2], high=row[3], medium=row[4], low=row[5] ) for row in timeseries_result.result_rows ] # Distribution par menace threat_distribution = { "CRITICAL": summary.critical_count, "HIGH": summary.high_count, "MEDIUM": summary.medium_count, "LOW": summary.low_count } return MetricsResponse( summary=summary, timeseries=timeseries, threat_distribution=threat_distribution ) except Exception as e: raise HTTPException(status_code=500, detail=f"Erreur lors de la récupération des métriques: {str(e)}") @router.get("/threats") async def get_threat_distribution(): """ Récupère la répartition par niveau de menace """ try: query = """ SELECT threat_level, count() AS count, round(count() * 100.0 / sum(count()) OVER (), 2) AS percentage FROM ml_detected_anomalies WHERE detected_at >= now() - INTERVAL 24 HOUR GROUP BY threat_level ORDER BY count DESC """ result = db.query(query) return { "items": [ {"threat_level": row[0], "count": row[1], "percentage": row[2]} for row in result.result_rows ] } except Exception as e: raise HTTPException(status_code=500, detail=f"Erreur: {str(e)}") @router.get("/baseline") async def get_metrics_baseline(): """ Compare les métriques actuelles (24h) vs hier (24h-48h) pour afficher les tendances. """ try: query = """ SELECT countIf(detected_at >= now() - INTERVAL 24 HOUR) AS today_total, countIf(detected_at >= now() - INTERVAL 48 HOUR AND detected_at < now() - INTERVAL 24 HOUR) AS yesterday_total, uniqIf(src_ip, detected_at >= now() - INTERVAL 24 HOUR) AS today_ips, uniqIf(src_ip, detected_at >= now() - INTERVAL 48 HOUR AND detected_at < now() - INTERVAL 24 HOUR) AS yesterday_ips, countIf(threat_level = 'CRITICAL' AND detected_at >= now() - INTERVAL 24 HOUR) AS today_critical, countIf(threat_level = 'CRITICAL' AND detected_at >= now() - INTERVAL 48 HOUR AND detected_at < now() - INTERVAL 24 HOUR) AS yesterday_critical FROM ml_detected_anomalies WHERE detected_at >= now() - INTERVAL 48 HOUR """ r = db.query(query) row = r.result_rows[0] if r.result_rows else None def pct_change(today: int, yesterday: int) -> float: if yesterday == 0: return 100.0 if today > 0 else 0.0 return round((today - yesterday) / yesterday * 100, 1) today_total = int(row[0] or 0) if row else 0 yesterday_total = int(row[1] or 0) if row else 0 today_ips = int(row[2] or 0) if row else 0 yesterday_ips = int(row[3] or 0) if row else 0 today_crit = int(row[4] or 0) if row else 0 yesterday_crit = int(row[5] or 0) if row else 0 return { "total_detections": { "today": today_total, "yesterday": yesterday_total, "pct_change": pct_change(today_total, yesterday_total), }, "unique_ips": { "today": today_ips, "yesterday": yesterday_ips, "pct_change": pct_change(today_ips, yesterday_ips), }, "critical_alerts": { "today": today_crit, "yesterday": yesterday_crit, "pct_change": pct_change(today_crit, yesterday_crit), }, } except Exception as e: raise HTTPException(status_code=500, detail=f"Erreur baseline: {str(e)}")