feat: implement native subdomain routing (*.hub.yesminedor.tn) for seamless absolute assets resolution across all universes

This commit is contained in:
bolbol
2026-08-20 09:03:30 +01:00
parent b6ce9788f3
commit e074015b72
3 changed files with 86 additions and 20 deletions
+50 -1
View File
@@ -3,13 +3,46 @@ from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from pathlib import Path
from typing import Optional, Tuple
from contextlib import asynccontextmanager
from app.config import settings, UNIVERSES, BASE_DIR
from app.routers import api, proxy
from app.proxy import close_http_client
from app.proxy import close_http_client, proxy_request
from app.personas import ensure_dirs
def get_subdomain_target(host: str) -> Optional[Tuple[str, str]]:
"""
Inspects Host header and returns (target_backend_url, universe_id) if it matches a subdomain.
Examples:
tt.hub.yesminedor.tn -> (HERMES_TT_URL, 'tt')
nyora.hub.yesminedor.tn -> (HERMES_NYORA_URL, 'nyora')
perso.hub.yesminedor.tn -> (HERMES_PERSO_URL, 'perso')
nabil.hub.yesminedor.tn -> (HERMES_NABIL_URL, 'nabil')
dsh.hub.yesminedor.tn -> (DSH_FILEBROWSER_URL, 'nabil')
"""
if not host:
return None
hostname = host.split(":")[0].lower()
sub = None
if hostname.endswith(".hub.yesminedor.tn"):
sub = hostname.rsplit(".hub.yesminedor.tn", 1)[0]
elif hostname.endswith(".localhost"):
sub = hostname.rsplit(".localhost", 1)[0]
if not sub or sub in ("hub", "www"):
return None
if sub in ("dsh", "files", "dsh-files"):
return (settings.dsh_filebrowser_url, "nabil")
if sub in UNIVERSES:
return (UNIVERSES[sub].backend_url, sub)
return None
@asynccontextmanager
async def lifespan(app: FastAPI):
ensure_dirs()
@@ -22,6 +55,22 @@ app = FastAPI(
lifespan=lifespan
)
# Subdomain Routing Middleware
@app.middleware("http")
async def subdomain_routing_middleware(request: Request, call_next):
host = request.headers.get("host", "")
target = get_subdomain_target(host)
if target:
backend_url, universe_id = target
path = request.url.path
return await proxy_request(
request=request,
backend_url=backend_url,
path=path,
universe_id=universe_id
)
return await call_next(request)
# Mount static files
static_dir = BASE_DIR / "static"
static_dir.mkdir(parents=True, exist_ok=True)