"""Admin endpoints: user management and site statistics.""" from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import func from sqlalchemy.orm import Session from config import settings from database import get_db from models.db_models import HistoryEntry, User from models.schemas import ( AdminModelInfo, AdminModelsResponse, AdminStatsResponse, AdminUserUpdate, UserResponse, ) from routers.auth import _current_user_id from services.auth_service import get_user_by_id from services.llm_client import PREMIUM_MODEL_PROFILES router = APIRouter(prefix="/api/admin", tags=["admin"]) MODEL_LABELS = { "gpt-oss-120b": "GPT-OSS 120B", "gpt-oss-20b": "GPT-OSS 20B", "compound": "Groq Compound", "compound-mini": "Groq Compound Mini", "qwen-3.6-27b": "Qwen 3.6 27B", } def _require_admin( user_id: int = Depends(_current_user_id), db: Session = Depends(get_db), ) -> User: """Dependency that requires the caller to be an admin.""" user = get_user_by_id(db, user_id) if not user: raise HTTPException(status_code=404, detail="User not found") if not user.is_admin: raise HTTPException(status_code=403, detail="Admin access required") return user @router.get("/users", response_model=list[UserResponse], summary="List all users") def list_users( skip: int = 0, limit: int = 50, search: str = "", admin: User = Depends(_require_admin), db: Session = Depends(get_db), ) -> list[UserResponse]: query = db.query(User) if search: pattern = f"%{search}%" query = query.filter( (User.username.ilike(pattern)) | (User.email.ilike(pattern)) ) users = query.order_by(User.created_at.desc()).offset(skip).limit(limit).all() return [UserResponse.model_validate(u) for u in users] @router.patch("/users/{user_id}", response_model=UserResponse, summary="Update a user") def update_user( user_id: int, updates: AdminUserUpdate, admin: User = Depends(_require_admin), db: Session = Depends(get_db), ) -> UserResponse: target = get_user_by_id(db, user_id) if not target: raise HTTPException(status_code=404, detail="User not found") if updates.is_premium is not None: target.is_premium = updates.is_premium if updates.is_admin is not None: target.is_admin = updates.is_admin db.commit() db.refresh(target) return UserResponse.model_validate(target) @router.delete("/users/{user_id}", status_code=204, summary="Delete a user") def delete_user( user_id: int, admin: User = Depends(_require_admin), db: Session = Depends(get_db), ): target = get_user_by_id(db, user_id) if not target: raise HTTPException(status_code=404, detail="User not found") if target.id == admin.id: raise HTTPException(status_code=400, detail="Cannot delete yourself") db.query(HistoryEntry).filter(HistoryEntry.user_id == user_id).delete() db.delete(target) db.commit() @router.get("/stats", response_model=AdminStatsResponse, summary="Site statistics") def get_stats( admin: User = Depends(_require_admin), db: Session = Depends(get_db), ) -> AdminStatsResponse: total_users = db.query(func.count(User.id)).scalar() or 0 premium_users = db.query(func.count(User.id)).filter(User.is_premium.is_(True)).scalar() or 0 admin_users = db.query(func.count(User.id)).filter(User.is_admin.is_(True)).scalar() or 0 total_history = db.query(func.count(HistoryEntry.id)).scalar() or 0 return AdminStatsResponse( total_users=total_users, premium_users=premium_users, admin_users=admin_users, total_history_entries=total_history, ) @router.get("/models", response_model=AdminModelsResponse, summary="Writing model status") def model_status( admin: User = Depends(_require_admin), ) -> AdminModelsResponse: """Return safe, non-secret provider information for the admin dashboard.""" return AdminModelsResponse( provider="Groq", provider_configured=bool(settings.groq_api_keys or settings.groq_api_key), standard_model=settings.groq_model, models=[ AdminModelInfo( id=profile, label=MODEL_LABELS[profile], provider_model=provider_model, status="preview" if profile == "qwen-3.6-27b" else "production", ) for profile, provider_model in PREMIUM_MODEL_PROFILES.items() ], )