mirror of
https://github.com/idrainformatica/PecFlow.git
synced 2026-06-16 20:55:41 +02:00
158 lines
4.9 KiB
Python
158 lines
4.9 KiB
Python
"""
|
||
Router API – Invio PEC (Fase 4).
|
||
|
||
Endpoint:
|
||
POST /send – invia una nuova PEC (crea Message + SendJob, accoda job)
|
||
GET /send/jobs – lista job di invio del tenant (paginata)
|
||
GET /send/jobs/{id} – dettaglio di un singolo job
|
||
DELETE /send/jobs/{id} – annulla job se ancora pending/retrying
|
||
"""
|
||
|
||
import uuid
|
||
from typing import Annotated
|
||
|
||
from fastapi import APIRouter, Query, status
|
||
|
||
from app.core.exceptions import ForbiddenError
|
||
from app.dependencies import AdminUser, CurrentUser, DB
|
||
from app.schemas.send import SendJobListResponse, SendJobResponse, SendPecRequest
|
||
from app.services.permission_service import PermissionService
|
||
from app.services.send_service import SendService
|
||
|
||
router = APIRouter(prefix="/send", tags=["Invio PEC"])
|
||
|
||
|
||
# ─── Helpers ──────────────────────────────────────────────────────────────────
|
||
|
||
def _svc(db) -> SendService:
|
||
return SendService(db)
|
||
|
||
|
||
def _job_response(job) -> SendJobResponse:
|
||
return SendJobResponse.model_validate(job)
|
||
|
||
|
||
# ─── Endpoints ────────────────────────────────────────────────────────────────
|
||
|
||
|
||
@router.post(
|
||
"",
|
||
response_model=SendJobResponse,
|
||
status_code=status.HTTP_201_CREATED,
|
||
summary="Invia una PEC",
|
||
description=(
|
||
"Crea un messaggio PEC in uscita e accoda il job di invio SMTP. "
|
||
"Il job viene eseguito in background con retry automatico. "
|
||
"Richiede permesso **can_send** sulla casella (gli admin possono inviare da qualsiasi casella del tenant)."
|
||
),
|
||
)
|
||
async def create_send_job(
|
||
data: SendPecRequest,
|
||
current_user: CurrentUser,
|
||
db: DB,
|
||
) -> SendJobResponse:
|
||
svc = _svc(db)
|
||
job = await svc.create_send_job(current_user=current_user, data=data)
|
||
await db.commit()
|
||
# Refresh per ottenere tutti i valori default dal DB
|
||
await db.refresh(job)
|
||
return _job_response(job)
|
||
|
||
|
||
@router.get(
|
||
"/jobs",
|
||
response_model=SendJobListResponse,
|
||
summary="Lista job di invio",
|
||
)
|
||
async def list_send_jobs(
|
||
current_user: CurrentUser,
|
||
db: DB,
|
||
page: int = Query(1, ge=1),
|
||
page_size: int = Query(50, ge=1, le=200),
|
||
mailbox_id: uuid.UUID | None = Query(None),
|
||
status_filter: str | None = Query(
|
||
None,
|
||
alias="status",
|
||
description="Filtra per stato: pending | sending | sent | failed | retrying",
|
||
),
|
||
) -> SendJobListResponse:
|
||
"""
|
||
Elenca i job di invio del tenant.
|
||
|
||
Gli admin vedono tutti i job; gli operatori vedono solo i job
|
||
delle caselle su cui hanno permesso can_read.
|
||
"""
|
||
svc = _svc(db)
|
||
|
||
# Filtro opzionale per casella: verifica accesso se non admin
|
||
if mailbox_id and not current_user.is_admin:
|
||
perm_svc = PermissionService(db)
|
||
if not await perm_svc.check_can_read(current_user, mailbox_id):
|
||
raise ForbiddenError("Accesso alla casella non autorizzato")
|
||
|
||
items, total = await svc.list_send_jobs(
|
||
tenant_id=current_user.tenant_id,
|
||
page=page,
|
||
page_size=page_size,
|
||
mailbox_id=mailbox_id,
|
||
status_filter=status_filter,
|
||
)
|
||
return SendJobListResponse(
|
||
items=[_job_response(j) for j in items],
|
||
total=total,
|
||
page=page,
|
||
page_size=page_size,
|
||
)
|
||
|
||
|
||
@router.get(
|
||
"/jobs/{job_id}",
|
||
response_model=SendJobResponse,
|
||
summary="Dettaglio job di invio",
|
||
)
|
||
async def get_send_job(
|
||
job_id: uuid.UUID,
|
||
current_user: CurrentUser,
|
||
db: DB,
|
||
) -> SendJobResponse:
|
||
"""Recupera lo stato di un singolo job di invio."""
|
||
svc = _svc(db)
|
||
job = await svc.get_send_job(job_id, current_user.tenant_id)
|
||
|
||
# Verifica accesso alla casella se non admin
|
||
if not current_user.is_admin:
|
||
perm_svc = PermissionService(db)
|
||
if not await perm_svc.check_can_read(current_user, job.mailbox_id):
|
||
raise ForbiddenError("Accesso non autorizzato")
|
||
|
||
return _job_response(job)
|
||
|
||
|
||
@router.delete(
|
||
"/jobs/{job_id}",
|
||
status_code=status.HTTP_204_NO_CONTENT,
|
||
summary="Annulla job di invio",
|
||
)
|
||
async def cancel_send_job(
|
||
job_id: uuid.UUID,
|
||
current_user: CurrentUser,
|
||
db: DB,
|
||
) -> None:
|
||
"""
|
||
Annulla un job di invio se è ancora in stato **pending** o **retrying**.
|
||
|
||
Non è possibile annullare un invio già partito (stato sending) o
|
||
completato (sent).
|
||
"""
|
||
svc = _svc(db)
|
||
|
||
# Verifica che l'utente possa agire su questo job
|
||
job = await svc.get_send_job(job_id, current_user.tenant_id)
|
||
if not current_user.is_admin:
|
||
perm_svc = PermissionService(db)
|
||
if not await perm_svc.check_can_send(current_user, job.mailbox_id):
|
||
raise ForbiddenError("Autorizzazione insufficiente per annullare questo invio")
|
||
|
||
await svc.cancel_send_job(job_id, current_user.tenant_id)
|
||
await db.commit()
|