"""Rotas libertação ACCOUNT_EXISTS — Spec 022.""" from __future__ import annotations from fastapi import APIRouter, Depends, HTTPException, Query from pydantic import BaseModel, Field from app import auth, carbonio_release_store, vm112_domains from app.permissions import can_read_tickets router = APIRouter(prefix="/api/v1/carbonio-blocks", tags=["carbonio-release"]) class ResolveBlockBody(BaseModel): confirm_email: str = Field(..., min_length=5) password: str = Field(..., min_length=1) def _require_ticket_reader(user: auth.DeskUser = Depends(auth.get_current_user)) -> auth.DeskUser: if not can_read_tickets(user.role): raise HTTPException(403, "permissão insuficiente") return user def _verify_user_password(conn, username: str, password: str) -> bool: row = conn.execute( "SELECT password_hash FROM desk_users WHERE username = ? AND active = 1", (username,), ).fetchone() if not row or not row["password_hash"]: return False return auth.verify_password(password, row["password_hash"]) @router.get("") def list_carbonio_blocks( status: str = Query("pending"), session_id: str = "", ticket_id: int | None = None, limit: int = Query(100, ge=1, le=500), user: auth.DeskUser = Depends(_require_ticket_reader), ): conn = auth.db() try: return carbonio_release_store.list_blocks( conn, status=status or "all", session_id=session_id.strip() or None, ticket_id=ticket_id, limit=limit, ) finally: conn.close() @router.get("/{block_id}") def get_carbonio_block( block_id: int, user: auth.DeskUser = Depends(_require_ticket_reader), ): conn = auth.db() try: block = carbonio_release_store.get_block(conn, block_id) finally: conn.close() if not block: raise HTTPException(404, "bloqueio não encontrado") return block @router.post("/{block_id}/resolve") def resolve_carbonio_block( block_id: int, body: ResolveBlockBody, user: auth.DeskUser = Depends(_require_ticket_reader), ): conn = auth.db() try: block = carbonio_release_store.get_block(conn, block_id) if not block: raise HTTPException(404, "bloqueio não encontrado") if block["status"] == "resolved": raise HTTPException(409, f"Já resolvido por {block.get('resolved_by') or 'outro técnico'}") if body.confirm_email.lower().strip() != block["email"].lower(): raise HTTPException(400, "E-mail de confirmação não coincide") if not _verify_user_password(conn, user.username, body.password): raise HTTPException(403, "Senha incorrecta") try: vm_result = vm112_domains.delete_carbonio_account(block["email"]) except Exception as e: raise HTTPException(502, f"VM112/Carbonio: {e}") from e try: resolved = carbonio_release_store.resolve_block( conn, block_id, resolved_by=user.username, note=vm_result.get("message", "zmprov da"), ) except ValueError as e: if str(e) == "already_resolved": raise HTTPException(409, "Outro técnico já resolveu este bloqueio") from e raise HTTPException(404, "Bloqueio indisponível") from e if block.get("ticket_id"): carbonio_release_store.append_ticket_resolution_note( conn, int(block["ticket_id"]), email=block["email"], by=user.username, ) return { "ok": True, "block": resolved, "vm112": vm_result, "message": f"Conta {block['email']} removida do Carbonio. Peça ao cliente para repetir «Criar conta» no wizard.", } finally: conn.close()