"""Configurare gateway. Env vars (prefix AUTOPASS_) + valori implicite. NU stocheaza parole RAR. Credentialele RAR vin per-cerere de la ROAAUTO. 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" # --- Observabilitate / jurnal aplicatie --- # Nivel minim al evenimentelor scrise in app_events + log text. Sub el, evenimentul # e ignorat (best-effort). DEBUG|INFO|WARNING|ERROR|CRITICAL. log_level: str = "INFO" log_retention_days: int = 90 # Director pentru log-ul text rotativ (RotatingFileHandler in aplicatie). # Fisier per-proces (app-api.log / app-worker.log) — rotatia nu e multiproces-safe. log_dir: Path = ROOT / ".run" log_file_max_bytes: int = 5_000_000 log_file_backup_count: int = 5 # Retentie randuri blocate (error/needs_data/needs_mapping). Mai scurt decat 90z # ale `sent` — un blocat n-are valoare de audit. blocked_retention_days: int = 30 # --- Securitate --- # 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 # Cheie Fernet pentru criptarea creds RAR efemere in submissions (zero-storage # at rest). Nesetata -> cheie efemera la runtime (creds nu supravietuiesc # restartului). In productie seteaz-o persistent. Genereaza: # python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" creds_key: str | None = None # --- Sesiuni web --- # Secret semnat cookie sesiune. None -> efemer la fiecare restart (dev ok; # in prod seteaza persistent ca si creds_key, altfel cookieurile se invalideaza # la restart). Genereaza: python -c "import secrets; print(secrets.token_hex(32))" session_secret: str | None = None # True (IMPLICIT, sigur pentru prod): rutele web fara sesiune -> redirect /login; # CSRF enforce. Pentru dev rapid pe contul implicit id=1, # seteaza explicit AUTOPASS_WEB_AUTH_REQUIRED=false. web_auth_required: bool = True # True (prod, in spatele Cloudflare Tunnel TLS): cookie cu Secure flag. # False (dev): cookie fara Secure, functioneaza pe HTTP. session_https_only: bool = False # --- Contact suport (US-001, PRD 5.12) --- # Email/canal de contact afisat in mesaje catre utilizatori (ex. CUI duplicat la signup). # Nesetat -> fallback la formularea generica fara canal concret. support_email: str | None = None # --- Notificare email admin la signup --- # Nesetat (smtp_host None) -> notificarea e DEGRADATA (doar log SIGNUP). smtp_host: str | None = None smtp_port: int = 587 smtp_user: str | None = None smtp_password: str | None = None smtp_from: str | None = None # --- Rate-limit signup + login --- # Max cereri POST /signup per IP in fereastra de timp (in-proces, fara dependinta noua). signup_rate_max: int = 5 signup_rate_window_s: int = 3600 # Max incercari POST /login per IP (brute-force parole). Fereastra impartita cu signup. login_rate_max: int = 10 # --- 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. 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 # Send DEZACTIVAT implicit (nu trimite la RAR). Activeaza-l explicit pentru # proba end-to-end. worker_send_enabled: bool = False # Dev: foloseste creds din settings.xml pt login worker. In productie # creds vin per-cerere de la ROAAUTO — lasa False. worker_use_test_creds: bool = False # Keepalive RAR: cand coada e goala, worker-ul face un login de proba la fiecare # atata timp ca sa pastreze last_rar_login_ok proaspat (sub pragul de 30h al # dashboard-ului) — altfel banner-ul "RAR inaccesibil" apare fals doar din lipsa # de trafic. 0 = dezactivat. Implicit o data pe zi (24h < 30h, margine de 6h). worker_rar_keepalive_interval_s: int = 86400 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 # --- Planuri de cont (PRD 5.17) --- # Enforcement DUR al limitelor de plan (volum + acces API). True (implicit) = activ. # False = kill-switch de operare: sare toate gate-urile de plan (util pentru debugging # sau rollback rapid fara revert de cod). Enforcement DUR e activ implicit de la deploy # (decizie user 2026-06-28, decizia #22 autoplan): nu exista conturi legacy, produs in TESTE. enforce_plans: bool = True # --- Embeddings (sugestie mapare, Stratul 2 PRD 5.14) --- # ACTIVAT implicit: editorul de mapari ofera sugestii semantice (model fastembed/ONNX). # Cost: prima folosire lazy-load-eaza modelul (~230MB pe disc) sincron in thread-ul de # cerere -> prima cerere /mapari poate dura 30-120s pana modelul intra in memorie; cererile # urmatoare sunt instant. SUGGESTION-ONLY: nu intra in resolve_prestatii (nu auto-trimite). # Pune-l pe False (start.sh/Docker/.env: AUTOPASS_EMBEDDINGS_ENABLED=false) cand vrei # /mapari instant la prima cerere sau suita de teste rapida (cade pe GOLD/SILVER+fuzzy). embeddings_enabled: bool = True # --- Seed corpus operatii etichetate (SILVER, PRD 5.18 US-004) --- # ACTIVAT implicit: la init_db, populeaza mapping_suggestions din artefactul comis # `app/data/operatii-etichetate.json` (INSERT OR IGNORE). Asa SILVER nu mai e gol in # productie -> sugestii exact-match + corpus k-NN reale. SUGGESTION-ONLY. # Pune-l pe False (AUTOPASS_SEED_OPERATII_ENABLED=false) cand vrei SILVER gol — # conftest il dezactiveaza global, testele care-l vor il pornesc punctual. seed_operatii_enabled: bool = True @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