diff --git a/tests/test_dashboard_server_url.py b/tests/test_dashboard_server_url.py new file mode 100644 index 000000000..c16edd78a --- /dev/null +++ b/tests/test_dashboard_server_url.py @@ -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" diff --git a/web_dashboard/app.py b/web_dashboard/app.py index 9f8f018b8..d385838d7 100644 --- a/web_dashboard/app.py +++ b/web_dashboard/app.py @@ -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"""\ # ============================================================