feat: 支持纯 FastAPI 模式(根路径服务前端)+ 修任务端点路径

- web/app.py:create_app(api_prefix=) 参数化前缀——'' 即根路径模式,
  87 条路由直接服务前端使用的路径,无需 Flask 兜底
- web/main.py:WEB_MODE=dual(默认,迁移期)/ fastapi(纯 FastAPI)切换
- 修真实路径缺陷:任务类端点漏了 /api 段(Flask 为 /api/topen 等,
  原写成 /topen),根路径模式下前端会 404。已改为 /api/topen、/api/queue、
  /api/batch_progress、/api/task/*;fe_error 同步修正
- 奇偶校验路径映射同步更新

验证:
- tests/test_root_mode.py 新增 66 项:前端调用的每条路径在纯 FastAPI 模式下
  均非 404;root 模式不再暴露 /api/v1
- 纯 FastAPI 模式真实启动(uvicorn, PORT=5002)实测:
  /health、/、/static 200,/song、/api/task/list 401(鉴权生效)
- 全套 pytest 135/135;ruff All checks passed;pyright 0 errors;奇偶 36/36
This commit is contained in:
snowy-yang committed 2026-09-26 10:25:05 +08:00
1 parent cd0f1f78a3
commit f5d1e3c3a9
5 files changed
+107 -28

No files matched your search

+24 -18
View File
@@ -64,8 +64,14 @@ def _init_audio_secret() -> None:
pass
def create_app(serve_flask_fallback: bool = False) -> FastAPI:
"""创建 FastAPI 应用。"""
def create_app(serve_flask_fallback: bool = False, api_prefix: str = '/api/v1') -> FastAPI:
"""创建 FastAPI 应用。
api_prefix:已迁移路由的挂载前缀。
- '/api/v1'(默认):与 Flask 根路径并存,便于逐端点契约比对(迁移期)
- ''(切换完成):FastAPI 直接服务前端使用的根路径,不再需要 Flask 兜底
serve_flask_fallback:把 Flask 应用挂到根路径兜底(仅 api_prefix 非空时可用)。
"""
_init_audio_secret()
app = FastAPI(
@@ -86,27 +92,27 @@ def create_app(serve_flask_fallback: bool = False) -> FastAPI:
app.mount('/static', StaticFiles(directory=str(_static_dir)), name='static')
# 已迁移路由:统一挂 /api/v1(迁移期与 Flask 根路径并存,便于对比)
app.include_router(system_router.router, prefix='/api/v1', tags=['system'])
app.include_router(site_router.router, prefix='/api/v1', tags=['site'])
app.include_router(music_router.router, prefix='/api/v1', tags=['music'])
app.include_router(account_router.router, prefix='/api/v1', tags=['account'])
app.include_router(podcast_router.router, prefix='/api/v1', tags=['podcast'])
app.include_router(tasks_router.router, prefix='/api/v1', tags=['tasks'])
app.include_router(delivery_router.router, prefix='/api/v1', tags=['delivery'])
app.include_router(stream_router.router, prefix='/api/v1', tags=['stream'])
app.include_router(auth_status_router.router, prefix='/api/v1', tags=['auth'])
app.include_router(auth_router.router, prefix='/api/v1', tags=['auth'])
app.include_router(user_router.router, prefix='/api/v1', tags=['user'])
app.include_router(admin_router.router, prefix='/api/v1', tags=['admin'])
app.include_router(register_router.router, prefix='/api/v1', tags=['auth'])
app.include_router(admin_site_router.router, prefix='/api/v1', tags=['admin'])
app.include_router(download_router.router, prefix='/api/v1', tags=['download'])
app.include_router(system_router.router, prefix=api_prefix, tags=['system'])
app.include_router(site_router.router, prefix=api_prefix, tags=['site'])
app.include_router(music_router.router, prefix=api_prefix, tags=['music'])
app.include_router(account_router.router, prefix=api_prefix, tags=['account'])
app.include_router(podcast_router.router, prefix=api_prefix, tags=['podcast'])
app.include_router(tasks_router.router, prefix=api_prefix, tags=['tasks'])
app.include_router(delivery_router.router, prefix=api_prefix, tags=['delivery'])
app.include_router(stream_router.router, prefix=api_prefix, tags=['stream'])
app.include_router(auth_status_router.router, prefix=api_prefix, tags=['auth'])
app.include_router(auth_router.router, prefix=api_prefix, tags=['auth'])
app.include_router(user_router.router, prefix=api_prefix, tags=['user'])
app.include_router(admin_router.router, prefix=api_prefix, tags=['admin'])
app.include_router(register_router.router, prefix=api_prefix, tags=['auth'])
app.include_router(admin_site_router.router, prefix=api_prefix, tags=['admin'])
app.include_router(download_router.router, prefix=api_prefix, tags=['download'])
# 首页与静态资源(迁移完成形态;fallback 模式下同路径也能由 Flask 提供)
app.include_router(index_router.router, tags=['index'])
install_error_handlers(app)
if serve_flask_fallback:
if serve_flask_fallback and api_prefix:
_mount_flask_fallback(app)
return app