refactor: noms publics dracodev par configuration (dev/prod)
Aucun nom public d'une autre app dracodev n'est codé en dur dans le code vivant : nouveau module app/services/draco_urls.py (env > config > défaut officiel, jamais de -dev en défaut). CSP connect-src/frame-src, bouton SSO ObsiGate, révocations SSO, événements agent, preset MCP TikTak, barre commune, panneau Spark (injection hôte DRACO_PANNEAU_CONFIG), refs croisées (hôte configuré + officiel reconnus, URL d'affichage reconstruite, aucune donnée stockée migrée), User-Agent OG, liens Gitea. Variables ajoutées DRACO_AGENT_URL/TIKTAK_URL/KANBANTE_URL/FLOWDECK_URL, réutilisées DRACO_SSO_URL/OAUTH_REDIRECT_URI/AGENT_EVENEMENTS_URL/GITEA_URL/APP_BASE_URL. Version 7.82.0.
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
"""URLs publiques des applications dracodev — résolution dev/prod.
|
||||
|
||||
Source unique pour tout le code vivant qui doit nommer une autre
|
||||
application (CSP, panneau Spark, barre commune, SSO, presets MCP,
|
||||
références croisées…). Règle du chantier dev/prod : **aucun nom
|
||||
public codé en dur ailleurs** ; les défauts ci-dessous sont les noms
|
||||
OFFICIELS (production) et un environnement dev déclare ses noms en
|
||||
``-dev`` dans le ``.env`` (qui survit aux déploiements par release,
|
||||
car il est passé par ``environment:``/``env_file:``) :
|
||||
|
||||
DRACO_SSO_URL=https://og-dev.dracodev.net
|
||||
DRACO_AGENT_URL=https://agent-dev.dracodev.net
|
||||
TIKTAK_URL=https://tiktak-dev.dracodev.net
|
||||
KANBANTE_URL=https://kanbante-dev.dracodev.net
|
||||
FLOWDECK_URL=https://flowdeck-dev.dracodev.net (ou OAUTH_REDIRECT_URI)
|
||||
|
||||
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``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from app.config import settings
|
||||
|
||||
OFFICIELLES = {
|
||||
"obsigate": "https://og.dracodev.net",
|
||||
"agent": "https://agent.dracodev.net",
|
||||
"tiktak": "https://tiktak.dracodev.net",
|
||||
"kanbante": "https://kanbante.dracodev.net",
|
||||
"flowdeck": "https://flowdeck.dracodev.net",
|
||||
"gitea": "https://git.dracodev.net",
|
||||
}
|
||||
|
||||
|
||||
def _nettoyer(url: str) -> str:
|
||||
return (url or "").strip().rstrip("/")
|
||||
|
||||
|
||||
def _resoudre(env_name: str, settings_attr: str, app: str) -> str:
|
||||
env = _nettoyer(os.environ.get(env_name, ""))
|
||||
if env:
|
||||
return env
|
||||
cfg = _nettoyer(getattr(settings, settings_attr, "") or "")
|
||||
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")
|
||||
|
||||
|
||||
def agent_url() -> str:
|
||||
"""Service d'agents (Spark) — base sans chemin."""
|
||||
return _resoudre("DRACO_AGENT_URL", "draco_agent_url", "agent")
|
||||
|
||||
|
||||
def tiktak_url() -> str:
|
||||
return _resoudre("TIKTAK_URL", "tiktak_url", "tiktak")
|
||||
|
||||
|
||||
def kanbante_url() -> str:
|
||||
return _resoudre("KANBANTE_URL", "kanbante_url", "kanbante")
|
||||
|
||||
|
||||
def gitea_url() -> str:
|
||||
return _resoudre("GITEA_URL", "gitea_url", "gitea")
|
||||
|
||||
|
||||
def flowdeck_url() -> str:
|
||||
"""URL publique de cette instance de Flowdeck.
|
||||
|
||||
FLOWDECK_URL > origine de OAUTH_REDIRECT_URI (déjà déclarée en
|
||||
dev : ``https://flowdeck-dev.dracodev.net/auth/callback``) >
|
||||
APP_BASE_URL si elle n'est pas locale > nom officiel.
|
||||
"""
|
||||
env = _nettoyer(os.environ.get("FLOWDECK_URL", ""))
|
||||
if env:
|
||||
return env
|
||||
cfg = _nettoyer(getattr(settings, "flowdeck_url", "") or "")
|
||||
if cfg:
|
||||
return cfg
|
||||
for candidate in (
|
||||
os.environ.get("OAUTH_REDIRECT_URI", ""),
|
||||
getattr(settings, "oauth_redirect_uri", "") or "",
|
||||
):
|
||||
candidate = _nettoyer(candidate)
|
||||
if candidate:
|
||||
p = urlparse(candidate)
|
||||
if p.scheme and p.netloc:
|
||||
return f"{p.scheme}://{p.netloc}"
|
||||
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
|
||||
return OFFICIELLES["flowdeck"]
|
||||
|
||||
|
||||
def agent_evenements_url() -> str:
|
||||
"""Endpoint du journal d'événements du service d'agents.
|
||||
|
||||
AGENT_EVENEMENTS_URL (déjà en service) prime ; sinon dérivée de
|
||||
la base de l'agent — jamais un nom figé.
|
||||
"""
|
||||
env = _nettoyer(os.environ.get("AGENT_EVENEMENTS_URL", ""))
|
||||
if env:
|
||||
return env
|
||||
cfg = _nettoyer(getattr(settings, "agent_evenements_url", "") or "")
|
||||
if cfg:
|
||||
return cfg
|
||||
return f"{agent_url()}/api/v1/evenements"
|
||||
|
||||
|
||||
def origine(url: str) -> str:
|
||||
"""``scheme://netloc`` d'une URL (pour la CSP)."""
|
||||
p = urlparse(url or "")
|
||||
if p.scheme and p.netloc:
|
||||
return f"{p.scheme}://{p.netloc}"
|
||||
return _nettoyer(url)
|
||||
|
||||
|
||||
def hote(url: str) -> str:
|
||||
return (urlparse(url or "").hostname or "").lower()
|
||||
|
||||
|
||||
def toutes() -> dict[str, str]:
|
||||
"""Toutes les URL résolues — injectées aux templates (hôte = config)."""
|
||||
return {
|
||||
"obsigate": obsigate_url(),
|
||||
"agent": agent_url(),
|
||||
"tiktak": tiktak_url(),
|
||||
"kanbante": kanbante_url(),
|
||||
"flowdeck": flowdeck_url(),
|
||||
"gitea": gitea_url(),
|
||||
}
|
||||
|
||||
|
||||
def hotes_connus(app: str) -> set[str]:
|
||||
"""Hôtes reconnus pour ``app`` : l'hôte configuré **et** l'hôte
|
||||
officiel. Les références croisées déjà stockées portent des URL
|
||||
absolues (officielles aujourd'hui) : elles doivent rester
|
||||
reconnaissables dans un environnement dev, et inversement —
|
||||
aucune migration de données n'est faite pour un changement de nom.
|
||||
"""
|
||||
resolues = toutes()
|
||||
hotes = {hote(OFFICIELLES[app])}
|
||||
if app in resolues:
|
||||
hotes.add(hote(resolues[app]))
|
||||
return {h for h in hotes if h}
|
||||
Reference in New Issue
Block a user