""" 依赖注入模块:认证和用户获取 """ from typing import Optional from fastapi import Request, HTTPException, Depends, status from app.core.security import decode_access_token, TokenData from app.core.supabase import get_supabase from loguru import logger async def get_token_from_cookie(request: Request) -> Optional[str]: """从 Cookie 中获取 Token""" return request.cookies.get("access_token") async def get_current_user_optional( request: Request ) -> Optional[dict]: """ 获取当前用户 (可选,未登录返回 None) """ token = await get_token_from_cookie(request) if not token: return None token_data = decode_access_token(token) if not token_data: return None # 验证 session_token 是否有效 (单设备登录检查) try: supabase = get_supabase() result = supabase.table("user_sessions").select("*").eq( "user_id", token_data.user_id ).eq( "session_token", token_data.session_token ).execute() if not result.data: logger.warning(f"Session token 无效: user_id={token_data.user_id}") return None # 获取用户信息 user_result = supabase.table("users").select("*").eq( "id", token_data.user_id ).single().execute() return user_result.data except Exception as e: logger.error(f"获取用户信息失败: {e}") return None async def get_current_user( request: Request ) -> dict: """ 获取当前用户 (必须登录) Raises: HTTPException 401: 未登录 HTTPException 403: 会话失效或授权过期 """ token = await get_token_from_cookie(request) if not token: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="未登录,请先登录" ) token_data = decode_access_token(token) if not token_data: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Token 无效或已过期" ) try: supabase = get_supabase() # 验证 session_token (单设备登录) session_result = supabase.table("user_sessions").select("*").eq( "user_id", token_data.user_id ).eq( "session_token", token_data.session_token ).execute() if not session_result.data: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="会话已失效,请重新登录(可能已在其他设备登录)" ) # 获取用户信息 user_result = supabase.table("users").select("*").eq( "id", token_data.user_id ).single().execute() user = user_result.data if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="用户不存在" ) # 检查授权是否过期 if user.get("expires_at"): from datetime import datetime, timezone expires_at = datetime.fromisoformat(user["expires_at"].replace("Z", "+00:00")) if datetime.now(timezone.utc) > expires_at: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="授权已过期,请联系管理员续期" ) return user except HTTPException: raise except Exception as e: logger.error(f"获取用户信息失败: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="服务器错误" ) async def get_current_admin( current_user: dict = Depends(get_current_user) ) -> dict: """ 获取当前管理员用户 Raises: HTTPException 403: 非管理员 """ if current_user.get("role") != "admin": raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="需要管理员权限" ) return current_user