# IliaCloud — Plan d'implementation API publique REST

## 1. Architecture

### Separation API interne vs API publique

L'application a deja des routes Express qui servent le frontend (`/auth`, `/servers`, `/billing`, etc.). L'API publique est **separee** :

- **Routes internes** : `/auth/*`, `/servers/*`, `/billing/*` — utilisees par le frontend, auth par cookie httpOnly
- **API publique** : `/api/v1/*` — utilisee par les clients externes, auth par API key dans le header

Les routes publiques sont **versionnees** (`/api/v1/`) pour garantir la retrocompatibilite. Un changement de route interne ne casse que le frontend. Un changement de route publique casse le code des clients.

### Politique de versionnage

- `/api/v1/` — version actuelle, stable
- Si un endpoint change de facon non retrocompatible → depreciation sur `/api/v1/` pendant 6 mois, nouvelle version sur `/api/v2/`
- Les reponses de depreciation incluent un header `Sunset: <date>` et `Link: </api/v2/...>; rel="successor-version"`

---

## 2. Authentification

### Table `user_api_keys`

Nouvelle table distincte de `api_keys` (qui stocke les cles IA des providers).

```sql
CREATE TABLE IF NOT EXISTS user_api_keys (
  id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id     UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  key_hash    TEXT NOT NULL UNIQUE,        -- SHA-256 de la cle (jamais en clair)
  key_prefix  TEXT NOT NULL,               -- 8 premiers caracteres (pour identification dans l'UI)
  label       TEXT NOT NULL DEFAULT '',     -- nom donne par l'user ("CI/CD", "monitoring", etc.)
  scopes      TEXT[] NOT NULL DEFAULT '{}', -- permissions : ['read', 'write', 'admin']
  last_used_at TIMESTAMPTZ,
  expires_at  TIMESTAMPTZ,                 -- null = pas d'expiration
  created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_user_api_keys_user ON user_api_keys(user_id);
CREATE INDEX IF NOT EXISTS idx_user_api_keys_hash ON user_api_keys(key_hash);
```

### Generation de cle

```
Format : iliacloud_<random 48 chars base62>
Exemple : iliacloud_a7Bf3kL9mN2pQ5rS8tU1vW4xY6zA0cD3eF5gH7iJ9k
```

- La cle en clair est affichee **une seule fois** a la creation
- Stockee en base sous forme de hash SHA-256
- Le prefix (8 chars) est stocke pour l'affichage dans l'UI (`iliacloud_a7Bf3kL9...`)

### Middleware d'authentification

```
Header : Authorization: Bearer iliacloud_xxxxx
```

Le middleware :
1. Extrait la cle du header `Authorization`
2. Hash la cle avec SHA-256
3. Cherche le hash dans `user_api_keys`
4. Verifie `expires_at` (si defini)
5. Met a jour `last_used_at`
6. Charge l'utilisateur et son plan
7. Injecte `req.user` et `req.apiKey` (avec scopes)

---

## 3. Endpoints a exposer

### Serveurs

| Methode | Endpoint | Scope | Description |
|---------|----------|-------|-------------|
| GET | `/api/v1/servers` | read | Liste des serveurs de l'utilisateur |
| GET | `/api/v1/servers/:id` | read | Detail d'un serveur (nom, host, status) |
| GET | `/api/v1/servers/:id/metrics` | read | Metriques actuelles (CPU, RAM, disk, load) |
| GET | `/api/v1/servers/:id/metrics/history` | read | Historique des metriques (query: `range=24h\|7d\|30d`) |

### Uptime monitors

| Methode | Endpoint | Scope | Description |
|---------|----------|-------|-------------|
| GET | `/api/v1/monitors` | read | Liste des monitors uptime |
| GET | `/api/v1/monitors/:id` | read | Detail d'un monitor (URL, status, uptime %) |
| GET | `/api/v1/monitors/:id/history` | read | Historique des checks |
| POST | `/api/v1/monitors` | write | Creer un monitor |
| PATCH | `/api/v1/monitors/:id` | write | Modifier un monitor |
| DELETE | `/api/v1/monitors/:id` | write | Supprimer un monitor |
| POST | `/api/v1/monitors/:id/pause` | write | Pauser/reprendre un monitor |

### SSL Certificats

| Methode | Endpoint | Scope | Description |
|---------|----------|-------|-------------|
| GET | `/api/v1/ssl` | read | Liste des certificats surveilles |
| GET | `/api/v1/ssl/:id` | read | Detail (domaine, expiration, statut) |
| POST | `/api/v1/ssl` | write | Ajouter un certificat a surveiller |
| DELETE | `/api/v1/ssl/:id` | write | Retirer un certificat |

### Alertes

| Methode | Endpoint | Scope | Description |
|---------|----------|-------|-------------|
| GET | `/api/v1/alerts/rules` | read | Liste des regles d'alertes |
| POST | `/api/v1/alerts/rules` | write | Creer une regle |
| PATCH | `/api/v1/alerts/rules/:id` | write | Modifier une regle |
| DELETE | `/api/v1/alerts/rules/:id` | write | Supprimer une regle |
| GET | `/api/v1/alerts/history` | read | Historique des alertes declenchees |

### Backups

| Methode | Endpoint | Scope | Description |
|---------|----------|-------|-------------|
| GET | `/api/v1/servers/:id/backups` | read | Liste des backups d'un serveur |
| POST | `/api/v1/servers/:id/backups` | write | Declencher un backup manuel |
| GET | `/api/v1/servers/:id/backups/:backupId/download` | read | Telecharger un backup |

### Quick Actions

| Methode | Endpoint | Scope | Description |
|---------|----------|-------|-------------|
| GET | `/api/v1/servers/:id/actions` | read | Liste des actions rapides |
| POST | `/api/v1/servers/:id/actions/:actionId/run` | write | Executer une action |

### Status Pages

| Methode | Endpoint | Scope | Description |
|---------|----------|-------|-------------|
| GET | `/api/v1/status-pages` | read | Liste des pages de statut |
| PATCH | `/api/v1/status-pages/:id` | write | Modifier une page |

### Annotations

| Methode | Endpoint | Scope | Description |
|---------|----------|-------|-------------|
| GET | `/api/v1/servers/:id/annotations` | read | Liste des annotations |
| POST | `/api/v1/servers/:id/annotations` | write | Creer une annotation |
| DELETE | `/api/v1/servers/:id/annotations/:annotId` | write | Supprimer |

### Webhooks

| Methode | Endpoint | Scope | Description |
|---------|----------|-------|-------------|
| GET | `/api/v1/webhooks` | read | Liste des webhooks configures |
| POST | `/api/v1/webhooks/:id/test` | write | Envoyer un payload de test au webhook |

### Compte

| Methode | Endpoint | Scope | Description |
|---------|----------|-------|-------------|
| GET | `/api/v1/me` | read | Profil de l'utilisateur (plan, email, usage) |
| GET | `/api/v1/me/usage` | read | Utilisation actuelle (serveurs, monitors, messages restants) |

---

## 4. Rate limiting par plan

### Limites par plan

| Plan | Requests/heure | Requests/jour | Burst (par seconde) |
|------|---------------|---------------|---------------------|
| Free | — | — | — |
| Pro | 1 000 | 10 000 | 20 |
| Business | 5 000 | 50 000 | 50 |
| Enterprise | 20 000 | 200 000 | 100 |

> Le plan Free n'a pas acces a l'API publique. L'API est un argument de conversion Pro.

### Implementation

Middleware qui :
1. Identifie l'utilisateur via la cle API
2. Lit son plan — retourne `403` si le plan n'a pas `api_access`
3. Applique la limite correspondante via `express-rate-limit` avec un store en memoire (suffisant pour une instance Docker solo, Redis si multi-instances plus tard)
4. Retourne les headers standard :
   - `X-RateLimit-Limit: 1000`
   - `X-RateLimit-Remaining: 847`
   - `X-RateLimit-Reset: 1609459200`
5. Retourne `429 Too Many Requests` avec `Retry-After` si depassement

---

## 5. Format des reponses

### Succes

```json
{
  "data": { ... },
  "meta": {
    "page": 1,
    "per_page": 20,
    "total": 42
  }
}
```

### Erreur

```json
{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Limite de 1000 requetes/heure atteinte.",
    "details": { "limit": 1000, "reset_at": "2026-04-04T12:00:00Z" }
  }
}
```

### Pagination

Tous les endpoints qui retournent des listes supportent :
- `?page=1&per_page=20` (defaut 20, max 100)
- Reponse inclut `meta.total`, `meta.page`, `meta.per_page`

---

## 6. Documentation automatique

### Stack technique

- **`swagger-autogen`** — genere `swagger.json` depuis les routes Express au build
- **`swagger-ui-express`** — sert la doc interactive sur `/api/docs`
- Les JSDoc sur chaque route alimentent la description Swagger

### Annotations dans le code

```javascript
// #swagger.tags = ['Monitors']
// #swagger.summary = 'Liste des monitors uptime'
// #swagger.security = [{ "ApiKeyAuth": [] }]
// #swagger.responses[200] = { description: 'Liste des monitors' }
```

### Endpoint documentation

| URL | Description |
|-----|-------------|
| `/api/docs` | Interface Swagger UI interactive |
| `/api/docs/json` | Specification OpenAPI en JSON |

---

## 7. Structure des fichiers

```
backend/src/
  routes/
    api/
      v1/
        index.js          -- routeur principal /api/v1
        servers.js         -- endpoints serveurs
        monitors.js        -- endpoints uptime
        ssl.js             -- endpoints SSL
        alerts.js          -- endpoints alertes
        backups.js         -- endpoints backups
        actions.js         -- endpoints quick actions
        statusPages.js     -- endpoints status pages
        annotations.js     -- endpoints annotations
        webhooks.js        -- endpoints webhooks + test
        me.js              -- endpoints compte/usage
    apiKeys.manage.js      -- CRUD des cles API (UI settings)
  middleware/
    apiAuth.js             -- middleware auth par API key
    apiRateLimit.js        -- rate limiting par plan
  swagger.js               -- config swagger-autogen
```

### Principe d'implementation

Les routes `/api/v1/*` **reutilisent la logique existante** des routes internes. Pas de duplication de code. Chaque route API appelle les memes fonctions/services que les routes internes, avec :
- Auth differente (API key au lieu de cookie)
- Format de reponse standardise (envelope `data`/`error`)
- Rate limiting specifique

---

## 8. Gestion des cles API dans l'UI

### Onglet "API" dans Settings

- Bouton "Generer une cle API"
- Modal : choisir un label + scopes (read / write / admin)
- Affichage de la cle en clair **une seule fois** (copier dans le presse-papier)
- Liste des cles actives avec : label, prefix, scopes, derniere utilisation, date de creation
- Bouton revoquer (supprime la cle)
- Option : date d'expiration (optionnelle)

---

## 9. Plan d'implementation (ordre)

### Etape 1 — Fondations (~3h) ✅
- [x] Table `user_api_keys` dans schema.sql
- [x] Middleware `apiAuth.js` (auth par header Bearer)
- [x] Middleware `apiRateLimit.js` (rate limiting par plan)
- [x] Format de reponse standardise (helper `apiResponse`)
- [x] Router `/api/v1/index.js` + montage dans `index.js`
- [x] Tests middleware (52 tests : apiAuth + apiRateLimit + apiResponse)

### Etape 2 — Routes API read-only (~4h) ✅
- [x] GET `/api/v1/servers` + `/:id` + `/:id/metrics` + `/:id/backups`
- [x] GET `/api/v1/monitors` + `/:id` + `/:id/history`
- [x] GET `/api/v1/ssl` + `/:id`
- [x] GET `/api/v1/alerts/rules` + `/history`
- [x] GET `/api/v1/me` + `/me/usage`
- [x] Tests pour chaque endpoint (33 tests dans apiRoutes.test.js)

### Etape 3 — Routes API write (~4h) ✅
- [x] POST/PATCH/DELETE monitors + POST pause
- [x] POST/DELETE SSL
- [x] POST/PATCH/DELETE alert rules
- [x] POST backup manuel
- [x] GET actions (liste) + GET/POST annotations + DELETE annotations
- [x] GET webhooks + POST webhook test
- [x] GET/PATCH status pages
- [x] Tests pour chaque endpoint (38 tests dans apiRoutesWrite.test.js)

### Etape 4 — Gestion des cles + UI (~3h) ✅
- [x] Routes CRUD cles API (`/user-api-keys` — GET, POST, DELETE)
- [x] Onglet API dans SettingsPage (frontend) — generation, scopes, copie, revocation
- [x] Generation format `iliacloud_<48 chars>`, affichage unique, hash SHA-256 en DB
- [x] Blocage plan Free (message "necessite Pro ou superieur")
- [x] Tests backend (20 tests dans userApiKeys.test.js) + frontend (SettingsPage)

### Etape 5 — Documentation (~2h) ✅
- [x] Spec OpenAPI 3.0.3 complete (swagger.json — tous les endpoints documentes)
- [x] Swagger UI via CDN (pas de dependance npm) sur `/api/docs`
- [x] Endpoint `/api/docs/json` pour la spec brute
- [x] Lien "API" dans le header du site vitrine (desktop + mobile)

### Etape 6 — Feature gate + plan Enterprise (~1h) ✅
- [x] `api_access` boolean dans les 4 plans (false free, true pro/business/enterprise)
- [x] Plan Free bloque avec 403 API_ACCESS_DENIED — argument de conversion Pro
- [x] Rate limits dynamiques depuis la DB (Pro 1000/h, Business 5000/h, Enterprise 20000/h)
- [x] Site marketing a jour (tableau comparatif avec API req/h, lien docs dans header)

---

## 10. Securite

- Les cles sont hashees SHA-256 en base (jamais en clair)
- Rate limiting par cle + par IP (double protection)
- Scopes (read/write/admin) pour limiter les permissions
- Les endpoints write sont loggues dans l'audit log
- Les cles expirees sont rejetees avec `401 Expired`
- Revocation instantanee (suppression du hash en base)
- CORS configure pour permettre les appels depuis n'importe quel domaine
- Pas de cookies, pas de CSRF — auth stateless uniquement

---

## 11. Partage d'acces (Groupes)

### Concept

Le systeme de groupes permet de partager l'acces a des serveurs et containers Docker avec des collaborateurs. Chaque groupe definit un perimetre de visibilite (quels containers) et des permissions par zone.

### Header `X-Group-Id`

Toutes les requetes API (internes et v1) supportent le header optionnel `X-Group-Id` :

```
X-Group-Id: <uuid du groupe>
```

- **Sans header** : mode global (owner voit tout)
- **Avec header** : mode groupe (donnees filtrees par le perimetre du groupe)
- Le header est injecte automatiquement par le frontend via `api.js`

### Routes CRUD Groupes

| Methode | Route | Description | Auth |
|---------|-------|-------------|------|
| `GET` | `/groups` | Lister ses groupes (owner + membre) | Cookie |
| `POST` | `/groups` | Creer un groupe | Cookie, owner |
| `GET` | `/groups/:id` | Detail d'un groupe | Cookie, owner/membre |
| `PATCH` | `/groups/:id` | Modifier un groupe | Cookie, owner |
| `DELETE` | `/groups/:id` | Supprimer un groupe (cascade) | Cookie, owner |

### Routes Membres

| Methode | Route | Description |
|---------|-------|-------------|
| `GET` | `/groups/:id/members` | Lister les membres |
| `PATCH` | `/groups/:id/members/:mid` | Modifier les permissions d'un membre |
| `DELETE` | `/groups/:id/members/:mid` | Revoquer un membre |

### Routes Invitations

| Methode | Route | Description |
|---------|-------|-------------|
| `GET` | `/groups/:id/invitations` | Lister les invitations en attente |
| `POST` | `/groups/:id/invitations` | Inviter par email (ou ajout direct si deja connu) |
| `DELETE` | `/groups/:id/invitations/:iid` | Revoquer une invitation |
| `POST` | `/groups/:id/invitations/:iid/resend` | Renvoyer l'email (max 5 fois) |
| `POST` | `/groups/invitations/accept/:token` | Accepter une invitation |

**Ajout direct** : si l'email est deja membre actif d'un autre groupe du meme owner, le membre est ajoute directement sans email. La reponse contient `direct_add: true`.

**Securite des invitations** :
- Token : 32 bytes aleatoires, stocke en SHA-256 (jamais en clair)
- Expiration : 72 heures
- Max 5 renvois par invitation
- Acceptation auto a l'inscription si invitation en attente

### Routes Serveurs du groupe

| Methode | Route | Description |
|---------|-------|-------------|
| `POST` | `/groups/:id/servers` | Lier un serveur au groupe |
| `DELETE` | `/groups/:id/servers/:sid` | Delier un serveur |

### Routes Docker Targets

Les docker targets definissent quels containers sont visibles pour les membres du groupe.

| Methode | Route | Description |
|---------|-------|-------------|
| `GET` | `/groups/:id/docker-targets` | Lister les targets |
| `POST` | `/groups/:id/docker-targets` | Ajouter un target (par nom ou label) |
| `DELETE` | `/groups/:id/docker-targets/:tid` | Supprimer un target |
| `GET` | `/groups/:id/servers/:sid/containers` | Lister les containers du serveur (pour selection) |

### Permissions

17 zones de permissions, chacune avec un niveau `none`, `read` ou `write` :

| Zone | Niveaux disponibles | Description |
|------|-------------------|-------------|
| `dashboard` | none, read | Metriques serveur |
| `metrics` | none, read | Historique metriques |
| `docker` | none, read, write | Containers, stacks, actions |
| `logs` | none, read | Logs Docker et systeme |
| `terminal` | none, write | Terminal Docker securise |
| `quick_actions` | none, write | Actions rapides dans les containers |
| `cron` | none, read, write | Gestion des crontabs |
| `backups` | none, read, write | Backups et restauration |
| `chat` | none, write | Chat IA |
| `alerts` | none, read, write | Regles d'alerte |
| `annotations` | none, read, write | Annotations timeline |
| `uptime` | none, read | Monitors uptime |
| `webhooks` | none, read | Webhooks de notification |
| `ssl` | none, read | Certificats SSL |
| `status_pages` | none, read | Pages de statut publiques |
| `audit` | none, read | Journal d'audit |

**Presets** :
- `lecteur` : read sur tout (sauf terminal, actions, chat = none)
- `operateur` : write sur Docker, cron, backups, actions, chat + read sur le reste
- `admin` : write/read max sur tout

### Filtrage automatique des donnees

En mode groupe, les donnees sont filtrees automatiquement :

| Donnee | Methode de filtrage |
|--------|-------------------|
| **Docker containers** | `access_group_docker_targets` (par nom de service ou label) |
| **Docker stacks** | Par services contenus dans la stack |
| **Logs** | Type `docker` : par docker_targets. Type `file` : masques |
| **Backups** | Par `db_container` matchant docker_targets |
| **Uptime monitors** | Par `docker_service` (rempli via scan Traefik) |
| **SSL certificats** | Par `docker_service` (rempli via scan Traefik) |
| **Alert history** | Par `server_id` dans les serveurs du groupe |
| **Audit logs** | Par `group_id` |
| **Annotations** | Par `server_id` + `ownerUserId` |

### Heritage du plan

Les membres heritent du plan du proprietaire du groupe :
- `getEffectivePlan(req)` retourne le plan du owner si `req.access.ownerUserId` differe de `req.user.id`
- `/billing/status` retourne le plan du owner quand `X-Group-Id` est present
- `checkWsFeature()` dans le WebSocket utilise aussi le plan du owner

### Terminal Docker (membres)

Les membres n'ont pas acces au terminal SSH du serveur. Ils ont un **terminal Docker** :
- Selection d'un container parmi ceux du groupe
- Execution via `docker exec -it <container> sh`
- Le membre est enferme dans le container (pas d'acces serveur)
- `docker ps` non disponible (pas de socket Docker)

### Actions rapides Docker (membres)

Les actions avec un `container_id` sont executees via `docker exec` :
- La commande est passee par stdin (`echo 'cmd' | docker exec -i container sh`)
- Le `container_id` doit matcher un docker_target du groupe
- Les actions sans `container_id` sont invisibles pour les membres (owner only)

### Codes d'erreur specifiques

| Code | Signification |
|------|---------------|
| `403 GROUP_PERMISSION_DENIED` | Permission insuffisante sur la zone |
| `403 PLAN_QUOTA_EXCEEDED` | Quota de groupes/membres atteint |
| `404` | Groupe, membre, invitation ou serveur introuvable |
| `409` | Membre deja dans le groupe / invitation deja acceptee |
| `410` | Invitation revoquee ou expiree |
| `429` | Limite de renvois d'invitation atteinte (max 5) |

### Quotas par plan

| Plan | Groupes/serveur | Membres/groupe |
|------|----------------|----------------|
| Free | 0 (desactive) | — |
| Pro | 0 (desactive) | — |
| Business | 10 | 3 |
| Enterprise | 20 | Illimite |

---

*Derniere mise a jour : 9 avril 2026*
