feat: HUB-21 — réglages généraux du Hub consommés par ObsiGate (bannière + drapeau recherche_partout) #229
CI / lint (pull_request) Successful in 1m16s
CI / security (pull_request) Successful in 19s
CI / test (pull_request) Successful in 3m41s
CI / build (pull_request) Successful in 13s
CI / e2e (pull_request) Failing after 14m58s

This commit is contained in:
Spark
2026-11-06 17:02:04 +00:00
parent 15d015e59b
commit b88191ffcb
17 changed files with 647 additions and 15 deletions
+23 -1
View File
@@ -1,6 +1,6 @@
# ObsiGate — Roadmap
> **Version :** 2.85.1 | **Dernière mise à jour :** 2026-11-06
> **Version :** 2.85.2 | **Dernière mise à jour :** 2026-11-06
> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact
> vers les fonctionnalités livrées.
> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)**
@@ -70,6 +70,28 @@
- [x] Garde `require_admin_hub` sur les routes `/api/auth/admin/*`
- [x] Tests `tests/test_hub_roles.py` (client + intégration du chemin draco)
### 229. Hub HUB-21 — consommation des réglages généraux du Hub par ObsiGate
- **Effort :** ~0,5 jour | **Impact :** 🟡 | **Ouvert :** 2026-11-06 | **Statut :** ✅ livré en 2.85.2 (voir CHANGELOG ; PR en attente de fusion ; redéploiement Docker requis)
- **Fiche :** [features/hub-reglages-obsigate-229.md](./features/hub-reglages-obsigate-229.md)
- **Description :** propagation de HUB-21 (pilote TikTak 0.61.22, liste
fermée D6(a)) : consultation côté serveur de
`GET {hub}/api/reglages/effectifs` (endpoint public, délai 2 s),
cache mémoire 60 s (horodatage posé avant l'appel), repli intégral
sur les défauts actuels si le Hub est muet, en erreur ou de forme
inattendue. Bannière d'annonce injectée sous la barre commune (page
principale et /recherche, texte échappé, fermable, masquage
mémorisé en `sessionStorage`) ; drapeau `recherche_partout` rendu
en `data-recherche-partout` de la page /recherche — faux = partie
fédérée désactivée, recherche locale intacte. `domaine_defaut`
**non consommé** : pas de notion de domaine propre à ObsiGate (le
sélecteur de la barre pilote le panneau Spark).
- **Sous-tâches :**
- [x] Client `backend/hub_reglages.py` (forme D6(a), cache/repli, URL du Hub résolue comme la barre — `draco_config.app_url("hub")`)
- [x] Bannière injectée (`backend/main.py::_fragment_banniere_hub`, `_index_html`, `serve_recherche_page`) + styles `frontend/style.css`
- [x] Jeton `{{RECHERCHE_PARTOUT}}` dans `frontend/recherche.html` + branchement du JS vendangé `frontend/js/recherche.js`
- [x] Tests `tests/test_hub_reglages.py` (client + intégration) et `tests/frontend/recherche.test.mjs` étendu
### 226. Hub HUB-13 — API d'administration des comptes (machine-à-machine)
- **Effort :** ~0,5 jour | **Impact :** 🟡 | **Ouvert :** 2026-11-05 | **Statut :** ✅ livré en 2.84.0 (PR en attente de fusion — voir CHANGELOG ; redéploiement Docker requis)
@@ -0,0 +1,82 @@
# #229 — HUB-21 : consommation des réglages généraux du Hub par ObsiGate
**Carte :** propagation de HUB-21 (fusion #135) — le pilote TikTak
(0.61.22, en production) a posé le patron de client ; ObsiGate le
consomme à son tour, comme les autres hôtes de la suite.
**Version cible :** 2.85.2 (branche depuis le main 2.85.1).
## Contrat consommé (liste fermée D6(a))
- `GET {hub}/api/reglages/effectifs` — endpoint **public** (aucun
cookie relayé), `Accept: application/json`, délai **2 s** ;
- réponse 200, forme figée : `{"recherche_partout": bool,
"banniere": str, "domaine_defaut": "pro"|"famille"|"perso"|null,
"ttl_secondes": 60}`, `Cache-Control: public, max-age=60` ;
- **URL du Hub** : `draco_config.app_url("hub")`, la résolution de la
barre commune (#223) — variable explicite d'abord, dérivation par
l'hôte sœur ensuite, défaut officiel enfin. Jamais d'URL dev figée.
## Client — `backend/hub_reglages.py`
Sur le patron du pilote TikTak (`app/hub_reglages.py`, httpx → urllib
comme `backend/hub_roles.py`) :
- cache mémoire **60 s** ; horodatage posé **avant** l'appel : un
échec conserve l'ancien cache et ne déclenche pas de rafale vers un
Hub en panne ;
- forme validée strictement ; toute erreur (réseau, délai, statut
≠ 200, JSON illisible, forme hors contrat) → **repli intégral** sur
les défauts actuels (`recherche_partout: true`, `banniere: ""`,
`domaine_defaut: null`) — le comportement d'ObsiGate avant HUB-21.
Le module ne lève jamais ; aucun appel au démarrage.
## Consommation
1. **`banniere`** — `backend/main.py::_fragment_banniere_hub()`
construit le fragment (texte **échappé** via `html.escape`, jamais
de HTML injecté), injecté sous la barre commune par `_index_html`
(page principale, juste avant le rappel de la barre) et par
`serve_recherche_page` (page /recherche, après l'en-tête de la
barre). Le script inline (nonce CSP ajouté par `inject_csp_nonce`)
révèle le bandeau sauf si le même texte a été fermé dans la
session du navigateur (`sessionStorage`, clé
`obsigate_banniere_masquee`) : une nouvelle annonce réapparaît.
Bannière vide = aucun fragment rendu, zéro changement visuel.
Styles `.banniere-hub*` dans `frontend/style.css`, sur les
variables du thème (`var(--accent)`).
2. **`recherche_partout`** — `serve_recherche_page` remplace le jeton
`{{RECHERCHE_PARTOUT}}` de `frontend/recherche.html`, rendu en
attribut `data-recherche-partout` du `<main id="recherche">`. Le
JS vendangé `frontend/js/recherche.js` (modification identique
dans les 4 hôtes applicatifs : fonction pure
`federationAutorisee`) n'interroge plus les applications sœurs ni
la console quand la valeur est `false` — leurs sections affichent
« Recherche fédérée désactivée… » — tandis que la recherche locale
ObsiGate (même origine) continue. Attribut absent ou illisible =
fédération active (repli).
3. **`domaine_defaut` — non consommé.** ObsiGate n'a pas de notion
de domaine (Pro/Famille/Perso) propre à ses vues : le sélecteur de
domaine de la barre commune pilote le panneau Spark, pas
ObsiGate. La valeur est lue et validée par le client, sans effet
local. Seul l'hôte console (agent) applique ce réglage.
## i18n
Aucun texte d'interface du SPA n'est ajouté : la bannière est un
contenu d'annonce servi par le Hub (texte libre de l'administrateur),
et le libellé « Fermer l'annonce » est l'attribut d'accessibilité du
fragment injecté côté serveur, sur le même patron que les autres
hôtes. Pas de clé `frontend/locales/*` ajoutée.
## Tests
- `tests/test_hub_reglages.py` (nouveau) : contrat exact de l'appel
(URL, aucun cookie, délai 2 s), formes inattendues → repli, erreur
HTTP / Hub injoignable / JSON illisible → repli, cache 60 s,
ancien cache conservé sans rafale ; intégration TestClient :
bannière présente et échappée sur `/` et `/recherche`, absente si
vide ou Hub muet, `data-recherche-partout` selon la valeur du
drapeau (faux / vrai / Hub muet).
- `tests/frontend/recherche.test.mjs` : assertions ajoutées pour
`federationAutorisee` (seules les valeurs explicitement négatives
désactivent ; absent/illisible = actif).