# ============================================ # Projet : API-KA # Fichier : src/api/routes/health.py # Node : m3u96b # Author : Simon-Pierre Boucher # Contact : contact@spboucher.ai # Date : 2026-08-16 # ============================================ """Route /health : node (m3u96b), état de la base, dernière collecte par service.""" from __future__ import annotations import socket from typing import Any from fastapi import APIRouter, Depends from sqlalchemy import func, select from sqlalchemy.orm import Session from src.api.routes import envelope from src.config import REQUIRED_NODE, SERVICES from src.database.db import get_db, healthcheck from src.database.models import CollectionRun, ConnectorHealth from src.monitoring.connector_health import ALL_STATUSES router = APIRouter(tags=["health"]) @router.get("/health") def health(db: Session = Depends(get_db)) -> dict[str, Any]: """État complet de la plateforme : node, base de données, dernières collectes.""" hostname = socket.gethostname() db_ok = healthcheck() last_collections: dict[str, Any] = {} for service in SERVICES: last_run = ( db.execute( select(CollectionRun) .where( CollectionRun.service == service, CollectionRun.status.in_(("success", "retried")), ) .order_by(CollectionRun.finished_at.desc()) .limit(1) ) .scalars() .first() ) last_collections[service] = ( { "date_key": last_run.date_key.isoformat(), "status": last_run.status, "records_count": last_run.records_count, "finished_at": last_run.finished_at.isoformat(), } if last_run else None ) # Résumé de la supervision des connecteurs de l'écosystème (table # connector_health, alimentée toutes les 2 h par apika-scheduler). connectors = dict.fromkeys(ALL_STATUSES, 0) for status, count in db.execute( select(ConnectorHealth.status, func.count()).group_by(ConnectorHealth.status) ): if status in connectors: connectors[status] = count data = { "status": "ok" if db_ok else "degraded", "node": hostname, "required_node": REQUIRED_NODE, "node_ok": hostname.split(".")[0].lower() == REQUIRED_NODE, "database": "ok" if db_ok else "error", "last_collections": last_collections, "connectors": connectors, } return envelope(data)