Files
PecHub/backend/app/api/v1/mailboxes.py
T
2026-03-18 17:30:13 +01:00

203 lines
6.0 KiB
Python
Raw 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 caselle PEC CRUD + test connessione.
Permessi:
- admin: CRUD completo su tutte le caselle del tenant
- altri ruoli: solo lettura (caselle accessibili tramite PermissionService)
"""
import uuid
from typing import Annotated
from fastapi import APIRouter, Depends, Query, status
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.exceptions import ForbiddenError
from app.database import get_db
from app.dependencies import AdminUser, CurrentUser, DB
from app.schemas.mailbox import (
ConnectionTestRequest,
ConnectionTestResult,
MailboxCreateRequest,
MailboxListResponse,
MailboxResponse,
MailboxUpdateRequest,
)
from app.services.mailbox_service import MailboxService
router = APIRouter(prefix="/mailboxes", tags=["Mailboxes"])
# ─── Helpers ──────────────────────────────────────────────────────────────────
def _svc(db: AsyncSession) -> MailboxService:
return MailboxService(db)
def _build_response(mailbox, svc: MailboxService) -> MailboxResponse:
return MailboxResponse(**MailboxService.to_response_dict(mailbox))
# ─── Endpoints ───────────────────────────────────────────────────────────────
@router.post("", response_model=MailboxResponse, status_code=status.HTTP_201_CREATED)
async def create_mailbox(
data: MailboxCreateRequest,
current_user: AdminUser,
db: DB,
) -> MailboxResponse:
"""
Crea una nuova casella PEC.
Richiede ruolo **admin** o **super_admin**.
Le credenziali vengono cifrate con AES-256-GCM prima della persistenza.
"""
svc = _svc(db)
mailbox = await svc.create_mailbox(
tenant_id=current_user.tenant_id,
data=data,
created_by=current_user.id,
)
await db.commit()
return _build_response(mailbox, svc)
@router.get("", response_model=MailboxListResponse)
async def list_mailboxes(
current_user: CurrentUser,
db: DB,
page: int = Query(1, ge=1),
page_size: int = Query(50, ge=1, le=200),
) -> MailboxListResponse:
"""
Elenca le caselle PEC.
- Admin vede tutte le caselle del tenant.
- Operatori vedono solo le caselle su cui hanno permesso can_read.
"""
svc = _svc(db)
if current_user.is_admin:
# Admin: tutte le caselle del tenant
items, total = await svc.list_mailboxes(
tenant_id=current_user.tenant_id,
page=page,
page_size=page_size,
)
else:
# Operatori: caselle con permesso
from app.services.permission_service import PermissionService
perm_svc = PermissionService(db)
visible_ids = await perm_svc.get_visible_mailboxes(current_user)
if not visible_ids:
return MailboxListResponse(items=[], total=0, page=page, page_size=page_size)
from sqlalchemy import select
from app.models.mailbox import Mailbox
from sqlalchemy import func
q = (
select(Mailbox)
.where(
Mailbox.id.in_(visible_ids),
Mailbox.status != "deleted",
)
.order_by(Mailbox.created_at.desc())
.offset((page - 1) * page_size)
.limit(page_size)
)
count_q = select(func.count()).select_from(
select(Mailbox.id).where(
Mailbox.id.in_(visible_ids),
Mailbox.status != "deleted",
).subquery()
)
result = await db.execute(q)
items = list(result.scalars().all())
total = (await db.execute(count_q)).scalar_one()
return MailboxListResponse(
items=[_build_response(m, svc) for m in items],
total=total,
page=page,
page_size=page_size,
)
@router.get("/{mailbox_id}", response_model=MailboxResponse)
async def get_mailbox(
mailbox_id: uuid.UUID,
current_user: CurrentUser,
db: DB,
) -> MailboxResponse:
"""Carica una casella PEC per ID."""
svc = _svc(db)
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, mailbox_id):
raise ForbiddenError("Accesso alla casella non autorizzato")
mailbox = await svc.get_mailbox(mailbox_id, current_user.tenant_id)
return _build_response(mailbox, svc)
@router.put("/{mailbox_id}", response_model=MailboxResponse)
async def update_mailbox(
mailbox_id: uuid.UUID,
data: MailboxUpdateRequest,
current_user: AdminUser,
db: DB,
) -> MailboxResponse:
"""
Aggiorna una casella PEC.
Richiede ruolo **admin** o **super_admin**.
Se vengono fornite nuove credenziali, vengono ri-cifrate.
"""
svc = _svc(db)
mailbox = await svc.update_mailbox(
mailbox_id=mailbox_id,
tenant_id=current_user.tenant_id,
data=data,
)
await db.commit()
return _build_response(mailbox, svc)
@router.delete("/{mailbox_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_mailbox(
mailbox_id: uuid.UUID,
current_user: AdminUser,
db: DB,
) -> None:
"""
Soft-delete di una casella PEC (status=deleted).
Richiede ruolo **admin**.
"""
svc = _svc(db)
await svc.delete_mailbox(mailbox_id, current_user.tenant_id)
await db.commit()
@router.post(
"/{mailbox_id}/test-connection",
response_model=ConnectionTestResult,
)
async def test_mailbox_connection(
mailbox_id: uuid.UUID,
data: ConnectionTestRequest,
current_user: AdminUser,
db: DB,
) -> ConnectionTestResult:
"""
Testa la connessione IMAP o SMTP della casella.
Non invia messaggi solo verifica la connessione.
Richiede ruolo **admin**.
"""
svc = _svc(db)
return await svc.test_connection(
mailbox_id=mailbox_id,
tenant_id=current_user.tenant_id,
data=data,
)