| |
| """ |
| FastAPI 依赖注入模块。 |
| 定义了用于在请求处理函数中获取共享资源(如数据库会话、Key 管理器、HTTP 客户端) |
| 以及执行认证/授权检查的依赖项函数。 |
| 这有助于解耦代码并提高可测试性。 |
| """ |
| from fastapi import Request, status, Depends, Security, HTTPException |
| from app.core.keys.manager import APIKeyManager |
| import httpx |
| from sqlalchemy.ext.asyncio import AsyncSession |
| from typing import AsyncGenerator, Optional |
| from app.core.cache.manager import CacheManager |
| from fastapi.security import APIKeyHeader, HTTPBearer, HTTPAuthorizationCredentials |
| import os |
| import logging |
|
|
| logger = logging.getLogger('my_logger') |
|
|
| |
|
|
| def get_key_manager(request: Request) -> APIKeyManager: |
| """ |
| FastAPI 依赖项函数,用于从应用状态 (`request.app.state`) 中获取共享的 `APIKeyManager` 实例。 |
| 该实例通常在应用启动时(lifespan 事件)创建并存储在 app.state 中。 |
| |
| Args: |
| request (Request): FastAPI 请求对象,用于访问应用状态。 |
| |
| Returns: |
| APIKeyManager: 共享的 APIKeyManager 实例。 |
| """ |
| |
| key_manager = getattr(request.app.state, 'key_manager', None) |
| if key_manager is None: |
| |
| logger.error("无法从 app.state 获取 APIKeyManager 实例!请检查 lifespan 配置。") |
| raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="服务器内部错误:Key 管理器未初始化") |
| return key_manager |
|
|
| def get_http_client(request: Request) -> httpx.AsyncClient: |
| """ |
| FastAPI 依赖项函数,用于从应用状态 (`request.app.state`) 中获取共享的 `httpx.AsyncClient` 实例。 |
| 该实例通常在应用启动时创建,用于执行异步 HTTP 请求。 |
| |
| Args: |
| request (Request): FastAPI 请求对象。 |
| |
| Returns: |
| httpx.AsyncClient: 共享的异步 HTTP 客户端实例。 |
| """ |
| |
| http_client = getattr(request.app.state, 'http_client', None) |
| if http_client is None: |
| |
| logger.error("无法从 app.state 获取 httpx.AsyncClient 实例!请检查 lifespan 配置。") |
| raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="服务器内部错误:HTTP 客户端未初始化") |
| return http_client |
|
|
| async def get_db_session(request: Request) -> AsyncGenerator[AsyncSession, None]: |
| """ |
| FastAPI 依赖项函数,用于获取一个 SQLAlchemy 异步数据库会话 (`AsyncSession`)。 |
| 此函数使用在应用启动时创建并存储在 `app.state` 中的异步会话工厂 (`AsyncSessionFactory`) |
| 来为每个请求创建一个独立的数据库会话。 |
| 使用 `yield` 将会话提供给路径操作函数,并在请求处理完成后(无论成功或失败)确保会话被关闭。 |
| 如果在路径操作函数中使用会话时发生异常,会自动执行回滚。 |
| |
| Args: |
| request (Request): FastAPI 请求对象。 |
| |
| Yields: |
| AsyncSession: 一个新的 SQLAlchemy 异步数据库会话实例。 |
| |
| Raises: |
| HTTPException: 如果无法从 app.state 获取会话工厂。 |
| """ |
| |
| AsyncSessionFactory = getattr(request.app.state, 'AsyncSessionFactory', None) |
| if AsyncSessionFactory is None: |
| |
| logger.error("无法从 app.state 获取 AsyncSessionFactory!请检查 lifespan 配置。") |
| raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="服务器内部错误:数据库会话工厂未初始化") |
|
|
| |
| session: AsyncSession = AsyncSessionFactory() |
| logger.debug(f"数据库会话 {id(session)} 已创建。") |
| try: |
| |
| yield session |
| |
| |
| except Exception as e: |
| |
| logger.error(f"数据库会话 {id(session)} 中发生异常,执行回滚: {e}", exc_info=True) |
| await session.rollback() |
| raise e |
| finally: |
| |
| logger.debug(f"关闭数据库会话 {id(session)}...") |
| await session.close() |
|
|
| def get_cache_manager(request: Request) -> CacheManager: |
| """ |
| FastAPI 依赖项函数,用于从应用状态 (`request.app.state`) 中获取共享的 `CacheManager` 实例。 |
| 该实例通常在应用启动时创建。 |
| |
| Args: |
| request (Request): FastAPI 请求对象。 |
| |
| Returns: |
| CacheManager: 共享的 CacheManager 实例。 |
| |
| Raises: |
| HTTPException: 如果无法从 app.state 获取 CacheManager 实例。 |
| """ |
| |
| cache_manager = getattr(request.app.state, 'cache_manager', None) |
| if cache_manager is None: |
| |
| logger.error("无法从 app.state 获取 CacheManager 实例!请检查 lifespan 配置。") |
| raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="服务器内部错误:缓存管理器未初始化") |
| return cache_manager |
|
|
| |
|
|
| |
| |
| |
| admin_token_header = APIKeyHeader(name="X-Admin-Token", auto_error=False) |
|
|
| async def verify_admin_token(admin_token: Optional[str] = Security(admin_token_header)): |
| """ |
| FastAPI 依赖项函数,用于验证管理员令牌。 |
| 从请求头 'X-Admin-Token' 中获取令牌,并与环境变量 `ADMIN_TOKEN` 进行比较。 |
| |
| Args: |
| admin_token (Optional[str]): 通过 `Security(admin_token_header)` 从请求头注入的令牌值。 |
| 如果请求头不存在,则为 None。 |
| |
| Returns: |
| bool: 如果验证通过(令牌匹配或环境变量未设置),返回 True。 |
| |
| Raises: |
| HTTPException (401 Unauthorized): 如果令牌不匹配或缺失(且环境变量已设置)。 |
| """ |
| |
| admin_token_from_env = os.environ.get("ADMIN_TOKEN") |
|
|
| |
| if admin_token_from_env is None: |
| logger.warning("环境变量 ADMIN_TOKEN 未设置,管理员接口将允许无令牌访问。") |
| return True |
|
|
| |
| |
| if admin_token and admin_token == admin_token_from_env: |
| logger.debug("管理员令牌验证通过。") |
| return True |
|
|
| |
| logger.warning(f"管理员令牌验证失败。请求提供令牌: {'***' if admin_token else '无'}") |
| |
| raise HTTPException( |
| status_code=status.HTTP_401_UNAUTHORIZED, |
| detail="无效或缺失的管理员令牌", |
| ) |
|
|
| |
| |
|
|