app.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. """
  2. FastAPI应用服务器
  3. 整合所有路由和中间件
  4. """
  5. import sys
  6. import os
  7. # 导入路径配置
  8. try:
  9. import path_config # 自动配置 Python 路径
  10. except ImportError:
  11. # 手动配置路径(兼容性)
  12. sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../..'))
  13. sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../..'))
  14. from fastapi import FastAPI, Request, status
  15. from fastapi.middleware.cors import CORSMiddleware
  16. from fastapi.responses import JSONResponse
  17. from fastapi.exceptions import RequestValidationError
  18. from contextlib import asynccontextmanager
  19. from datetime import datetime, timezone
  20. import logging
  21. import logging.handlers
  22. # 导入配置
  23. from app.core.config import config_handler
  24. from app.base import init_db, close_db, init_redis, close_redis
  25. from app.core.exceptions import BaseAPIException
  26. from app.schemas.base import ResponseSchema
  27. from app.logger.config import configure_logging, get_logger
  28. # 配置全局日志(在导入其他模块之前)
  29. configure_logging(
  30. log_level=config_handler.get("admin_app", "LOG_LEVEL", "INFO").lower(),
  31. log_dir="logs",
  32. log_to_file=True,
  33. log_to_console=True
  34. )
  35. # 获取logger
  36. logger = get_logger("lqadmin_server")
  37. # 导入视图路由
  38. from views.system_view import router as system_router
  39. from views.oauth_view import router as oauth_router
  40. from views.sample_view import router as sample_router
  41. from views.auth_view import router as auth_router
  42. from views.knowledge_base_view import router as knowledge_base_router
  43. from views.snippet_view import router as snippet_router
  44. # 导入现有API路由
  45. from app.api.v1.api_router import api_router
  46. @asynccontextmanager
  47. async def lifespan(app: FastAPI):
  48. """应用生命周期管理"""
  49. # 启动时执行
  50. logger.info("=" * 60)
  51. logger.info("正在启动 LQAdminPlatform 服务...")
  52. logger.info("=" * 60)
  53. try:
  54. # 初始化数据库
  55. await init_db()
  56. logger.info("✅ 数据库初始化成功")
  57. except Exception as e:
  58. logger.error(f"❌ 数据库初始化失败: {e}")
  59. try:
  60. # 初始化Redis
  61. await init_redis()
  62. logger.info("✅ Redis初始化成功")
  63. except Exception as e:
  64. logger.warning(f"⚠️ Redis初始化失败: {e}")
  65. logger.info("=" * 60)
  66. logger.info(f"🚀 服务启动成功!")
  67. logger.info(f"📍 服务地址: http://{config_handler.get('admin_app', 'HOST', '0.0.0.0')}:{config_handler.get_int('admin_app', 'PORT', 8000)}")
  68. logger.info(f"📚 API文档: http://{config_handler.get('admin_app', 'HOST', '0.0.0.0')}:{config_handler.get_int('admin_app', 'PORT', 8000)}/docs")
  69. logger.info("=" * 60)
  70. yield
  71. # 关闭时执行
  72. logger.info("=" * 60)
  73. logger.info("正在关闭 LQAdminPlatform 服务...")
  74. logger.info("=" * 60)
  75. try:
  76. await close_db()
  77. logger.info("✅ 数据库连接已关闭")
  78. except Exception as e:
  79. logger.error(f"❌ 关闭数据库连接失败: {e}")
  80. try:
  81. await close_redis()
  82. logger.info("✅ Redis连接已关闭")
  83. except Exception as e:
  84. logger.warning(f"⚠️ 关闭Redis连接失败: {e}")
  85. logger.info("=" * 60)
  86. logger.info("👋 服务已关闭")
  87. logger.info("=" * 60)
  88. # 创建FastAPI应用实例
  89. app = FastAPI(
  90. title=config_handler.get("admin_app", "APP_NAME", "后台管理"),
  91. version=config_handler.get("admin_app", "APP_VERSION", "1.0.0"),
  92. description="OAuth2单点登录认证中心 - 统一管理平台",
  93. docs_url="/docs" if config_handler.get_bool("admin_app", "DEBUG", False) else None,
  94. redoc_url="/redoc" if config_handler.get_bool("admin_app", "DEBUG", False) else None,
  95. lifespan=lifespan
  96. )
  97. # 配置CORS - 允许前端跨域访问
  98. app.add_middleware(
  99. CORSMiddleware,
  100. allow_origins=[
  101. "http://localhost:8000",
  102. "http://localhost:3000",
  103. "http://localhost:3001",
  104. "http://localhost:5173", # Vite默认端口
  105. "http://localhost:8080",
  106. "http://127.0.0.1:3000",
  107. "http://127.0.0.1:3001",
  108. "http://127.0.0.1:5173",
  109. "http://127.0.0.1:8080",
  110. "*" # 临时允许所有,方便调试
  111. ],
  112. allow_credentials=True,
  113. allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
  114. allow_headers=["*"],
  115. )
  116. # --- 调试中间件 ---
  117. @app.middleware("http")
  118. async def log_requests(request: Request, call_next):
  119. # logger.info(f"收到请求: {request.method} {request.url}")
  120. try:
  121. response = await call_next(request)
  122. # logger.info(f"请求响应: {response.status_code}")
  123. return response
  124. except Exception as e:
  125. logger.error(f"请求处理异常: {e}")
  126. raise
  127. # ------------------
  128. # 全局异常处理
  129. @app.exception_handler(BaseAPIException)
  130. async def api_exception_handler(request: Request, exc: BaseAPIException):
  131. """处理自定义API异常"""
  132. logger.error(f"API异常: {exc.message} - {exc.details}")
  133. return JSONResponse(
  134. status_code=exc.status_code,
  135. content=ResponseSchema(
  136. code=exc.code,
  137. message=exc.message,
  138. data=exc.details,
  139. timestamp=datetime.now(timezone.utc)
  140. ).model_dump(mode='json')
  141. )
  142. @app.exception_handler(RequestValidationError)
  143. async def validation_exception_handler(request: Request, exc: RequestValidationError):
  144. """处理请求验证异常"""
  145. errors = []
  146. for error in exc.errors():
  147. errors.append({
  148. "field": ".".join(str(loc) for loc in error["loc"]),
  149. "message": error["msg"],
  150. "type": error["type"]
  151. })
  152. logger.warning(f"参数验证失败: {errors}")
  153. return JSONResponse(
  154. status_code=status.HTTP_400_BAD_REQUEST,
  155. content=ResponseSchema(
  156. code=100001,
  157. message="参数验证失败",
  158. data={"errors": errors},
  159. timestamp=datetime.now(timezone.utc)
  160. ).model_dump(mode='json')
  161. )
  162. @app.exception_handler(Exception)
  163. async def general_exception_handler(request: Request, exc: Exception):
  164. """处理通用异常"""
  165. logger.error(f"未处理的异常: {exc}", exc_info=True)
  166. return JSONResponse(
  167. status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
  168. content=ResponseSchema(
  169. code=500001,
  170. message="服务器内部错误" if not config_handler.get_bool("admin_app", "DEBUG", False) else str(exc),
  171. data=None,
  172. timestamp=datetime.now(timezone.utc)
  173. ).model_dump(mode='json')
  174. )
  175. # 健康检查端点
  176. @app.get("/health", tags=["系统"])
  177. async def health_check():
  178. """健康检查"""
  179. return ResponseSchema(
  180. code=0,
  181. message="服务正常运行",
  182. data={
  183. "status": "healthy",
  184. "version": config_handler.get("admin_app", "APP_VERSION", "1.0.0"),
  185. "timestamp": datetime.now(timezone.utc)
  186. }
  187. )
  188. # 根路径
  189. @app.get("/", tags=["系统"])
  190. async def root():
  191. """根路径"""
  192. return ResponseSchema(
  193. code=0,
  194. message="欢迎使用 LQAdminPlatform",
  195. data={
  196. "name": config_handler.get("admin_app", "APP_NAME", "后台管理"),
  197. "version": config_handler.get("admin_app", "APP_VERSION", "1.0.0"),
  198. "docs": "/docs" if config_handler.get_bool("admin_app", "DEBUG", False) else None,
  199. "modules": ["系统管理", "授权管理", "样本中心"]
  200. }
  201. )
  202. # 包含API路由
  203. # 现有的API路由(保持兼容)
  204. app.include_router(api_router, prefix="/api/v1")
  205. # 新的模块化视图路由
  206. app.include_router(system_router, prefix="/api/v1")
  207. app.include_router(oauth_router, prefix="")
  208. app.include_router(auth_router, prefix="/api/v1")
  209. app.include_router(sample_router, prefix="/api/v1")
  210. app.include_router(knowledge_base_router, prefix="/api/v1")
  211. app.include_router(snippet_router, prefix="/api/v1")
  212. def create_app() -> FastAPI:
  213. """创建并返回FastAPI应用实例"""
  214. return app
  215. if __name__ == "__main__":
  216. import uvicorn
  217. # 查找可用端口
  218. import socket
  219. def check_port(port):
  220. """检查端口是否可用"""
  221. with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
  222. try:
  223. s.bind(('localhost', port))
  224. return True
  225. except OSError:
  226. return False
  227. def find_available_port(start_port=8000, max_port=8010):
  228. """查找可用端口"""
  229. for port in range(start_port, max_port + 1):
  230. if check_port(port):
  231. return port
  232. return None
  233. # 获取配置的端口或查找可用端口
  234. port = config_handler.get_int("admin_app", "PORT", 8000)
  235. if not check_port(port):
  236. logger.warning(f"端口 {port} 已被占用,正在查找可用端口...")
  237. port = find_available_port(port, port + 10)
  238. if port:
  239. logger.info(f"找到可用端口: {port}")
  240. else:
  241. logger.error("未找到可用端口")
  242. sys.exit(1)
  243. # 启动服务器
  244. uvicorn.run(
  245. "app.server.app:app",
  246. host=config_handler.get("admin_app", "HOST", "0.0.0.0"),
  247. port=port,
  248. reload=config_handler.get_bool("admin_app", "RELOAD", False),
  249. log_level=config_handler.get("admin_app", "LOG_LEVEL", "INFO").lower()
  250. )