Specs stay at repo root (cross-VM). Move deploy and code into logical projects with README per domain, updated manifest.yaml, and symlinks at legacy paths for VM122 backward compatibility.
120 lines
3.9 KiB
Python
120 lines
3.9 KiB
Python
"""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()
|