ligbox-ops-platform/projects/ops-desk/api/app/ops_inbox_routes.py
Ligbox Spec Hub b03bb2c37c feat(desk): UI/API Spec 039-041 + deploy atómico e smoke GREEN
Commita governance, user-wizard, operational-feed e catálogo RBAC;
adiciona deploy-desk-full.sh, smoke-desk.sh e regra anti-deploy parcial;
documenta credencial VM112 @betinplace.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 13:32:15 +00:00

119 lines
3.1 KiB
Python

"""Operational feed API. Spec 041 · OF-API-002."""
from __future__ import annotations
from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel, Field
from app import auth
from app.ops_inbox_store import (
add_message,
get_event,
inbox_stats,
init_inbox_schema,
list_events,
patch_event,
)
from app.permissions import can_manage_users
router = APIRouter(prefix="/api/v1/ops-inbox", tags=["ops-inbox"])
class InboxMessageBody(BaseModel):
body: str = Field(min_length=1, max_length=8000)
note_type: str = Field(default="reply", pattern="^(reply|internal_note)$")
class InboxPatchBody(BaseModel):
status: str | None = Field(default=None, pattern="^(open|pending|resolved)$")
assignee: str | None = None
priority: str | None = Field(default=None, pattern="^(normal|high|critical)$")
def _db():
conn = auth.db()
try:
init_inbox_schema(conn)
yield conn
finally:
conn.close()
def _require_inbox(user: auth.DeskUser) -> None:
if not can_manage_users(user.role):
raise HTTPException(403, "insufficient permissions")
@router.get("/stats")
def inbox_statistics(
user: auth.DeskUser = Depends(auth.get_current_user),
conn=Depends(_db),
):
_require_inbox(user)
return inbox_stats(conn)
@router.get("/events")
def inbox_events(
channel: str | None = Query(default="all"),
priority: str | None = Query(default=None),
status: str | None = Query(default=None),
q: str | None = Query(default=None),
limit: int = Query(default=128, ge=1, le=500),
user: auth.DeskUser = Depends(auth.get_current_user),
conn=Depends(_db),
):
_require_inbox(user)
items = list_events(conn, channel=channel, priority=priority, status=status, q=q, limit=limit)
return {"events": items, "total": len(items)}
@router.get("/events/{event_id}")
def inbox_event_detail(
event_id: str,
user: auth.DeskUser = Depends(auth.get_current_user),
conn=Depends(_db),
):
_require_inbox(user)
ev = get_event(conn, event_id)
if not ev:
raise HTTPException(404, "event not found")
return {"event": ev}
@router.post("/events/{event_id}/messages")
def inbox_post_message(
event_id: str,
body: InboxMessageBody,
user: auth.DeskUser = Depends(auth.get_current_user),
conn=Depends(_db),
):
_require_inbox(user)
if not get_event(conn, event_id):
raise HTTPException(404, "event not found")
author_type = "internal" if body.note_type == "internal_note" else "operator"
label = user.display_name or user.username
msg = add_message(conn, event_id, author_type, label, body.body)
conn.commit()
return {"message": msg}
@router.patch("/events/{event_id}")
def inbox_patch(
event_id: str,
body: InboxPatchBody,
user: auth.DeskUser = Depends(auth.get_current_user),
conn=Depends(_db),
):
_require_inbox(user)
ev = patch_event(
conn,
event_id,
status=body.status,
assignee=body.assignee,
priority=body.priority,
)
if not ev:
raise HTTPException(404, "event not found")
conn.commit()
return {"event": ev}