GET /v1/ping intorcea settings.rar_env (mediul global al serverului, ex. "prod") in campul `mediu`, desi contul poate opera pe alt mediu. Un cont cu doar creds de test primea "prod", iar clientul ROAAUTO afisa badge rosu PRODUCTIE desi prezentarile ruleaza in test. Acum `mediu` = rar_env_efectiv al contului (doar din mediile disponibile = activate + cu creds), cu fallback pe ancora globala doar cand contul nu are niciun mediu disponibil. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
196 lines
7.2 KiB
Python
196 lines
7.2 KiB
Python
"""Router integrare — endpoint-uri de integrare externe.
|
|
|
|
Endpointuri:
|
|
GET /v1/ping — readiness check per cont (autentificat sau dev fallback)
|
|
GET /v1/integrare/postman.json — export colectie Postman v2.1.0
|
|
|
|
Ruta /v1/ping foloseste `resolve_account_id` (dependinta standard) pentru 401
|
|
pe cheie invalida / prod fara cheie. Flag-ul `autentificat_cu_cheie` e derivat
|
|
separat citind header-ele brute si verificand cheia real-time (fara sa dubleze
|
|
logica de 401 — aceea ramane in `resolve_account_id`).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
from fastapi import APIRouter, Depends, Header
|
|
from fastapi.responses import JSONResponse
|
|
|
|
from ...auth import _extract_key, account_for_key, resolve_account_id
|
|
from ...config import get_settings
|
|
from ...db import get_connection
|
|
from ...mapping import account_or_default
|
|
from ...rar_env import rar_env_efectiv
|
|
|
|
router = APIRouter(prefix="/v1", tags=["integrare"])
|
|
|
|
|
|
@router.get("/ping")
|
|
def ping(
|
|
account_id: int = Depends(resolve_account_id),
|
|
x_api_key: str | None = Header(default=None, alias="X-API-Key"),
|
|
authorization: str | None = Header(default=None),
|
|
) -> JSONResponse:
|
|
"""Readiness check per cont.
|
|
|
|
Intoarce:
|
|
account_id — contul rezolvat din cheie (sau 1 in dev fara cheie)
|
|
mediu — mediul RAR efectiv al contului ("test"/"prod"),
|
|
nu ancora globala settings.rar_env
|
|
autentificat_cu_cheie — True daca cererea a venit cu o cheie API reala valida
|
|
are_creds_rar — True daca contul are creds RAR stocate pe cel putin un mediu (test sau prod)
|
|
are_creds_test — True daca contul are creds RAR pentru mediul Testare
|
|
are_creds_prod — True daca contul are creds RAR pentru mediul Productie
|
|
ts — timestamp ISO UTC al cererii
|
|
"""
|
|
settings = get_settings()
|
|
|
|
# Detectam daca s-a folosit o cheie reala (nu fallback dev).
|
|
# `resolve_account_id` a garantat deja ca nu e cheie invalida (ar fi dat 401).
|
|
# Acum verificam doar daca exista o cheie extrasa si daca e valida pentru cont.
|
|
cheie_bruta = _extract_key(x_api_key, authorization)
|
|
autentificat_cu_cheie = False
|
|
if cheie_bruta:
|
|
conn = get_connection()
|
|
try:
|
|
acct = account_for_key(conn, cheie_bruta)
|
|
finally:
|
|
conn.close()
|
|
autentificat_cu_cheie = acct is not None
|
|
|
|
# Verificam daca contul are creds RAR stocate (per mediu).
|
|
aid = account_or_default(account_id)
|
|
conn = get_connection()
|
|
try:
|
|
row = conn.execute(
|
|
"SELECT rar_test_enabled, rar_prod_enabled, rar_creds_test_enc, "
|
|
"rar_creds_prod_enc, rar_env_default FROM accounts WHERE id=?", (aid,)
|
|
).fetchone()
|
|
finally:
|
|
conn.close()
|
|
|
|
are_creds_test = bool(row and row["rar_creds_test_enc"])
|
|
are_creds_prod = bool(row and row["rar_creds_prod_enc"])
|
|
are_creds_rar = are_creds_test or are_creds_prod
|
|
|
|
# mediu = mediul RAR EFECTIV al contului (REQ-DEFAULT), nu ancora globala
|
|
# settings.rar_env: un cont cu doar creds de test trebuie sa raporteze "test"
|
|
# chiar daca serverul ruleaza global pe "prod". Fallback pe ancora globala doar
|
|
# cand contul nu are niciun mediu disponibil (rar_env_efectiv -> None).
|
|
mediu_efectiv = (rar_env_efectiv(row) if row else None) or settings.rar_env
|
|
|
|
return JSONResponse({
|
|
"account_id": aid,
|
|
"mediu": mediu_efectiv,
|
|
"autentificat_cu_cheie": autentificat_cu_cheie,
|
|
"are_creds_rar": are_creds_rar,
|
|
"are_creds_test": are_creds_test,
|
|
"are_creds_prod": are_creds_prod,
|
|
"ts": datetime.now(timezone.utc).isoformat(),
|
|
})
|
|
|
|
|
|
# Allowlist hardcodat (NU derivat din app.routes) — cele 3 rute de integrare expuse extern.
|
|
_POSTMAN_ITEMS = [
|
|
{
|
|
"name": "Trimite prezentari",
|
|
"request": {
|
|
"method": "POST",
|
|
"header": [
|
|
{"key": "X-API-Key", "value": "{{api_key}}"},
|
|
{"key": "Content-Type", "value": "application/json"},
|
|
],
|
|
"url": {
|
|
"raw": "{{base_url}}/v1/prezentari",
|
|
"host": ["{{base_url}}"],
|
|
"path": ["v1", "prezentari"],
|
|
},
|
|
"body": {
|
|
"mode": "raw",
|
|
"options": {"raw": {"language": "json"}},
|
|
# rar_credentials e optional: cererea trimite doar cheia API + datele
|
|
# prezentarii; worker-ul foloseste creds-urile RAR salvate pe cont.
|
|
"raw": (
|
|
'{\n'
|
|
' "prezentari": [\n'
|
|
' {\n'
|
|
' "vin": "WVWZZZ1KZAW000123",\n'
|
|
' "nr_inmatriculare": "B999TST",\n'
|
|
' "data_prestatie": "2026-06-15",\n'
|
|
' "odometru_final": "123456",\n'
|
|
' "prestatii": [\n'
|
|
' {"cod_prestatie": "OE-1"}\n'
|
|
' ]\n'
|
|
' }\n'
|
|
' ]\n'
|
|
'}'
|
|
),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
"name": "Import fisier (xlsx/csv)",
|
|
"request": {
|
|
"method": "POST",
|
|
"header": [
|
|
{"key": "X-API-Key", "value": "{{api_key}}"},
|
|
],
|
|
"url": {
|
|
"raw": "{{base_url}}/v1/import",
|
|
"host": ["{{base_url}}"],
|
|
"path": ["v1", "import"],
|
|
},
|
|
"body": {
|
|
"mode": "formdata",
|
|
"formdata": [
|
|
{
|
|
"key": "file",
|
|
"type": "file",
|
|
"description": "Fisier xlsx sau csv cu prezentarile de importat",
|
|
}
|
|
],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
"name": "Ping (readiness check)",
|
|
"request": {
|
|
"method": "GET",
|
|
"header": [
|
|
{"key": "X-API-Key", "value": "{{api_key}}"},
|
|
],
|
|
"url": {
|
|
"raw": "{{base_url}}/v1/ping",
|
|
"host": ["{{base_url}}"],
|
|
"path": ["v1", "ping"],
|
|
},
|
|
},
|
|
},
|
|
]
|
|
|
|
|
|
@router.get("/integrare/postman.json")
|
|
def postman_export() -> JSONResponse:
|
|
"""Export colectie Postman v2.1.0 cu cele 3 rute de integrare.
|
|
|
|
Allowlist hardcodat — NU deriva din app.routes pentru a nu expune
|
|
rute interne (ex. /v1/conturi/rar-creds, rutele web etc.).
|
|
"""
|
|
colectie = {
|
|
"info": {
|
|
"name": "RAR AUTOPASS Gateway",
|
|
"description": (
|
|
"Colectie de integrare pentru gateway-ul RAR AUTOPASS (Legea 142/2023, OM 210/2024). "
|
|
"Seteaza variabilele `base_url` si `api_key` inainte de utilizare."
|
|
),
|
|
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
|
|
},
|
|
"variable": [
|
|
{"key": "base_url", "value": "http://localhost:8010", "type": "string"},
|
|
{"key": "api_key", "value": "", "type": "string"},
|
|
],
|
|
"item": _POSTMAN_ITEMS,
|
|
}
|
|
return JSONResponse(content=colectie, media_type="application/json")
|