Terminal / api /me_tasks.py
Baida07's picture
sync: 172 file da Baida98/AI@789d6ce1 (2026-08-22 17:46 UTC) [deploy-all] (#55)
ed6f314
Raw
History Blame
3.83 kB
"""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
@router.get("")
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
@router.post("", status_code=201)
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
@router.patch("/{task_id}")
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
@router.post("/{task_id}/cancel")
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)