admin share

This commit is contained in:
朱潮 2026-02-01 21:48:23 +08:00
parent f110b26efc
commit 35f91a7d03

View File

@ -144,6 +144,34 @@ async def get_user_id_from_token(authorization: Optional[str]) -> Optional[str]:
# ============== 权限检查辅助函数 ==============
async def is_admin_user(authorization: Optional[str]) -> bool:
"""
检查当前请求是否来自管理员admin token is_admin=True 的用户
Args:
authorization: Authorization header
Returns:
bool: 是否是管理员
"""
admin_valid, _ = await verify_admin_auth(authorization)
if admin_valid:
return True
user_valid, user_id, _ = await verify_user_auth(authorization)
if not user_valid or not user_id:
return False
pool = get_db_pool_manager().pool
async with pool.connection() as conn:
async with conn.cursor() as cursor:
await cursor.execute("""
SELECT is_admin FROM agent_user WHERE id = %s
""", (user_id,))
row = await cursor.fetchone()
return row and row[0]
async def check_bot_access(bot_id: str, user_id: str, required_permission: str) -> bool:
"""
检查用户对 Bot 的访问权限
@ -2246,7 +2274,7 @@ async def search_users(
Returns:
List[UserSearchResponse]: 用户列表
"""
# 支持管理员认证用户认证
# 支持管理员认证<EFBFBD><EFBFBD>用户认证
admin_valid, _ = await verify_admin_auth(authorization)
user_valid, user_id, _ = await verify_user_auth(authorization)
@ -2295,6 +2323,441 @@ async def search_users(
]
# ============== 用户管理 API仅管理员=============
class UserListResponse(BaseModel):
"""用户列表响应"""
id: str
username: str
email: Optional[str] = None
is_admin: bool = False
is_active: bool = True
created_at: str
last_login: Optional[str] = None
class UserCreateRequest(BaseModel):
"""创建用户请求"""
email: str
username: Optional[str] = None
password: str
is_admin: bool = False
class UserUpdateRequest(BaseModel):
"""更新用户请求"""
email: Optional[str] = None
username: Optional[str] = None
class ResetPasswordRequest(BaseModel):
"""重置密码请求"""
new_password: str
@router.get("/api/v1/users/list", response_model=List[UserListResponse])
async def get_all_users(authorization: Optional[str] = Header(None)):
"""
获取所有用户列表<EFBFBD><EFBFBD>管理员
Args:
authorization: Bearer token
Returns:
List[UserListResponse]: 用户列表
"""
if not await is_admin_user(authorization):
raise HTTPException(
status_code=403,
detail="Admin access required"
)
pool = get_db_pool_manager().pool
async with pool.connection() as conn:
async with conn.cursor() as cursor:
await cursor.execute("""
SELECT id, username, email, is_admin, is_active, created_at, last_login
FROM agent_user
ORDER BY created_at DESC
""")
rows = await cursor.fetchall()
return [
UserListResponse(
id=str(row[0]),
username=row[1],
email=row[2],
is_admin=row[3] or False,
is_active=row[4],
created_at=row[5].isoformat() if row[5] else "",
last_login=row[6].isoformat() if row[6] else None
)
for row in rows
]
@router.get("/api/v1/users/{user_id}", response_model=UserListResponse)
async def get_user(user_id: str, authorization: Optional[str] = Header(None)):
"""
获取单个用户信息仅管理员
Args:
user_id: 用户 ID
authorization: Bearer token
Returns:
UserListResponse: 用户信息
"""
if not await is_admin_user(authorization):
raise HTTPException(
status_code=403,
detail="Admin access required"
)
pool = get_db_pool_manager().pool
async with pool.connection() as conn:
async with conn.cursor() as cursor:
await cursor.execute("""
SELECT id, username, email, is_admin, is_active, created_at, last_login
FROM agent_user
WHERE id = %s
""", (user_id,))
row = await cursor.fetchone()
if not row:
raise HTTPException(
status_code=404,
detail="User not found"
)
return UserListResponse(
id=str(row[0]),
username=row[1],
email=row[2],
is_admin=row[3] or False,
is_active=row[4],
created_at=row[5].isoformat() if row[5] else "",
last_login=row[6].isoformat() if row[6] else None
)
@router.post("/api/v1/users", response_model=UserListResponse)
async def create_user(
request: UserCreateRequest,
authorization: Optional[str] = Header(None)
):
"""
创建新用户仅管理员
Args:
request: 创建用户请求
authorization: Bearer token
Returns:
UserListResponse: 创建的用户信息
"""
if not await is_admin_user(authorization):
raise HTTPException(
status_code=403,
detail="Admin access required"
)
pool = get_db_pool_manager().pool
async with pool.connection() as conn:
async with conn.cursor() as cursor:
# 如果提供了用户名,检查是否已存在
if request.username:
await cursor.execute("""
SELECT id FROM agent_user WHERE username = %s
""", (request.username,))
if await cursor.fetchone():
raise HTTPException(
status_code=400,
detail="用户名已存在"
)
# 检查邮箱是否已存在
await cursor.execute("""
SELECT id FROM agent_user WHERE email = %s
""", (request.email,))
if await cursor.fetchone():
raise HTTPException(
status_code=400,
detail="邮箱已被注册"
)
# 创建用户
password_hash = hash_password(request.password)
username = request.username or request.email.split('@')[0]
await cursor.execute("""
INSERT INTO agent_user (username, email, password_hash, is_admin)
VALUES (%s, %s, %s, %s)
RETURNING id, created_at
""", (username, request.email, password_hash, request.is_admin))
user_id, created_at = await cursor.fetchone()
await conn.commit()
return UserListResponse(
id=str(user_id),
username=username,
email=request.email,
is_admin=request.is_admin,
is_active=True,
created_at=created_at.isoformat() if created_at else "",
last_login=None
)
@router.put("/api/v1/users/{user_id}", response_model=UserListResponse)
async def update_user(
user_id: str,
request: UserUpdateRequest,
authorization: Optional[str] = Header(None)
):
"""
更新用户信息仅管理员
Args:
user_id: 用户 ID
request: 更新请求
authorization: Bearer token
Returns:
UserListResponse: 更新后的用户信息
"""
if not await is_admin_user(authorization):
raise HTTPException(
status_code=403,
detail="Admin access required"
)
pool = get_db_pool_manager().pool
async with pool.connection() as conn:
async with conn.cursor() as cursor:
# 构建更新字段
update_fields = []
values = []
if request.username is not None:
# 检查用户名是否已被其他用户使用
await cursor.execute("""
SELECT id FROM agent_user WHERE username = %s AND id != %s
""", (request.username, user_id))
if await cursor.fetchone():
raise HTTPException(
status_code=400,
detail="用户名已存在"
)
update_fields.append("username = %s")
values.append(request.username)
if request.email is not None:
# 检查邮箱是否已被其他用户使用
await cursor.execute("""
SELECT id FROM agent_user WHERE email = %s AND id != %s
""", (request.email, user_id))
if await cursor.fetchone():
raise HTTPException(
status_code=400,
detail="邮箱已被使用"
)
update_fields.append("email = %s")
values.append(request.email)
if not update_fields:
raise HTTPException(
status_code=400,
detail="No fields to update"
)
update_fields.append("updated_at = NOW()")
values.append(user_id)
await cursor.execute(f"""
UPDATE agent_user
SET {', '.join(update_fields)}
WHERE id = %s
RETURNING id, username, email, is_admin, is_active, created_at, last_login
""", values)
row = await cursor.fetchone()
if not row:
raise HTTPException(
status_code=404,
detail="User not found"
)
await conn.commit()
return UserListResponse(
id=str(row[0]),
username=row[1],
email=row[2],
is_admin=row[3] or False,
is_active=row[4],
created_at=row[5].isoformat() if row[5] else "",
last_login=row[6].isoformat() if row[6] else None
)
@router.delete("/api/v1/users/{user_id}", response_model=SuccessResponse)
async def delete_user(user_id: str, authorization: Optional[str] = Header(None)):
"""
删除用户仅管理员
Args:
user_id: 用户 ID
authorization: Bearer token
Returns:
SuccessResponse: 删除结果
"""
if not await is_admin_user(authorization):
raise HTTPException(
status_code=403,
detail="Admin access required"
)
# 不允许删除默认 admin 用户
if user_id == '00000000-0000-0000-0000-000000000001':
raise HTTPException(
status_code=400,
detail="Cannot delete default admin user"
)
pool = get_db_pool_manager().pool
async with pool.connection() as conn:
async with conn.cursor() as cursor:
await cursor.execute("DELETE FROM agent_user WHERE id = %s RETURNING username", (user_id,))
row = await cursor.fetchone()
if not row:
raise HTTPException(
status_code=404,
detail="User not found"
)
await conn.commit()
return SuccessResponse(
success=True,
message=f"User '{row[0]}' deleted successfully"
)
@router.patch("/api/v1/users/{user_id}/toggle-admin", response_model=SuccessResponse)
async def toggle_user_admin(user_id: str, authorization: Optional[str] = Header(None)):
"""
切换用户管理员状态仅管理员
Args:
user_id: 用户 ID
authorization: Bearer token
Returns:
SuccessResponse: 操作结果
"""
if not await is_admin_user(authorization):
raise HTTPException(
status_code=403,
detail="Admin access required"
)
# 不允许取消默认 admin 的管理员权限
if user_id == '00000000-0000-0000-0000-000000000001':
raise HTTPException(
status_code=400,
detail="Cannot modify default admin user"
)
pool = get_db_pool_manager().pool
async with pool.connection() as conn:
async with conn.cursor() as cursor:
await cursor.execute("""
UPDATE agent_user
SET is_admin = NOT is_admin,
updated_at = NOW()
WHERE id = %s
RETURNING is_admin, username
""", (user_id,))
row = await cursor.fetchone()
if not row:
raise HTTPException(
status_code=404,
detail="User not found"
)
new_status, username = row
await conn.commit()
return SuccessResponse(
success=True,
message=f"User '{username}' is now {'an admin' if new_status else 'a regular user'}"
)
@router.post("/api/v1/users/{user_id}/reset-password", response_model=SuccessResponse)
async def reset_user_password(
user_id: str,
request: ResetPasswordRequest,
authorization: Optional[str] = Header(None)
):
"""
重置用户密码仅管理员
Args:
user_id: 用户 ID
request: 重置密码请求
authorization: Bearer token
Returns:
SuccessResponse: 操作结果
"""
if not await is_admin_user(authorization):
raise HTTPException(
status_code=403,
detail="Admin access required"
)
pool = get_db_pool_manager().pool
async with pool.connection() as conn:
async with conn.cursor() as cursor:
password_hash = hash_password(request.new_password)
await cursor.execute("""
UPDATE agent_user
SET password_hash = %s,
updated_at = NOW()
WHERE id = %s
RETURNING username
""", (password_hash, user_id))
row = await cursor.fetchone()
if not row:
raise HTTPException(
status_code=404,
detail="User not found"
)
await conn.commit()
return SuccessResponse(
success=True,
message=f"Password reset for user '{row[0]}'"
)
@router.post("/api/v1/auth/logout", response_model=SuccessResponse)
async def user_logout(authorization: Optional[str] = Header(None)):
"""