---
name: project_lease_release
description: Libération de leases DHCP — bouton page Leases + warning lease actif à la création/édition de résa
metadata:
  type: project
---

Chantier livré le 2026-07-15 (commits `153de78`→`cd70608`, sur master). Permet de libérer
un lease DHCP actif via l'API Kea (`lease4-del`). Spec/plan dans `docs/superpowers/`.

**Brique** (`lib/LeaseService.php`, testée + vérifiée E2E contre le vrai Kea) :
- `releaseLease($ip)` → `['success','existed','message']` ; `result 3` (lease absent) = succès.
- `findActiveLeases($ips)` → leases state=0 par IP ; `[]` si API KO (best-effort).
- `orphanLeasesForReservation($macNew, $ipNew, $ipOld)` → orphelins (IP_new tenue par autre MAC ;
  IP_old = bail fantôme si l'IP change en édition).
- `httpPost` passé `private`→`protected` + appels en `static::` pour l'override en test.

**Feature 1 — bouton « Libérer »** page Leases (admin|tech, `CAN_EDIT` JS + garde serveur) :
confirmation JS → POST `public/lease_release.php` → `releaseLease`. Audit `lease.release`.
leases.php rend désormais le flash de session (ne le faisait pas avant).

**Feature 2 — warning lease actif** à la création/édition/**suppression** de résa : à la
soumission, avant écriture, détection des leases concernés. Si présents et pas de décision
(`$_POST['lease_action']` vide) → confirmation (boutons). **`ignore`/`sans libérer` est le 1er
bouton du DOM** (Entrée = choix sûr, pas de libération accidentelle) ; `release` en btn-danger.
Échec de libération = warning, pas de rollback.
- **add/edit** (`reservation_add/edit.php`) : réaffichage du form, 3 boutons, `orphanLeasesForReservation`.
- **delete** (`reservation_delete.php`, commit `290dc93`) : la page de confirmation existe déjà ;
  `findActiveLeases([$ip])` sur l'IP de la résa, 2 boutons « Supprimer sans libérer » / « Supprimer
  et libérer le lease ».

**Pièges rencontrés (vérifiés en revue) :**
- Rappel : les **leases** vivent en **memfile** (mode FreeBSD ici), PAS en base — d'où l'API
  obligatoire, jamais d'écriture SQL. Voir [[project_leases_grouping]].
- `reservation_add.php` n'avait aucun réaffichage sur POST valide avant ce chantier ; le nouvel
  écran de confirmation en introduit un → il a fallu re-binder l'état des champs (bug corrigé :
  checkbox `no_gateway` perdait son état, commit `cd70608`). Leçon : quand on ajoute un
  réaffichage à un formulaire, vérifier que TOUS les champs re-bindent leur valeur.
- `request_id` (suppression de demande publique) venait de `$_GET` seul → fallback `$_POST`
  ajouté pour survivre au resubmit du flux de confirmation.
- API Kea réelle : `http://192.168.1.2:8000`, Basic Auth user `kea-api` (creds dans app_settings).
