Paillasse/docs/runbooks/portail.md
Adam Belkacemi 30e14b1c54 ajoute le portail Laravel : connexion prof/élève, session unique, gestion de classe, salle d'attente
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-08 02:01:58 +02:00

97 lines
4.9 KiB
Markdown

# Runbook — Portail Laravel
## Où sont les choses
| Élément | Emplacement |
|---|---|
| Code de l'application | `/var/www/portail` (propriétaire `adam`, sauf `storage/` et `bootstrap/cache/` → `www-data`) |
| Secrets Laravel (`.env`) | `/var/www/portail/.env`, `640 adam:www-data`, généré une seule fois, jamais dans Git |
| Secrets MariaDB/Redis de l'app | `/etc/portail/.env`, `600 root:root`, générés une seule fois |
| Config Docker Compose (MariaDB + Redis app) | `/etc/portail/docker-compose.yml` |
| Vhost Nginx | `/etc/nginx/sites-available/portail.conf` |
| PHP | 8.3 (dépôt Sury), FPM sur `/run/php/php8.3-fpm.sock` |
Déployé par `ansible/portail.yml` (rôle `portail`) :
```
cd ansible && script -q /dev/null ansible-playbook portail.yml -i inventory.ini
```
## Architecture de l'app
- Laravel 13, Blade + Vite (Tailwind v4 via le plugin Vite officiel).
- Deux comptes distincts, deux guards (`config/auth.php`) :
- `web` → `User` (profs), email + mot de passe.
- `eleve` → `Eleve` (élèves), identifiant (`prenom.initiale`, suffixe numérique si homonyme) + code secret à 6 caractères (haché).
- **Une seule session active par compte** (`App\Models\Concerns\GereSessionUnique` + middleware `session.unique`) : un jeton est stocké en session ET en base à chaque connexion ; s'ils ne correspondent plus, la session est coupée à la requête suivante avec un message.
- Vues reprises telles quelles depuis `design/` (voir le journal du 2026-10-07, étape 8, pour les deux écarts assumés : mot de passe ajouté au formulaire d'inscription, sélecteur d'établissement retiré du formulaire de connexion).
- Écrans sans maquette, construits sobres avec les jetons visuels existants : `resources/views/mes-classes/*` (gestion des élèves, import CSV, fiches QR) et `resources/views/seances/*` (salle d'attente).
## Base de données et cache de l'app
MariaDB et Redis **dédiés à l'app**, en Docker Compose, liés à `127.0.0.1` uniquement (jamais sur `10.10.0.1`, qui est le réseau de Kolla) :
- MariaDB : port `3307` (celui de Kolla est sur `10.10.0.1:3306`).
- Redis : port `6380`.
```
cd /etc/portail && docker compose ps
```
## Déployer une mise à jour du code
```
cd ansible && script -q /dev/null ansible-playbook portail.yml -i inventory.ini
```
Le rôle synchronise le code (`rsync`, sans `vendor/`, `node_modules/`, `.env`,
ni `storage/`/`bootstrap/cache/` — ce sont de l'état runtime, jamais du code),
puis `composer install --no-dev`, `npm ci && npm run build`,
`php artisan migrate --force`, et recrée les caches
(`config:cache`, `route:cache`, `view:cache`).
**Après toute modification de code qui touche les routes ou la config**, les
caches doivent être régénérés (le rôle le fait automatiquement). Si on modifie
une vue sans repasser par Ansible, penser à `php artisan view:clear` à la main.
## Lancer les tests (en local, avant tout déploiement)
```
cd portail && ./vendor/bin/pest
```
11 tests : connexion prof, connexion élève, session unique (prof et élève),
création de classe + établissement. Base de test : SQLite en mémoire
(`phpunit.xml`), aucun rapport avec MariaDB de prod.
## Écarts au design assumés le 2026-10-07
- `auth/inscription.blade.php` : champ mot de passe ajouté (absent de la
maquette), confirmé par Adam — sans lui, la connexion prof décrite dans le
prompt de déploiement n'aurait pas pu fonctionner sans serveur mail.
- `auth/connexion.blade.php` : le sélecteur d'établissement du formulaire
enseignant a été retiré (un prof qui se connecte a déjà un établissement en
base ; ce sélecteur n'avait de sens que pour l'inscription).
- `paillasse-contenus/README.md` corrigé : `tree` n'est pas préinstallé dans
l'image (le lab 02 demande à l'élève de l'installer lui-même).
- Gestion des élèves (ajout, import CSV, fiches QR) et salle d'attente :
aucune maquette fournie, design minimal sobre construit avec les jetons
visuels existants (confirmé par Adam).
## Piège vécu le 2026-10-07
Un handler Ansible qui notifie un autre handler (`recharger nginx` →
`redémarrer nginx`) ne s'est **pas** propagé avant la fin du play : Nginx a
continué de servir sa page par défaut malgré un déploiement « réussi ».
Corrigé en fusionnant les deux handlers en un seul
(`nginx -t && systemctl reload nginx`). Si un jour un changement Nginx semble
sans effet, vérifier d'abord `sudo nginx -T` sur le serveur, puis forcer
`sudo systemctl reload nginx` à la main.
## Vérifié le 2026-10-07
- `curl http://163.172.4.195/connexion` : les deux formulaires s'affichent.
- Un élève fictif (`lea.m` / code à 6 caractères) s'est connecté et a vu son
tableau de bord (« Salut Léa », nom de sa classe) — données de test
supprimées juste après (accord d'Adam).
- `nft list tables` après déploiement : toujours seulement `inet filter` et
`ip nat` (+ `f2b-table` de fail2ban) — Nginx n'a rien ajouté au pare-feu.