Spaces:
Running
Running
| """Task personali del prodotto pubblico. | |
| Tutte le query applicano owner_id derivato dal JWT Supabase verificato. Il client | |
| non può scegliere o sostituire il proprietario nel body o nella query. | |
| """ | |
| from __future__ import annotations | |
| import asyncio | |
| import logging | |
| from typing import Any | |
| from uuid import UUID | |
| from fastapi import APIRouter, Depends, HTTPException, Query | |
| from pydantic import BaseModel, Field | |
| from .auth_guard import require_supabase_user | |
| from .state import sb | |
| _logger = logging.getLogger("agente_ai.api.me_tasks") | |
| router = APIRouter(prefix="/api/me/tasks", tags=["me"]) | |
| class TaskCreate(BaseModel): | |
| goal: str = Field(min_length=1, max_length=10_000) | |
| class TaskUpdate(BaseModel): | |
| status: str = Field(pattern="^(queued|in_progress|done|failed|cancelled)$") | |
| _ALLOWED = "id,goal,status,created_at,updated_at" | |
| def _owner(user: dict[str, Any]) -> str: | |
| return str(user["id"]) | |
| def _client(): | |
| client = sb() | |
| if client is None: | |
| raise HTTPException(status_code=503, detail="Database non configurato") | |
| return client | |
| async def list_my_tasks( | |
| user: dict[str, Any] = Depends(require_supabase_user), | |
| limit: int = Query(50, ge=1, le=100), | |
| offset: int = Query(0, ge=0), | |
| ) -> dict[str, Any]: | |
| client = _client() | |
| owner_id = _owner(user) | |
| def operation(): | |
| return client.table("user_agent_tasks").select(_ALLOWED).eq("owner_id", owner_id).order("updated_at", desc=True).range(offset, offset + limit - 1).execute() | |
| try: | |
| result = await asyncio.to_thread(operation) | |
| return {"tasks": result.data or [], "offset": offset, "limit": limit} | |
| except Exception as exc: | |
| _logger.warning("list own tasks failed: %s", type(exc).__name__) | |
| raise HTTPException(status_code=503, detail="Task personali temporaneamente non disponibili") from exc | |
| async def create_my_task( | |
| body: TaskCreate, | |
| user: dict[str, Any] = Depends(require_supabase_user), | |
| ) -> dict[str, Any]: | |
| client = _client() | |
| owner_id = _owner(user) | |
| def operation(): | |
| return client.table("user_agent_tasks").insert({"owner_id": owner_id, "goal": body.goal.strip(), "status": "queued"}).select(_ALLOWED).single().execute() | |
| try: | |
| result = await asyncio.to_thread(operation) | |
| if not result.data: | |
| raise HTTPException(status_code=502, detail="Task personale non creato") | |
| return result.data | |
| except HTTPException: | |
| raise | |
| except Exception as exc: | |
| _logger.warning("create own task failed: %s", type(exc).__name__) | |
| raise HTTPException(status_code=503, detail="Task personale temporaneamente non disponibile") from exc | |
| async def update_my_task( | |
| task_id: UUID, | |
| body: TaskUpdate, | |
| user: dict[str, Any] = Depends(require_supabase_user), | |
| ) -> dict[str, Any]: | |
| client = _client() | |
| owner_id = _owner(user) | |
| def operation(): | |
| return client.table("user_agent_tasks").update({"status": body.status}).eq("id", str(task_id)).eq("owner_id", owner_id).select(_ALLOWED).maybe_single().execute() | |
| try: | |
| result = await asyncio.to_thread(operation) | |
| if not result.data: | |
| raise HTTPException(status_code=404, detail="Task personale non trovato") | |
| return result.data | |
| except HTTPException: | |
| raise | |
| except Exception as exc: | |
| _logger.warning("update own task failed: %s", type(exc).__name__) | |
| raise HTTPException(status_code=503, detail="Task personale temporaneamente non disponibile") from exc | |
| async def cancel_my_task( | |
| task_id: UUID, | |
| user: dict[str, Any] = Depends(require_supabase_user), | |
| ) -> dict[str, Any]: | |
| return await update_my_task(task_id, TaskUpdate(status="cancelled"), user) | |