admin share
This commit is contained in:
parent
f110b26efc
commit
35f91a7d03
@ -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)):
|
||||
"""
|
||||
|
||||
Loading…
Reference in New Issue
Block a user