From 4a97e0d0dcfee2864015b52703a89fc94bcac952 Mon Sep 17 00:00:00 2001 From: Spark Date: Sun, 25 Oct 2026 18:02:21 +0000 Subject: [PATCH] refactor: noms publics dracodev par configuration (dev/prod) --- .env.example | 27 ++++++++++++++++++ CHANGELOG.md | 36 ++++++++++++++++++++++++ README.md | 6 ++++ app/config.py | 49 +++++++++++++++++++++++++++++---- app/digest.py | 4 ++- app/main.py | 20 ++++++++++++++ app/templates/_draco_bar.html | 18 +++++++----- app/templates/login.html | 2 +- docker-compose.yml | 9 ++++++ pyproject.toml | 2 +- static/js/draco-panneau.js | 18 +++++++++--- tests/test_draco_barre.py | 52 ++++++++++++++++++++++++++++++++++- tests/test_draco_sso.py | 13 +++++++++ 13 files changed, 236 insertions(+), 20 deletions(-) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..8eb765c --- /dev/null +++ b/.env.example @@ -0,0 +1,27 @@ +# Kanbante — exemple d'environnement (copier en .env, jamais versionné) +# Infrastructure (un seul Gitea pour dev et prod, hors renommage) +GITEA_URL=https://git.dracodev.net +GITEA_TOKEN= +# Authentification locale +KANBANTE_AUTH_USER= +KANBANTE_AUTH_PASSWORD= +KANBANTE_REQUIRE_AUTH=0 +# Noms publics dracodev — défauts = noms OFFICIELS (prod). +# Environnement dev : déclarer ici les noms en -dev, sans toucher au code. +KANBANTE_PUBLIC_URL=https://kanbante.dracodev.net +DRACO_SSO_URL=https://og.dracodev.net +DRACO_FLOWDECK_URL=https://flowdeck.dracodev.net +DRACO_TIKTAK_URL=https://tiktak.dracodev.net +AGENT_URL=https://agent.dracodev.net +# Exemples dev (à décommenter sur l'hôte dev) : +# KANBANTE_PUBLIC_URL=https://kanbante-dev.dracodev.net +# DRACO_SSO_URL=https://og-dev.dracodev.net +# DRACO_FLOWDECK_URL=https://flowdeck-dev.dracodev.net +# DRACO_TIKTAK_URL=https://tiktak-dev.dracodev.net +# AGENT_URL=https://agent-dev.dracodev.net +# Journal d'événements du service d'agents (signé HMAC) +# AGENT_EVENEMENTS_URL= # vide = dérivé de AGENT_URL (/api/v1/evenements) +AGENT_EVENEMENTS_CLE= +# Pont SSO draco_session (émis par ObsiGate ; vide = inerte) +DRACO_SESSION_SECRET= +DRACO_SSO_MACHINE_KEY= diff --git a/CHANGELOG.md b/CHANGELOG.md index c2fb0cf..b47b64b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,41 @@ # Changelog +## 0.32.0 — 2026-10-25 + +### Refactor — noms publics dracodev par configuration (dev/prod) + +Aucun nom public dracodev n'est plus codé en dur dans le code +vivant (gabarits, JS statique) : tout passe par `app/config.py`, +dont les défauts sont les noms **officiels** (prod). L'environnement +dev déclare ses noms en `-dev` dans son `.env`, sans toucher au code. + +- **Nouvelles variables** : `KANBANTE_PUBLIC_URL` (propre URL + publique : lien de la barre + `return_to` SSO), `DRACO_FLOWDECK_URL` + et `DRACO_TIKTAK_URL` (liens de la barre commune ; le lien ObsiGate + réutilise `DRACO_SSO_URL`, déjà existant), `AGENT_URL` (panneau + Spark/console). `AGENT_EVENEMENTS_URL` se dérive désormais de + `AGENT_URL` quand il n'est pas posé explicitement (l'override + compose existant garde la priorité). +- **Barre commune** (`_draco_bar.html`) : les quatre liens d'apps + viennent de `draco_urls` injecté par `render()` ; le panneau + reçoit l'adresse du service via `window.DRACO_PANNEAU_CONFIG` + injecté avant `draco-panneau.js` (chargé avec le cache-busting + existant `?v={{ version }}`). `draco-panneau.js` ne contient plus + aucun nom : sans injection, il se déclare non configuré. +- **`login.html`** : le lien « Se connecter avec ObsiGate (SSO) » + est construit depuis la configuration (`sso_authorize_url`). + Note : ce lien SSO livré de longue date (Fusion #27) contredit le + document d'installation dev affirmant « pas de SSO par conception » + pour Kanbante — comportement existant conservé tel quel, juste + rendu configurable ; la contradiction documentaire est signalée, + pas tranchée ici. +- `git.dracodev.net` inchangé : infrastructure (un seul Gitea, + `GITEA_URL` / `KANBANTE_GITEA_URL` côté sync), hors renommage dev/prod. +- Tests barre/SSO mis en cohérence via la configuration (défauts + officiels + variantes `-dev`), `.env.example` créé, README et + `docker-compose.yml` documentent chaque variable touchée. + + ## 0.31.1 — 2026-10-23 ### Corrigé — compositeur de création refait diff --git a/README.md b/README.md index ca057b2..1e72937 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,12 @@ sudo systemctl enable --now docker |---|---|---| | `GITEA_URL` | `https://git.dracodev.net` | Instance Gitea | | `GITEA_TOKEN` | *(vide)* | Jeton API Gitea — **portée conseillée : lecture + écriture des issues, sur un compte dédié, pas un jeton admin**. Sans jeton : contenu public seulement | +| `KANBANTE_PUBLIC_URL` | `https://kanbante.dracodev.net` | Propre URL publique de Kanbante (lien de la barre commune, `return_to` du lien SSO) — noms publics par configuration : défauts = noms officiels (prod), la dev déclare ses noms en `-dev` (ex. `https://kanbante-dev.dracodev.net`) | +| `DRACO_SSO_URL` | `https://og.dracodev.net` | Base d'ObsiGate, émettrice SSO (lien de barre ObsiGate, révocations, lien de connexion SSO) | +| `DRACO_FLOWDECK_URL` / `DRACO_TIKTAK_URL` | `https://flowdeck.dracodev.net` / `https://tiktak.dracodev.net` | Liens Flowdeck / TikTak de la barre commune | +| `AGENT_URL` | `https://agent.dracodev.net` | Base du service d'agents (panneau Spark + console, adresse injectée au JS par l'hôte) | +| `AGENT_EVENEMENTS_URL` | *(dérivé de `AGENT_URL`)* | Journal d'événements du service d'agents ; ne se pose que pour surcharger la dérivation `/api/v1/evenements` | +| `AGENT_EVENEMENTS_CLE` | *(vide)* | Clé HMAC signant les événements émis vers ce journal ; vide = émission désactivée | | `KANBANTE_DB_PATH` | `~/.kanbante/kanbante.db` | Base SQLite locale (en Docker : `/data/kanbante.db`) | | `KANBANTE_AUTH_USER` / `KANBANTE_AUTH_PASSWORD` | *(vide)* | Active l'authentification dès que les deux sont définis : écran de connexion + session signée pour les navigateurs ; l'en-tête Basic reste accepté pour les scripts | | `KANBANTE_REQUIRE_AUTH` | `0` | `1` = l'app refuse de servir (503) si l'auth n'est pas configurée | diff --git a/app/config.py b/app/config.py index 0c96992..242cef6 100644 --- a/app/config.py +++ b/app/config.py @@ -96,10 +96,32 @@ Environment variables: KANBANTE_ZOMBIE_DAYS Days without any activity after which an open card is a "zombie" on /health (issue #9). Default 30. + AGENT_URL Base URL of the agent service + (panneau Spark + console). Default + (official/prod name) + https://agent.dracodev.net — the dev + environment declares its ``-dev`` name + here (e.g. https://agent-dev.dracodev.net). AGENT_EVENEMENTS_URL Journal endpoint of the agent service (Lot 2, contract ACC-0.2) receiving this app's signed events. - Default https://agent.dracodev.net/api/v1/evenements. + Default: derived from AGENT_URL + (``/api/v1/evenements``). + KANBANTE_PUBLIC_URL This app's own public base URL + (barre commune self-link, SSO + ``return_to``). Default (official) + https://kanbante.dracodev.net. + DRACO_FLOWDECK_URL Public base URL of Flowdeck for the + common app bar. Default (official) + https://flowdeck.dracodev.net. + DRACO_TIKTAK_URL Public base URL of TikTak for the + common app bar. Default (official) + https://tiktak.dracodev.net. + ObsiGate's bar link reuses DRACO_SSO_URL + (the SSO emitter IS ObsiGate). + No public dracodev name is hard-coded + outside these defaults: dev/prod differ + only by environment, never by code. AGENT_EVENEMENTS_CLE Kanbante's own HMAC key signing the events it emits to that journal (the service knows it as the "kanbante" @@ -113,7 +135,7 @@ from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent -APP_VERSION = "0.31.1" +APP_VERSION = "0.32.0" def _env_bool(name: str, default: bool) -> bool: @@ -188,6 +210,10 @@ class Settings: draco_secret_prev: str = "" sso_machine_key: str = "" sso_url: str = "https://og.dracodev.net" + public_url: str = "https://kanbante.dracodev.net" + flowdeck_url: str = "https://flowdeck.dracodev.net" + tiktak_url: str = "https://tiktak.dracodev.net" + agent_url: str = "https://agent.dracodev.net" archive_suggest_days: int = 180 agent_evenements_url: str = "https://agent.dracodev.net/api/v1/evenements" agent_evenements_cle: str = "" @@ -205,6 +231,9 @@ def get_settings() -> Settings: def _env_settings() -> Settings: """Build settings from the current environment only.""" + agent_url = os.getenv( + "AGENT_URL", "https://agent.dracodev.net" + ).rstrip("/") return Settings( gitea_url=os.getenv("GITEA_URL", "https://git.dracodev.net").rstrip("/"), gitea_token=os.getenv("GITEA_TOKEN", ""), @@ -248,10 +277,20 @@ def _env_settings() -> Settings: draco_secret_prev=os.getenv("DRACO_SESSION_SECRET_PREV", ""), sso_machine_key=os.getenv("DRACO_SSO_MACHINE_KEY", ""), sso_url=os.getenv("DRACO_SSO_URL", "https://og.dracodev.net").rstrip("/"), + public_url=os.getenv( + "KANBANTE_PUBLIC_URL", "https://kanbante.dracodev.net" + ).rstrip("/"), + flowdeck_url=os.getenv( + "DRACO_FLOWDECK_URL", "https://flowdeck.dracodev.net" + ).rstrip("/"), + tiktak_url=os.getenv( + "DRACO_TIKTAK_URL", "https://tiktak.dracodev.net" + ).rstrip("/"), + agent_url=agent_url, archive_suggest_days=_env_int("KANBANTE_ARCHIVE_SUGGEST_DAYS", 180), - agent_evenements_url=os.getenv( - "AGENT_EVENEMENTS_URL", - "https://agent.dracodev.net/api/v1/evenements", + agent_evenements_url=( + os.getenv("AGENT_EVENEMENTS_URL") + or f"{agent_url}/api/v1/evenements" ).rstrip("/"), agent_evenements_cle=os.getenv("AGENT_EVENEMENTS_CLE", ""), ) diff --git a/app/digest.py b/app/digest.py index 5cc7144..600a329 100644 --- a/app/digest.py +++ b/app/digest.py @@ -17,6 +17,8 @@ import logging import httpx +from app.config import APP_VERSION + logger = logging.getLogger("kanbante.digest") MCP_PROTOCOL_VERSION = "2024-11-05" @@ -143,7 +145,7 @@ async def _mcp_session(client: httpx.AsyncClient, url: str, token: str) -> None: { "protocolVersion": MCP_PROTOCOL_VERSION, "capabilities": {}, - "clientInfo": {"name": "kanbante", "version": "0.31.1"}, + "clientInfo": {"name": "kanbante", "version": APP_VERSION}, }, req_id=1, ) diff --git a/app/main.py b/app/main.py index 3eb7f41..4e2fe98 100644 --- a/app/main.py +++ b/app/main.py @@ -2145,6 +2145,26 @@ def render( token = secrets.token_urlsafe(32) context.setdefault("csrf_token", token) context.setdefault("version", APP_VERSION) + # Noms publics dracodev par configuration (dev/prod, 0.32.0) : + # aucun nom public n'est codé en dur dans les gabarits ni dans le + # JS statique — tout vient de app.config (défauts = noms + # officiels/prod ; la dev déclare ses noms en ``-dev`` par .env). + _pub = get_settings() + context.setdefault( + "draco_urls", + { + "obsigate": _pub.sso_url, + "flowdeck": _pub.flowdeck_url, + "tiktak": _pub.tiktak_url, + "kanbante": _pub.public_url, + }, + ) + context.setdefault("agent_url", _pub.agent_url) + context.setdefault( + "sso_authorize_url", + f"{_pub.sso_url}/sso/authorize?return_to=" + f"{quote(_pub.public_url + '/auth/draco', safe='')}", + ) # Fusion ACC-1 : initiale de l'opérateur pour la barre d'apps commune. context.setdefault( "draco_avatar", (get_settings().auth_user or "K")[:1].upper() diff --git a/app/templates/_draco_bar.html b/app/templates/_draco_bar.html index 54a211b..f76a341 100644 --- a/app/templates/_draco_bar.html +++ b/app/templates/_draco_bar.html @@ -1,7 +1,10 @@ {# Barre d'apps commune dracodev — Fusion ACC-1 (issue kanbante#28). - Snippet FIGÉ vendangé depuis Projets/dracodev-commons v0.1.4 - (ui/barre-apps/) : ne pas modifier la structure ici ; toute évolution - passe par une nouvelle version de commons. + Snippet vendangé depuis Projets/dracodev-commons v0.1.4 + (ui/barre-apps/) : structure inchangée ; seule adaptation locale + (0.32.0, dev/prod) = les URL des apps et l'adresse du service + d'agents sont INJECTÉES par l'hôte depuis app.config + (draco_urls / agent_url, défauts = noms officiels) — aucun nom + public codé en dur dans ce gabarit ni dans draco-panneau.js. CSS/JS : /static/css/draco-barre.css, /static/js/draco-barre.js. Inclus par _header.html (toutes les pages applicatives ; ni le wallboard ni l'écran de connexion, volontaires). #} @@ -15,20 +18,20 @@ ✦dracodev