"""Configurare gateway. Env vars (prefix AUTOPASS_) + valori implicite. NU stocheaza parole RAR. Credentialele RAR vin per-cerere de la ROAAUTO (vezi plan.md sect. 5). Helper-ul `load_test_credentials` citeste blocul din settings.xml DOAR pentru dev local / probe pe mediul de test. """ from __future__ import annotations import xml.etree.ElementTree as ET from functools import lru_cache from pathlib import Path from pydantic_settings import BaseSettings, SettingsConfigDict ROOT = Path(__file__).resolve().parent.parent class Settings(BaseSettings): model_config = SettingsConfigDict(env_prefix="AUTOPASS_", env_file=".env", extra="ignore") # --- Bază de date --- db_path: Path = ROOT / "data" / "autopass.db" # --- Securitate (CORE) --- # Enforcement auth API-key pe /v1/* protejat. False (dev/test): fara cheie -> # cont implicit id=1. True (prod): fara cheie valida -> 401. O cheie PREZENTA # dar invalida da 401 indiferent de flag. require_api_key: bool = False # --- RAR --- rar_env: str = "test" # "test" | "prod" rar_base_url_test: str = "https://apps.rarom.ro/test-rar-autopass" rar_base_url_prod: str = "https://apps.rarom.ro/rar-autopass" # WAF-ul RAR da 403 fara User-Agent de browser (confirmat live, vezi # docs/api-rar-contract.md). Toate apelurile httpx il trimit. http_user_agent: str = "Mozilla/5.0" http_timeout_s: float = 30.0 # --- Worker --- worker_poll_interval_s: float = 5.0 worker_heartbeat_stale_s: int = 30 # /healthz considera worker-ul mort peste atat # In schelet send-ul e DEZACTIVAT (nu trimite la RAR). Activeaza-l explicit # pentru proba end-to-end. Reconcilierea/retry-ul complet = T2. worker_send_enabled: bool = False # Dev: foloseste creds din settings.xml pt login worker. In productie # creds vin per-cerere de la ROAAUTO (T2) — lasa False. worker_use_test_creds: bool = False # T2 — recuperare orfane + retry/backoff: worker_sending_lease_s: int = 120 # rand 'sending' mai vechi de atat = orfan (worker mort mid-POST) worker_retry_base_s: int = 5 # backoff = base * 2^retry (plafonat la max) worker_retry_max_s: int = 300 worker_max_retries: int = 8 # peste atat -> error + banner (pana persistenta) @property def rar_base_url(self) -> str: return self.rar_base_url_prod if self.rar_env == "prod" else self.rar_base_url_test @lru_cache def get_settings() -> Settings: return Settings() def load_test_credentials(settings_xml: Path | None = None) -> dict | None: """Citeste credentialele din settings.xml (dev local / probe test). Intoarce {"email", "password"} sau None daca fisierul lipseste / e template. NU se foloseste in productie — acolo creds vin per-cerere de la ROAAUTO. """ path = settings_xml or (ROOT / "settings.xml") if not path.exists(): return None try: root = ET.parse(path).getroot() node = root.find("./test/credentials") if node is None: return None email = (node.findtext("email") or "").strip() password = (node.findtext("password") or "").strip() if not email or not password or email.startswith("EMAIL_"): return None return {"email": email, "password": password} except ET.ParseError: return None