2026-01-31 19:15:41 +08:00
|
|
|
from fastapi import FastAPI, HTTPException, Request
|
|
|
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
|
from fastapi.responses import JSONResponse
|
2026-02-01 15:39:13 +08:00
|
|
|
from fastapi.staticfiles import StaticFiles
|
2026-01-31 19:15:41 +08:00
|
|
|
import logging
|
2026-02-01 14:19:11 +08:00
|
|
|
import dashscope
|
2026-02-01 15:39:13 +08:00
|
|
|
from pathlib import Path
|
2026-02-02 20:08:28 +08:00
|
|
|
from contextlib import asynccontextmanager
|
2026-01-31 19:15:41 +08:00
|
|
|
|
2026-02-02 20:08:28 +08:00
|
|
|
from lover.routers import config as config_router
|
|
|
|
|
from lover.routers import lover as lover_router
|
|
|
|
|
from lover.routers import user_basic as user_basic_router
|
|
|
|
|
from lover.response import ApiResponse
|
|
|
|
|
from lover.routers import outfit as outfit_router
|
|
|
|
|
from lover.routers import chat as chat_router
|
|
|
|
|
from lover.routers import voice_call as voice_call_router
|
|
|
|
|
from lover.routers import dance as dance_router
|
|
|
|
|
from lover.routers import dynamic as dynamic_router
|
|
|
|
|
from lover.routers import sing as sing_router
|
|
|
|
|
from lover.routers import friend as friend_router
|
|
|
|
|
from lover.routers import msg as msg_router
|
|
|
|
|
from lover.routers import huanxin as huanxin_router
|
|
|
|
|
from lover.routers import user as user_router
|
2026-02-03 17:13:56 +08:00
|
|
|
from lover.routers import music_library as music_library_router
|
2026-02-02 20:08:28 +08:00
|
|
|
from lover.task_queue import start_sing_workers
|
|
|
|
|
from lover.config import settings
|
2026-01-31 19:15:41 +08:00
|
|
|
|
2026-02-01 14:19:11 +08:00
|
|
|
# 初始化 DashScope API Key
|
|
|
|
|
if settings.DASHSCOPE_API_KEY:
|
|
|
|
|
dashscope.api_key = settings.DASHSCOPE_API_KEY
|
|
|
|
|
|
2026-02-02 20:08:28 +08:00
|
|
|
# 配置日志
|
|
|
|
|
logging.basicConfig(
|
|
|
|
|
level=logging.INFO,
|
|
|
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@asynccontextmanager
|
|
|
|
|
async def lifespan(app: FastAPI):
|
|
|
|
|
# 启动时执行
|
|
|
|
|
logger = logging.getLogger("main")
|
|
|
|
|
logger.info("应用启动中...")
|
|
|
|
|
start_sing_workers()
|
|
|
|
|
logger.info("应用启动完成")
|
|
|
|
|
yield
|
|
|
|
|
# 关闭时执行
|
|
|
|
|
logger.info("应用关闭")
|
|
|
|
|
|
|
|
|
|
app = FastAPI(title="LOVER API", lifespan=lifespan)
|
2026-01-31 19:15:41 +08:00
|
|
|
|
2026-02-01 15:39:13 +08:00
|
|
|
# 创建 TTS 文件目录
|
|
|
|
|
tts_dir = Path("public/tts")
|
|
|
|
|
tts_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
|
|
|
|
# 挂载静态文件服务(用于提供 TTS 音频文件)
|
|
|
|
|
app.mount("/tts", StaticFiles(directory=str(tts_dir)), name="tts")
|
|
|
|
|
|
2026-01-31 19:15:41 +08:00
|
|
|
app.add_middleware(
|
|
|
|
|
CORSMiddleware,
|
|
|
|
|
allow_origins=[
|
|
|
|
|
"http://localhost",
|
|
|
|
|
"http://localhost:5173",
|
|
|
|
|
"http://localhost:8080",
|
|
|
|
|
"http://127.0.0.1",
|
|
|
|
|
"http://127.0.0.1:5173",
|
|
|
|
|
"http://127.0.0.1:8080",
|
|
|
|
|
],
|
|
|
|
|
allow_credentials=True,
|
|
|
|
|
allow_methods=["*"],
|
|
|
|
|
allow_headers=["*"],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
app.include_router(config_router.router)
|
|
|
|
|
app.include_router(lover_router.router)
|
2026-02-02 20:08:28 +08:00
|
|
|
app.include_router(user_basic_router.router)
|
2026-01-31 19:15:41 +08:00
|
|
|
app.include_router(outfit_router.router)
|
|
|
|
|
app.include_router(chat_router.router)
|
|
|
|
|
app.include_router(voice_call_router.router)
|
|
|
|
|
app.include_router(dance_router.router)
|
|
|
|
|
app.include_router(dynamic_router.router)
|
|
|
|
|
app.include_router(sing_router.router)
|
2026-02-02 20:08:28 +08:00
|
|
|
app.include_router(friend_router.router)
|
|
|
|
|
app.include_router(msg_router.router)
|
|
|
|
|
app.include_router(huanxin_router.router)
|
|
|
|
|
app.include_router(user_router.router)
|
2026-02-03 17:13:56 +08:00
|
|
|
app.include_router(music_library_router.router)
|
2026-01-31 19:15:41 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.exception_handler(HTTPException)
|
|
|
|
|
async def http_exception_handler(request: Request, exc: HTTPException):
|
|
|
|
|
# 统一错误响应结构,便于前端通过 code 判定
|
|
|
|
|
detail = exc.detail
|
|
|
|
|
msg = detail if isinstance(detail, str) else str(detail)
|
|
|
|
|
return JSONResponse(
|
|
|
|
|
status_code=exc.status_code,
|
|
|
|
|
content={"code": exc.status_code, "msg": msg, "data": None},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.exception_handler(Exception)
|
|
|
|
|
async def generic_exception_handler(request: Request, exc: Exception):
|
|
|
|
|
logging.exception("Unhandled error", exc_info=exc)
|
|
|
|
|
return JSONResponse(
|
|
|
|
|
status_code=500,
|
|
|
|
|
content={"code": 500, "msg": "服务器内部错误", "data": None},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.get("/health", response_model=ApiResponse[dict])
|
|
|
|
|
async def health():
|
|
|
|
|
return ApiResponse(code=1, msg="ok", data={"status": "ok"})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.get("/debug/auth")
|
|
|
|
|
async def debug_auth(request: Request):
|
|
|
|
|
"""调试认证信息"""
|
|
|
|
|
headers = dict(request.headers)
|
|
|
|
|
return {
|
|
|
|
|
"app_env": settings.APP_ENV,
|
|
|
|
|
"debug": settings.DEBUG,
|
|
|
|
|
"headers": headers,
|
|
|
|
|
"token": headers.get("token"),
|
|
|
|
|
"authorization": headers.get("authorization"),
|
|
|
|
|
}
|