| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- """
- FastAPI应用服务器
- 整合所有路由和中间件
- """
- import sys
- import os
- # 导入路径配置
- try:
- import path_config # 自动配置 Python 路径
- except ImportError:
- # 手动配置路径(兼容性)
- sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../..'))
- sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../..'))
- from fastapi import FastAPI, Request, status
- from fastapi.middleware.cors import CORSMiddleware
- from fastapi.responses import JSONResponse
- from fastapi.exceptions import RequestValidationError
- from contextlib import asynccontextmanager
- from datetime import datetime, timezone
- import logging
- import logging.handlers
- # 导入配置
- from app.core.config import config_handler
- from app.base import init_db, close_db, init_redis, close_redis
- from app.core.exceptions import BaseAPIException
- from app.schemas.base import ResponseSchema
- from app.logger.config import configure_logging, get_logger
- # 配置全局日志(在导入其他模块之前)
- configure_logging(
- log_level=config_handler.get("admin_app", "LOG_LEVEL", "INFO").lower(),
- log_dir="logs",
- log_to_file=True,
- log_to_console=True
- )
- # 获取logger
- logger = get_logger("lqadmin_server")
- # 导入视图路由
- from views.system_view import router as system_router
- from views.oauth_view import router as oauth_router
- from views.sample_view import router as sample_router
- from views.auth_view import router as auth_router
- from views.knowledge_base_view import router as knowledge_base_router
- from views.snippet_view import router as snippet_router
- # 导入现有API路由
- from app.api.v1.api_router import api_router
- @asynccontextmanager
- async def lifespan(app: FastAPI):
- """应用生命周期管理"""
- # 启动时执行
- logger.info("=" * 60)
- logger.info("正在启动 LQAdminPlatform 服务...")
- logger.info("=" * 60)
-
- try:
- # 初始化数据库
- await init_db()
- logger.info("✅ 数据库初始化成功")
- except Exception as e:
- logger.error(f"❌ 数据库初始化失败: {e}")
-
- try:
- # 初始化Redis
- await init_redis()
- logger.info("✅ Redis初始化成功")
- except Exception as e:
- logger.warning(f"⚠️ Redis初始化失败: {e}")
-
- logger.info("=" * 60)
- logger.info(f"🚀 服务启动成功!")
- logger.info(f"📍 服务地址: http://{config_handler.get('admin_app', 'HOST', '0.0.0.0')}:{config_handler.get_int('admin_app', 'PORT', 8000)}")
- logger.info(f"📚 API文档: http://{config_handler.get('admin_app', 'HOST', '0.0.0.0')}:{config_handler.get_int('admin_app', 'PORT', 8000)}/docs")
- logger.info("=" * 60)
-
- yield
-
- # 关闭时执行
- logger.info("=" * 60)
- logger.info("正在关闭 LQAdminPlatform 服务...")
- logger.info("=" * 60)
-
- try:
- await close_db()
- logger.info("✅ 数据库连接已关闭")
- except Exception as e:
- logger.error(f"❌ 关闭数据库连接失败: {e}")
-
- try:
- await close_redis()
- logger.info("✅ Redis连接已关闭")
- except Exception as e:
- logger.warning(f"⚠️ 关闭Redis连接失败: {e}")
-
- logger.info("=" * 60)
- logger.info("👋 服务已关闭")
- logger.info("=" * 60)
- # 创建FastAPI应用实例
- app = FastAPI(
- title=config_handler.get("admin_app", "APP_NAME", "后台管理"),
- version=config_handler.get("admin_app", "APP_VERSION", "1.0.0"),
- description="OAuth2单点登录认证中心 - 统一管理平台",
- docs_url="/docs" if config_handler.get_bool("admin_app", "DEBUG", False) else None,
- redoc_url="/redoc" if config_handler.get_bool("admin_app", "DEBUG", False) else None,
- lifespan=lifespan
- )
- # 配置CORS - 允许前端跨域访问
- app.add_middleware(
- CORSMiddleware,
- allow_origins=[
- "http://localhost:8000",
- "http://localhost:3000",
- "http://localhost:3001",
- "http://localhost:5173", # Vite默认端口
- "http://localhost:8080",
- "http://127.0.0.1:3000",
- "http://127.0.0.1:3001",
- "http://127.0.0.1:5173",
- "http://127.0.0.1:8080",
- "*" # 临时允许所有,方便调试
- ],
- allow_credentials=True,
- allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
- allow_headers=["*"],
- )
- # --- 调试中间件 ---
- @app.middleware("http")
- async def log_requests(request: Request, call_next):
- # logger.info(f"收到请求: {request.method} {request.url}")
- try:
- response = await call_next(request)
- # logger.info(f"请求响应: {response.status_code}")
- return response
- except Exception as e:
- logger.error(f"请求处理异常: {e}")
- raise
- # ------------------
- # 全局异常处理
- @app.exception_handler(BaseAPIException)
- async def api_exception_handler(request: Request, exc: BaseAPIException):
- """处理自定义API异常"""
- logger.error(f"API异常: {exc.message} - {exc.details}")
- return JSONResponse(
- status_code=exc.status_code,
- content=ResponseSchema(
- code=exc.code,
- message=exc.message,
- data=exc.details,
- timestamp=datetime.now(timezone.utc)
- ).model_dump(mode='json')
- )
- @app.exception_handler(RequestValidationError)
- async def validation_exception_handler(request: Request, exc: RequestValidationError):
- """处理请求验证异常"""
- errors = []
- for error in exc.errors():
- errors.append({
- "field": ".".join(str(loc) for loc in error["loc"]),
- "message": error["msg"],
- "type": error["type"]
- })
-
- logger.warning(f"参数验证失败: {errors}")
-
- return JSONResponse(
- status_code=status.HTTP_400_BAD_REQUEST,
- content=ResponseSchema(
- code=100001,
- message="参数验证失败",
- data={"errors": errors},
- timestamp=datetime.now(timezone.utc)
- ).model_dump(mode='json')
- )
- @app.exception_handler(Exception)
- async def general_exception_handler(request: Request, exc: Exception):
- """处理通用异常"""
- logger.error(f"未处理的异常: {exc}", exc_info=True)
-
- return JSONResponse(
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
- content=ResponseSchema(
- code=500001,
- message="服务器内部错误" if not config_handler.get_bool("admin_app", "DEBUG", False) else str(exc),
- data=None,
- timestamp=datetime.now(timezone.utc)
- ).model_dump(mode='json')
- )
- # 健康检查端点
- @app.get("/health", tags=["系统"])
- async def health_check():
- """健康检查"""
- return ResponseSchema(
- code=0,
- message="服务正常运行",
- data={
- "status": "healthy",
- "version": config_handler.get("admin_app", "APP_VERSION", "1.0.0"),
- "timestamp": datetime.now(timezone.utc)
- }
- )
- # 根路径
- @app.get("/", tags=["系统"])
- async def root():
- """根路径"""
- return ResponseSchema(
- code=0,
- message="欢迎使用 LQAdminPlatform",
- data={
- "name": config_handler.get("admin_app", "APP_NAME", "后台管理"),
- "version": config_handler.get("admin_app", "APP_VERSION", "1.0.0"),
- "docs": "/docs" if config_handler.get_bool("admin_app", "DEBUG", False) else None,
- "modules": ["系统管理", "授权管理", "样本中心"]
- }
- )
- # 包含API路由
- # 现有的API路由(保持兼容)
- app.include_router(api_router, prefix="/api/v1")
- # 新的模块化视图路由
- app.include_router(system_router, prefix="/api/v1")
- app.include_router(oauth_router, prefix="")
- app.include_router(auth_router, prefix="/api/v1")
- app.include_router(sample_router, prefix="/api/v1")
- app.include_router(knowledge_base_router, prefix="/api/v1")
- app.include_router(snippet_router, prefix="/api/v1")
- def create_app() -> FastAPI:
- """创建并返回FastAPI应用实例"""
- return app
- if __name__ == "__main__":
- import uvicorn
-
- # 查找可用端口
- import socket
-
- def check_port(port):
- """检查端口是否可用"""
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
- try:
- s.bind(('localhost', port))
- return True
- except OSError:
- return False
-
- def find_available_port(start_port=8000, max_port=8010):
- """查找可用端口"""
- for port in range(start_port, max_port + 1):
- if check_port(port):
- return port
- return None
-
- # 获取配置的端口或查找可用端口
- port = config_handler.get_int("admin_app", "PORT", 8000)
- if not check_port(port):
- logger.warning(f"端口 {port} 已被占用,正在查找可用端口...")
- port = find_available_port(port, port + 10)
- if port:
- logger.info(f"找到可用端口: {port}")
- else:
- logger.error("未找到可用端口")
- sys.exit(1)
-
- # 启动服务器
- uvicorn.run(
- "app.server.app:app",
- host=config_handler.get("admin_app", "HOST", "0.0.0.0"),
- port=port,
- reload=config_handler.get_bool("admin_app", "RELOAD", False),
- log_level=config_handler.get("admin_app", "LOG_LEVEL", "INFO").lower()
- )
|