"""FastAPI app 工厂。G1 = 脚手架 + 占位 /;G2 起接 PG。 设计: - 单 FastAPI 进程,模板走 Jinja2,静态走 StaticFiles - 模板里 path 显示一律 `Path.as_posix()`,Win / Linux 看到统一形态 - SSE 在 G4 加,响应头会带 `X-Accel-Buffering: no`(nginx 反代友好) - 本地形态 sentinel user 固定;Phase D 加 OIDC 之后才有真正 user 态 """ from __future__ import annotations from pathlib import Path from fastapi import FastAPI, Request from fastapi.responses import HTMLResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates WEB_ROOT = Path(__file__).resolve().parent TEMPLATES_DIR = WEB_ROOT / "templates" STATIC_DIR = WEB_ROOT / "static" def create_app() -> FastAPI: """FastAPI 工厂。uvicorn --reload 模式需要工厂签名(factory=True)。""" app = FastAPI(title="zcbot web", version="0.1") templates = Jinja2Templates(directory=str(TEMPLATES_DIR)) app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static") @app.get("/", response_class=HTMLResponse) def home(request: Request): # Starlette 新签名:request 升一等位置参数,context 不再带 request return templates.TemplateResponse( request, "home.html", {"version": app.version} ) @app.get("/healthz", response_class=HTMLResponse) def healthz(): return HTMLResponse("ok") return app