diff --git a/backend/app/api/auth.py b/backend/app/api/auth.py index 34d2d62..32bfd2c 100644 --- a/backend/app/api/auth.py +++ b/backend/app/api/auth.py @@ -11,7 +11,7 @@ from app.models import User from app.models.password_reset import PasswordResetToken from app.schemas import ( - LoginRequest, Token, UserCreate, UserOut, UserInfoResponse, + LoginRequest, Token, UserCreate, UserUpdate, UserOut, UserInfoResponse, InitialSetupRequest, PasswordResetRequest, PasswordResetConfirm, SystemStatusResponse, ) @@ -245,6 +245,102 @@ def list_all_users( return users +@router.patch("/users/{user_id}", response_model=UserOut) +def update_user( + user_id: int, + user_update: UserUpdate, + current_user: User = Depends(get_current_active_superuser), + db: Session = Depends(get_db), +): + """ + Update a user's information. Only superusers can update users. + """ + target_user = db.query(User).filter(User.id == user_id).first() + if not target_user: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="用户不存在", + ) + + # Prevent demoting yourself + if user_update.is_superuser is False and target_user.id == current_user.id: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="不能取消自己的超级管理员权限", + ) + + # Prevent demoting the last superuser + if user_update.is_superuser is False and target_user.is_superuser: + superuser_count = db.query(User).filter( + User.is_superuser == True, User.is_default_admin == False # noqa: E712 + ).count() + if superuser_count <= 1: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="不能取消最后一个超级管理员的权限", + ) + + # Check email uniqueness if updating email + if user_update.email is not None: + existing = db.query(User).filter( + User.email == user_update.email, User.id != user_id + ).first() + if existing: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="该邮箱已被其他用户使用", + ) + + # Apply updates + update_data = user_update.model_dump(exclude_unset=True) + if "password" in update_data: + target_user.hashed_password = get_password_hash(update_data.pop("password")) + for field, value in update_data.items(): + setattr(target_user, field, value) + + db.commit() + db.refresh(target_user) + return target_user + + +@router.delete("/users/{user_id}", status_code=status.HTTP_204_NO_CONTENT) +def delete_user( + user_id: int, + current_user: User = Depends(get_current_active_superuser), + db: Session = Depends(get_db), +): + """ + Delete a user. Only superusers can delete users. + """ + target_user = db.query(User).filter(User.id == user_id).first() + if not target_user: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="用户不存在", + ) + + # Cannot delete yourself + if target_user.id == current_user.id: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="不能删除自己的账号", + ) + + # Cannot delete the last superuser + if target_user.is_superuser: + superuser_count = db.query(User).filter( + User.is_superuser == True, User.is_default_admin == False # noqa: E712 + ).count() + if superuser_count <= 1: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="不能删除最后一个超级管理员", + ) + + db.delete(target_user) + db.commit() + + @router.post("/password-reset/request", status_code=status.HTTP_200_OK) def request_password_reset( reset_request: PasswordResetRequest, diff --git a/backend/app/schemas/user.py b/backend/app/schemas/user.py index d021db7..08e7dc7 100644 --- a/backend/app/schemas/user.py +++ b/backend/app/schemas/user.py @@ -20,6 +20,7 @@ class UserUpdate(BaseModel): full_name: Optional[str] = None password: Optional[str] = Field(None, min_length=6, max_length=100) is_active: Optional[bool] = None + is_superuser: Optional[bool] = None class UserOut(UserBase): diff --git a/frontend/public/logo.svg b/frontend/public/logo.svg deleted file mode 100644 index a899ad7..0000000 --- a/frontend/public/logo.svg +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/api/auth.ts b/frontend/src/api/auth.ts index e185372..03cf913 100644 --- a/frontend/src/api/auth.ts +++ b/frontend/src/api/auth.ts @@ -83,3 +83,17 @@ export async function listAllUsers(): Promise { const { data } = await apiClient.get('/auth/users') return data } + +export async function updateUser(userId: number, userData: { + email?: string + full_name?: string + is_superuser?: boolean + is_active?: boolean +}): Promise { + const { data } = await apiClient.patch(`/auth/users/${userId}`, userData) + return data +} + +export async function deleteUser(userId: number): Promise { + await apiClient.delete(`/auth/users/${userId}`) +} diff --git a/frontend/src/views/Login.vue b/frontend/src/views/Login.vue index c72250c..c63dc6e 100644 --- a/frontend/src/views/Login.vue +++ b/frontend/src/views/Login.vue @@ -164,6 +164,9 @@ const handleLogin = async () => { height: auto; margin: 0 auto 16px; display: block; + background-color: #fff; + border-radius: 8px; + padding: 8px; } h2 { diff --git a/frontend/src/views/UserManage.vue b/frontend/src/views/UserManage.vue index fed0d09..af9d029 100644 --- a/frontend/src/views/UserManage.vue +++ b/frontend/src/views/UserManage.vue @@ -29,6 +29,17 @@ + + + @@ -71,21 +82,64 @@ 注册 + + + + + + + + + + + + + + + + + + + 超级管理员 + + + + +