mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-05 02:13:24 +00:00
pip install docsgpt now brings the web UI with it: `docsgpt api` serves the API and the UI on one port. - scripts/build_frontend.sh builds the frontend into docsgpt/static (gitignored) the way the frontend image does: .env.development as the production baseline, and index.html loading /config.js ahead of the bundle. hatch admits the directory into the wheel and the sdist through `artifacts`; the package workflows run the script before `uv build` and fail if the wheel lacks the UI. The backend image keeps ignoring it. - docsgpt/ui.py serves the build in front of Flask: files as they are, hashed assets immutable, Flask's own path prefixes (taken from its URL map, so new blueprints need no registration) passed through, every other GET rendered as index.html for the client-side router. /config.js is generated per request with VITE_API_HOST and VITE_BASE_URL set to the page's origin, VITE_* environment variables winning. SERVE_UI=false leaves the API alone. - docsgpt api configures gunicorn in code (gunicorn.app.base.Application) instead of rewriting sys.argv, so the SIGUSR2 re-exec that gunicorn uses for zero-downtime upgrades runs the docsgpt console script again and works; verified with a live handover. - Docs: the pip page says the UI is included, that DOCSGPT_HOME and DOCSGPT_ENV_FILE are process environment variables rather than .env entries, and the settings page describes SERVE_UI.
55 lines
1.9 KiB
Python
55 lines
1.9 KiB
Python
"""ASGI entrypoint: Flask (WSGI) + FastMCP on the same process."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from a2wsgi import WSGIMiddleware
|
|
from starlette.applications import Starlette
|
|
from starlette.middleware import Middleware
|
|
from starlette.middleware.cors import CORSMiddleware
|
|
from starlette.routing import Mount
|
|
|
|
from docsgpt.api.async_sse import async_sse_routes
|
|
from docsgpt.app import app as flask_app
|
|
from docsgpt.core.settings import settings
|
|
from docsgpt.mcp_server import mcp
|
|
from docsgpt.ui import StaticUI
|
|
|
|
_WSGI_THREADPOOL = int(settings.WSGI_THREADPOOL_WORKERS)
|
|
|
|
mcp_app = mcp.http_app(path="/")
|
|
|
|
# The web UI, when the package ships one (docsgpt/static) and SERVE_UI is on:
|
|
# files are served directly, Flask's own path prefixes pass through, and any
|
|
# other GET renders index.html for the client-side router.
|
|
_backend = StaticUI.wrap(
|
|
WSGIMiddleware(flask_app, workers=_WSGI_THREADPOOL), flask_app.url_map, enabled=settings.SERVE_UI
|
|
)
|
|
|
|
asgi_app = Starlette(
|
|
routes=[
|
|
Mount("/mcp", app=mcp_app),
|
|
# Native-async SSE readers intercept their exact paths before the
|
|
# Flask catch-all, so a mostly-idle reconnect tail rides the event
|
|
# loop instead of pinning a WSGI threadpool slot. Order matters:
|
|
# Starlette matches routes top-to-bottom, so these must precede the
|
|
# Mount("/") that hands everything else to Flask.
|
|
*async_sse_routes,
|
|
Mount("/", app=_backend),
|
|
],
|
|
middleware=[
|
|
Middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["*"],
|
|
allow_methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
|
|
allow_headers=[
|
|
"Content-Type",
|
|
"Authorization",
|
|
"Mcp-Session-Id",
|
|
"Idempotency-Key",
|
|
],
|
|
expose_headers=["Mcp-Session-Id"],
|
|
),
|
|
],
|
|
lifespan=mcp_app.lifespan,
|
|
)
|