Recherche partout (#68) : page de résultats unifiée + endpoint JSON /api/search #35

Merged
bruno merged 11 commits from recherche-partout-68 into main 2026-10-28 12:40:47 -04:00
Owner

Rechercher partout — page de résultats unifiée + endpoint JSON (fusion #68, chantier 1)

Branche recherche-partout-68 → main. Ne pas merger sans le go du coordinateur ; aucune version bumpée, aucune issue fermée par cette PR.

Audit des endpoints de recherche réels (vérifié sur main, 2026-10-28)

App Endpoint JSON Auth acceptée Lien profond vérifié
ObsiGate GET /api/search?q=&vault=all&limit= (SearchResponse.results[] : vault, path, title, snippet) Bearer access_token, cookie access_token, ou repli cookie SSO draco_session {og}/#file={vault}:{path}
Flowdeck GET /api/search?q= cookie flowdeck_session seulement (sa PR corrige cela) {fd}/pages/<id>, {fd}/db/<id>
TikTak GET /api/search?q= (et /api/v1/search) session native, cookie SSO draco_session, ou Bearer tck_… {tk}/search?q=<q>&task=<id>
Kanbante aucun endpoint JSON — page SSR GET /search uniquement (recherche Gitea search_issues via le token serveur, groupée par dépôt, lien /board/<full_name>) — {kb}/board/<owner>/<repo> (la carte n'a pas d'URL propre adressable — même limite que la page /search native)
Console agent-commun aucun endpoint de recherche des discussions ; l'agent n'est pas un hôte de la barre — —

Conséquence honnête : pas de section Console (rien à agréger sans inventer une API), et c'est cette PR qui fournit l'endpoint JSON Kanbante manquant.

Architecture choisie : page par hôte + agrégation côté client

Identique aux autres PR du chantier (voir PR TikTak #220 pour l'argumentaire complet) : pas de hub central (SPOF, dépendance runtime entre apps), pas de proxy fan-out serveur (l'hôte ne possède pas de credential sœur sans créer un relais d'identité générique). La page appelle les 4 API en parallèle depuis le navigateur (credentials: "include" : cookies de chaque origine, dont le SSO partagé draco_session, Domain=.dracodev.net), avec debounce 250 ms, timeout 8 s par section, et états chargement/vide/erreur par application (une sœur en panne ne casse pas les autres). Résolution des bases : data-base-* rendus par l'hôte > DRACO_BAR.urls (barre v0.1.6) > dérivation -dev > href des onglets > défauts prod. Aucun fichier ui/barre-apps.* touché. Clavier : / ou Ctrl+K focalise la recherche, ↑/↓ navigue, Entrée ouvre, Échap efface.

Ce qui change (Kanbante)

  • app/templates/recherche.html (nouveau) + static/js/recherche.js (nouveau, logique partagée du chantier) + static/css/recherche.css (nouveau) : page GET /recherche avec 4 sections (ObsiGate, Flowdeck, TikTak, Kanbante), titre/extrait/contexte par résultat, liens profonds.
  • app/main.py : route GET /recherche (même render() que /search, qui injecte déjà draco_urls) ; nouvelle route GET /api/search qui réutilise exactement la source de /search (search_issues(q, state="all") sur les connexions du workspace + _group_issue_results) et ne l'expose qu'en JSON filtré (voir ci-dessous). La page /search native est inchangée.
  • app/recherche.py (nouveau) : conversion groupes_vers_json (seuls numéro, titre, état, corps tronqué à 160 caractères, labels, colonne — aucun autre champ de l'issue Gitea ne sort), CORS bornée (voir ci-dessous) sous forme de middleware ASGI pur, et fonctions pures testables.
  • app/main.py (AuthMiddleware) : exception étroite — pour le seul chemin /api/search, un jeton SSO draco_session valide (draco.claims_for) autorise la requête, comme les API de recherche des sœurs. Les sessions, Basic et jetons API gardent leurs règles ; les autres chemins sont inchangés. Sans session ni draco, /api/search répond 401 comme les autres /api/* (la section affiche alors « Connexion requise » + le lien vers /search).
  • app/main.py (middleware) : RechercheCorsMiddleware ajouté après AuthMiddleware (donc exécuté avant lui) — il répond lui-même aux preflights OPTIONS (qui ne portent jamais de cookies) et ajoute les en-têtes CORS aux réponses de /api/search uniquement pour les origines sœurs résolues (explicite > hôte > dérivation > défaut), jamais *. Les autres routes traversent sans modification.
  • app/templates/_draco_bar.html : 1 ligne — l'action palette de la barre va désormais sur /recherche au lieu d'ouvrir la palette locale. La palette locale (.palette-trigger, Ctrl+K dans les pages) reste inchangée ; le reste du fragment est inchangé (attention au chantier parallèle sur la barre : conflit possible sur ce fichier, à résoudre au merge).
  • Tests : tests/test_recherche.py (pytest, 7 tests) + tests/recherche.test.js (Node, 25 assertions : résolution d'URL, liens profonds, normalisation des 4 formats).
  • CSP : Kanbante n'émet pas de connect-src restrictif (render() ne pose que frame-src) — aucune modification CSP nécessaire ici.

Limites connues (à valider après déploiement)

  • Les sections ObsiGate, Flowdeck et TikTak restent en erreur tant que leurs PR respectives (CORS bornée, repli draco + auth requise, CORS bornée) ne sont pas déployées — comportement prévu, pas une régression.
  • Lien profond Kanbante = tableau (/board/<owner>/<repo>), pas la carte elle-même : aucune URL adressable de carte n'existe dans Kanbante (la carte s'ouvre par JS dans le tableau). Le numéro et la colonne sont affichés pour retrouver la carte.
  • Validation en direct (clic réel sur chaque lien profond, depuis kanbante-dev et kanbante) à faire par Bruno après redéploiement — non vérifiable depuis la sandbox (pas d'accès aux instances).

Tests (chiffres réels, exécutés le 2026-10-28)

  • node tests/recherche.test.js : 25 assertions OK.
  • Vérification Python de app/recherche.py (chargé par chemin, pytest absent de la sandbox) : assertions OK — origines sœurs dev, refus tiers, preflight, conversion JSON (plafond, troncature corps à 160, champ parasite exclu), middleware ASGI (preflight sœur → 204 + Access-Control-Allow-Origin exact, preflight tiers → transmis, GET sœur → en-têtes ajoutés).
  • python3 -m py_compile sur app/main.py, app/recherche.py, tests/test_recherche.py : OK.
  • Non exécutés ici (dépendances applicatives absentes de la sandbox) : pytest tests/test_recherche.py et le serveur — à valider en CI / après déploiement. Zéro régression revendiquée au-delà de ce qui est mesuré ci-dessus.
## Rechercher partout — page de résultats unifiée + endpoint JSON (fusion #68, chantier 1) Branche `recherche-partout-68` → `main`. Ne pas merger sans le go du coordinateur ; aucune version bumpée, aucune issue fermée par cette PR. ### Audit des endpoints de recherche réels (vérifié sur `main`, 2026-10-28) | App | Endpoint JSON | Auth acceptée | Lien profond vérifié | |---|---|---|---| | ObsiGate | `GET /api/search?q=&vault=all&limit=` (`SearchResponse.results[] : vault, path, title, snippet`) | Bearer `access_token`, cookie `access_token`, ou repli cookie SSO `draco_session` | `{og}/#file={vault}:{path}` | | Flowdeck | `GET /api/search?q=` | cookie `flowdeck_session` seulement (sa PR corrige cela) | `{fd}/pages/<id>`, `{fd}/db/<id>` | | TikTak | `GET /api/search?q=` (et `/api/v1/search`) | session native, cookie SSO `draco_session`, ou Bearer `tck_…` | `{tk}/search?q=<q>&task=<id>` | | Kanbante | **aucun endpoint JSON** — page SSR `GET /search` uniquement (recherche Gitea `search_issues` via le token serveur, groupée par dépôt, lien `/board/<full_name>`) | — | `{kb}/board/<owner>/<repo>` (la carte n'a pas d'URL propre adressable — même limite que la page `/search` native) | | Console agent-commun | **aucun endpoint de recherche** des discussions ; l'agent n'est pas un hôte de la barre | — | — | Conséquence honnête : **pas de section Console** (rien à agréger sans inventer une API), et c'est cette PR qui fournit l'endpoint JSON Kanbante manquant. ### Architecture choisie : page par hôte + agrégation côté client Identique aux autres PR du chantier (voir PR TikTak #220 pour l'argumentaire complet) : pas de hub central (SPOF, dépendance runtime entre apps), pas de proxy fan-out serveur (l'hôte ne possède pas de credential sœur sans créer un relais d'identité générique). La page appelle les 4 API en parallèle depuis le navigateur (`credentials: "include"` : cookies de chaque origine, dont le SSO partagé `draco_session`, `Domain=.dracodev.net`), avec debounce 250 ms, timeout 8 s par section, et états chargement/vide/erreur **par application** (une sœur en panne ne casse pas les autres). Résolution des bases : `data-base-*` rendus par l'hôte > `DRACO_BAR.urls` (barre v0.1.6) > dérivation `-dev` > href des onglets > défauts prod. Aucun fichier `ui/barre-apps.*` touché. Clavier : `/` ou Ctrl+K focalise la recherche, ↑/↓ navigue, Entrée ouvre, Échap efface. ### Ce qui change (Kanbante) - `app/templates/recherche.html` (nouveau) + `static/js/recherche.js` (nouveau, logique partagée du chantier) + `static/css/recherche.css` (nouveau) : page `GET /recherche` avec 4 sections (ObsiGate, Flowdeck, TikTak, Kanbante), titre/extrait/contexte par résultat, liens profonds. - `app/main.py` : route `GET /recherche` (même `render()` que `/search`, qui injecte déjà `draco_urls`) ; **nouvelle route `GET /api/search`** qui réutilise exactement la source de `/search` (`search_issues(q, state="all")` sur les connexions du workspace + `_group_issue_results`) et ne l'expose qu'en JSON filtré (voir ci-dessous). La page `/search` native est **inchangée**. - `app/recherche.py` (nouveau) : conversion `groupes_vers_json` (seuls numéro, titre, état, corps tronqué à 160 caractères, labels, colonne — aucun autre champ de l'issue Gitea ne sort), CORS bornée (voir ci-dessous) sous forme de middleware ASGI pur, et fonctions pures testables. - `app/main.py` (`AuthMiddleware`) : **exception étroite** — pour le seul chemin `/api/search`, un jeton SSO `draco_session` valide (`draco.claims_for`) autorise la requête, comme les API de recherche des sœurs. Les sessions, Basic et jetons API gardent leurs règles ; les autres chemins sont inchangés. Sans session ni draco, `/api/search` répond 401 comme les autres `/api/*` (la section affiche alors « Connexion requise » + le lien vers `/search`). - `app/main.py` (middleware) : `RechercheCorsMiddleware` ajouté après `AuthMiddleware` (donc exécuté avant lui) — il répond lui-même aux preflights OPTIONS (qui ne portent jamais de cookies) et ajoute les en-têtes CORS aux réponses de `/api/search` **uniquement pour les origines sœurs résolues** (explicite > hôte > dérivation > défaut), jamais `*`. Les autres routes traversent sans modification. - `app/templates/_draco_bar.html` : **1 ligne** — l'action `palette` de la barre va désormais sur `/recherche` au lieu d'ouvrir la palette locale. La palette locale (`.palette-trigger`, Ctrl+K dans les pages) reste inchangée ; le reste du fragment est inchangé (attention au chantier parallèle sur la barre : conflit possible sur ce fichier, à résoudre au merge). - Tests : `tests/test_recherche.py` (pytest, 7 tests) + `tests/recherche.test.js` (Node, 25 assertions : résolution d'URL, liens profonds, normalisation des 4 formats). - CSP : Kanbante n'émet pas de `connect-src` restrictif (`render()` ne pose que `frame-src`) — aucune modification CSP nécessaire ici. ### Limites connues (à valider après déploiement) - Les sections ObsiGate, Flowdeck et TikTak restent en erreur tant que leurs PR respectives (CORS bornée, repli draco + auth requise, CORS bornée) ne sont pas déployées — comportement prévu, pas une régression. - Lien profond Kanbante = tableau (`/board/<owner>/<repo>`), pas la carte elle-même : aucune URL adressable de carte n'existe dans Kanbante (la carte s'ouvre par JS dans le tableau). Le numéro et la colonne sont affichés pour retrouver la carte. - Validation en direct (clic réel sur chaque lien profond, depuis `kanbante-dev` et `kanbante`) à faire par Bruno après redéploiement — non vérifiable depuis la sandbox (pas d'accès aux instances). ### Tests (chiffres réels, exécutés le 2026-10-28) - `node tests/recherche.test.js` : **25 assertions OK**. - Vérification Python de `app/recherche.py` (chargé par chemin, pytest absent de la sandbox) : **assertions OK** — origines sœurs dev, refus tiers, preflight, conversion JSON (plafond, troncature corps à 160, champ parasite exclu), middleware ASGI (preflight sœur → 204 + `Access-Control-Allow-Origin` exact, preflight tiers → transmis, GET sœur → en-têtes ajoutés). - `python3 -m py_compile` sur `app/main.py`, `app/recherche.py`, `tests/test_recherche.py` : **OK**. - Non exécutés ici (dépendances applicatives absentes de la sandbox) : `pytest tests/test_recherche.py` et le serveur — à valider en CI / après déploiement. Zéro régression revendiquée au-delà de ce qui est mesuré ci-dessus.
bruno added 8 commits 2026-10-28 12:08:46 -04:00
bruno added 1 commit 2026-10-28 12:13:07 -04:00
Recherche partout (#68) : CORS résolue avec l'hôte de la requête (ordre middlewares)
CI / test (push) Failing after 1s
CI / test (pull_request) Failing after 0s
39750851b0
bruno added 1 commit 2026-10-28 12:13:12 -04:00
Recherche partout (#68) : test hôte de la requête
CI / test (push) Failing after 1s
CI / test (pull_request) Failing after 1s
725f78cb68
bruno added 1 commit 2026-10-28 12:18:21 -04:00
Recherche partout (#68) : JS compatible ESM (test ObsiGate)
CI / test (push) Failing after 1s
CI / test (pull_request) Failing after 3s
d41a79a9ed
Author
Owner

Complément poussé sur cette branche après création de la PR : sister_origins() accepte désormais l'hôte de la requête (resoudre_urls_draco(hote_requete=...)), et le middleware CORS le lui transmet depuis l'en-tête Host — sinon la résolution retombait sur la configuration seule et ratait l'environnement -dev (le middleware s'exécute avant tout poseur de contexte). Le JS partagé a aussi été mis à jour (compatibilité ESM, globalThis) pour le test ObsiGate. PR toujours mergeable, vérifiée après ces commits.

Complément poussé sur cette branche après création de la PR : sister_origins() accepte désormais l'hôte de la requête (resoudre_urls_draco(hote_requete=...)), et le middleware CORS le lui transmet depuis l'en-tête Host — sinon la résolution retombait sur la configuration seule et ratait l'environnement -dev (le middleware s'exécute avant tout poseur de contexte). Le JS partagé a aussi été mis à jour (compatibilité ESM, globalThis) pour le test ObsiGate. PR toujours mergeable, vérifiée après ces commits.
bruno merged commit cf98880fc8 into main 2026-10-28 12:40:47 -04:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Projets/kanbante#35