Serve dashboard at /, version the REST API under /api/v1
The SvelteKit build was always made for the root (no base path); it now mounts at / — registered last so /api/v1, /ws, /health, and /mcp match first — and the JSON root endpoint is gone (its info lives in /health and gateway_status). REST routers move from /api/* to /api/v1/*; /ws and /health stay put; /mcp/ unchanged. Dashboard API client, tests, README, and docs updated; dashboard rebuilt (build/ is gitignored). Verified live: / serves the UI, /api/v1 answers 200/401, the old /api paths 404, MCP still lists 15 tools. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
64
main.py
64
main.py
@@ -248,62 +248,30 @@ app = FastAPI(
|
||||
"🗡️ AI PSTN Gateway — Navigate IVRs, wait on hold, "
|
||||
"and connect you when a human answers.\n\n"
|
||||
"## Quick Start\n"
|
||||
"1. **POST /api/calls/hold-slayer** — Launch the Hold Slayer\n"
|
||||
"2. **GET /api/calls/{call_id}** — Check call status\n"
|
||||
"1. **POST /api/v1/calls/hold-slayer** — Launch the Hold Slayer\n"
|
||||
"2. **GET /api/v1/calls/{call_id}** — Check call status\n"
|
||||
"3. **WS /ws/events** — Real-time event stream\n"
|
||||
"4. **GET /api/call-flows** — Manage stored IVR trees\n"
|
||||
"4. **GET /api/v1/call-flows** — Manage stored IVR trees\n"
|
||||
),
|
||||
version="0.1.0",
|
||||
lifespan=lifespan,
|
||||
)
|
||||
|
||||
# === API Routes ===
|
||||
# call_history must register before calls: both live under /api/calls and
|
||||
# call_history must register before calls: both live under /api/v1/calls and
|
||||
# calls' GET /{call_id} would otherwise capture the literal path "history".
|
||||
_auth = [Depends(require_token)]
|
||||
app.include_router(call_history.router, prefix="/api/calls", tags=["Call History"], dependencies=_auth)
|
||||
app.include_router(calls.router, prefix="/api/calls", tags=["Calls"], dependencies=_auth)
|
||||
app.include_router(call_flows.router, prefix="/api/call-flows", tags=["Call Flows"], dependencies=_auth)
|
||||
app.include_router(devices.router, prefix="/api/devices", tags=["Devices"], dependencies=_auth)
|
||||
app.include_router(routing.router, prefix="/api/routing", tags=["Routing"], dependencies=_auth)
|
||||
app.include_router(call_history.router, prefix="/api/v1/calls", tags=["Call History"], dependencies=_auth)
|
||||
app.include_router(calls.router, prefix="/api/v1/calls", tags=["Calls"], dependencies=_auth)
|
||||
app.include_router(call_flows.router, prefix="/api/v1/call-flows", tags=["Call Flows"], dependencies=_auth)
|
||||
app.include_router(devices.router, prefix="/api/v1/devices", tags=["Devices"], dependencies=_auth)
|
||||
app.include_router(routing.router, prefix="/api/v1/routing", tags=["Routing"], dependencies=_auth)
|
||||
# WebSocket endpoints check the token themselves (query param or header)
|
||||
app.include_router(websocket.router, prefix="/ws", tags=["WebSocket"])
|
||||
|
||||
# === MCP (streamable HTTP; clients connect to /mcp/ with the bearer token) ===
|
||||
app.mount("/mcp", mcp_http_app)
|
||||
|
||||
# === Dashboard (built SvelteKit static) ===
|
||||
import os as _os
|
||||
_dashboard_build = _os.path.join(_os.path.dirname(__file__), "dashboard", "build")
|
||||
if _os.path.isdir(_dashboard_build):
|
||||
app.mount(
|
||||
"/dashboard",
|
||||
StaticFiles(directory=_dashboard_build, html=True),
|
||||
name="dashboard",
|
||||
)
|
||||
|
||||
|
||||
# === Root Endpoint ===
|
||||
@app.get("/", tags=["System"])
|
||||
async def root():
|
||||
"""Gateway root — health check and quick status."""
|
||||
gateway = getattr(app.state, "gateway", None)
|
||||
if gateway:
|
||||
status = await gateway.status()
|
||||
return {
|
||||
"name": "Hold Slayer Gateway",
|
||||
"version": "0.1.0",
|
||||
"status": "running",
|
||||
"uptime": status["uptime"],
|
||||
"active_calls": status["active_calls"],
|
||||
"trunk": status["trunk"],
|
||||
}
|
||||
return {
|
||||
"name": "Hold Slayer Gateway",
|
||||
"version": "0.1.0",
|
||||
"status": "starting",
|
||||
}
|
||||
|
||||
|
||||
@app.get("/health", tags=["System"])
|
||||
async def health():
|
||||
@@ -370,6 +338,20 @@ def _availability(service) -> str:
|
||||
return "ok" if available else "unreachable"
|
||||
|
||||
|
||||
# === Dashboard (built SvelteKit static, served at the root) ===
|
||||
# Registered last: a "/" mount matches every path, so the API, WS,
|
||||
# health, and MCP routes above must come first.
|
||||
import os as _os # noqa: E402
|
||||
|
||||
_dashboard_build = _os.path.join(_os.path.dirname(__file__), "dashboard", "build")
|
||||
if _os.path.isdir(_dashboard_build):
|
||||
app.mount(
|
||||
"/",
|
||||
StaticFiles(directory=_dashboard_build, html=True),
|
||||
name="dashboard",
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
|
||||
Reference in New Issue
Block a user