# Chei API

Cheile API ale firmei: cum creezi una, cum o trimiți, ce poate face, cât durează și cum o revoci.

## La ce folosește

O cheie API lasă un script sau un server al firmei să citească date fără sesiunea unui om. Cheia aparține firmei, are un rol de firmă, o dată de expirare și se poate revoca oricând, fără să schimbi parola cuiva.

Cheia arată așa: `ilk_` urmat de 43 de caractere (32 de octeți aleatori în base64url). Platforma păstrează doar amprenta ei SHA-256, nu cheia. De aceea cheia întreagă **se vede o singură dată**, în răspunsul la creare.

> [!WARNING]
> Ține cheia într-o variabilă de mediu sau într-un seif, nu în cod, în commituri sau în mesaje. Dacă ai pierdut-o sau a ajuns unde nu trebuie, revoc-o și creează alta.

## Cum o trimiți

În antetul `Authorization`, cu schema `Bearer`:

```bash title="O citire cu cheia API"
curl https://app.ilustromania.com/nucleu/v1/stoc \
  -H "Authorization: Bearer $CHEIE_API"
```

O cerere cu `Bearer ilk_…` se judecă **doar** după cheie: cookie-ul de sesiune din aceeași cerere nu se mai citește. Firma și rolul vin din cheie; antetul `X-Firma-Tab` poate numi doar firma cheii, altfel răspunsul este `403`.

## Ce poate face o cheie

Accesul este închis implicit: o cheie trece doar pe rutele dintr-o listă fixă, numai cu metodele din listă. Pe orice altă rută răspunsul este `403` cu `{"eroare": "interzis_cu_cheie_api"}`.

| Rută | Metode | Ce primești |
| --- | --- | --- |
| `/v1/stoc` | `GET`, `HEAD` | stocul firmei, citit din casa de marcat; cere un rol cu drept de citire a stocului (`bucatar`, `patron`, `gestiune`, `finante`); pe o firmă fără stoc legat, `404` |
| `/v1/info` | `GET`, `HEAD` | este pe listă, dar ruta cere rolul `admin`, pe care o cheie nu îl are niciodată: răspunsul este `403` |

În referința API, rutele care merg cu o cheie au mențiunea „merge și cu o cheie API”. Lista se lărgește doar cu rutele care decid accesul pe rolul și firma cererii, fără să recitească drepturile contului care a creat cheia; o rută nouă pe listă apare în [Noutăți](#note-versiune).

Pe lângă listă, mai contează două lucruri:

- **Doar citire.** O cheie creată cu `doarCitire: true` (implicit) primește `403` cu `{"eroare": "cheie_doar_citire"}` la orice metodă în afară de `GET` și `HEAD`.
- **Rolul nu trece peste cel al creatorului.** La fiecare cerere, contul care a creat cheia trebuie să fie încă activ pe firmă și să fie `admin` sau să aibă chiar rolul cheii. Dacă nu mai este, cheia nu mai merge (`401`).

## Gestionarea cheilor

Cheile se creează, se văd și se revocă doar de un **Administrator al firmei**, doar din sesiune (cookie). O cerere făcută cu o cheie API pe aceste rute primește `403`: o cheie nu poate crea altă cheie.

| Rută | Ce face |
| --- | --- |
| `GET /v1/chei-api` | lista cheilor firmei (cel mult 200), întâi cele care încă merg; fără cheia întreagă |
| `POST /v1/chei-api` | creează o cheie; răspunsul `201` conține cheia întreagă, o singură dată |
| `POST /v1/chei-api/{id}/revoca` | revocă cheia; de acum orice cerere cu ea primește `401` |

### Creează cheia

```bash title="Creare"
curl -X POST https://app.ilustromania.com/nucleu/v1/chei-api \
  -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \
  -H "Content-Type: application/json" \
  -d '{"nume": "Raport stoc zilnic", "rol": "gestiune", "doarCitire": true, "zile": 90}'
```

| Câmp | Tip | Obligatoriu | Detalii |
| --- | --- | --- | --- |
| `nume` | `string` | da | între 1 și 80 de caractere, fără caractere de control; te ajută să recunoști cheia în listă |
| `rol` | `string` | da | una din: `bucatar`, `casier`, `gestiune`, `patron`, `marketing`, `angajat`, `finante`; niciodată `admin` |
| `doarCitire` | `boolean` | nu | implicit `true`; `false` = cheia poate și scrie, în limita rolului |
| `zile` | `integer` | nu | după câte zile expiră: între 1 și 365, implicit 90 |

Câmpurile necunoscute sunt refuzate (`400` cu `{"eroare": "corp_invalid"}`).

### Salvează cheia din răspuns

```json title="Răspuns 201"
{
  "cheie": "ilk_…",
  "detalii": {
    "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f",
    "nume": "Raport stoc zilnic",
    "prefix": "ilk_Ab12Cd34",
    "rol": "gestiune",
    "doarCitire": true,
    "creataLa": "2026-11-12T09:30:00.000Z",
    "expiraLa": "2027-02-10T09:30:00.000Z",
    "revocataLa": null,
    "ultimaFolosire": null,
    "creataDe": "9b2e4c1a-7d3f-4e8b-a1c2-5f6e7d8c9b0a"
  }
}
```

`cheie` nu mai apare nicăieri după acest răspuns. În listă rămâne doar `prefix` (`ilk_` și primele 8 caractere), ca să știi despre ce cheie e vorba.

### Folosește-o, apoi revoc-o când nu mai e nevoie

```bash title="Revocare"
curl -X POST https://app.ilustromania.com/nucleu/v1/chei-api/$ID_CHEIE/revoca \
  -H "Cookie: __Host-sesiune=$JETON_SESIUNE"
```

Răspunsul conține cheia cu `revocataLa` completat. O cheie deja revocată sau a altei firme dă `404` cu `{"eroare": "negasit"}`.

## Câmpurile din listă

| Câmp | Ce înseamnă |
| --- | --- |
| `id` | identificatorul cheii (pentru revocare) |
| `nume`, `rol`, `doarCitire` | cum a fost creată |
| `prefix` | începutul cheii (`ilk_` + 8 caractere) |
| `creataLa`, `expiraLa` | momentele ISO 8601, în UTC |
| `revocataLa` | momentul revocării sau `null` |
| `ultimaFolosire` | ultima dată când cheia a fost acceptată la o cerere, actualizată cel mult o dată pe minut, sau `null` |
| `creataDe` | contul Administratorului care a creat-o |

## Limite și răspunsuri

- **Expirarea este obligatorie:** cel mult 365 de zile de la creare. O cheie expirată dă `401`; pentru continuitate creezi una nouă înainte de expirare și o schimbi în script.
- **Cel mult 20 de chei active** pe firmă (nerevocate și neexpirate). A 21-a primește `409` cu `{"eroare": "plafon_chei"}`.
- **Limita de cereri:** fiecare cheie are numărătoarea ei, cu aceleași praguri ca o sesiune (vezi [Limite](#limite)); adresa IP se numără și ea.

| Situație | Răspuns |
| --- | --- |
| Cheie necunoscută, revocată sau expirată; creatorul nu mai are rolul | `401` cu `{"eroare": "neautentificat"}`, fără alte detalii |
| Rută care nu e pe lista cheilor | `403` cu `{"eroare": "interzis_cu_cheie_api"}` |
| Scriere cu o cheie doar de citire | `403` cu `{"eroare": "cheie_doar_citire"}` |
| `X-Firma-Tab` cu altă firmă decât a cheii | `403` cu `{"eroare": "interzis"}` |
| Gestionarea cheilor fără sesiune / fără rolul `admin` / cu o cheie | `401` / `403` / `403` |

**Cum se verifică o cheie pe server**
La fiecare cerere, serverul calculează SHA-256 al cheii primite și caută amprenta în tabelul `chei_api`. Cheia contează doar dacă nu e revocată, nu a expirat, iar contul care a creat-o este încă activ pe firmă, cu rolul `admin` sau cu rolul cheii. Căutarea se face printr-o funcție a bazei (`cheie_api_verifica`), fiindcă firma nu se știe încă; după ea, cererea lucrează sub izolarea firmei, ca orice altă cerere. Tabelul este izolat pe firmă: un Administrator vede și revocă doar cheile firmei lui.
