"""FastAPI 应用工厂。 路由概览: GET / -> 仅返回版本号 GET /docs -> Swagger UI(Basic Auth) GET /redoc -> ReDoc (Basic Auth) GET /openapi.json -> OpenAPI 文档(Basic Auth) GET /health -> 存活探针(公开) GET /api/... -> 业务接口 """ from __future__ import annotations import logging from contextlib import asynccontextmanager from fastapi import Depends, FastAPI from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html from fastapi.responses import JSONResponse, PlainTextResponse from .controllers import file_router, system_router from .database import init_db_schema from .security import require_docs_auth logger = logging.getLogger("zikai") @asynccontextmanager async def lifespan(app: FastAPI): try: init_db_schema() logger.info("数据库表已就绪。") except Exception as exc: # pragma: no cover logger.error("初始化数据库失败:%s", exc) yield def create_app() -> FastAPI: app = FastAPI( title="zikai file service", description=( "f.zikai.wang 的文件上传与主机监控服务。\n\n" "- `GET /api/system/status` — CPU / 内存 / 磁盘(HTML 或 JSON)\n" "- `POST /api/files/upload` — 大文件流式上传\n" "- 内置 SFTP 服务(详见 README)\n\n" "/docs 等接口需 Basic Auth,凭据见 config.yaml 的 docs 段。" ), version="1.0.0", docs_url=None, redoc_url=None, openapi_url=None, lifespan=lifespan, ) app.include_router(system_router) app.include_router(file_router) # 受 Basic Auth 保护的文档接口 @app.get("/openapi.json", include_in_schema=False) def protected_openapi(_: str = Depends(require_docs_auth)) -> JSONResponse: return JSONResponse(app.openapi()) @app.get("/docs", include_in_schema=False) def protected_docs(_: str = Depends(require_docs_auth)): return get_swagger_ui_html( openapi_url="/openapi.json", title="zikai docs", swagger_favicon_url="" ) @app.get("/redoc", include_in_schema=False) def protected_redoc(_: str = Depends(require_docs_auth)): return get_redoc_html( openapi_url="/openapi.json", title="zikai docs", redoc_favicon_url="" ) # 公开元信息(不暴露接口列表) @app.get("/", include_in_schema=False, response_class=PlainTextResponse) def root() -> PlainTextResponse: return PlainTextResponse(f"zikai {app.version}\n") @app.get("/health", include_in_schema=False) def health() -> dict: return {"status": "ok"} return app app = create_app()