Files
2026-03-19 14:28:09 +01:00

238 lines
8.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Router tag (Label).
Endpoint:
- GET /labels elenca i tag del tenant
- POST /labels crea un nuovo tag (admin)
- PATCH /labels/{id} modifica un tag (admin)
- DELETE /labels/{id} elimina un tag (admin)
- GET /messages/{id}/labels tag di un messaggio
- PUT /messages/{id}/labels imposta i tag di un messaggio (sostituisce)
- POST /messages/{id}/labels/add aggiunge tag a un messaggio
- POST /messages/{id}/labels/remove rimuove tag da un messaggio
- POST /messages/bulk-labels aggiunge/rimuove tag in blocco
Permessi:
- GET /labels: tutti gli utenti autenticati
- POST/PATCH/DELETE: solo admin
- Operazioni su messaggi: utenti con accesso alla casella del messaggio
"""
import uuid
from fastapi import APIRouter, status
from sqlalchemy import select
from app.core.exceptions import ForbiddenError, NotFoundError
from app.dependencies import AdminUser, CurrentUser, DB
from app.models.message import Message
from app.schemas.label import (
LabelCreate,
LabelResponse,
LabelUpdate,
MessageBulkLabelRequest,
MessageBulkLabelResponse,
MessageLabelAddRequest,
MessageLabelRemoveRequest,
MessageLabelSetRequest,
)
from app.services.label_service import LabelService
router = APIRouter(tags=["Labels"])
# ─── Helpers ──────────────────────────────────────────────────────────────────
async def _check_message_access(
message_id: uuid.UUID,
current_user,
db,
) -> Message:
"""Verifica che il messaggio esista e l'utente vi abbia accesso."""
result = await db.execute(
select(Message).where(
Message.id == message_id,
Message.tenant_id == current_user.tenant_id,
)
)
message = result.scalar_one_or_none()
if not message:
raise NotFoundError(f"Messaggio {message_id} non trovato")
if not current_user.is_admin:
from app.services.permission_service import PermissionService
perm_svc = PermissionService(db)
if not await perm_svc.check_can_read(current_user, message.mailbox_id):
raise ForbiddenError("Accesso al messaggio non autorizzato")
return message
# ─── CRUD Label ───────────────────────────────────────────────────────────────
@router.get("/labels", response_model=list[LabelResponse])
async def list_labels(
current_user: CurrentUser,
db: DB,
) -> list[LabelResponse]:
"""Elenca tutti i tag del tenant corrente."""
svc = LabelService(db)
labels = await svc.list_labels(current_user.tenant_id)
return [LabelResponse.model_validate(l) for l in labels]
@router.post("/labels", response_model=LabelResponse, status_code=status.HTTP_201_CREATED)
async def create_label(
data: LabelCreate,
current_user: AdminUser,
db: DB,
) -> LabelResponse:
"""Crea un nuovo tag (solo admin)."""
svc = LabelService(db)
label = await svc.create_label(current_user.tenant_id, data)
return LabelResponse.model_validate(label)
@router.patch("/labels/{label_id}", response_model=LabelResponse)
async def update_label(
label_id: uuid.UUID,
data: LabelUpdate,
current_user: AdminUser,
db: DB,
) -> LabelResponse:
"""Modifica un tag esistente (solo admin)."""
svc = LabelService(db)
label = await svc.update_label(current_user.tenant_id, label_id, data)
return LabelResponse.model_validate(label)
@router.delete("/labels/{label_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_label(
label_id: uuid.UUID,
current_user: AdminUser,
db: DB,
) -> None:
"""Elimina un tag (solo admin). Viene rimosso automaticamente da tutti i messaggi."""
svc = LabelService(db)
await svc.delete_label(current_user.tenant_id, label_id)
# ─── Tag su singolo messaggio ─────────────────────────────────────────────────
@router.get("/messages/{message_id}/labels", response_model=list[LabelResponse])
async def get_message_labels(
message_id: uuid.UUID,
current_user: CurrentUser,
db: DB,
) -> list[LabelResponse]:
"""Elenca i tag assegnati a un messaggio."""
await _check_message_access(message_id, current_user, db)
svc = LabelService(db)
labels = await svc.get_message_labels(message_id, current_user.tenant_id)
return [LabelResponse.model_validate(l) for l in labels]
@router.put("/messages/{message_id}/labels", response_model=list[LabelResponse])
async def set_message_labels(
message_id: uuid.UUID,
data: MessageLabelSetRequest,
current_user: CurrentUser,
db: DB,
) -> list[LabelResponse]:
"""
Sostituisce tutti i tag di un messaggio.
Passare una lista vuota per rimuovere tutti i tag.
"""
await _check_message_access(message_id, current_user, db)
svc = LabelService(db)
labels = await svc.set_message_labels(
message_id, current_user.tenant_id, data.label_ids
)
return [LabelResponse.model_validate(l) for l in labels]
@router.post("/messages/{message_id}/labels/add", response_model=list[LabelResponse])
async def add_message_labels(
message_id: uuid.UUID,
data: MessageLabelAddRequest,
current_user: CurrentUser,
db: DB,
) -> list[LabelResponse]:
"""Aggiunge tag a un messaggio senza rimuovere quelli esistenti."""
await _check_message_access(message_id, current_user, db)
svc = LabelService(db)
labels = await svc.add_message_labels(
message_id, current_user.tenant_id, data.label_ids
)
return [LabelResponse.model_validate(l) for l in labels]
@router.post("/messages/{message_id}/labels/remove", response_model=list[LabelResponse])
async def remove_message_labels(
message_id: uuid.UUID,
data: MessageLabelRemoveRequest,
current_user: CurrentUser,
db: DB,
) -> list[LabelResponse]:
"""Rimuove specifici tag da un messaggio."""
await _check_message_access(message_id, current_user, db)
svc = LabelService(db)
labels = await svc.remove_message_labels(
message_id, current_user.tenant_id, data.label_ids
)
return [LabelResponse.model_validate(l) for l in labels]
# ─── Bulk labels ──────────────────────────────────────────────────────────────
@router.post("/messages/bulk-labels", response_model=MessageBulkLabelResponse)
async def bulk_labels(
data: MessageBulkLabelRequest,
current_user: CurrentUser,
db: DB,
) -> MessageBulkLabelResponse:
"""
Aggiunge o rimuove tag da più messaggi in blocco.
- action="add": aggiunge i tag ai messaggi indicati
- action="remove": rimuove i tag dai messaggi indicati
I messaggi non accessibili all'utente vengono silenziosamente ignorati.
"""
if not data.message_ids or not data.label_ids:
return MessageBulkLabelResponse(updated=0)
# Per utenti non-admin filtra per caselle accessibili
message_ids = [str(mid) for mid in data.message_ids]
if not current_user.is_admin:
from app.services.permission_service import PermissionService
perm_svc = PermissionService(db)
visible = await perm_svc.get_visible_mailboxes(current_user)
visible_set = set(visible) if visible else set()
# Filtra i messaggi per caselle visibili
result = await db.execute(
select(Message.id).where(
Message.id.in_(data.message_ids),
Message.tenant_id == current_user.tenant_id,
Message.mailbox_id.in_(visible_set),
)
)
filtered_ids = list(result.scalars().all())
else:
filtered_ids = data.message_ids
svc = LabelService(db)
if data.action == "add":
updated = await svc.bulk_add_labels(
filtered_ids, current_user.tenant_id, data.label_ids
)
else:
updated = await svc.bulk_remove_labels(
filtered_ids, current_user.tenant_id, data.label_ids
)
return MessageBulkLabelResponse(updated=updated)