mirror of
https://github.com/idrainformatica/PecFlow.git
synced 2026-06-16 20:55:41 +02:00
fase 4
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
"""
|
||||
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()
|
||||
Reference in New Issue
Block a user