231 lines
7.6 KiB
Python
231 lines
7.6 KiB
Python
"""API endpoints for scheduled task management."""
|
|
|
|
from typing import Annotated
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from sqlmodel import select
|
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
|
|
|
from app.core.database import get_db
|
|
from app.core.dependencies import (
|
|
get_admin_user,
|
|
get_current_active_user,
|
|
)
|
|
from app.core.services import get_global_scheduler_service
|
|
from app.models.scheduled_task import ScheduledTask, TaskStatus, TaskType
|
|
from app.models.user import User
|
|
from app.repositories.scheduled_task import ScheduledTaskRepository
|
|
from app.schemas.scheduler import (
|
|
ScheduledTaskCreate,
|
|
ScheduledTaskResponse,
|
|
ScheduledTaskUpdate,
|
|
TaskFilterParams,
|
|
)
|
|
from app.services.scheduler import SchedulerService
|
|
|
|
router = APIRouter(prefix="/scheduler")
|
|
|
|
|
|
def get_scheduler_service() -> SchedulerService:
|
|
"""Get the global scheduler service instance."""
|
|
return get_global_scheduler_service()
|
|
|
|
|
|
def get_task_filters(
|
|
status: Annotated[
|
|
TaskStatus | None, Query(description="Filter by task status"),
|
|
] = None,
|
|
task_type: Annotated[
|
|
TaskType | None, Query(description="Filter by task type"),
|
|
] = None,
|
|
limit: Annotated[int, Query(description="Maximum number of tasks to return")] = 50,
|
|
offset: Annotated[int, Query(description="Number of tasks to skip")] = 0,
|
|
) -> TaskFilterParams:
|
|
"""Create task filter parameters from query parameters."""
|
|
return TaskFilterParams(
|
|
status=status,
|
|
task_type=task_type,
|
|
limit=limit,
|
|
offset=offset,
|
|
)
|
|
|
|
|
|
@router.post("/tasks", response_model=ScheduledTaskResponse)
|
|
async def create_task(
|
|
task_data: ScheduledTaskCreate,
|
|
current_user: Annotated[User, Depends(get_current_active_user)],
|
|
scheduler_service: Annotated[SchedulerService, Depends(get_scheduler_service)],
|
|
) -> ScheduledTask:
|
|
"""Create a new scheduled task."""
|
|
try:
|
|
return await scheduler_service.create_task(
|
|
task_data=task_data,
|
|
user_id=current_user.id,
|
|
)
|
|
except Exception as e:
|
|
raise HTTPException(status_code=400, detail=str(e)) from e
|
|
|
|
|
|
@router.get("/tasks", response_model=list[ScheduledTaskResponse])
|
|
async def get_user_tasks(
|
|
filters: Annotated[TaskFilterParams, Depends(get_task_filters)],
|
|
current_user: Annotated[User, Depends(get_current_active_user)],
|
|
scheduler_service: Annotated[SchedulerService, Depends(get_scheduler_service)],
|
|
) -> list[ScheduledTask]:
|
|
"""Get user's scheduled tasks."""
|
|
return await scheduler_service.get_user_tasks(
|
|
user_id=current_user.id,
|
|
status=filters.status,
|
|
task_type=filters.task_type,
|
|
limit=filters.limit,
|
|
offset=filters.offset,
|
|
)
|
|
|
|
|
|
@router.get("/tasks/{task_id}", response_model=ScheduledTaskResponse)
|
|
async def get_task(
|
|
task_id: int,
|
|
current_user: Annotated[User, Depends(get_current_active_user)] = ...,
|
|
db_session: Annotated[AsyncSession, Depends(get_db)] = ...,
|
|
) -> ScheduledTask:
|
|
"""Get a specific scheduled task."""
|
|
repo = ScheduledTaskRepository(db_session)
|
|
task = await repo.get_by_id(task_id)
|
|
|
|
if not task:
|
|
raise HTTPException(status_code=404, detail="Task not found")
|
|
|
|
# Check if user owns the task or is admin
|
|
if task.user_id != current_user.id and not current_user.is_admin:
|
|
raise HTTPException(status_code=403, detail="Access denied")
|
|
|
|
return task
|
|
|
|
|
|
@router.patch("/tasks/{task_id}", response_model=ScheduledTaskResponse)
|
|
async def update_task(
|
|
task_id: int,
|
|
task_update: ScheduledTaskUpdate,
|
|
current_user: Annotated[User, Depends(get_current_active_user)] = ...,
|
|
db_session: Annotated[AsyncSession, Depends(get_db)] = ...,
|
|
) -> ScheduledTask:
|
|
"""Update a scheduled task."""
|
|
repo = ScheduledTaskRepository(db_session)
|
|
task = await repo.get_by_id(task_id)
|
|
|
|
if not task:
|
|
raise HTTPException(status_code=404, detail="Task not found")
|
|
|
|
# Check if user owns the task or is admin
|
|
if task.user_id != current_user.id and not current_user.is_admin:
|
|
raise HTTPException(status_code=403, detail="Access denied")
|
|
|
|
# Update task fields
|
|
update_data = task_update.model_dump(exclude_unset=True)
|
|
for field, value in update_data.items():
|
|
setattr(task, field, value)
|
|
|
|
return await repo.update(task)
|
|
|
|
|
|
@router.delete("/tasks/{task_id}")
|
|
async def cancel_task(
|
|
task_id: int,
|
|
current_user: Annotated[User, Depends(get_current_active_user)] = ...,
|
|
scheduler_service: Annotated[
|
|
SchedulerService, Depends(get_scheduler_service),
|
|
] = ...,
|
|
db_session: Annotated[AsyncSession, Depends(get_db)] = ...,
|
|
) -> dict:
|
|
"""Cancel a scheduled task."""
|
|
repo = ScheduledTaskRepository(db_session)
|
|
task = await repo.get_by_id(task_id)
|
|
|
|
if not task:
|
|
raise HTTPException(status_code=404, detail="Task not found")
|
|
|
|
# Check if user owns the task or is admin
|
|
if task.user_id != current_user.id and not current_user.is_admin:
|
|
raise HTTPException(status_code=403, detail="Access denied")
|
|
|
|
success = await scheduler_service.cancel_task(task_id)
|
|
if not success:
|
|
raise HTTPException(status_code=400, detail="Failed to cancel task")
|
|
|
|
return {"message": "Task cancelled successfully"}
|
|
|
|
|
|
# Admin-only endpoints
|
|
@router.get("/admin/tasks", response_model=list[ScheduledTaskResponse])
|
|
async def get_all_tasks(
|
|
status: Annotated[
|
|
TaskStatus | None, Query(description="Filter by task status"),
|
|
] = None,
|
|
task_type: Annotated[
|
|
TaskType | None, Query(description="Filter by task type"),
|
|
] = None,
|
|
limit: Annotated[
|
|
int | None, Query(description="Maximum number of tasks to return"),
|
|
] = 100,
|
|
offset: Annotated[
|
|
int | None, Query(description="Number of tasks to skip"),
|
|
] = 0,
|
|
_: Annotated[User, Depends(get_admin_user)] = ...,
|
|
db_session: Annotated[AsyncSession, Depends(get_db)] = ...,
|
|
) -> list[ScheduledTask]:
|
|
"""Get all scheduled tasks (admin only)."""
|
|
# Build query with pagination and filtering
|
|
|
|
statement = select(ScheduledTask)
|
|
|
|
if status:
|
|
statement = statement.where(ScheduledTask.status == status)
|
|
|
|
if task_type:
|
|
statement = statement.where(ScheduledTask.task_type == task_type)
|
|
|
|
statement = statement.order_by(ScheduledTask.scheduled_at.desc())
|
|
|
|
if offset:
|
|
statement = statement.offset(offset)
|
|
|
|
if limit:
|
|
statement = statement.limit(limit)
|
|
|
|
result = await db_session.exec(statement)
|
|
return list(result.all())
|
|
|
|
|
|
@router.get("/admin/system-tasks", response_model=list[ScheduledTaskResponse])
|
|
async def get_system_tasks(
|
|
status: Annotated[
|
|
TaskStatus | None, Query(description="Filter by task status"),
|
|
] = None,
|
|
task_type: Annotated[
|
|
TaskType | None, Query(description="Filter by task type"),
|
|
] = None,
|
|
_: Annotated[User, Depends(get_admin_user)] = ...,
|
|
db_session: Annotated[AsyncSession, Depends(get_db)] = ...,
|
|
) -> list[ScheduledTask]:
|
|
"""Get system tasks (admin only)."""
|
|
repo = ScheduledTaskRepository(db_session)
|
|
return await repo.get_system_tasks(status=status, task_type=task_type)
|
|
|
|
|
|
@router.post("/admin/system-tasks", response_model=ScheduledTaskResponse)
|
|
async def create_system_task(
|
|
task_data: ScheduledTaskCreate,
|
|
_: Annotated[User, Depends(get_admin_user)] = ...,
|
|
scheduler_service: Annotated[
|
|
SchedulerService, Depends(get_scheduler_service),
|
|
] = ...,
|
|
) -> ScheduledTask:
|
|
"""Create a system task (admin only)."""
|
|
try:
|
|
return await scheduler_service.create_task(
|
|
task_data=task_data,
|
|
user_id=None, # System task
|
|
)
|
|
except Exception as e:
|
|
raise HTTPException(status_code=400, detail=str(e)) from e
|