feat: one gateway port with path-mounted frontends, markdown chat, collapsible sidebars

This commit is contained in:
hh
2026-08-29 00:49:06 +02:00
parent 540327efa1
commit 16b6bbddda
32 changed files with 691 additions and 441 deletions
+49
View File
@@ -0,0 +1,49 @@
"""The one ASGI app the gateway listens with.
Every HTTP frontend is mounted under its ``path`` (``/anthropic``,
``/mcp``, ``/md``, ``/api``, ``/admin``); ``/healthz`` answers for the
whole process and ``/`` redirects to the landing frontend (the admin
console) when one is configured.
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from starlette.applications import Starlette
from starlette.responses import JSONResponse, RedirectResponse
from starlette.routing import Mount, Route
if TYPE_CHECKING:
from collections.abc import Iterable
from starlette.requests import Request
from beaver_gateway.frontends.base import Frontend
__all__ = ["build_root_app"]
def build_root_app(frontends: Iterable[Frontend]) -> Starlette:
mounted = [fe for fe in frontends if fe.path and fe.app() is not None]
landing = next((fe for fe in mounted if fe.landing), None)
paths = [fe.path for fe in mounted]
async def healthz(_request: Request) -> JSONResponse:
return JSONResponse({"status": "ok", "frontends": paths})
async def index(_request: Request) -> JSONResponse | RedirectResponse:
if landing is not None:
return RedirectResponse(f"{landing.path}/", status_code=307)
return JSONResponse({"frontends": paths})
routes: list[Route | Mount] = [
Route("/healthz", healthz, methods=["GET"]),
Route("/", index, methods=["GET"]),
]
for fe in mounted:
app = fe.app()
assert app is not None # noqa: S101 - filtered above; narrows for ty
assert fe.path is not None # noqa: S101
routes.append(Mount(fe.path, app=app, name=fe.name or fe.path.strip("/")))
return Starlette(routes=routes)