Files
ObsiGate/backend/draco_config.py
T
Spark f29a22886a
CI / lint (push) Successful in 3m11s
CI / security (push) Failing after 1m55s
CI / test (push) Successful in 5m23s
CI / build (push) Successful in 2m35s
CI / e2e (push) Successful in 18m24s
feat: URLs publiques de l'écosystème par configuration — dev/prod sans noms en dur #223
2026-10-25 18:10:59 +00:00

132 lines
4.5 KiB
Python

"""URLs publiques de l'écosystème dracodev — par configuration (#223).
Règle dev/prod (Bruno, 2026-10-25) : **aucun nom public de
l'écosystème n'est figé dans le code vivant**. Chaque URL publique
vient de l'environnement ; les valeurs par défaut sont les noms
**officiels** (sans suffixe) de la future production. L'environnement
dev déclare ses noms en ``-dev`` dans son seul ``.env`` — le code
n'en connaît aucun.
Variables (voir ``.env.example``) :
- ``DRACO_AGENT_URL`` — base publique du service d'agents ;
à défaut, l'origine est **dérivée** de ``AGENT_EVENEMENTS_URL``
(la configuration réelle de l'émission d'événements, #220), puis
le défaut officiel.
- ``DRACO_OBSIGATE_URL`` — base publique d'ObsiGate (liens profonds).
- ``DRACO_FLOWDECK_URL`` — base publique de Flowdeck.
- ``DRACO_TIKTAK_URL`` — base publique de TikTak.
- ``DRACO_KANBANTE_URL`` — base publique de Kanbante.
- ``DRACO_GITEA_URL`` — base publique de Gitea (les cartes
Kanbante *sont* des issues Gitea, contrat liens-profonds) ;
distincte de ``OBSIGATE_GITEA_URL`` (source connectée de
l'assistant, réglable depuis l'UI, pas une URL d'affichage).
Ce module ne lit l'environnement qu'à l'appel (jamais à l'import) :
les tests et un changement de ``.env`` suivi d'un redémarrage
prennent effet sans état figé.
"""
from __future__ import annotations
import os
from urllib.parse import urlparse
# Défauts officiels (production future) — jamais de nom en ``-dev`` ici.
DEFAUTS: dict[str, str] = {
"agent": "https://agent.dracodev.net",
"obsigate": "https://og.dracodev.net",
"flowdeck": "https://flowdeck.dracodev.net",
"tiktak": "https://tiktak.dracodev.net",
"kanbante": "https://kanbante.dracodev.net",
"gitea": "https://git.dracodev.net",
}
_VARIABLES: dict[str, str] = {
"agent": "DRACO_AGENT_URL",
"obsigate": "DRACO_OBSIGATE_URL",
"flowdeck": "DRACO_FLOWDECK_URL",
"tiktak": "DRACO_TIKTAK_URL",
"kanbante": "DRACO_KANBANTE_URL",
"gitea": "DRACO_GITEA_URL",
}
def _nettoyer(valeur: str) -> str:
return valeur.strip().rstrip("/")
def origine(url: str) -> str:
"""Origine ``schéma://hôte[:port]`` d'une URL (``""`` si illisible)."""
u = urlparse(url or "")
if u.scheme and u.netloc:
return f"{u.scheme}://{u.netloc}"
return ""
def agent_url() -> str:
"""Base publique du service d'agents.
Priorité : ``DRACO_AGENT_URL``, puis l'origine de
``AGENT_EVENEMENTS_URL`` (déjà la configuration réelle du poste),
puis le défaut officiel.
"""
explicite = _nettoyer(os.environ.get(_VARIABLES["agent"], ""))
if explicite:
return explicite
evenements = _nettoyer(os.environ.get("AGENT_EVENEMENTS_URL", ""))
if evenements:
derivee = origine(evenements)
if derivee:
return derivee
return DEFAUTS["agent"]
def agent_origine() -> str:
"""Origine du service d'agents, pour la CSP (``connect-src``/``frame-src``)."""
return origine(agent_url()) or DEFAUTS["agent"]
def app_url(app: str) -> str:
"""Base publique d'une application de l'écosystème (sans ``/`` final)."""
if app == "agent":
return agent_url()
variable = _VARIABLES[app]
return _nettoyer(os.environ.get(variable, "")) or DEFAUTS[app]
def app_hote(app: str) -> str:
"""Nom d'hôte (minuscules) de l'URL publique configurée de *app*."""
return (urlparse(app_url(app)).hostname or "").lower()
def hotes_reconnus() -> dict[str, str]:
"""``{hôte: app}`` pour le parseur de liens profonds.
Contient **toujours** les hôtes officiels (un lien collé depuis la
production reste reconnaissable en dev, et réciproquement) **plus**
l'hôte configuré de chaque application (les noms en ``-dev`` du
poste quand ils sont déclarés dans son ``.env``).
"""
hotes: dict[str, str] = {}
for app, defaut in DEFAUTS.items():
hote = (urlparse(defaut).hostname or "").lower()
if hote:
hotes[hote] = app
for app in DEFAUTS:
hote = app_hote(app)
if hote:
hotes[hote] = app
return hotes
def urls_publiques() -> dict[str, str]:
"""Carte des URL publiques injectée à l'hôte web (``window.DRACO_URLS``).
C'est le contrat d'injection de dracodev-commons : l'application
hôte (ici ObsiGate, qui sert ``index.html``) fournit les URL aux
assets partagés (panneau Spark, barre, liens) — les assets ne
portent que les défauts officiels en repli.
"""
return {app: app_url(app) for app in DEFAUTS}