mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-03 12:46:49 +02:00
206 lines
6.7 KiB
Python
206 lines
6.7 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from backend.app.core.auth import RequireAdmin, get_password_hash
|
|
from backend.app.core.database import get_db
|
|
from backend.app.models.user import User
|
|
from backend.app.schemas.auth import UserCreate, UserResponse, UserUpdate
|
|
|
|
router = APIRouter(prefix="/users", tags=["users"])
|
|
|
|
|
|
@router.get("", response_model=list[UserResponse])
|
|
@router.get("/", response_model=list[UserResponse])
|
|
async def list_users(
|
|
current_user: User = RequireAdmin(),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""List all users (admin only)."""
|
|
result = await db.execute(select(User).order_by(User.created_at))
|
|
users = result.scalars().all()
|
|
return [
|
|
UserResponse(
|
|
id=user.id,
|
|
username=user.username,
|
|
role=user.role,
|
|
is_active=user.is_active,
|
|
created_at=user.created_at.isoformat(),
|
|
)
|
|
for user in users
|
|
]
|
|
|
|
|
|
@router.post("", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
|
|
@router.post("/", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
|
|
async def create_user(
|
|
user_data: UserCreate,
|
|
current_user: User = RequireAdmin(),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""Create a new user (admin only)."""
|
|
# Check if username already exists
|
|
existing_user = await db.execute(select(User).where(User.username == user_data.username))
|
|
if existing_user.scalar_one_or_none():
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Username already exists",
|
|
)
|
|
|
|
# Validate role
|
|
if user_data.role not in ["admin", "user"]:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Role must be 'admin' or 'user'",
|
|
)
|
|
|
|
new_user = User(
|
|
username=user_data.username,
|
|
password_hash=get_password_hash(user_data.password),
|
|
role=user_data.role,
|
|
is_active=True,
|
|
)
|
|
db.add(new_user)
|
|
await db.commit()
|
|
await db.refresh(new_user)
|
|
|
|
return UserResponse(
|
|
id=new_user.id,
|
|
username=new_user.username,
|
|
role=new_user.role,
|
|
is_active=new_user.is_active,
|
|
created_at=new_user.created_at.isoformat(),
|
|
)
|
|
|
|
|
|
@router.get("/{user_id}", response_model=UserResponse)
|
|
async def get_user(
|
|
user_id: int,
|
|
current_user: User = RequireAdmin(),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""Get a user by ID (admin only)."""
|
|
result = await db.execute(select(User).where(User.id == user_id))
|
|
user = result.scalar_one_or_none()
|
|
if not user:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="User not found",
|
|
)
|
|
|
|
return UserResponse(
|
|
id=user.id,
|
|
username=user.username,
|
|
role=user.role,
|
|
is_active=user.is_active,
|
|
created_at=user.created_at.isoformat(),
|
|
)
|
|
|
|
|
|
@router.patch("/{user_id}", response_model=UserResponse)
|
|
async def update_user(
|
|
user_id: int,
|
|
user_data: UserUpdate,
|
|
current_user: User = RequireAdmin(),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""Update a user (admin only)."""
|
|
result = await db.execute(select(User).where(User.id == user_id))
|
|
user = result.scalar_one_or_none()
|
|
if not user:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="User not found",
|
|
)
|
|
|
|
# Prevent deactivating the last admin
|
|
if user_data.is_active is False and user.role == "admin":
|
|
admin_count_result = await db.execute(select(User).where(User.role == "admin", User.is_active.is_(True)))
|
|
admin_count = len(admin_count_result.scalars().all())
|
|
if admin_count <= 1:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Cannot deactivate the last admin user",
|
|
)
|
|
|
|
# Prevent changing role of last admin
|
|
if user_data.role and user_data.role != "admin" and user.role == "admin":
|
|
admin_count_result = await db.execute(select(User).where(User.role == "admin", User.is_active.is_(True)))
|
|
admin_count = len(admin_count_result.scalars().all())
|
|
if admin_count <= 1:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Cannot change role of the last admin user",
|
|
)
|
|
|
|
if user_data.username is not None:
|
|
# Check if new username already exists
|
|
existing_user = await db.execute(select(User).where(User.username == user_data.username, User.id != user_id))
|
|
if existing_user.scalar_one_or_none():
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Username already exists",
|
|
)
|
|
user.username = user_data.username
|
|
|
|
if user_data.password is not None:
|
|
user.password_hash = get_password_hash(user_data.password)
|
|
|
|
if user_data.role is not None:
|
|
if user_data.role not in ["admin", "user"]:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Role must be 'admin' or 'user'",
|
|
)
|
|
user.role = user_data.role
|
|
|
|
if user_data.is_active is not None:
|
|
user.is_active = user_data.is_active
|
|
|
|
await db.commit()
|
|
await db.refresh(user)
|
|
|
|
return UserResponse(
|
|
id=user.id,
|
|
username=user.username,
|
|
role=user.role,
|
|
is_active=user.is_active,
|
|
created_at=user.created_at.isoformat(),
|
|
)
|
|
|
|
|
|
@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
async def delete_user(
|
|
user_id: int,
|
|
current_user: User = RequireAdmin(),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""Delete a user (admin only)."""
|
|
result = await db.execute(select(User).where(User.id == user_id))
|
|
user = result.scalar_one_or_none()
|
|
if not user:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="User not found",
|
|
)
|
|
|
|
# Prevent deleting the last admin
|
|
if user.role == "admin":
|
|
admin_count_result = await db.execute(select(User).where(User.role == "admin", User.id != user_id))
|
|
admin_count = len(admin_count_result.scalars().all())
|
|
if admin_count == 0:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Cannot delete the last admin user",
|
|
)
|
|
|
|
# Prevent deleting yourself
|
|
if user.id == current_user.id:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Cannot delete your own account",
|
|
)
|
|
|
|
await db.delete(user)
|
|
await db.commit()
|