feat(stt-client): pages détail par service + santé live (probe + Prometheus + Alertmanager) (#36)

Transforme le portail (tuiles ouvrant un lien) en mini-pages par service, avec état
de santé temps réel — comme demandé.

Page détail (clic sur une tuile) : icône+nom, pilule d'état (en ligne/dégradé/hors
ligne + latence), description, composants (pastilles), alertes actives, infos (URL),
bouton « Ouvrir ». Tuiles : pastille de santé live + badge global d'alertes.

- registry.py : Service enrichi (description, health_url, components[{name,prom}],
  alerts_match) — toujours config-driven.
- health.py : StatusPoller en thread. Probe HTTP (up/down+latence) + Prometheus
  /api/v1/query (composants via up{}/kube-state-metrics) + Alertmanager /api/v2/alerts
  (hors Watchdog). Parallélisé (ThreadPoolExecutor), pousse portal-status au HUD.
- config.py : métadonnées homelab vérifiées en live (7 services ; traefik retiré —
  URL injoignable) + section [portal] (intervalle, URLs, timeouts).
- app.py : démarre le poller, diffuse portal-status (+ dernier état au connect).
- HUD : vues liste⟷détail, pastilles, badge alertes, mise à jour live.
- bump 0.9.0 → 0.10.0.

Validé : StatusPoller en direct contre le homelab (cycle 0.12s, 7 services ok,
composants Prometheus résolus, 0 alerte) ; HUD via Playwright (pastilles ok/dégradé/
hors-ligne, page détail complète, alerte rendue, bouton Ouvrir → open-service).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ALI YESILKAYA 2026-06-20 23:17:27 +02:00 committed by GitHub
parent 3b6099ffa0
commit d4b053622c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 600 additions and 50 deletions

View file

@ -70,52 +70,110 @@ DEFAULT_CONFIG: dict[str, Any] = {
"enabled": True,
"local_db": str(DATA_DIR / "memory.sqlite"),
},
# Portail de services — alimente le panneau « Services » du HUD ET la commande
# vocale « ouvre <service> ». Ajouter un service = ajouter une entrée ici (ou
# un bloc [[services]] dans stt.toml, qui remplace alors toute cette liste).
# Champs : id (unique), name, url, icon (emoji), aliases (mots déclencheurs voix).
# Portail de services — panneau « Services » du HUD (tuiles + pages détail), santé
# live, ET commande vocale « ouvre <service> ». Ajouter un service = une entrée ici
# (ou un bloc [[services]] dans stt.toml, qui remplace alors toute cette liste).
# Champs : id, name, url, icon, aliases (voix) ; + pour la page détail :
# description, health_url (sondé), components[{name, prom}] (PromQL → 1/0),
# alerts_match (labels Alertmanager : alerte pertinente si TOUS matchent).
"services": [
{
"id": "ghostfolio", "name": "Ghostfolio",
"url": "http://ghostfolio.lab.local", "icon": "💰",
"aliases": ["portefeuille", "portfolio", "ghosto", "bourse", "investissements"],
"description": "Suivi de portefeuille et d'investissements (actions, crypto). "
"Base PostgreSQL sur storage-01 + cache Redis in-cluster.",
"health_url": "http://ghostfolio.lab.local/api/v1/health",
"components": [
{"name": "Application", "prom": 'kube_pod_status_ready{namespace="ai",pod=~"ghostfolio-.+",pod!~"ghostfolio-redis-.+",condition="true"}'},
{"name": "Redis (cache)", "prom": 'kube_pod_status_ready{namespace="ai",pod=~"ghostfolio-redis-.+",condition="true"}'},
{"name": "PostgreSQL — storage-01", "prom": 'up{job="storage-01"}'},
],
"alerts_match": {"namespace": "ai"},
},
{
"id": "grafana", "name": "Grafana",
"url": "http://grafana.lab.local", "icon": "📊",
"aliases": ["graphana", "metriques", "monitoring", "dashboard", "graphiques"],
"description": "Tableaux de bord et visualisation des métriques du homelab "
"(Prometheus comme source de données).",
"health_url": "http://grafana.lab.local/api/health",
"components": [
{"name": "Pod", "prom": 'kube_pod_status_ready{namespace="monitoring",pod=~".*grafana.*",condition="true"}'},
],
"alerts_match": {"namespace": "monitoring"},
},
{
"id": "argocd", "name": "ArgoCD",
"url": "http://argocd.lab.local", "icon": "🚢",
"aliases": ["argo", "argo cd", "deploiements", "gitops"],
"description": "Déploiement continu GitOps : synchronise les workloads k8s "
"depuis le dépôt Git (pattern App-of-Apps).",
"health_url": "http://argocd.lab.local/healthz",
"components": [
{"name": "Pods (server, repo, redis…)", "prom": 'kube_pod_status_ready{namespace="argocd",condition="true"}'},
],
"alerts_match": {"namespace": "argocd"},
},
{
"id": "n8n", "name": "n8n",
"url": "http://n8n.lab.local", "icon": "🔀",
"aliases": ["workflows", "automatisation", "nodes", "huit n huit"],
"description": "Automatisation et workflows (alertes, rapport horaire, worker "
"doc Hermès). Base PostgreSQL sur storage-01.",
"health_url": "http://n8n.lab.local/healthz",
"components": [
{"name": "Application", "prom": 'kube_pod_status_ready{namespace="ai",pod=~"n8n-.+",condition="true"}'},
{"name": "PostgreSQL — storage-01", "prom": 'up{job="storage-01"}'},
],
"alerts_match": {"namespace": "ai"},
},
{
"id": "openwebui", "name": "Open WebUI",
"url": "http://openwebui.lab.local", "icon": "💬",
"aliases": ["open web ui", "webui", "chat", "llm", "interface ia"],
"description": "Interface de chat web vers les LLM du homelab "
"(backend LiteLLM sur storage-01).",
"health_url": "http://openwebui.lab.local/health",
"components": [
{"name": "Application", "prom": 'kube_pod_status_ready{namespace="ai",pod=~"open-webui-.+",condition="true"}'},
{"name": "LiteLLM — storage-01", "prom": 'up{job="storage-01"}'},
],
"alerts_match": {"namespace": "ai"},
},
{
"id": "prometheus", "name": "Prometheus",
"url": "http://prometheus.lab.local", "icon": "🔥",
"aliases": ["prometheus", "metriques brutes", "tsdb"],
"description": "Collecte et stockage des métriques (TSDB) — scrute hôtes, "
"nœuds k8s et services.",
"health_url": "http://prometheus.lab.local/-/healthy",
"components": [
{"name": "Pod", "prom": 'kube_pod_status_ready{namespace="monitoring",pod=~"prometheus-.+",condition="true"}'},
],
"alerts_match": {"namespace": "monitoring"},
},
{
"id": "alertmanager", "name": "Alertmanager",
"url": "http://alertmanager.lab.local", "icon": "🚨",
"aliases": ["alertes", "alert manager", "alarmes"],
},
{
"id": "traefik", "name": "Traefik",
"url": "http://traefik.lab.local", "icon": "🛣️",
"aliases": ["reverse proxy", "ingress", "routeur", "proxy"],
"description": "Routage et notification des alertes Prometheus "
"(→ webhook Hermès, n8n, e-mail).",
"health_url": "http://alertmanager.lab.local/-/healthy",
"components": [
{"name": "Pod", "prom": 'kube_pod_status_ready{namespace="monitoring",pod=~"alertmanager-.+",condition="true"}'},
],
"alerts_match": {"namespace": "monitoring"},
},
],
# Santé live du portail (pages détail). Sondage non bloquant en thread.
"portal": {
"status_enabled": True,
"status_interval_sec": 15, # cadence de rafraîchissement
"probe_timeout_sec": 4,
"prometheus_url": "http://prometheus.lab.local",
"alertmanager_url": "http://alertmanager.lab.local",
},
}