Textul din bannerul de import (0 medii) si din antetul formularului de credentiale nu spunea concret ce mediu foloseste instanta curenta. Vechiul "Trimiterea va folosi configuratia globala" era jargon, iar "Pentru a activa Testare sau Productie" nu clarifica relatia instanta<->mediu. - Adauga globalul Jinja `mediu_instanta()` = eticheta umana a ancorei globale AUTOPASS_RAR_ENV (Testare/Productie), fallback sigur pe Testare. - `_upload.html`: bannerul de 0 medii numeste concret mediul global al instantei pe care cad trimiterile pana la activarea unui mediu. - `_cont.html`: nota onesta sub antetul "Credentiale RAR" — instanta ruleaza pe mediul global X, ambele medii se pot configura aici (fiecare validat separat), iar la 0 medii active trimiterile cad pe mediul global al instantei. Fara selector nou si fara schimbari in logica de scriere a credentialelor (A1, aliniat PRD 5.20: instanta = ancora de fallback pentru env). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Gateway RAR AUTOPASS
Gateway web (Python / FastAPI) care preia prezentarile de service-auto si le declara la RAR AUTOPASS (Legea 142/2023, OM 210/2024). Inlocuieste integrarea Visual FoxPro (ROAAUTO).
Doua procese peste acelasi SQLite, care comunica prin tabela submissions:
- API (
app.main:app) — dashboard web, API v1, signup/login, panou admin,/healthz,/metrics. - Worker (
app.worker) — login RAR, trimite prezentarile din coada, retry/backoff, heartbeat.
Trimiterea catre RAR e dezactivata implicit (AUTOPASS_WORKER_SEND_ENABLED=false) — sigur pentru probe.
Sursa de adevar pentru contractul RAR: docs/api-rar-contract.md.
Progres + proces: docs/ROADMAP.md.
Ghid pentru utilizatori (service-uri), in termeni simpli: docs/ghid-utilizator.md.
Pornire rapida
pip3 install -r requirements.txt # Python 3.12+
uvicorn app.main:app --reload --port 8010 # API (dashboard /, Swagger /docs)
python3 -m app.worker # worker (doar daca vrei sa procesezi coada)
La prima pornire se creeaza schema SQLite si se face seed la nomenclatorul RAR — dashboard-ul si maparile merg imediat, offline. Pentru testarea UI-ului si a importului nu ai nevoie de worker.
Dev rapid fara login: porneste cu AUTOPASS_WEB_AUTH_REQUIRED=false (dashboard pe contul implicit id=1).
Cu start.sh (ambaleaza mediu + rol)
./start.sh test both --send # API + worker, trimite 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 (test/prod), forwardeaza rolul
Pagini web
| URL | Ce vezi |
|---|---|
/ |
Dashboard: coada, prezentari blocate, stare worker, import fisier, mapari, nomenclator |
/signup · /login |
Inregistrare cont (emite cheia API o data) · autentificare |
/admin |
Panou admin: conturi pe stari, activare/blocare/arhivare (doar admini) |
/integrare |
Exemple cod (Python/C#/Node/VFP), export Postman/OpenAPI, testeaza conexiunea |
/docs |
Swagger UI — API v1 interactiv |
/healthz · /metrics |
sanatate JSON · metrici text |
Import fisier (xlsx / csv)
Pe dashboard: incarca fisierul → mapeaza coloanele (sugerate automat fuzzy; maparea se retine
pe semnatura coloanelor, per cont) → preview (fiecare rand: ok / needs_mapping / needs_data /
already_sent / ...) → confirma (retastezi numarul de randuri ok). Randurile intra in coada.
Coloane recunoscute (cu sinonime): VIN, Nr inmatriculare, Data prestatie, Odometru final,
Odometru initial, Operatie, Observatii. Fiecare cont poate avea mai multe formate memorate.
API v1 (curl)
Dev: fara cheie → cont id=1. Productie (AUTOPASS_REQUIRE_API_KEY=true): header X-API-Key: rfak_....
curl -s http://localhost:8010/healthz | python3 -m json.tool # sanatate
curl -s http://localhost:8010/v1/nomenclator # coduri RAR (cache local)
curl -s http://localhost:8010/v1/prezentari # coada
# Trimite o prezentare. `rar_credentials` e OPTIONAL: daca lipseste, worker-ul
# foloseste creds-urile RAR salvate pe cont (POST /v1/conturi/rar-creds). Trimite-le
# explicit doar cand vrei sa le suprascrii pe acea cerere.
curl -s -X POST http://localhost:8010/v1/prezentari \
-H 'X-API-Key: rfak_...' -H 'Content-Type: application/json' \
-d '{
"prezentari": [{
"vin": "WAUZZZ8K0AA000001", "nr_inmatriculare": "B123ABC",
"data_prestatie": "2026-06-15", "odometru_final": "120000",
"prestatii": [{"cod_op_service": "REVIZIE PERIODICA", "denumire": "REVIZIE PERIODICA"}]
}]
}'
# Dry-run: valideaza payload + mapare, FARA enqueue, FARA creds
curl -s -X POST http://localhost:8010/v1/prezentari/valideaza \
-H 'X-API-Key: rfak_...' -H 'Content-Type: application/json' -d '{ "prezentari": [ ... ] }'
# Import fisier
curl -s -X POST http://localhost:8010/v1/import -H 'X-API-Key: rfak_...' -F 'file=@import.xlsx'
Toate endpointurile sunt in /docs. Exemple gata facute + Postman/OpenAPI: hub-ul /integrare.
Conturi si chei API
Fiecare service = un cont (accounts) cu lifecycle (pending → active → blocked / archived / deleted).
Worker-ul trimite doar pentru conturi active. Web-ul se autentifica prin sesiune (login email+parola),
API-ul prin cheie API (X-API-Key). Cheia identifica contul, e separata de credentialele RAR.
# Self-onboarding: service-ul deschide /signup → primeste cheia o data.
# Primul user inregistrat in toata baza (indiferent de account_id) devine admin
# automat (is_admin, acces /admin) — nu e hardcodat in .env. Doar al doilea+ signup
# creeaza cont obisnuit, neadmin. Daca a trecut deja primul signup, acorzi admin manual:
python3 -m tools.account set-admin --account N
# Sau din CLI (admin, pe masina gateway-ului):
python3 -m tools.account create --name "Service Auto SRL" --cui RO12345678 --with-key
python3 -m tools.account list [--pending] | activate --account N | set-admin --account N
python3 -m tools.apikey create|list|rotate|revoke --account N # cheie afisata O SINGURA DATA
Creds RAR per cont, pe medii — fiecare cont are doua sloturi separate de credentiale RAR,
Testare si Productie (sisteme RAR distincte; un set de creds merge pe exact unul). Criptate
Fernet at-rest. Din web se seteaza in Cont → Credentiale RAR (doua sectiuni, cu validare prin login
si confirmare unica la activarea Productie). Din API, campul rar_target alege slotul:
# Seteaza creds pe mediul Testare (lipsa rar_target -> mediul implicit / ancora globala)
curl -s -X POST http://localhost:8010/v1/conturi/rar-creds \
-H 'X-API-Key: rfak_...' -H 'Content-Type: application/json' \
-d '{"email": "service@exemplu.ro", "password": "parola-rar", "rar_target": "test"}'
La trimitere, POST /v1/prezentari accepta rar_env (test/prod); lipsa lui -> mediul implicit al
contului. Pe ce mediu a mers fiecare rand vezi in GET /v1/prezentari (camp rar_env) si in badge-ul din
dashboard. Explicatie pentru operatori: docs/ghid-utilizator.md.
POST-urile si listarile per-cont (
/v1/prezentari,/v1/audit/export, fragmentele web) sunt filtrate pe contul cheii API.GET /v1/nomenclatorramane public intentionat (coduri RAR publice, fara date personale).
Proba reala la RAR (mediu test)
- Pune creds de test in
settings.xml(copiaza dinsettings.xml.example, bloc<test>; nu se comite).settings.xmltine un singur cont RAR doar pentru dev/test — creds-urile conturilor reale stau criptate in DB. - Baga prezentari in coada (import sau API).
./start.sh test worker --send— worker-ul trimite si trece fiecare rand insent(cuid_prezentare),needs_datasauerror.- Verifica: dashboard,
curl /v1/prezentari, sau./start.sh test finalizate(listeaza direct de la RAR).
sent+id_prezentare= RAR a acceptat. La raspuns pierdut, worker-ul reconciliaza anti-duplicat (cauta in finalizate, marcheazasentfara re-trimitere).FINALIZATAe terminal la RAR.
Configurare (AUTOPASS_*)
| Variabila | Implicit | Rol |
|---|---|---|
DB_PATH |
./data/autopass.db |
calea SQLite |
RAR_ENV |
test |
ancora globala test / prod; mediile per-cont (Testare/Productie) au prioritate cand contul le are configurate |
REQUIRE_API_KEY |
false |
true = cere cheie pe /v1/* (prod) |
WEB_AUTH_REQUIRED |
true |
false = dashboard fara login, cont id=1 (dev) |
CREDS_KEY |
(efemera) | cheie Fernet creds RAR — trebuie PARTAJATA intre API si worker |
SESSION_SECRET |
(efemer) | secret cookie sesiune; persistent in prod |
WORKER_SEND_ENABLED |
false |
true = trimite efectiv la RAR |
SMTP_HOST (+ _PORT/_USER/_PASSWORD/_FROM) |
(none) | notificare admin la signup (best-effort) |
Genereaza chei: python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
(CREDS_KEY) si python3 -c "import secrets; print(secrets.token_hex(32))" (SESSION_SECRET).
Teste
python3 -m pytest -q # toata suita
python3 -m pytest tests/test_x.py -q # un fisier
Docker / deploy
cp .env.example .env # CRITIC: completeaza AUTOPASS_CREDS_KEY (partajata api+worker)
docker compose up --build # api (:8010) + worker + autoheal, acelasi image + volum SQLite
Structura
app/
main.py # FastAPI: API v1 + dashboard + auth + admin
api/v1/ # router.py (prezentari, valideaza, nomenclator, mapari, conturi),
# import_router.py, integrare_router.py (ping, postman/openapi)
web/ # routes.py (dashboard + import HTMX), auth_routes.py, admin_routes.py,
# session.py, csrf.py, labels.py, templates/, static/
worker/ # proces separat: login RAR, send, retry, heartbeat
rar_client.py # client HTTP RAR (login/JWT, postPrezentare, nomenclator)
auth.py users.py accounts.py # chei API, parole scrypt + admin, lifecycle conturi
validation.py mapping.py errors.py crypto.py # validare, mapare cod, erori 3-niveluri, Fernet
schema.sql # schema SQLite
tools/ # CLI admin: account, apikey, backup, rar_finalizate, import_dbf
docs/ # contract RAR + ROADMAP + prd/
tests/ legacy-vfp/ # suita pytest · arhiva ROAAUTO (referinta)