102 lines
3.0 KiB
Python
102 lines
3.0 KiB
Python
from fastapi import FastAPI, Request
|
|
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, 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()
|
|
yield
|
|
await close_http_client()
|
|
|
|
app = FastAPI(
|
|
title=settings.app_name,
|
|
version="1.0.0",
|
|
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)
|
|
app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
|
|
|
|
# Templates
|
|
templates_dir = BASE_DIR / "app" / "templates"
|
|
templates = Jinja2Templates(directory=str(templates_dir))
|
|
|
|
# Include Routers
|
|
app.include_router(api.router)
|
|
app.include_router(proxy.router)
|
|
|
|
@app.get("/", response_class=HTMLResponse)
|
|
@app.head("/", response_class=HTMLResponse)
|
|
async def index_view(request: Request):
|
|
return templates.TemplateResponse(
|
|
request=request,
|
|
name="index.html",
|
|
context={
|
|
"universes": UNIVERSES,
|
|
"app_name": settings.app_name
|
|
}
|
|
)
|
|
|
|
if __name__ == "__main__":
|
|
import uvicorn
|
|
uvicorn.run("app.main:app", host=settings.host, port=settings.port, reload=settings.debug)
|