feat(dashboard): enrôlement lit l'adresse serveur depuis system_config.json
Câble l'éditeur adresses/ports du dashboard (services.streaming) vers le RPA_SERVER_URL généré pour chaque agent Léa. Priorité config > env > défaut ; host loopback/vide = non configuré (fallback env → pas de régression). Permet de changer l'IP serveur (labo .45 → clinique .178) depuis l'UI sans toucher l'env ni le code. +3 tests TDD. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
44
tests/test_dashboard_server_url.py
Normal file
44
tests/test_dashboard_server_url.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""TDD — résolution de l'URL serveur d'enrôlement depuis system_config.json.
|
||||
|
||||
Câble l'éditeur adresses/ports du dashboard (`services.streaming`) vers le
|
||||
`RPA_SERVER_URL` généré pour chaque agent Léa.
|
||||
|
||||
Priorité : config (`system_config.json`) > variable d'env > défaut.
|
||||
Un host loopback/vide dans la config = « non configuré » → fallback env, pour
|
||||
ne PAS régresser le déploiement actuel où l'URL vient de l'environnement.
|
||||
"""
|
||||
import os
|
||||
|
||||
# L'import du dashboard est fail-closed sur l'auth → escape dev/test documenté.
|
||||
os.environ.setdefault("DASHBOARD_AUTH_DISABLED", "true")
|
||||
|
||||
from web_dashboard import app as dash # noqa: E402
|
||||
|
||||
|
||||
def test_resolve_url_from_config_streaming_host(monkeypatch):
|
||||
"""La config (host streaming édité dans l'UI) prime, même si l'env existe."""
|
||||
monkeypatch.setattr(
|
||||
dash, "load_system_config",
|
||||
lambda: {"services": {"streaming": {"host": "192.168.1.178", "port": 5005}}},
|
||||
)
|
||||
monkeypatch.setenv("RPA_SERVER_URL", "http://192.168.1.45:5005/api/v1")
|
||||
assert dash._resolve_public_server_url() == "http://192.168.1.178:5005/api/v1"
|
||||
|
||||
|
||||
def test_resolve_url_loopback_in_config_falls_back_to_env(monkeypatch):
|
||||
"""host=localhost dans la config = non configuré → on garde l'env (pas de régression)."""
|
||||
monkeypatch.setattr(
|
||||
dash, "load_system_config",
|
||||
lambda: {"services": {"streaming": {"host": "localhost", "port": 5005}}},
|
||||
)
|
||||
monkeypatch.delenv("RPA_PUBLIC_URL", raising=False)
|
||||
monkeypatch.setenv("RPA_SERVER_URL", "http://192.168.1.45:5005/api/v1")
|
||||
assert dash._resolve_public_server_url() == "http://192.168.1.45:5005/api/v1"
|
||||
|
||||
|
||||
def test_resolve_url_no_config_uses_env(monkeypatch):
|
||||
"""Aucune section streaming → fallback env, normalisé en /api/v1."""
|
||||
monkeypatch.setattr(dash, "load_system_config", lambda: {"services": {}})
|
||||
monkeypatch.delenv("RPA_PUBLIC_URL", raising=False)
|
||||
monkeypatch.setenv("RPA_SERVER_URL", "http://10.0.0.5:5005/api/v1")
|
||||
assert dash._resolve_public_server_url() == "http://10.0.0.5:5005/api/v1"
|
||||
@@ -2248,6 +2248,33 @@ def _extract_host(url: str) -> str:
|
||||
_RPA_PUBLIC_HOST = _extract_host(_RPA_PUBLIC_URL)
|
||||
|
||||
|
||||
def _resolve_public_server_url() -> str:
|
||||
"""URL publique du serveur Léa pour l'enrôlement (config.txt des agents).
|
||||
|
||||
Priorité :
|
||||
1. system_config.json → services.streaming.{host,port} (édité dans le dashboard)
|
||||
2. variables d'env RPA_PUBLIC_URL / RPA_SERVER_URL
|
||||
3. défaut historique
|
||||
|
||||
Un host vide ou loopback dans la config = « non configuré » : on retombe sur
|
||||
l'env pour ne pas régresser un déploiement qui pilote l'URL par l'environnement.
|
||||
Toujours normalisée pour se terminer par /api/v1.
|
||||
"""
|
||||
_NON_ROUTABLE = {"", "localhost", "127.0.0.1", "0.0.0.0", "configure_me"}
|
||||
try:
|
||||
streaming = (load_system_config().get("services") or {}).get("streaming") or {}
|
||||
host = str(streaming.get("host") or "").strip()
|
||||
if host.lower() not in _NON_ROUTABLE:
|
||||
port = streaming.get("port") or 5005
|
||||
return _normalize_server_url(f"http://{host}:{port}")
|
||||
except Exception as exc: # config illisible → ne jamais casser l'enrôlement
|
||||
api_logger.warning(f"Résolution URL via system_config échouée: {exc}")
|
||||
env_url = os.getenv("RPA_PUBLIC_URL") or os.getenv("RPA_SERVER_URL")
|
||||
if env_url:
|
||||
return _normalize_server_url(env_url)
|
||||
return _normalize_server_url("https://lea.labs.laurinebazin.design")
|
||||
|
||||
|
||||
def _fetch_fleet_agent(machine_id: str):
|
||||
"""Récupère un agent depuis le serveur streaming (5005).
|
||||
|
||||
@@ -2284,7 +2311,7 @@ def _build_custom_config(machine_id: str, user_name: str, token: str) -> str:
|
||||
Le host est extrait proprement via urlparse (sans schema/port/path).
|
||||
"""
|
||||
now = datetime.now().strftime("%Y-%m-%d %H:%M")
|
||||
server_url = _normalize_server_url(_RPA_PUBLIC_URL)
|
||||
server_url = _resolve_public_server_url()
|
||||
|
||||
return f"""\
|
||||
# ============================================================
|
||||
|
||||
Reference in New Issue
Block a user