# =========================================== # 班级操行分管理系统 - 主入口 # # 开发者: Canglan # 联系方式: admin@sea-studio.top # 版权归属: Sea Network Technology Studio # 许可证: MIT License # # 版权所有 © Sea Network Technology Studio # =========================================== from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from contextlib import asynccontextmanager import traceback import uvicorn from config import settings from utils.logger import setup_logger, log_access from utils.database import init_db_pool, close_db_pool from utils.redis_client import init_redis_pool, close_redis_pool from middleware.auth_middleware import AuthMiddleware from routes import auth, student, parent, admin, subject, semester, debug # 设置日志 logger = setup_logger() @asynccontextmanager async def lifespan(app: FastAPI): """应用生命周期管理""" logger.info("正在启动应用...") await init_db_pool() await init_redis_pool() logger.info(f"CORS 允许域名: {settings.CORS_ORIGINS}") logger.info(f"{settings.APP_NAME} 启动完成") yield logger.info("正在关闭应用...") await close_db_pool() await close_redis_pool() logger.info("应用已关闭") # 创建FastAPI应用 app = FastAPI( title=settings.APP_NAME, version=settings.API_VERSION, debug=settings.DEBUG, lifespan=lifespan ) # 访问日志中间件 @app.middleware("http") async def access_log_middleware(request: Request, call_next): log_access(request) response = await call_next(request) return response # 认证中间件(先注册,后执行) app.add_middleware(AuthMiddleware) # CORS中间件(后注册,先执行)- 从环境变量读取允许的域名 cors_origins = settings.CORS_ORIGINS if not cors_origins: logger.warning("CORS_ORIGINS 未配置或为空,跨域请求将被拒绝!请检查 .env 文件中的 CORS_ORIGINS 配置") app.add_middleware( CORSMiddleware, allow_origins=cors_origins, allow_credentials=True, allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"], allow_headers=["*"], expose_headers=["*"], ) # 全局异常处理器 @app.exception_handler(Exception) async def global_exception_handler(request: Request, exc: Exception): """全局异常处理器 - 捕获所有未处理异常""" logger.error(f"未处理异常: {exc}", exc_info=True) # 获取origin用于CORS头 origin = request.headers.get("origin", "") allowed_origins = settings.CORS_ORIGINS or [] # 使用HTTP 200 + 业务错误码返回,避免CORS头丢失问题 # (FastAPI exception_handler返回的500响应可能不经过CORS中间件,导致跨域读取失败) headers = {} if origin in allowed_origins: headers["access-control-allow-origin"] = origin headers["access-control-allow-credentials"] = "true" headers["access-control-expose-headers"] = "*" return JSONResponse( status_code=200, content={ "success": False, "code": 500, "message": f"服务器内部错误: {str(exc)}", "detail": traceback.format_exc() if settings.DEBUG else None }, headers=headers ) # 注册路由 app.include_router(auth.router, prefix="/api/auth", tags=["认证"]) app.include_router(student.router, prefix="/api/student", tags=["学生端"]) app.include_router(parent.router, prefix="/api/parent", tags=["家长端"]) app.include_router(admin.router, prefix="/api/admin", tags=["管理端"]) app.include_router(subject.router, prefix="/api/subject", tags=["科目管理"]) app.include_router(semester.router, prefix="/api/semester", tags=["学期管理"]) app.include_router(debug.router, tags=["调试"]) @app.get("/") async def root(): return {"status": "ok", "message": f"{settings.APP_NAME} API 运行中"} @app.get("/health") async def health_check(): return {"status": "healthy"} if __name__ == "__main__": uvicorn.run( "main:app", host="0.0.0.0", port=8000, reload=settings.DEBUG )