""" 管理员 API:用户管理 """ from fastapi import APIRouter, HTTPException, Depends, status from pydantic import BaseModel from typing import Optional, List from datetime import datetime, timezone, timedelta from app.core.supabase import get_supabase from app.core.deps import get_current_admin from loguru import logger router = APIRouter(prefix="/api/admin", tags=["管理"]) class UserListItem(BaseModel): id: str email: str username: Optional[str] role: str is_active: bool expires_at: Optional[str] created_at: str class ActivateRequest(BaseModel): expires_days: Optional[int] = None # 授权天数,None 表示永久 @router.get("/users", response_model=List[UserListItem]) async def list_users(admin: dict = Depends(get_current_admin)): """获取所有用户列表""" try: supabase = get_supabase() result = supabase.table("users").select("*").order("created_at", desc=True).execute() return [ UserListItem( id=u["id"], email=u["email"], username=u.get("username"), role=u["role"], is_active=u["is_active"], expires_at=u.get("expires_at"), created_at=u["created_at"] ) for u in result.data ] except Exception as e: logger.error(f"获取用户列表失败: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="获取用户列表失败" ) @router.post("/users/{user_id}/activate") async def activate_user( user_id: str, request: ActivateRequest, admin: dict = Depends(get_current_admin) ): """ 激活用户 Args: user_id: 用户 ID request.expires_days: 授权天数 (None 表示永久) """ try: supabase = get_supabase() # 计算过期时间 expires_at = None if request.expires_days: expires_at = (datetime.now(timezone.utc) + timedelta(days=request.expires_days)).isoformat() # 更新用户 result = supabase.table("users").update({ "is_active": True, "role": "user", "expires_at": expires_at }).eq("id", user_id).execute() if not result.data: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="用户不存在" ) logger.info(f"管理员 {admin['email']} 激活用户 {user_id}, 有效期: {request.expires_days or '永久'} 天") return { "success": True, "message": f"用户已激活,有效期: {request.expires_days or '永久'} 天" } except HTTPException: raise except Exception as e: logger.error(f"激活用户失败: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="激活用户失败" ) @router.post("/users/{user_id}/deactivate") async def deactivate_user( user_id: str, admin: dict = Depends(get_current_admin) ): """停用用户""" try: supabase = get_supabase() # 不能停用管理员 user_result = supabase.table("users").select("role").eq("id", user_id).single().execute() if user_result.data and user_result.data["role"] == "admin": raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="不能停用管理员账号" ) # 更新用户 result = supabase.table("users").update({ "is_active": False }).eq("id", user_id).execute() # 清除用户 session supabase.table("user_sessions").delete().eq("user_id", user_id).execute() logger.info(f"管理员 {admin['email']} 停用用户 {user_id}") return {"success": True, "message": "用户已停用"} except HTTPException: raise except Exception as e: logger.error(f"停用用户失败: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="停用用户失败" ) @router.post("/users/{user_id}/extend") async def extend_user( user_id: str, request: ActivateRequest, admin: dict = Depends(get_current_admin) ): """延长用户授权期限""" try: supabase = get_supabase() if not request.expires_days: # 设为永久 expires_at = None else: # 获取当前过期时间 user_result = supabase.table("users").select("expires_at").eq("id", user_id).single().execute() user = user_result.data if user and user.get("expires_at"): current_expires = datetime.fromisoformat(user["expires_at"].replace("Z", "+00:00")) base_time = max(current_expires, datetime.now(timezone.utc)) else: base_time = datetime.now(timezone.utc) expires_at = (base_time + timedelta(days=request.expires_days)).isoformat() result = supabase.table("users").update({ "expires_at": expires_at }).eq("id", user_id).execute() logger.info(f"管理员 {admin['email']} 延长用户 {user_id} 授权 {request.expires_days or '永久'} 天") return { "success": True, "message": f"授权已延长 {request.expires_days or '永久'} 天" } except Exception as e: logger.error(f"延长授权失败: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="延长授权失败" )