# API Framework

Ce répertoire est un **framework d'API générique**. Il ne connaît aucune feature spécifique — les features s'y enregistrent elles-mêmes. Ajouter une nouvelle route ne nécessite jamais de modifier `router.js`.

---

## Structure

```
src/core/api/
├── index.js           Exports publics (routeRequest, requireAuth, apiSuccess, defineApiRoutes…)
├── router.js          Orchestration : rate limit, CSRF, auth, dispatch
├── runtime.js         État global persisté : resolver de session + registre des feature routes
├── route-handler.js   Intégration Next.js App Router (GET/POST/PUT/DELETE/PATCH)
├── define.js          defineApiRoutes() — validateur de définitions de routes
├── respond.js         apiSuccess() / apiError() / getStatusCode() — utilitaires de réponse
├── core-routes.js     Index des routes built-in (seul fichier à toucher pour un nouveau handler core)
├── file-response.js   Réponse streaming pour les fichiers (GET /zen/api/storage/**)
└── health.js          GET /zen/api/health

src/features/auth/api.js     Routes /zen/api/users/* (vivent avec la feature auth)
src/core/storage/api.js      Routes /zen/api/storage/** (vivent avec le module storage)
```

Les routes vivent **avec leur feature**, pas dans le framework. `src/core/api/` ne contient que l'infrastructure.

---

## Cycle de vie d'une requête

```
route-handler.js (GET / POST / PUT / DELETE / PATCH)
  └─→ routeRequest(request, path)
        ├─ Rate limit (IP, sauf GET /health)
        ├─ CSRF (méthodes state-mutating uniquement)
        └─ Boucle sur toutes les routes (core d'abord, modules ensuite)
             ├─ matchRoute(pattern, path)   — exact, :param, /**
             ├─ Auth enforcement (depuis la définition de la route)
             │    'admin' → requireAdmin()  — session dans context.session
             │    │          si `permission` est défini → hasPermission() → 403 si refusé
             │    'user'  → requireAuth()   — session dans context.session
             │    'public'→ aucun           — context.session = undefined
             └─ handler(request, params, context)
```

---

## Niveaux d'auth

| Niveau | Qui peut appeler | `context.session` |
|--------|-----------------|-------------------|
| `'public'` | Tout le monde, sans cookie | `undefined` |
| `'user'` | Utilisateur avec une session valide | `{ user: { id, email, name, role, … } }` |
| `'admin'` | Utilisateur admin (`role === 'admin'`) | `{ user: { id, email, name, role: 'admin', … } }` |

L'auth est **toujours déclarée dans la définition de route**, jamais dans le handler.

---

## Ajouter des routes à une feature core

Deux étapes :

**1. Créer un fichier `api.js` dans le répertoire de la feature :**

```js
// src/features/myfeature/api.js
import { defineApiRoutes } from '../../core/api/define.js';
import { apiSuccess, apiError } from '../../core/api/respond.js';

async function handleGetSettings(_request, _params, _context) {
  return apiSuccess({ theme: 'light' });
}

async function handleUpdateSettings(request, _params, { session }) {
  const body = await request.json();
  // ... validation + update ...
  return apiSuccess({ success: true, message: 'Settings updated' });
}

export const routes = defineApiRoutes([
  { path: '/settings', method: 'GET', handler: handleGetSettings,    auth: 'user'  },
  { path: '/settings', method: 'PUT', handler: handleUpdateSettings,  auth: 'admin' },
]);
```

**2. L'enregistrer dans `initializeZen()` (src/shared/lib/init.js) :**

```js
import { registerFeatureRoutes } from '../../core/api/index.js';
import { routes as settingsRoutes } from '../../features/myfeature/api.js';

registerFeatureRoutes(settingsRoutes);
```

**C'est tout.** Les routes sont disponibles à `GET /zen/api/settings` et `PUT /zen/api/settings`.

> Note : `core-routes.js` n'est utilisé que pour les routes built-in de l'infrastructure (health, storage). Les routes de features passent par `registerFeatureRoutes()` dans `initializeZen()`.

---

## Ajouter des routes depuis un module

Les modules définissent leurs routes dans leur propre `api.js` — le système de découverte les charge automatiquement. Aucune modification dans `src/core/api/` n'est nécessaire.

```js
// src/modules/mymodule/api.js
import { defineApiRoutes } from '@zen/core/api';

export default {
  routes: defineApiRoutes([
    { path: '/admin/mymodule/items', method: 'GET',  handler: handleList,   auth: 'admin'  },
    { path: '/mymodule/:slug',       method: 'GET',  handler: handlePublic, auth: 'public' },
  ])
};
```

---

## Signature des handlers

```js
async function handleSomething(request, params, context) {
  // request  — objet Request Next.js
  // params   — paramètres extraits du chemin { id: '42', … }
  //            Pour les routes wildcard (/storage/**) : params.wildcard
  // context  — { session? } — session présente si auth !== 'public'
}
```

---

## Utilitaires de réponse

Toujours utiliser `apiSuccess` et `apiError` pour que l'intention soit explicite.

```js
import { apiSuccess, apiError } from '../../core/api/respond.js';

// Succès — GET
return apiSuccess({ user: { id, email, name } });

// Succès — mutation (avec confirmation pour le frontend)
return apiSuccess({ success: true, user: updatedUser, message: 'Profil mis à jour' });

// Erreur
return apiError('Not Found',    'Utilisateur introuvable');
return apiError('Bad Request',  'Le nom est requis');
return apiError('Forbidden',    'Accès refusé');
```

Codes d'erreur reconnus par `getStatusCode()` → HTTP status :

| Code | Status |
|------|--------|
| `'Unauthorized'` | 401 |
| `'Admin access required'` | 403 |
| `'Forbidden'` | 403 |
| `'Not Found'` | 404 |
| `'Bad Request'` | 400 |
| `'Too Many Requests'` | 429 |
| `'Internal Server Error'` | 500 |

---

## defineApiRoutes()

Valide les définitions de routes à l'import (au démarrage). Une erreur de configuration lève immédiatement une `TypeError` avec un message précis.

Champs requis par route :

| Champ | Type | Valeurs valides |
|-------|------|-----------------|
| `path` | `string` | Commence par `/`. Supporte `:param` et `/**` en fin |
| `method` | `string` | `GET` \| `POST` \| `PUT` \| `PATCH` \| `DELETE` |
| `handler` | `Function` | Signature : `(request, params, context) => Promise<Object>` |
| `auth` | `string` | `'public'` \| `'user'` \| `'admin'` |

Champs optionnels :

| Champ | Type | Description |
|-------|------|-------------|
| `skipRateLimit` | `boolean` | Exempte la route du rate limiting par IP (ex. health checks) |
| `permission` | `string` | Sur une route `auth: 'admin'`, exige en plus cette clé de permission granulaire (ex. `'users.manage'`). Retourne 403 si l'utilisateur ne la possède pas. Voir `PERMISSIONS` dans `src/core/users/constants.js` |

---

## Note — handler storage

Le handler storage (`src/core/storage/api.js`) est déclaré `auth: 'public'` mais effectue sa propre validation d'accès en interne, basée sur le chemin du fichier :

- **Prefixes publics** (déclarés via `storagePublicPrefixes` dans chaque module) → aucune session requise
- **`/users/{userId}/…`** → session requise ; l'utilisateur ne peut accéder qu'à ses propres fichiers (sauf admin)
- **`/organizations/…`** → admin uniquement
- **`/posts/…`** (non couverts par un prefix public) → admin uniquement
- **Tout autre chemin** → refusé

---

## Compatibilité des modules existants

Les handlers de modules existants avec la signature `(request, params)` continuent de fonctionner — JavaScript ignore les arguments supplémentaires non utilisés.
