Files
rar-autopass/CLAUDE.md
Claude Agent c8a19e2f06 chore: muta portul implicit 8000 -> 8010 (evita coliziunea cu roa2web)
start.sh, docker-compose.yml, README.md, CLAUDE.md aliniate la 8010
pentru a nu se suprapune cu backend-ul roa2web pe masina de test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 09:11:37 +00:00

6.8 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Ce este

Gateway web (Python / FastAPI) care preia prezentari de service-auto si le declara la RAR AUTOPASS (Legea 142/2023, OM 210/2024). Inlocuieste integrarea Visual FoxPro RarAutoPass (ROAAUTO), arhivata in legacy-vfp/ (doar referinta).

Limba proiectului este romana: cod, comentarii, commit-uri, documentatie. Fara emoji in raspunsuri sau fisiere noi (preferinta proiect).

Surse de adevar (citeste-le inainte sa modifici comportament)

  • docs/api-rar-contract.md — contractul RAR. Unde un plan/cod difera de contract, contractul are dreptate.
  • docs/ROADMAP.md — singura sursa de progres + procesul de lucru. O sesiune noua porneste de aici. Doar sectiunea "Stadiu Implementare" se modifica pe parcurs; detaliile stau in PRD-uri (docs/prd/).

Comenzi

pip3 install -r requirements.txt          # Python 3.12+

# Rulare locala (dev): API + worker sunt PROCESE SEPARATE
uvicorn app.main:app --reload --port 8010 # API: dashboard /, Swagger /docs, /healthz, /metrics
python3 -m app.worker                      # worker (necesar doar pentru a procesa coada)

# Wrapper-ul start.sh ambaleaza mediu (test/prod) + rol (api/worker/both/finalizate)
./start.sh test both --send     # API + worker, trimite efectiv la RAR test (loguri in .run/)
./start.sh test finalizate      # listeaza prezentarile inregistrate la RAR (verificare independenta)
./start.sh status               # stare procese + /healthz
./start.sh stop                 # opreste procesele pornite cu "both"
./start-test.sh / ./start-prod.sh   # fixeaza mediul, forwardeaza rolul

# Teste (pytest, fara config special; folosesc FastAPI TestClient + SQLite temporar)
python3 -m pytest -q
python3 -m pytest tests/test_worker_reconcile.py -q          # un fisier
python3 -m pytest tests/test_worker_reconcile.py::test_x -q  # un singur test

# Lifecycle chei API (admin, doar din CLI — nu exista suprafata HTTP)
python3 -m tools.apikey create --account 2   # cheie afisata O SINGURA DATA (rfak_...)
python3 -m tools.apikey list|rotate|revoke

# Docker (deploy): api + worker + autoheal, acelasi image + volum SQLite
docker compose up --build

Arhitectura

Doua procese peste acelasi SQLite (WAL) persistent, care comunica EXCLUSIV prin tabela submissions:

  • API (app.main:app) — API v1 (app/api/v1/router.py + import_router.py), dashboard web HTMX (app/web/routes.py + templates/), /healthz, /metrics. Worker-ul nu ruleaza ca task aici: un worker mort nu trebuie sa lase containerul "sanatos".
  • Worker (app/worker/__main__.py) — bucla: heartbeat → recupereaza orfane → claim atomic (BEGIN IMMEDIATE) → login RAR (per cont) → postPrezentare → update. Retry/backoff exponential, reconciliere anti-duplicat, lease pe randuri sending orfane, re-login la JWT expirat (TTL 30h). Send DEZACTIVAT implicit (AUTOPASS_WORKER_SEND_ENABLED=false) — sigur pentru probe.

Doua canale de intrare convergent in coada submissions:

  • Canal API (Treapta 1): POST /v1/prezentari pentru ROAAUTO / soft propriu.
  • Import web (Treapta 2): upload xlsx/csv → mapare coloane → preview → commit (app/import_parse.py, import_router.py, dashboard).

Flux: validare (validation.py) → mapare operatie→cod (mapping.py) → enqueue cu PII criptat → worker trimite → dashboard monitorizeaza live.

Invariante critice (usor de stricat)

  • AUTOPASS_CREDS_KEY trebuie sa fie ACEEASI intre API si worker. API cripteaza creds RAR (Fernet), worker le decripteaza. Chei diferite → worker nu poate decripta → trimiterile esueaza. start.sh both genereaza o cheie efemera partajata; pentru prod pune una persistenta in .env. (crypto.py)
  • Idempotenta = hash de continut canonic server-side (idempotency.py), pentru ca RAR accepta duplicate si nu are nr. comanda. build_key normalizeaza INTOTDEAUNA account_id la account_or_default (None == 1) INAINTE de hash — altfel acelasi rand logic primeste chei diferite pe canalele API vs import (OV-2). canonicalize_row normeaza VIN/nr/odometru (strip ".0" din coercion Excel) inainte de validare si de cheie.
  • FINALIZATA e terminal la RAR — fara anulare/corectie prin API. De aceea reconcilierea anti-duplicat: pe eroare tranzitorie sau rand sending orfan, worker-ul cauta in finalizate (match pe vin+dataPrestatie+odometruFinal) si marcheaza sent fara a re-trimite (reconcile.py).
  • Creds RAR per cont: durabile in accounts.rar_creds_enc (canal web, fallback re-login) SAU efemere in submissions.rar_creds_enc (canal API, sterse dupa primul login reusit). Worker incearca submission-ul intai, apoi fallback la cont. Purjarea sterge DOAR submissions.rar_creds_enc, NU accounts.rar_creds_enc.
  • Auth API-key (auth.py): identifica CONTUL ROAAUTO, separat de credentialele RAR. Stocam doar SHA-256 al cheii. Enforcement prin AUTOPASS_REQUIRE_API_KEY: false (dev) → fara cheie merge pe cont id=1, cheie invalida → 401; true (prod) → cheie obligatorie pe /v1/* protejat. POST-urile + rutele de import sunt account-scoped; GET-urile de listare sunt momentan globale + neprotejate (de remediat — vezi ROADMAP).
  • Mapare coloane retinuta per (account_id, signature_coloane) (column_mappings): la urmatorul fisier cu aceleasi coloane, pentru acelasi cont, maparea se reaplica automat. Un cont poate avea mai multe formate memorate simultan.
  • Mapare operatie→cod: prestatie poate veni cu cod_prestatie (cod RAR direct) sau cod_op_service (cod intern) + denumire. Nerezolvat → submission needs_mapping (nu se trimite), apare in editorul web cu sugestie fuzzy; la salvarea maparii se re-rezolva automat submission-urile blocate.
  • WAF RAR da 403 fara User-Agent de browser — toate apelurile httpx trimit User-Agent: Mozilla/5.0 (config.py, confirmat live).
  • 422 fara echo de credentiale: handler-ul global de validare in main.py pastreaza type/loc/msg dar DROP-a input/ctx (altfel ar reflecta rar_credentials.password).
  • Retentie: submissions sent + import_batches primesc purge_after = now + 90 zile; worker-ul purjeaza odata pe ora (T16, GDPR/L.142).

Masina de stari submissions

queued → sending → sent (succes, cu id_prezentare de la RAR). Ramuri: needs_mapping (cod nerezolvat), needs_data (RAR 400, validare continut), error (max retries / 4xx nerecuperabil / creds invalide / login 401 — NU se face retry pe creds gresite). Backoff: next_attempt_at = now + base*2^retry, plafonat. Schema completa: app/schema.sql.

Mod non-interactiv

Vezi /workspace/CLAUDE.md (workspace-level): cand esti lansat cu claude -p, creeaza fisiere noi DOAR in /workspace/.claude-work/<task>/, nu in /workspace. Modificarile la fisiere existente se fac in locatia originala.