# 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`](docs/api-rar-contract.md).
Progres + proces: [`docs/ROADMAP.md`](docs/ROADMAP.md).
**Ghid pentru utilizatori (service-uri), in termeni simpli:** [`docs/ghid-utilizator.md`](docs/ghid-utilizator.md).

## Pornire rapida

```bash
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)

```bash
./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_...`.

```bash
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.

```bash
# 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:

```bash
# 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`](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/nomenclator` ramane public intentionat (coduri RAR publice,
> fara date personale).

## Proba reala la RAR (mediu test)

1. Pune creds de test in `settings.xml` (copiaza din `settings.xml.example`, bloc `<test>`; **nu** se comite).
   `settings.xml` tine un singur cont RAR doar pentru dev/test — creds-urile conturilor reale stau criptate in DB.
2. Baga prezentari in coada (import sau API).
3. `./start.sh test worker --send` — worker-ul trimite si trece fiecare rand in `sent` (cu `id_prezentare`),
   `needs_data` sau `error`.
4. 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, marcheaza `sent` fara re-trimitere). `FINALIZATA` e 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

```bash
python3 -m pytest -q                 # toata suita
python3 -m pytest tests/test_x.py -q # un fisier
```

## Docker / deploy

```bash
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)
```
