Files
OCR/backend/app/routers/admin.py
egg 012da1abc4 fix: migrate UI to V2 API and fix admin dashboard
Backend fixes:
- Fix markdown generation using correct 'markdown_content' key in tasks.py
- Update admin service to return flat data structure matching frontend types
- Add task_count and failed_tasks fields to user statistics
- Fix top users endpoint to return complete user data

Frontend fixes:
- Migrate ResultsPage from V1 batch API to V2 task API with polling
- Create TaskDetailPage component with markdown preview and download buttons
- Refactor ExportPage to support multi-task selection using V2 download endpoints
- Fix login infinite refresh loop with concurrency control flags
- Create missing Checkbox UI component

New features:
- Add /tasks/:taskId route for task detail view
- Implement multi-task batch export functionality
- Add real-time task status polling (2s interval)

OpenSpec:
- Archive completed proposal 2025-11-17-fix-v2-api-ui-issues
- Create result-export and task-management specifications

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 08:55:50 +08:00

189 lines
5.6 KiB
Python

"""
Tool_OCR - Admin Router
Administrative endpoints for system management
"""
import logging
from typing import Optional
from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session
from app.core.deps import get_db, get_current_admin_user
from app.models.user import User
from app.services.admin_service import admin_service
from app.services.audit_service import audit_service
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/v2/admin", tags=["Admin"])
@router.get("/stats", summary="Get system statistics")
async def get_system_stats(
db: Session = Depends(get_db),
admin_user: User = Depends(get_current_admin_user)
):
"""
Get overall system statistics
Requires admin privileges
"""
try:
stats = admin_service.get_system_statistics(db)
return stats
except Exception as e:
logger.exception("Failed to get system stats")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to get system stats: {str(e)}"
)
@router.get("/users", summary="List all users")
async def list_users(
page: int = Query(1, ge=1),
page_size: int = Query(50, ge=1, le=100),
db: Session = Depends(get_db),
admin_user: User = Depends(get_current_admin_user)
):
"""
Get list of all users with statistics
Requires admin privileges
"""
try:
skip = (page - 1) * page_size
users, total = admin_service.get_user_list(db, skip=skip, limit=page_size)
return {
"users": users,
"total": total,
"page": page,
"page_size": page_size,
"has_more": (skip + len(users)) < total
}
except Exception as e:
logger.exception("Failed to list users")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to list users: {str(e)}"
)
@router.get("/users/top", summary="Get top users")
async def get_top_users(
metric: str = Query("tasks", regex="^(tasks|completed_tasks)$"),
limit: int = Query(10, ge=1, le=50),
db: Session = Depends(get_db),
admin_user: User = Depends(get_current_admin_user)
):
"""
Get top users by metric
- **metric**: Ranking metric (tasks or completed_tasks)
- **limit**: Number of users to return
Requires admin privileges
"""
try:
top_users = admin_service.get_top_users(db, metric=metric, limit=limit)
return top_users
except Exception as e:
logger.exception("Failed to get top users")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to get top users: {str(e)}"
)
@router.get("/audit-logs", summary="Get audit logs")
async def get_audit_logs(
user_id: Optional[int] = Query(None),
event_category: Optional[str] = Query(None),
event_type: Optional[str] = Query(None),
date_from: Optional[str] = Query(None),
date_to: Optional[str] = Query(None),
success_only: Optional[bool] = Query(None),
page: int = Query(1, ge=1),
page_size: int = Query(100, ge=1, le=500),
db: Session = Depends(get_db),
admin_user: User = Depends(get_current_admin_user)
):
"""
Get audit logs with filtering
- **user_id**: Filter by user ID (optional)
- **event_category**: Filter by category (authentication, task, admin, system)
- **event_type**: Filter by event type (optional)
- **date_from**: Filter from date (YYYY-MM-DD, optional)
- **date_to**: Filter to date (YYYY-MM-DD, optional)
- **success_only**: Filter by success status (optional)
Requires admin privileges
"""
try:
# Parse dates
date_from_dt = datetime.fromisoformat(date_from) if date_from else None
date_to_dt = datetime.fromisoformat(date_to) if date_to else None
skip = (page - 1) * page_size
logs, total = audit_service.get_logs(
db=db,
user_id=user_id,
event_category=event_category,
event_type=event_type,
date_from=date_from_dt,
date_to=date_to_dt,
success_only=success_only,
skip=skip,
limit=page_size
)
return {
"logs": [log.to_dict() for log in logs],
"total": total,
"page": page,
"page_size": page_size,
"has_more": (skip + len(logs)) < total
}
except Exception as e:
logger.exception("Failed to get audit logs")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to get audit logs: {str(e)}"
)
@router.get("/audit-logs/user/{user_id}/summary", summary="Get user activity summary")
async def get_user_activity_summary(
user_id: int,
days: int = Query(30, ge=1, le=365),
db: Session = Depends(get_db),
admin_user: User = Depends(get_current_admin_user)
):
"""
Get user activity summary for the last N days
- **user_id**: User ID
- **days**: Number of days to look back (default: 30)
Requires admin privileges
"""
try:
summary = audit_service.get_user_activity_summary(db, user_id=user_id, days=days)
return summary
except Exception as e:
logger.exception(f"Failed to get activity summary for user {user_id}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to get user activity summary: {str(e)}"
)