fix(draco): URLs sœurs dérivées de l'environnement de l'agent (7.82.3)
Résolution uniforme des quatre sœurs (ObsiGate, Flowdeck, TikTak, Kanbante) dans app/services/draco_urls.py : explicite prioritaire (contrat inchangé), sinon dérivation depuis l'hôte de l'agent connu via AGENT_EVENEMENTS_URL (agent<SUFFIXE>.dracodev.net → <slug><SUFFIXE>.dracodev.net), sinon défaut officiel. Hors motif (autre domaine, LAN, IP) : aucune dérivation. Gitea inchangé. Tests +4 dans test_draco_urls_devprod.py.
This commit is contained in:
@@ -1,5 +1,34 @@
|
||||
# Changelog - FlowDeck
|
||||
|
||||
## v7.82.3 (2026-10-25) — URLs sœurs dérivées de l'environnement de l'agent
|
||||
|
||||
- **Bug corrigé (dev)** : les URL sœurs servies par `draco_urls()`
|
||||
(menu inter-apps, SSO, preset TikTak, références croisées)
|
||||
restaient aux noms **prod** quand le `.env` ne déclarait pas les
|
||||
quatre URL une à une — seul l'agent était en `-dev`, via
|
||||
`AGENT_EVENEMENTS_URL`. Le menu inter-apps de la dev envoyait
|
||||
donc vers la prod.
|
||||
- **Dérivation d'environnement** (`app/services/draco_urls.py`,
|
||||
résolution uniforme des quatre sœurs — ObsiGate, Flowdeck,
|
||||
TikTak, Kanbante) : 1) URL explicite prioritaire, contrat
|
||||
inchangé (`DRACO_SSO_URL`, `TIKTAK_URL`, `KANBANTE_URL`,
|
||||
`FLOWDECK_URL`/`OAUTH_REDIRECT_URI`…) ; 2) sinon, si l'hôte de
|
||||
l'agent tel que l'app le connaît déjà (`AGENT_EVENEMENTS_URL`,
|
||||
via `agent_evenements_url()`) est de la forme
|
||||
`agent<SUFFIXE>.dracodev.net` (SUFFIXE éventuellement vide,
|
||||
typiquement `-dev`), l'URL sœur est dérivée :
|
||||
`https://<slug><SUFFIXE>.dracodev.net` (slugs `og`, `flowdeck`,
|
||||
`tiktak`, `kanbante`) ; hôte hors motif (autre domaine, LAN,
|
||||
IP) = **aucune dérivation**, jamais un lien deviné ; 3) sinon
|
||||
défaut officiel, comme avant. L'URL **Gitea ne change pas** ;
|
||||
les gabarits et le JS de la barre, qui consomment `draco_urls()`,
|
||||
sont inchangés.
|
||||
- Tests : `tests/test_draco_urls_devprod.py` +4 — seul
|
||||
`AGENT_EVENEMENTS_URL=…agent-dev…` posé → sœurs en `-dev` ;
|
||||
explicite gagne sur la dérivation ; hôtes d'agent hors motif
|
||||
→ défauts officiels ; agent officiel (suffixe vide) → sœurs
|
||||
officielles. Non-régression « aucun env » déjà couverte.
|
||||
|
||||
## v7.82.2 (2026-10-25) — CI test entièrement verte : pages-supports de templates exclues des listes
|
||||
|
||||
Cause racine commune à 7 des 8 échecs restants : la refonte templates
|
||||
|
||||
+1
-1
@@ -216,7 +216,7 @@ def _lire_version() -> str:
|
||||
|
||||
|
||||
# Littéral de version synchronisé avec le fichier VERSION :
|
||||
# version="7.82.2"
|
||||
# version="7.82.3"
|
||||
# Patron historique des bumps (voir scripts/bump_version.py, qui cherche
|
||||
# ce littéral) et contrôle de tests/test_pwa_offline.py. La version
|
||||
# runtime reste lue dynamiquement depuis VERSION par _lire_version()
|
||||
|
||||
@@ -18,11 +18,26 @@ Priorité de résolution, à chaque appel (les tests et les modules qui
|
||||
lisaient déjà ``os.environ`` au moment de l'appel gardent ce
|
||||
comportement) : variable d'environnement > ``app.config.settings`` >
|
||||
défaut officiel. Aucun défaut ne contient ``-dev``.
|
||||
|
||||
**Dérivation d'environnement (v7.82.3)** — pour les quatre URL
|
||||
sœurs (ObsiGate, Flowdeck, TikTak, Kanbante), entre la configuration
|
||||
explicite et le défaut officiel : si l'origine de l'agent telle que
|
||||
l'app la connaît déjà (``AGENT_EVENEMENTS_URL``, via
|
||||
``agent_evenements_url()``) a un hôte de la forme
|
||||
``agent<SUFFIXE>.dracodev.net`` (SUFFIXE éventuellement vide,
|
||||
typiquement ``-dev``), l'URL sœur est dérivée :
|
||||
``https://<slug><SUFFIXE>.dracodev.net`` (slugs : ``og``,
|
||||
``flowdeck``, ``tiktak``, ``kanbante``). Ainsi une installation dev
|
||||
qui ne déclare que l'agent pointe ses liens inter-apps en ``-dev``
|
||||
sans quatre variables de plus. Si l'hôte de l'agent ne correspond
|
||||
pas au motif (autre domaine, LAN, IP), **aucune dérivation** : on
|
||||
ne fabrique jamais un lien deviné. L'URL Gitea ne change pas.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from app.config import settings
|
||||
@@ -51,9 +66,70 @@ def _resoudre(env_name: str, settings_attr: str, app: str) -> str:
|
||||
return OFFICIELLES[app]
|
||||
|
||||
|
||||
# Slugs d'hôte des applications sœurs (dérivation d'environnement).
|
||||
_SLUGS_SOEURS = {
|
||||
"obsigate": "og",
|
||||
"flowdeck": "flowdeck",
|
||||
"tiktak": "tiktak",
|
||||
"kanbante": "kanbante",
|
||||
}
|
||||
|
||||
# Hôte de l'agent : agent<SUFFIXE>.dracodev.net (SUFFIXE = ``-dev``…).
|
||||
_MOTIF_HOTE_AGENT = re.compile(r"^agent(-[a-z0-9-]+)?\.dracodev\.net$")
|
||||
|
||||
|
||||
def _suffixe_environnement() -> str | None:
|
||||
"""Suffixe d'environnement porté par l'hôte de l'agent, ou None.
|
||||
|
||||
Source : l'origine de l'agent telle que l'app la connaît déjà —
|
||||
``agent_evenements_url()`` (``AGENT_EVENEMENTS_URL`` explicite,
|
||||
sinon dérivée de la base de l'agent). Retourne ``""`` pour
|
||||
l'hôte officiel, ``"-dev"`` pour ``agent-dev.dracodev.net``, et
|
||||
None si l'hôte ne correspond pas au motif (aucune dérivation).
|
||||
"""
|
||||
hote_agent = (urlparse(agent_evenements_url()).hostname or "").lower()
|
||||
m = _MOTIF_HOTE_AGENT.match(hote_agent)
|
||||
if not m:
|
||||
return None
|
||||
return m.group(1) or ""
|
||||
|
||||
|
||||
def _url_derivee(app: str) -> str:
|
||||
"""URL sœur dérivée de l'environnement de l'agent ("" si aucune)."""
|
||||
slug = _SLUGS_SOEURS.get(app)
|
||||
if not slug:
|
||||
return ""
|
||||
suffixe = _suffixe_environnement()
|
||||
if suffixe is None:
|
||||
return ""
|
||||
return f"https://{slug}{suffixe}.dracodev.net"
|
||||
|
||||
|
||||
def _resoudre_soeur(env_name: str, settings_attr: str, app: str) -> str:
|
||||
"""Résolution d'une URL sœur : explicite > dérivation > officiel.
|
||||
|
||||
La valeur ``settings`` égale au nom officiel est le *défaut* de
|
||||
``app/config.py``, pas un choix explicite : elle ne doit pas
|
||||
bloquer la dérivation d'environnement. Une valeur settings
|
||||
différente (dev, LAN, domaine propre) reste prioritaire.
|
||||
"""
|
||||
env = _nettoyer(os.environ.get(env_name, ""))
|
||||
if env:
|
||||
return env
|
||||
cfg = _nettoyer(getattr(settings, settings_attr, "") or "")
|
||||
if cfg and cfg != OFFICIELLES[app]:
|
||||
return cfg
|
||||
derivee = _url_derivee(app)
|
||||
if derivee:
|
||||
return derivee
|
||||
if cfg:
|
||||
return cfg
|
||||
return OFFICIELLES[app]
|
||||
|
||||
|
||||
def obsigate_url() -> str:
|
||||
"""ObsiGate — aussi l'émettrice SSO (DRACO_SSO_URL, déjà en service)."""
|
||||
return _resoudre("DRACO_SSO_URL", "draco_sso_url", "obsigate")
|
||||
return _resoudre_soeur("DRACO_SSO_URL", "draco_sso_url", "obsigate")
|
||||
|
||||
|
||||
def agent_url() -> str:
|
||||
@@ -62,11 +138,11 @@ def agent_url() -> str:
|
||||
|
||||
|
||||
def tiktak_url() -> str:
|
||||
return _resoudre("TIKTAK_URL", "tiktak_url", "tiktak")
|
||||
return _resoudre_soeur("TIKTAK_URL", "tiktak_url", "tiktak")
|
||||
|
||||
|
||||
def kanbante_url() -> str:
|
||||
return _resoudre("KANBANTE_URL", "kanbante_url", "kanbante")
|
||||
return _resoudre_soeur("KANBANTE_URL", "kanbante_url", "kanbante")
|
||||
|
||||
|
||||
def gitea_url() -> str:
|
||||
@@ -98,6 +174,9 @@ def flowdeck_url() -> str:
|
||||
base = _nettoyer(getattr(settings, "app_base_url", "") or "")
|
||||
if base and "localhost" not in base and "127.0.0.1" not in base:
|
||||
return base
|
||||
derivee = _url_derivee("flowdeck")
|
||||
if derivee:
|
||||
return derivee
|
||||
return OFFICIELLES["flowdeck"]
|
||||
|
||||
|
||||
|
||||
@@ -110,6 +110,72 @@ def test_preset_tiktak_depuis_config(monkeypatch):
|
||||
"https://tiktak-dev.dracodev.net/mcp"
|
||||
|
||||
|
||||
def test_derivation_dev_depuis_agent_evenements_seul(monkeypatch):
|
||||
# Cas réel de la dev : seul AGENT_EVENEMENTS_URL est posé —
|
||||
# les quatre URLs sœurs servies suivent en ``-dev``, sans
|
||||
# déclarer quatre variables de plus. Gitea ne change pas.
|
||||
monkeypatch.setenv(
|
||||
"AGENT_EVENEMENTS_URL",
|
||||
"https://agent-dev.dracodev.net/api/v1/evenements")
|
||||
urls = draco_urls.toutes()
|
||||
assert urls["obsigate"] == "https://og-dev.dracodev.net"
|
||||
assert urls["flowdeck"] == "https://flowdeck-dev.dracodev.net"
|
||||
assert urls["tiktak"] == "https://tiktak-dev.dracodev.net"
|
||||
assert urls["kanbante"] == "https://kanbante-dev.dracodev.net"
|
||||
assert urls["gitea"] == "https://git.dracodev.net"
|
||||
# Les accesseurs individuels partagent la même résolution.
|
||||
assert draco_urls.obsigate_url() == "https://og-dev.dracodev.net"
|
||||
assert draco_urls.flowdeck_url() == "https://flowdeck-dev.dracodev.net"
|
||||
assert draco_urls.tiktak_url() == "https://tiktak-dev.dracodev.net"
|
||||
assert draco_urls.kanbante_url() == "https://kanbante-dev.dracodev.net"
|
||||
|
||||
|
||||
def test_derivation_explicite_gagne_sur_derivation(monkeypatch):
|
||||
monkeypatch.setenv(
|
||||
"AGENT_EVENEMENTS_URL",
|
||||
"https://agent-dev.dracodev.net/api/v1/evenements")
|
||||
# Explicites (contrat inchangé) : elles priment sur la dérivation,
|
||||
# y compris quand elles pointent le nom officiel.
|
||||
monkeypatch.setenv("DRACO_SSO_URL", "https://og.dracodev.net")
|
||||
monkeypatch.setenv("TIKTAK_URL", "https://tiktak.example.net")
|
||||
urls = draco_urls.toutes()
|
||||
assert urls["obsigate"] == "https://og.dracodev.net"
|
||||
assert urls["tiktak"] == "https://tiktak.example.net"
|
||||
# Non déclarées : dérivées.
|
||||
assert urls["flowdeck"] == "https://flowdeck-dev.dracodev.net"
|
||||
assert urls["kanbante"] == "https://kanbante-dev.dracodev.net"
|
||||
|
||||
|
||||
def test_derivation_hote_agent_hors_motif_defauts_officiels(monkeypatch):
|
||||
# Autre domaine, LAN, IP : aucune dérivation, jamais un lien deviné.
|
||||
for url_agent in (
|
||||
"https://agent.example.com/api/v1/evenements",
|
||||
"https://agent-dev.example.net/api/v1/evenements",
|
||||
"https://monagent-dev.dracodev.net/api/v1/evenements",
|
||||
"https://agent-dev.dracodev.net.evil.com/api/v1/evenements",
|
||||
"http://192.168.30.101:8790/api/v1/evenements",
|
||||
"http://127.0.0.1:8790/api/v1/evenements",
|
||||
"http://agent-dev.local:8790/api/v1/evenements",
|
||||
):
|
||||
monkeypatch.setenv("AGENT_EVENEMENTS_URL", url_agent)
|
||||
urls = draco_urls.toutes()
|
||||
assert urls["obsigate"] == "https://og.dracodev.net", url_agent
|
||||
assert urls["flowdeck"] == "https://flowdeck.dracodev.net", url_agent
|
||||
assert urls["tiktak"] == "https://tiktak.dracodev.net", url_agent
|
||||
assert urls["kanbante"] == "https://kanbante.dracodev.net", url_agent
|
||||
|
||||
|
||||
def test_derivation_agent_officiel_suffixe_vide(monkeypatch):
|
||||
# Hôte agent officiel : suffixe vide, les sœurs restent officielles.
|
||||
monkeypatch.setenv(
|
||||
"AGENT_EVENEMENTS_URL", "https://agent.dracodev.net/api/v1/evenements")
|
||||
urls = draco_urls.toutes()
|
||||
assert urls["obsigate"] == "https://og.dracodev.net"
|
||||
assert urls["flowdeck"] == "https://flowdeck.dracodev.net"
|
||||
assert urls["tiktak"] == "https://tiktak.dracodev.net"
|
||||
assert urls["kanbante"] == "https://kanbante.dracodev.net"
|
||||
|
||||
|
||||
def test_bouton_sso_login_depuis_config(monkeypatch):
|
||||
from app.routers.auth import _sso_login_url
|
||||
|
||||
|
||||
Reference in New Issue
Block a user