71 lines
2.5 KiB
Python
71 lines
2.5 KiB
Python
"""FluentGerman.ai — User management router (admin only)."""
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.auth import hash_password, require_admin
|
|
from app.database import get_db
|
|
from app.models import User
|
|
from app.schemas import UserCreate, UserOut, UserUpdate
|
|
|
|
router = APIRouter(prefix="/api/users", tags=["users"], dependencies=[Depends(require_admin)])
|
|
|
|
|
|
@router.get("/", response_model=list[UserOut])
|
|
async def list_users(db: AsyncSession = Depends(get_db)):
|
|
result = await db.execute(select(User).where(User.is_admin == False).order_by(User.created_at.desc())) # noqa: E712
|
|
return result.scalars().all()
|
|
|
|
|
|
@router.post("/", response_model=UserOut, status_code=status.HTTP_201_CREATED)
|
|
async def create_user(body: UserCreate, db: AsyncSession = Depends(get_db)):
|
|
# Check uniqueness
|
|
existing = await db.execute(
|
|
select(User).where((User.username == body.username) | (User.email == body.email))
|
|
)
|
|
if existing.scalar_one_or_none():
|
|
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Username or email already exists")
|
|
|
|
user = User(
|
|
username=body.username,
|
|
email=body.email,
|
|
hashed_password=hash_password(body.password),
|
|
)
|
|
db.add(user)
|
|
await db.commit()
|
|
await db.refresh(user)
|
|
return user
|
|
|
|
|
|
@router.put("/{user_id}", response_model=UserOut)
|
|
async def update_user(user_id: int, body: UserUpdate, db: AsyncSession = Depends(get_db)):
|
|
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")
|
|
|
|
if body.username is not None:
|
|
user.username = body.username
|
|
if body.email is not None:
|
|
user.email = body.email
|
|
if body.password is not None:
|
|
user.hashed_password = hash_password(body.password)
|
|
if body.is_active is not None:
|
|
user.is_active = body.is_active
|
|
|
|
await db.commit()
|
|
await db.refresh(user)
|
|
return user
|
|
|
|
|
|
@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
async def delete_user(user_id: int, db: AsyncSession = Depends(get_db)):
|
|
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")
|
|
|
|
await db.delete(user)
|
|
await db.commit()
|