from __future__ import annotations from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy import func, select from sqlalchemy.orm import Session, selectinload from app.auth import get_active_user, get_admin_user from app.config import Settings, get_settings from app.database import get_db from app.models import Quiz, User from app.schemas import BanUserIn, QuizSummaryOut, UserOut from app.services.quiz_service import enrich_quizzes router = APIRouter(prefix="/api/v1/admin", tags=["Moderation"]) @router.post("/claim", response_model=UserOut) def claim_initial_moderator( db: Session = Depends(get_db), user: User = Depends(get_active_user), settings: Settings = Depends(get_settings), ) -> UserOut: if settings.admin_user.strip(): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Moderator account is locked to the configured ADMIN_USER container setting.", ) existing_admins = db.scalar(select(func.count(User.id)).where(User.is_admin.is_(True))) or 0 if existing_admins > 0: raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="An initial moderator account has already been configured.", ) user.is_admin = True db.commit() db.refresh(user) return UserOut.model_validate(user) @router.get("/users", response_model=list[UserOut]) def list_users_for_moderation( db: Session = Depends(get_db), _admin: User = Depends(get_admin_user), ) -> list[UserOut]: users = db.scalars(select(User).order_by(User.created_at.desc(), User.id.desc())).all() return [UserOut.model_validate(u) for u in users] @router.post("/users/{user_id}/ban", response_model=UserOut) def ban_user( user_id: int, payload: BanUserIn, db: Session = Depends(get_db), admin: User = Depends(get_admin_user), ) -> UserOut: if user_id == admin.id: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Moderators cannot ban their own account.", ) target = db.get(User, user_id) if target is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found.") target.is_banned = True target.ban_reason = payload.reason.strip() or "Suspended by moderator" db.commit() db.refresh(target) return UserOut.model_validate(target) @router.post("/users/{user_id}/unban", response_model=UserOut) def unban_user( user_id: int, db: Session = Depends(get_db), _admin: User = Depends(get_admin_user), ) -> UserOut: target = db.get(User, user_id) if target is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found.") target.is_banned = False target.ban_reason = None db.commit() db.refresh(target) return UserOut.model_validate(target) @router.get("/quizzes", response_model=list[QuizSummaryOut]) def list_quizzes_for_moderation( db: Session = Depends(get_db), admin: User = Depends(get_admin_user), ) -> list[QuizSummaryOut]: quizzes = list( db.scalars( select(Quiz) .options(selectinload(Quiz.author), selectinload(Quiz.questions)) .where(Quiz.is_removed.is_(False)) .order_by(Quiz.created_at.desc(), Quiz.id.desc()) ).all() ) return enrich_quizzes(db, quizzes, admin) @router.delete("/quizzes/{quiz_id}", status_code=status.HTTP_204_NO_CONTENT) def moderator_remove_quiz( quiz_id: int, db: Session = Depends(get_db), _admin: User = Depends(get_admin_user), ) -> None: quiz = db.get(Quiz, quiz_id) if quiz is None or quiz.is_removed: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Quiz not found.") quiz.is_removed = True db.commit()