"""Saved server actions/tasks router.""" from __future__ import annotations import logging from typing import Any from fastapi import APIRouter, Depends, HTTPException, Query, status from pydantic import BaseModel, Field from media_library_viewer_api.dependencies import get_settings_store from media_library_viewer_api.services.settings_store import SettingsStore from media_library_viewer_api.services.task_runner import run_saved_task from media_library_viewer_api.widgets.sources import ServiceRecord, build_service_record logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/tasks", tags=["tasks"]) class TaskInput(BaseModel): id: str | None = None name: str = Field(default="") task_type: str = Field(default="shell", description="shell or python") content: str = Field(default="") enabled: bool = True default_service_id: str = "" notes: str = "" class RunTaskRequest(BaseModel): task_id: str def _service_label(service: dict[str, Any] | None) -> str: if not service: return "" return str(service.get("name") or service.get("id") or "") def _resolve_service_for_task( store: SettingsStore, task: dict[str, Any], service_id: str | None, ) -> dict[str, Any] | None: if service_id: return store.get_service(service_id) default_service_id = str(task.get("default_service_id") or "").strip() if default_service_id: return store.get_service(default_service_id) services = [svc for svc in store.list_services("ssh_tasks") if svc.get("enabled")] return services[0] if services else None def _service_row_to_record(service_row: dict[str, Any]) -> ServiceRecord: """Build a ServiceRecord from a raw settings_store service row.""" from media_library_viewer_api.services.settings_store import get_settings_store return build_service_record(get_settings_store(), service_row) @router.get("") def list_tasks(store: SettingsStore = Depends(get_settings_store)) -> list[dict[str, Any]]: return store.list_tasks() @router.post("", status_code=status.HTTP_201_CREATED) def create_task(task: TaskInput, store: SettingsStore = Depends(get_settings_store)) -> dict[str, Any]: return store.upsert_task(task.model_dump(exclude_none=True), task.id) @router.put("/{task_id}") def update_task(task_id: str, task: TaskInput, store: SettingsStore = Depends(get_settings_store)) -> dict[str, Any]: if not store.get_task(task_id): raise HTTPException(status_code=404, detail="Task not found") return store.upsert_task(task.model_dump(exclude_none=True), task_id) @router.delete("/{task_id}") def delete_task(task_id: str, store: SettingsStore = Depends(get_settings_store)) -> dict[str, str]: if not store.get_task(task_id): raise HTTPException(status_code=404, detail="Task not found") store.delete_task(task_id) return {"status": "deleted"} @router.get("/{task_id}/runs") def list_task_runs( task_id: str, limit: int = Query(default=10, ge=1, le=50), store: SettingsStore = Depends(get_settings_store), ) -> dict[str, Any]: if not store.get_task(task_id): raise HTTPException(status_code=404, detail="Task not found") runs = store.list_service_task_runs(task_id=task_id, limit=limit) return {"items": runs, "total": len(runs)} @router.post("/run") def run_task( request: RunTaskRequest, service_id: str | None = Query(default=None), store: SettingsStore = Depends(get_settings_store), ) -> dict[str, Any]: task = store.get_task(request.task_id) if not task: raise HTTPException(status_code=404, detail="Task not found") if not task.get("enabled", True): raise HTTPException(status_code=400, detail="Task is disabled") service_row = _resolve_service_for_task(store, task, service_id) if not service_row: raise HTTPException(status_code=400, detail="No SSH task service is available for this action") if not service_row.get("enabled", True): raise HTTPException(status_code=400, detail="Selected SSH task service is disabled") service = _service_row_to_record(service_row) result = run_saved_task(store, task, service) return { "task_id": task["id"], "task_name": task["name"], "service_id": service.id, "service_name": _service_label(service_row), "task_type": task.get("task_type", "shell"), "exit_status": result.exit_status, "stdout": result.stdout, "stderr": result.stderr, }