users.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. """
  2. Superadmin endpoints for user management.
  3. """
  4. from typing import Annotated
  5. from fastapi import APIRouter, Depends, HTTPException, Query, status
  6. from pydantic import BaseModel
  7. from sqlalchemy.ext.asyncio import AsyncSession
  8. from app.api.deps import get_current_superadmin
  9. from app.core.database import get_db
  10. from app.models.user import User
  11. from app.schemas.user import UserCreate, UserListResponse, UserResponse, UserUpdate
  12. from app.services import user_service
  13. router = APIRouter()
  14. class ChangePasswordRequest(BaseModel):
  15. """Request schema for changing user password."""
  16. new_password: str
  17. @router.get("", response_model=UserListResponse)
  18. async def list_users(
  19. db: Annotated[AsyncSession, Depends(get_db)],
  20. current_user: Annotated[User, Depends(get_current_superadmin)],
  21. skip: int = Query(0, ge=0, description="Number of records to skip"),
  22. limit: int = Query(100, ge=1, le=1000, description="Max records to return"),
  23. organization_id: int | None = Query(None, description="Filter by organization"),
  24. role: str | None = Query(None, description="Filter by role"),
  25. status: str | None = Query(None, description="Filter by status"),
  26. ):
  27. """
  28. List all users (superadmin only).
  29. Returns paginated list of users with optional filters.
  30. """
  31. users, total = await user_service.list_users(
  32. db,
  33. skip=skip,
  34. limit=limit,
  35. organization_id=organization_id,
  36. role=role,
  37. status=status,
  38. )
  39. return UserListResponse(
  40. users=users,
  41. total=total,
  42. )
  43. @router.get("/{user_id}", response_model=UserResponse)
  44. async def get_user(
  45. user_id: int,
  46. db: Annotated[AsyncSession, Depends(get_db)],
  47. current_user: Annotated[User, Depends(get_current_superadmin)],
  48. ):
  49. """
  50. Get user by ID (superadmin only).
  51. """
  52. user = await user_service.get_user(db, user_id)
  53. if not user:
  54. raise HTTPException(
  55. status_code=status.HTTP_404_NOT_FOUND,
  56. detail="User not found",
  57. )
  58. return user
  59. @router.post("", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
  60. async def create_user(
  61. data: UserCreate,
  62. db: Annotated[AsyncSession, Depends(get_db)],
  63. current_user: Annotated[User, Depends(get_current_superadmin)],
  64. ):
  65. """
  66. Create a new user (superadmin only).
  67. Creates an active user with email verified.
  68. """
  69. # Check if email already exists
  70. existing_user = await user_service.get_user_by_email(db, data.email)
  71. if existing_user:
  72. raise HTTPException(
  73. status_code=status.HTTP_400_BAD_REQUEST,
  74. detail="Email already registered",
  75. )
  76. user = await user_service.create_user(db, data)
  77. return user
  78. @router.patch("/{user_id}", response_model=UserResponse)
  79. async def update_user(
  80. user_id: int,
  81. data: UserUpdate,
  82. db: Annotated[AsyncSession, Depends(get_db)],
  83. current_user: Annotated[User, Depends(get_current_superadmin)],
  84. ):
  85. """
  86. Update user (superadmin only).
  87. Can update user details, role, and status.
  88. """
  89. user = await user_service.update_user(db, user_id, data)
  90. if not user:
  91. raise HTTPException(
  92. status_code=status.HTTP_404_NOT_FOUND,
  93. detail="User not found",
  94. )
  95. return user
  96. @router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
  97. async def delete_user(
  98. user_id: int,
  99. db: Annotated[AsyncSession, Depends(get_db)],
  100. current_user: Annotated[User, Depends(get_current_superadmin)],
  101. ):
  102. """
  103. Delete user (superadmin only).
  104. Warning: This permanently deletes the user.
  105. """
  106. # Prevent deleting yourself
  107. if user_id == current_user.id:
  108. raise HTTPException(
  109. status_code=status.HTTP_400_BAD_REQUEST,
  110. detail="Cannot delete yourself",
  111. )
  112. deleted = await user_service.delete_user(db, user_id)
  113. if not deleted:
  114. raise HTTPException(
  115. status_code=status.HTTP_404_NOT_FOUND,
  116. detail="User not found",
  117. )
  118. @router.post("/{user_id}/change-password", response_model=UserResponse)
  119. async def change_user_password(
  120. user_id: int,
  121. data: ChangePasswordRequest,
  122. db: Annotated[AsyncSession, Depends(get_db)],
  123. current_user: Annotated[User, Depends(get_current_superadmin)],
  124. ):
  125. """
  126. Change user password (superadmin only).
  127. Allows superadmin to reset any user's password.
  128. """
  129. user = await user_service.change_user_password(db, user_id, data.new_password)
  130. if not user:
  131. raise HTTPException(
  132. status_code=status.HTTP_404_NOT_FOUND,
  133. detail="User not found",
  134. )
  135. return user