Spaces:
Running
Running
File size: 5,917 Bytes
09801ca | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | """
Admin API Endpoints β /api/v2/admin/*
All endpoints require 'admin' role or higher.
"""
import logging
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy.ext.asyncio import AsyncSession
from app.database.session import get_db
from app.services.admin_service import AdminService
from app.core.permissions import require_role, AuthenticatedUser
from app.schemas.admin import (
AdminDashboardStats,
AdminUserListItem,
AdminUserUpdate,
AuditLogEntry,
RoleInfo,
)
from app.schemas.common import PaginationParams, PaginatedResponse, MessageResponse
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/admin", tags=["Admin"])
# ββ Dashboard βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
@router.get("/dashboard/stats", response_model=AdminDashboardStats)
async def get_dashboard_stats(
user: AuthenticatedUser = Depends(require_role("admin")),
db: AsyncSession = Depends(get_db),
):
"""Get platform-wide dashboard statistics."""
service = AdminService(db)
return await service.get_dashboard_stats()
# ββ User Management βββββββββββββββββββββββββββββββββββββββββββββββββ
@router.get("/users", response_model=PaginatedResponse[AdminUserListItem])
async def list_users(
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100),
sort_by: str = Query("created_at"),
sort_order: str = Query("desc", pattern="^(asc|desc)$"),
search: Optional[str] = Query(None, description="Search by email or name"),
role: Optional[str] = Query(None, description="Filter by role"),
is_active: Optional[bool] = Query(None, description="Filter by active status"),
user: AuthenticatedUser = Depends(require_role("admin")),
db: AsyncSession = Depends(get_db),
):
"""List all users with pagination, search, and filters."""
params = PaginationParams(page=page, page_size=page_size, sort_by=sort_by, sort_order=sort_order)
service = AdminService(db)
items, total = await service.list_users(params, search=search, role_filter=role, is_active=is_active)
return PaginatedResponse.create(items=items, total=total, params=params)
@router.get("/users/{user_id}", response_model=AdminUserListItem)
async def get_user(
user_id: str,
admin: AuthenticatedUser = Depends(require_role("admin")),
db: AsyncSession = Depends(get_db),
):
"""Get a single user's details."""
service = AdminService(db)
try:
return await service.get_user(user_id)
except ValueError as e:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
@router.patch("/users/{user_id}", response_model=AdminUserListItem)
async def update_user(
user_id: str,
body: AdminUserUpdate,
admin: AuthenticatedUser = Depends(require_role("admin")),
db: AsyncSession = Depends(get_db),
):
"""Update a user's profile or roles."""
service = AdminService(db)
try:
return await service.update_user(user_id, body)
except ValueError as e:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
@router.delete("/users/{user_id}", response_model=MessageResponse)
async def delete_user(
user_id: str,
admin: AuthenticatedUser = Depends(require_role("admin")),
db: AsyncSession = Depends(get_db),
):
"""Soft delete a user."""
# Prevent self-deletion
if user_id == admin.id:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Cannot delete your own account via admin API",
)
service = AdminService(db)
await service.soft_delete_user(user_id)
return MessageResponse(message=f"User {user_id} has been deactivated")
@router.post("/users/{user_id}/restore", response_model=MessageResponse)
async def restore_user(
user_id: str,
admin: AuthenticatedUser = Depends(require_role("admin")),
db: AsyncSession = Depends(get_db),
):
"""Restore a soft-deleted user."""
service = AdminService(db)
await service.restore_user(user_id)
return MessageResponse(message=f"User {user_id} has been restored")
# ββ Roles βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
@router.get("/roles", response_model=list[RoleInfo])
async def list_roles(
admin: AuthenticatedUser = Depends(require_role("admin")),
db: AsyncSession = Depends(get_db),
):
"""List all available roles."""
service = AdminService(db)
return await service.list_roles()
# ββ Audit Logs ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
@router.get("/audit-logs", response_model=PaginatedResponse[AuditLogEntry])
async def get_audit_logs(
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100),
user_id: Optional[str] = Query(None, description="Filter by user ID"),
action: Optional[str] = Query(None, description="Filter by action"),
resource_type: Optional[str] = Query(None, description="Filter by resource type"),
admin: AuthenticatedUser = Depends(require_role("admin")),
db: AsyncSession = Depends(get_db),
):
"""View audit logs with pagination and filters."""
params = PaginationParams(page=page, page_size=page_size)
service = AdminService(db)
items, total = await service.get_audit_logs(
params, user_id=user_id, action=action, resource_type=resource_type
)
return PaginatedResponse.create(items=items, total=total, params=params)
|