"""backend/api/conversations.py — Conversations + Messages CRUD (S354).""" import json, logging from .state import safe_json_dumps from typing import Optional, Any from fastapi import APIRouter, Body, HTTPException from pydantic import BaseModel from .state import sb router = APIRouter() _logger = logging.getLogger("conversations") class ConversationIn(BaseModel): id: str title: str = 'Nuova conversazione' created_at: int updated_at: int class MessageIn(BaseModel): id: str conversation_id: str role: str content: str created_at: int error: Optional[bool] = False steps: Optional[Any] = None agent_status: Optional[str] = None # ── Conversations ────────────────────────────────────────────────────────────── @router.get('/api/conversations') async def list_conversations(): try: data = sb().table('conversations').select('*').order('updated_at', desc=True).execute() return {'conversations': data.data} except Exception as exc: _logger.warning("list_conversations: %s", exc) # S750-GAP-I: Supabase non configurato o irraggiungibile → lista vuota invece di 500 return {'conversations': [], '_error': str(exc)[:120]} @router.post('/api/conversations') async def upsert_conversation(conv: ConversationIn): try: data = sb().table('conversations').upsert(conv.model_dump()).execute() return {'conversation': data.data[0] if data.data else conv.model_dump()} except Exception as exc: _logger.warning("upsert_conversation %s: %s", conv.id, exc) return {'conversation': conv.model_dump(), '_error': str(exc)[:120]} @router.put('/api/conversations/{conv_id}') async def update_conversation(conv_id: str, body: dict = Body(...)): body['id'] = conv_id try: data = sb().table('conversations').upsert(body).execute() return {'conversation': data.data[0] if data.data else body} except Exception as exc: _logger.warning("update_conversation %s: %s", conv_id, exc) return {'conversation': body, '_error': str(exc)[:120]} @router.delete('/api/conversations/{conv_id}') async def delete_conversation(conv_id: str): try: sb().table('messages').delete().eq('conversation_id', conv_id).execute() sb().table('conversations').delete().eq('id', conv_id).execute() except Exception as exc: _logger.warning("delete_conversation %s: %s", conv_id, exc) return {'deleted': conv_id} # ── Messages ─────────────────────────────────────────────────────────────────── @router.get('/api/conversations/{conv_id}/messages') async def list_messages(conv_id: str): try: data = sb().table('messages').select('*').eq('conversation_id', conv_id).order('created_at').execute() return {'messages': data.data} except Exception as exc: _logger.warning("list_messages %s: %s", conv_id, exc) return {'messages': [], '_error': str(exc)[:120]} @router.post('/api/conversations/{conv_id}/messages') async def upsert_messages(conv_id: str, body: dict = Body(...)): msgs = body.get('messages', []) if not msgs: return {'upserted': 0} for m in msgs: m['conversation_id'] = conv_id if 'steps' in m and m['steps'] is not None: m['steps'] = safe_json_dumps(m['steps']) if not isinstance(m['steps'], str) else m['steps'] try: data = sb().table('messages').upsert(msgs).execute() return {'upserted': len(data.data)} except Exception as exc: _logger.warning("upsert_messages %s: %s", conv_id, exc) return {'upserted': 0, '_error': str(exc)[:120]} @router.delete('/api/conversations/{conv_id}/messages') async def clear_messages(conv_id: str): try: sb().table('messages').delete().eq('conversation_id', conv_id).execute() except Exception as exc: _logger.warning("clear_messages %s: %s", conv_id, exc) return {'cleared': conv_id}