# 🌐 API vitrine — modèles

API en lecture seule qui expose l'arborescence **type > styles > sous-styles > modèles > variations**
à un projet extérieur (le site WordPress, une future application, un partenaire).

Elle remplace côté Laravel le service PHP pur `src/Services/Wordpress/ModeleService.php`
de l'API Slim, qui tapait la même base en PDO.

---

## 🧭 Qui est où

Trois domaines interviennent, et ce ne sont pas les mêmes machines :

| Domaine | Ce que c'est | Rôle ici |
|---|---|---|
| **logiciel.acportail.fr** | ce projet Laravel, le portail des techniciens | **héberge l'API** décrite ci-dessous |
| **acportail.fr** | le site vitrine WordPress | **consomme** l'API |
| **modeles.acportail.fr** | l'hébergement des images des modèles | fournit les visuels pointés par le champ `image` |

Autrement dit, le site vitrine appelle `https://logiciel.acportail.fr/api/wordpress/modeles`
et affiche des images servies par `modeles.acportail.fr`. L'API ne sert **jamais** les images
elle-même : elle renvoie leur URL absolue, construite à partir de `MODELES_IMAGE_BASE`.

En local, l'API répond sur l'URL du `php artisan serve` (par exemple
`http://127.0.0.1:8000/api/wordpress/modeles`) : seul le domaine change, les chemins sont identiques.

---

## 📌 Les deux routes

À préfixer par le domaine du portail, `https://logiciel.acportail.fr` en production.

| Méthode | URL | Réponse |
|---|---|---|
| `GET` | `/api/wordpress/modeles` | tous les types autorisés |
| `GET` | `/api/wordpress/modeles/{type}` | un seul type, ex. `/api/wordpress/modeles/coulissant` |

Le `{type}` s'écrit comme en base : **minuscules, sans accent** (`coulissant`, `battant`, `portillon`).
La casse de l'URL est tolérée, les accents non.

Codes de retour :

| Code | Cas |
|---|---|
| `200` | OK |
| `401` | en-tête `Authorization` absent ou illisible |
| `403` | token fourni mais faux |
| `404` | type inconnu ou hors liste blanche |
| `429` | plus de 60 requêtes par minute pour cette IP |
| `503` | `WORDPRESS_API_TOKEN` vide côté serveur — la route se ferme au lieu de s'ouvrir à tous |

---

## 📦 Le JSON renvoyé

Seul ce que la vitrine affiche sort de l'application : le `nom` à chaque niveau, plus l'`image`
sur les modèles et les variations. Ni identifiants, ni horodatages, ni **prix ni marges**.

```json
{
  "nom": "coulissant",
  "styles": [
    {
      "nom": "style classique",
      "modeles": [],
      "sous_styles": [
        {
          "nom": "style classique plein",
          "modeles": [
            {
              "nom": "c-100",
              "image": "https://modeles.acportail.fr/images/COULISSANTS/.../C-100.jpg",
              "variations": [
                { "nom": "c-100-1", "image": "https://modeles.acportail.fr/images/.../C-100-1.jpg" }
              ]
            }
          ]
        }
      ]
    }
  ]
}
```

À savoir pour le code qui consomme :

- `modeles` au niveau du style contient les modèles **sans sous-style**. Il est souvent vide,
  ce n'est pas une erreur : il faut lire les deux niveaux.
- Un type qui n'aurait aucun style renvoie directement `{"nom": ..., "modeles": [...]}`, sans clé `styles`.
- `image` vaut `null` quand la ligne n'a pas d'image.
- Les libellés sortent en minuscules, comme en base. La mise en majuscules est du ressort de
  l'affichage (CSS `text-transform`).
- L'ordre est celui de la vitrine : « style classique » d'abord, puis les styles par id.

### Ce qui est filtré

1. **`vitrine = true`** — un modèle passé à `vitrine = 0` dans le back-office disparaît de l'API,
   ainsi que le style ou le sous-style qui n'aurait plus que des modèles masqués.
2. **Liste blanche des types** — `$allowedTypes` dans `app/Services/Wordpress/ModeleService.php`,
   aujourd'hui `coulissant`, `battant`, `portillon`. Portes de garage, stores et clôtures ont
   beau être en `vitrine = 1`, ils ne sortent pas tant qu'ils ne sont pas ajoutés là.
3. **Corbeille** — les lignes supprimées en douceur (`deleted_at`) sont exclues.

Les deux premiers filtres se cumulent : la liste blanche dit quels **types** sont publiables,
`vitrine` dit quels **modèles** le sont à l'intérieur.

---

## ⚙️ Côté portail (logiciel.acportail.fr) : la configuration

Deux variables dans le `.env` :

```dotenv
# Token partagé avec les projets clients
WORDPRESS_API_TOKEN=une-longue-chaine-aleatoire
# Base des URLs d'images (les chemins sont relatifs en base)
MODELES_IMAGE_BASE=https://modeles.acportail.fr
```

Générer un token :

```bash
php -r "echo bin2hex(random_bytes(32)) . PHP_EOL;"
```

En production, après toute modification du `.env` :

```bash
php artisan config:cache
```

Sans ça la configuration en cache reste l'ancienne et l'API répond `503` ou refuse le nouveau token.

---

## 🚀 Utiliser l'API depuis un autre projet

### 1. Récupérer le token

Il est dans le `.env` du portail, sur `logiciel.acportail.fr`, jamais dans le dépôt. Transmettre par un canal privé,
et prévoir un token différent par projet client le jour où il y en a plusieurs (il suffira de
dupliquer l'entrée `services.wordpress` : le middleware lit la clef de configuration qu'on lui passe).

### 2. Vérifier l'accès

```bash
curl -H "Authorization: Bearer LE_TOKEN" https://logiciel.acportail.fr/api/wordpress/modeles/coulissant
```

Le token peut aussi voyager en HTTP Basic pour les clients qui ne savent pas faire autrement :
il est alors le **mot de passe**, le nom d'utilisateur n'est pas vérifié.

### 3. Appeler depuis WordPress

Toujours **de serveur à serveur**. Le token ne doit jamais se retrouver dans du JavaScript
envoyé au navigateur : il donnerait à n'importe quel visiteur le catalogue complet.

Attention au domaine : c'est bien `logiciel.acportail.fr`, le portail Laravel, et non `acportail.fr`
qui est le site vitrine lui-même.

Dans `wp-config.php` :

```php
define('ACPORTAIL_API_URL', 'https://logiciel.acportail.fr/api/wordpress');
define('ACPORTAIL_API_TOKEN', 'le-token');
```

Dans le thème ou un plugin maison :

```php
function acportail_modeles(string $type = '') {
    $key = 'acportail_modeles_' . ($type ?: 'all');

    if ($cache = get_transient($key)) {
        return $cache;
    }

    $res = wp_remote_get(ACPORTAIL_API_URL . '/modeles/' . $type, [
        'headers' => ['Authorization' => 'Bearer ' . ACPORTAIL_API_TOKEN],
        'timeout' => 15,
    ]);

    if (is_wp_error($res) || wp_remote_retrieve_response_code($res) !== 200) {
        return null; // on garde l'affichage precedent plutot qu'une page vide
    }

    $data = json_decode(wp_remote_retrieve_body($res), true);
    set_transient($key, $data, 15 * MINUTE_IN_SECONDS);

    return $data;
}
```

Puis, à l'affichage :

```php
$type = acportail_modeles('coulissant');

foreach ($type['styles'] as $style) {
    echo '<h2>' . esc_html($style['nom']) . '</h2>';

    // modeles sans sous-style
    foreach ($style['modeles'] as $modele) {
        echo '<img src="' . esc_url($modele['image']) . '" alt="' . esc_attr($modele['nom']) . '">';
    }

    foreach ($style['sous_styles'] as $sousStyle) {
        echo '<h3>' . esc_html($sousStyle['nom']) . '</h3>';

        foreach ($sousStyle['modeles'] as $modele) {
            echo '<img src="' . esc_url($modele['image']) . '" alt="' . esc_attr($modele['nom']) . '">';
        }
    }
}
```

### 4. Mettre en cache côté client

La liste complète pèse environ **167 Ko** et la limite est de **60 requêtes par minute et par IP**.
Un cache de quelques minutes chez le client (transient WordPress, `Cache::remember` en Laravel,
Redis…) évite d'aller chercher un catalogue qui ne bouge que quand quelqu'un modifie un modèle.

### 5. Cas particulier : appel depuis un navigateur

`acportail.fr` et `logiciel.acportail.fr` sont deux **origines différentes** pour le navigateur,
même sous-domaine parent ou pas : un `fetch()` depuis les pages du site vitrine est donc soumis au CORS.
L'appel en JavaScript demande alors deux choses de plus, et reste déconseillé :

- publier la configuration CORS (`php artisan config:publish cors`) et y lister les origines
  autorisées — l'équivalent du `CORS_ORIGINS_WORDPRESS` de l'API Slim. Par défaut Laravel
  autorise `api/*` depuis `*`, ce qui est trop large pour une route à token ;
- accepter que le token soit lisible par tout le monde dans le code de la page. Mieux vaut
  un petit proxy côté serveur du projet client.

---

## 🔧 Faire évoluer l'API

| Besoin | Où intervenir |
|---|---|
| Ouvrir un type de plus | `$allowedTypes` dans `app/Services/Wordpress/ModeleService.php` |
| Publier ou masquer un modèle | case **vitrine** du modèle, dans le back-office |
| Renvoyer un champ de plus (ex. `prix`) | `fetchModeles()` du même service |
| Changer le domaine des images | `MODELES_IMAGE_BASE` |
| Changer la limite de débit | `throttle:60,1` dans `routes/api.php` |

### Les fichiers concernés

| Fichier | Rôle |
|---|---|
| `routes/api.php` | déclaration des deux routes, token et limite de débit |
| `app/Http/Controllers/Api/Wordpress/ModeleController.php` | entrée HTTP, encodage JSON |
| `app/Services/Wordpress/ModeleService.php` | requêtes et construction de l'arborescence |
| `app/Http/Middleware/VerifyApiToken.php` | contrôle du token (alias `api.token`) |
| `config/services.php` | bloc `wordpress` : token et base des images |
