4.2 KiB
4.2 KiB
#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
banniere—backend/main.py::_fragment_banniere_hub()construit le fragment (texte échappé viahtml.escape, jamais de HTML injecté), injecté sous la barre commune par_index_html(page principale, juste avant le rappel de la barre) et parserve_recherche_page(page /recherche, après l'en-tête de la barre). Le script inline (nonce CSP ajouté parinject_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*dansfrontend/style.css, sur les variables du thème (var(--accent)).recherche_partout—serve_recherche_pageremplace le jeton{{RECHERCHE_PARTOUT}}defrontend/recherche.html, rendu en attributdata-recherche-partoutdu<main id="recherche">. Le JS vendangéfrontend/js/recherche.js(modification identique dans les 4 hôtes applicatifs : fonction purefederationAutorisee) n'interroge plus les applications sœurs ni la console quand la valeur estfalse— 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).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-partoutselon la valeur du drapeau (faux / vrai / Hub muet).tests/frontend/recherche.test.mjs: assertions ajoutées pourfederationAutorisee(seules les valeurs explicitement négatives désactivent ; absent/illisible = actif).