""" FastAPI 应用入口 ================ """ import logging import sys from contextlib import asynccontextmanager from datetime import datetime from pathlib import Path import uvicorn from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from app.api.v1.router import api_router from app.config import get_settings from app.db.session import close_db, init_db from app.schemas.common import ApiResponse settings = get_settings() # 配置日志 - 同时输出到控制台和文件 log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" log_level = getattr(logging, settings.LOG_LEVEL) # 创建日志目录(在用户文档目录下) log_dir = Path.home() / "Documents" / "Meijiaka" / "logs" log_dir.mkdir(parents=True, exist_ok=True) # 日志文件名按日期 log_file = log_dir / f"api_{datetime.now().strftime('%Y%m%d')}.log" # 配置根日志记录器 logging.basicConfig( level=log_level, format=log_format, handlers=[ logging.StreamHandler(sys.stdout), # 控制台输出 logging.FileHandler(log_file, encoding="utf-8", mode="a"), # 文件输出 ], ) logger = logging.getLogger(__name__) logger.info(f"日志文件位置: {log_file}") @asynccontextmanager async def lifespan(app: FastAPI): """ 应用生命周期管理 - 启动时:初始化数据库、加载模型配置 - 关闭时:清理资源 """ logger.info(f"Starting {settings.APP_NAME} v{settings.APP_VERSION}") # 开发环境自动创建表 if settings.DEBUG and settings.ENV == "development": logger.info("Initializing database tables...") try: # 确保所有模型已注册到 metadata from app.models import Avatar, ModelUsageLog, User # noqa: F401 await init_db() logger.info("Database tables initialized") except Exception as e: logger.warning(f"Database initialization skipped: {e}") # 加载 AI 模型配置(从 YAML 文件) try: from app.core.config_loader import get_config_loader config_loader = get_config_loader() platforms_count = len(config_loader.get_all_platforms()) models_count = len(config_loader.get_enabled_models()) logger.info(f"Loaded {platforms_count} platforms, {models_count} models from config file") except Exception as e: logger.warning(f"Failed to load models from config: {e}") yield # 关闭时清理 logger.info("Shutting down...") await close_db() logger.info("Cleanup complete") def create_app() -> FastAPI: """创建 FastAPI 应用实例""" app = FastAPI( title=settings.APP_NAME, version=settings.APP_VERSION, description="美家卡智影 - AI 视频创作后端 API", docs_url="/docs" if settings.DEBUG else None, redoc_url="/redoc" if settings.DEBUG else None, lifespan=lifespan, ) # CORS 配置 # 开发环境下允许所有来源,避免跨域问题 allow_origins = ["*"] if settings.DEBUG else settings.cors_origins_list app.add_middleware( CORSMiddleware, allow_origins=allow_origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # 注册路由 app.include_router(api_router, prefix="/api/v1") # 全局异常处理(统一返回 ApiResponse 格式) @app.exception_handler(Exception) async def global_exception_handler(request, exc): """全局异常捕获""" logger.exception("Unhandled exception") return JSONResponse( status_code=500, content={ "code": 500, "message": "服务器内部错误", "data": None, "detail": {"error": str(exc)} if settings.DEBUG else None, }, ) # 健康检查 @app.get("/health", tags=["System"]) async def health_check(): """服务健康检查""" return ApiResponse( code=200, data={ "status": "healthy", "version": settings.APP_VERSION, "environment": settings.ENV, }, message="服务运行正常", ) # 根路由 @app.get("/", tags=["System"]) async def root(): """API 根路径""" return ApiResponse( code=200, data={ "name": settings.APP_NAME, "version": settings.APP_VERSION, "docs": "/docs" if settings.DEBUG else None, }, message="美家卡智影 API 服务", ) return app # 创建应用实例 app = create_app() def main(): """入口函数(用于命令行启动)""" uvicorn.run( "app.main:app", host=settings.HOST, port=settings.PORT, workers=settings.WORKERS if not settings.DEBUG else 1, reload=settings.DEBUG, log_level=settings.LOG_LEVEL.lower(), ) if __name__ == "__main__": main() # test