File size: 3,828 Bytes
ed6f314
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
"""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)