# Admin booth a distance -- version hebergement web mutualise

Remplace l'idee initiale d'un `cloud-backend` (Python/FastAPI, qui a besoin
d'un vrai serveur/VPS pour tourner) par une version en PHP, deployable sur
l'hebergement web classique que tu as deja chez OVH (Apache + `.htaccess`,
pas d'acces SSH/root necessaire) -- voir la decision dans le doc
d'architecture du projet. Fournit les memes capacites que `cloud-backend` +
`cloud-admin-frontend` (multi-booth, multi-evenement, envoi de template,
diagnostic), juste avec une interface plus simple (pas d'editeur visuel de
template -- voir plus bas).

## Ce que ca fait (et ne fait pas)

Chaque NUC continue d'appeler periodiquement ce serveur (heartbeat sortant --
jamais de port ouvert sur le NUC, voir `booth-backend/app/cloud_sync/
agent.py`, **inchange**) pour signaler son etat et recuperer des commandes en
attente :

- changer le quota d'impression
- remettre le compteur d'impressions a 0
- changer le reseau Wi-Fi
- recevoir un template compose depuis l'admin (voir "Templates" plus bas)
- changer le theme/nom d'evenement/logo (branding, voir plus bas)
- lancer un diagnostic (base de donnees, camera, imprimante, reseau, espace
  disque) ou recuperer les dernieres lignes de `booth.log`, affiches dans
  "Commandes recentes" au heartbeat suivant
- redemarrer/eteindre le booth, ou forcer une mise a jour immediate (`git
  pull` + redemarrage, au lieu d'attendre le prochain passage du timer deja
  en place sur le NUC)

**Plusieurs booths, plusieurs evenements** : chaque booth a ses propres
identifiants (generes depuis l'admin, voir "Ajouter un booth" plus bas) et
peut etre rattache a un evenement ; un evenement peut avoir plusieurs
templates, pousses vers n'importe quel booth qui lui est rattache (ou vers
n'importe quel autre, rien n'empeche de pousser un template "hors evenement").

**Volontairement absent** (demande un vrai process applicatif, pas
compatible avec de l'hebergement mutualise sans execution continue) :
apercu camera en direct, galerie, reglages de reconnaissance faciale. Pour
ca, utilise l'admin locale du NUC (sur site, ou via un VPN type Tailscale si
tu veux y acceder a distance un jour -- voir le doc d'architecture).

**Pas d'editeur visuel de template** (glisser/deposer une zone photo,
contrairement a `cloud-admin-frontend`) : construire ca en PHP/JS natif
serait un chantier a part entiere pour un gain limite sur un admin delibere
"simplifie". A la place, la page "Templates" accepte un SVG deja prepare
(upload de fichier ou texte colle) avec ses zones `data-slot="N"` deja
placees -- voir "Preparer un SVG de template" plus bas pour le detail du
format attendu.

## Deploiement

1. **Upload des fichiers** -- copie tout le contenu de ce dossier
   (`cloud-php-admin/`) a la racine de ton hebergement web (FTP, ou Git si ton
   panneau OVH le propose). `data/` doit etre inscriptible par PHP (c'est le
   cas par defaut sur un hebergement partage classique).

2. **Configuration** -- copie `config.php.example` en `config.php`. Aucune
   valeur obligatoire a renseigner (les identifiants de booth se generent
   depuis l'admin, pas ici) -- ajuste juste `booth_online_threshold_seconds`
   si le defaut (90s) ne convient pas.

3. **Mot de passe** -- l'INTEGRALITE de ce service est derriere un Basic Auth
   Apache, pas seulement `admin/` : `api/` (appele par le booth) a son
   **propre** compte, distinct de celui de l'admin humain -- si le `.env`
   d'un NUC fuite un jour, il ne doit donner acces qu'a cette API machine,
   jamais a l'admin humain. Pour CHACUN des deux dossiers (`admin/` et
   `api/`), deux facons au choix :
   - *Simple* : si ton panneau OVH propose une protection par mot de passe
     graphique pour un dossier, applique-la et ignore le `.htaccess`
     correspondant.
   - *Manuelle* : cree un fichier `.htpasswd` (hors du dossier servi
     publiquement si possible) avec `htpasswd -c .htpasswds/photobooth-admin
     tonidentifiant` (resp. `.htpasswds/photobooth-api` pour l'API), puis
     remplace la ligne `AuthUserFile` dans `admin/.htaccess` (resp.
     `api/.htaccess`) par le vrai chemin absolu.

   Cote NUC, renseigne les identifiants du compte **api** (pas celui de
   l'admin) dans `booth-backend/.env` :
   ```
   BOOTH_CLOUD_HTTP_USER=<identifiant du compte api/>
   BOOTH_CLOUD_HTTP_PASSWORD=<mot de passe du compte api/>
   ```

4. **HTTPS** -- active le certificat SSL gratuit propose par OVH pour ce
   domaine (bouton dans le panneau d'hebergement). Sans ca, les mots de passe
   de l'etape 3, le secret de signature (voir "Signature des commandes" plus
   bas) et les cles API de booth circulent en clair.

## Ajouter un booth

1. Ouvre `https://ton-domaine.fr/admin/` (page "Booths"), renseigne un nom et
   clique "Creer".
2. La cle API generee s'affiche **UNE SEULE FOIS** -- note-la immediatement
   (elle n'est jamais stockee en clair ailleurs que dans `data/state.json`,
   jamais re-affichee).
3. Cote NUC, dans `booth-backend/.env` :
   ```
   BOOTH_CLOUD_BASE_URL=https://ton-domaine.fr/chemin-si-sous-dossier
   BOOTH_CLOUD_BOOTH_ID=<id affiche>
   BOOTH_CLOUD_BOOTH_API_KEY=<cle affichee>
   BOOTH_CLOUD_HTTP_USER=<compte api/, voir "Deploiement" etape 3>
   BOOTH_CLOUD_HTTP_PASSWORD=<mot de passe du compte api/>
   BOOTH_CLOUD_COMMAND_SECRET=<secret de signature, voir "Signature des commandes">
   ```
   Puis redemarre `booth-backend` (`sudo systemctl restart booth-backend`
   sur le NUC). Aucun autre changement de code -- `cloud_sync/agent.py`
   parle a ce serveur PHP exactement comme il parlait au `cloud-backend`
   Python ou a `cloud-admin-frontend`.
4. Repete pour chaque booth supplementaire -- chacun a sa propre paire
   id/cle, jamais partagee entre deux NUC. `BOOTH_CLOUD_HTTP_USER`/
   `_PASSWORD` et `BOOTH_CLOUD_COMMAND_SECRET` peuvent en revanche etre les
   memes sur tous les booths d'un meme deploiement cloud-php-admin.

## Signature des commandes

Sans elle, un admin cloud-php-admin compromis (identifiants Basic Auth
voles, session PHP volee, future faille applicative dans ce dossier)
pourrait mettre en file n'importe quelle commande (changer le Wi-Fi,
pousser un faux template...) et le booth l'executerait sans discussion.
Avec elle, toute commande doit porter une signature HMAC qu'un attaquant
ne peut pas forger sans connaitre `BOOTH_CLOUD_COMMAND_SECRET` -- un secret
qui n'est **jamais ecrit sur le serveur cloud** (ni `config.php`, ni
`data/state.json`), seulement tape par toi a chaque session.

1. **Choisis un secret fort** : `openssl rand -hex 32` (une phrase
   memorisable est un mauvais choix -- voir le commentaire en tete de
   `lib/command_signing.php` sur le brute-force hors-ligne). Garde-le dans
   un gestionnaire de mots de passe.
2. **Colle-le dans `BOOTH_CLOUD_COMMAND_SECRET`** sur chaque NUC qui doit
   accepter des commandes signees avec ce secret (voir "Ajouter un booth"
   ci-dessus), puis redemarre `booth-backend`.
3. **A chaque session admin**, sur la page d'un booth (`admin/booth.php`),
   tape ce meme secret dans le bloc "Signature des commandes" et clique
   "Deverrouiller" -- une seule fois par session (session PHP standard : tant
   que ton navigateur garde ses cookies, jusqu'a expiration/fermeture). Tant
   que ce n'est pas fait, aucune commande (quota, Wi-Fi, reset, template) ne
   peut etre envoyee a aucun booth.

Si le secret cote booth et celui tape cote admin ne correspondent pas, la
commande apparait dans "Commandes recentes" avec le statut `failed` et le
detail "Signature de commande absente ou invalide" -- jamais executee.

## Diagnostic, logs, branding et maintenance

Toutes ces actions (page d'un booth) passent par le meme mecanisme que les
autres commandes -- mises en file, signees, appliquees et acquittees au
heartbeat suivant (pas immediat, borne par `cloud_sync_interval_seconds`
cote NUC, 30s par defaut).

- **Diagnostic** et **Logs** : le resultat (texte) apparait dans "Commandes
  recentes" une fois le booth passe -- jamais dans le heartbeat periodique
  lui-meme (lire une image camera a chaque heartbeat entrerait en contention
  avec le flux live du kiosque), seulement a la demande.
- **Branding** : theme + nom d'evenement + logo (optionnel -- laisser le
  champ vide garde le logo actuel). S'applique a l'evenement **actif** sur
  le booth ; rien n'est pre-rempli ici (le cloud ne connait pas la valeur
  actuelle, seul le booth la connait).
- **Redemarrer/Eteindre** : necessitent `deploy/setup-power-control.sh`
  deja execute sur le NUC (voir `deploy/README.md`) -- sans ca, la commande
  revient en `failed` avec "refuse par polkit".
- **Forcer la mise a jour** : necessite `deploy/setup-force-update.sh`
  deja execute sur le NUC (voir `deploy/README.md`) -- sans ca, la commande
  revient en `failed` avec "refusee (regle sudoers manquante)". Declenche
  immediatement `photobooth-auto-update.service` (le meme `git pull` +
  reconstruction + redemarrage que le timer automatique, qui continue par
  ailleurs de tourner toutes les 5 min) : utile juste apres avoir pousse un
  correctif sur GitHub, sans attendre le prochain passage.

## Preparer un SVG de template

La page "Templates" attend un SVG ou chaque zone photo porte un attribut
`data-slot="N"` (N = ordre de prise de vue, 1, 2, 3...), exactement comme
`booth-backend/app/core/templates_engine.py::parse_slots` le lit a
l'import local :

```xml
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1200">
  <rect x="0" y="0" width="1200" height="1200" fill="#fff"/>
  <rect data-slot="1" x="40" y="40" width="520" height="1120"/>
  <rect data-slot="2" x="600" y="40" width="520" height="1120"/>
</svg>
```

Le champ "Configuration des slots" (JSON, optionnel) permet d'ajouter des
filtres/un mode de cadrage par slot :

```json
{"1": {"filters": ["sepia"], "fit_mode": "cover"}, "2": {"filters": [], "fit_mode": "contain"}}
```

Un template cloud n'est jamais edite en place une fois cree (pas de route de
mise a jour, meme limite que `cloud-backend`) : pour le changer, cree-en un
nouveau et supprime l'ancien.

## Verifier que ca marche

- `https://ton-domaine.fr/admin/` doit demander un mot de passe (celui de
  `admin/.htaccess`), et `https://ton-domaine.fr/api/booth/heartbeat` un
  AUTRE mot de passe (celui de `api/.htaccess`) si tu y vas directement au
  navigateur -- confirme que les deux comptes sont bien actifs et distincts.
  Puis `/admin/` doit lister les booths crees (statut "hors ligne" tant
  qu'un booth n'a pas encore fait son premier heartbeat).
- Sur le NUC, apres redemarrage de `booth-backend` : les logs doivent
  montrer `Agent de synchronisation cloud demarre`, puis, au heartbeat
  suivant, le statut du booth dans l'admin doit passer "en ligne" (sinon,
  verifie `BOOTH_CLOUD_HTTP_USER`/`_PASSWORD` dans les logs -- une erreur 401
  signifie un mauvais mot de passe `api/.htaccess`).
- Deverrouille la signature des commandes (page du booth, voir "Signature
  des commandes" plus haut), teste une commande (ex: changer le quota),
  attends le prochain intervalle de sync, et verifie dans "Commandes
  recentes" que son statut passe `pending` -> `delivered` -> `acked` (pas
  `failed` avec "Signature de commande absente ou invalide" -- sinon,
  `BOOTH_CLOUD_COMMAND_SECRET` ne correspond pas entre le NUC et ce que tu as
  tape).
- Le "Diagnostic" (page du booth) affiche le dernier rapport brut remonte
  par le NUC (quota, compteur d'impressions...).

## Limites assumees

- Stockage en fichier JSON plat (pas de base de donnees) : largement
  suffisant pour ce volume (quelques booths/evenements/templates), a ne pas
  garder si ce service grossit beaucoup -- tout `data/state.json` est
  reecrit a chaque modification.
- Pas d'editeur visuel de template (voir plus haut) : prepare le SVG a part
  (Inkscape, ou l'editeur de `booth-frontend`/`cloud-admin-frontend` sur un
  evenement existant, en recuperant le SVG genere) avant de l'uploader ici.
