Files
MoviePilot/app/factory.py
2026-08-06 17:32:33 +08:00

99 lines
2.8 KiB
Python

import json
from typing import Any, Awaitable, Callable
from fastapi import FastAPI, HTTPException, Request, Response
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from app.api.apiv2_utils import OPENAPI_V2_PATH, V2ResponseMiddleware
from app.core.config import settings
from app.helper.locale import LocaleHelper
from app.startup.lifecycle import lifespan
from version import APP_VERSION
def _get_http_exception_message(detail: Any) -> str:
"""将 HTTPException 的 detail 转换为统一消息文本。"""
if isinstance(detail, str) and detail:
return detail
if detail is None:
return "请求失败"
try:
return json.dumps(detail, ensure_ascii=False)
except TypeError:
return str(detail)
async def localized_http_exception_handler(
_request: Request,
exc: HTTPException,
) -> JSONResponse:
"""
将 HTTPException 响应统一封装为 Response 结构并保留原始错误消息。
:param _request: 当前 HTTP 请求
:param exc: FastAPI HTTP 异常
:return: 统一 JSON 错误响应
"""
message = _get_http_exception_message(exc.detail)
return JSONResponse(
status_code=exc.status_code,
content={
"success": False,
"message": message,
"data": {},
},
headers=exc.headers,
)
def create_app() -> FastAPI:
"""
创建并配置 FastAPI 应用实例。
"""
_app = FastAPI(
title=settings.PROJECT_NAME,
version=APP_VERSION,
openapi_url=OPENAPI_V2_PATH,
lifespan=lifespan
)
@_app.get(f"{settings.API_V1_STR}/openapi.json", include_in_schema=False)
def get_v1_openapi_schema() -> dict[str, Any]:
"""保留旧版 OpenAPI 地址并返回当前完整接口文档。"""
return _app.openapi()
_app.add_exception_handler(HTTPException, localized_http_exception_handler)
# 配置 CORS 中间件
_app.add_middleware(
CORSMiddleware, # noqa
allow_origins=settings.ALLOWED_HOSTS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
_app.add_middleware(V2ResponseMiddleware)
@_app.middleware("http")
async def locale_context_middleware(
request: Request,
call_next: Callable[[Request], Awaitable[Response]],
) -> Response:
"""
为每个请求设置后端多语言上下文。
"""
token = LocaleHelper.set_current_locale(
LocaleHelper.get_locale_from_request(request)
)
try:
return await call_next(request)
finally:
LocaleHelper.reset_current_locale(token)
return _app
# 创建 FastAPI 应用实例
app = create_app()