mirror of
https://github.com/idrainformatica/PecFlow.git
synced 2026-06-16 12:45:42 +02:00
Trash!
This commit is contained in:
@@ -4,7 +4,8 @@ Router messaggi PEC.
|
||||
Fornisce:
|
||||
- GET /messages – lista messaggi con filtri (inbox/sent/search/...)
|
||||
- GET /messages/{id} – singolo messaggio
|
||||
- PATCH /messages/{id} – aggiorna flags (is_read, is_starred, is_archived)
|
||||
- PATCH /messages/{id} – aggiorna flags (is_read, is_starred, is_archived, is_trashed)
|
||||
- PATCH /messages/bulk – aggiorna in blocco piu messaggi
|
||||
- GET /messages/{id}/attachments – lista allegati
|
||||
- GET /messages/{id}/attachments/{att_id}/download – scarica allegato da MinIO
|
||||
- GET /messages/{id}/receipts – ricevute (messaggi figlio)
|
||||
@@ -58,7 +59,7 @@ def _apply_vbox_rule(q, field: str, operator: str, value: str):
|
||||
elif field == "from_address":
|
||||
col = Message.from_address
|
||||
elif field == "to_address":
|
||||
# to_addresses è ARRAY(Text) – converte in stringa per il confronto
|
||||
# to_addresses e ARRAY(Text) – converte in stringa per il confronto
|
||||
arr_text = func.array_to_string(Message.to_addresses, ",")
|
||||
if operator == "contains":
|
||||
return q.where(arr_text.ilike(f"%{value}%"))
|
||||
@@ -94,7 +95,7 @@ async def _get_visible_mailbox_ids(
|
||||
) -> Optional[list[uuid.UUID]]:
|
||||
"""
|
||||
Per utenti non-admin restituisce la lista di mailbox_id accessibili.
|
||||
Restituisce None se l'utente è admin (accesso illimitato al tenant).
|
||||
Restituisce None se l'utente e admin (accesso illimitato al tenant).
|
||||
"""
|
||||
if user.is_admin:
|
||||
return None # nessun filtro per admin
|
||||
@@ -111,10 +112,10 @@ async def _resolve_message(
|
||||
) -> Message:
|
||||
"""Carica il messaggio e verifica i permessi di accesso.
|
||||
|
||||
L'accesso è consentito se:
|
||||
1. L'utente è admin del tenant, oppure
|
||||
L'accesso e consentito se:
|
||||
1. L'utente e admin del tenant, oppure
|
||||
2. L'utente ha un permesso diretto can_read sulla casella, oppure
|
||||
3. L'utente è assegnato a una Virtual Box attiva che include la casella.
|
||||
3. L'utente e assegnato a una Virtual Box attiva che include la casella.
|
||||
"""
|
||||
result = await db.execute(
|
||||
select(Message)
|
||||
@@ -182,6 +183,7 @@ async def list_messages(
|
||||
is_read: Optional[bool] = Query(None),
|
||||
is_starred: Optional[bool] = Query(None),
|
||||
is_archived: Optional[bool] = Query(False),
|
||||
is_trashed: Optional[bool] = Query(False),
|
||||
search: Optional[str] = Query(None, max_length=200),
|
||||
pec_type: Optional[str] = Query(None),
|
||||
# Paginazione
|
||||
@@ -192,6 +194,7 @@ async def list_messages(
|
||||
Elenca i messaggi PEC con filtri opzionali.
|
||||
|
||||
- `is_archived=False` (default) esclude i messaggi archiviati.
|
||||
- `is_trashed=False` (default) esclude i messaggi nel cestino.
|
||||
- `search` cerca su subject, from_address, to_addresses.
|
||||
- `vbox_id` filtra per Virtual Box assegnata all'utente corrente.
|
||||
"""
|
||||
@@ -278,6 +281,9 @@ async def list_messages(
|
||||
if is_archived is not None:
|
||||
q = q.where(Message.is_archived == is_archived)
|
||||
|
||||
if is_trashed is not None:
|
||||
q = q.where(Message.is_trashed == is_trashed)
|
||||
|
||||
if search:
|
||||
term = f"%{search}%"
|
||||
q = q.where(
|
||||
@@ -325,7 +331,7 @@ async def bulk_update_messages(
|
||||
db: DB,
|
||||
) -> MessageBulkUpdateResponse:
|
||||
"""
|
||||
Aggiorna in blocco i flag operativi (is_starred, is_archived) di più messaggi.
|
||||
Aggiorna in blocco i flag operativi (is_starred, is_archived, is_trashed) di piu messaggi.
|
||||
|
||||
Restituisce il numero di messaggi aggiornati e la lista aggiornata.
|
||||
I messaggi non trovati o non accessibili vengono silenziosamente ignorati.
|
||||
@@ -352,6 +358,8 @@ async def bulk_update_messages(
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
for message in messages:
|
||||
if data.is_read is not None:
|
||||
message.is_read = data.is_read
|
||||
if data.is_starred is not None:
|
||||
message.is_starred = data.is_starred
|
||||
if data.is_archived is not None:
|
||||
@@ -360,6 +368,12 @@ async def bulk_update_messages(
|
||||
message.archived_at = now
|
||||
elif not data.is_archived:
|
||||
message.archived_at = None
|
||||
if data.is_trashed is not None:
|
||||
message.is_trashed = data.is_trashed
|
||||
if data.is_trashed and not message.trashed_at:
|
||||
message.trashed_at = now
|
||||
elif not data.is_trashed:
|
||||
message.trashed_at = None
|
||||
|
||||
await db.commit()
|
||||
|
||||
@@ -399,7 +413,7 @@ async def update_message(
|
||||
) -> MessageResponse:
|
||||
"""
|
||||
Aggiorna i flag operativi di un messaggio:
|
||||
is_read, is_starred, is_archived.
|
||||
is_read, is_starred, is_archived, is_trashed.
|
||||
"""
|
||||
message = await _resolve_message(message_id, current_user, db)
|
||||
|
||||
@@ -413,6 +427,12 @@ async def update_message(
|
||||
message.archived_at = datetime.now(timezone.utc)
|
||||
elif not data.is_archived:
|
||||
message.archived_at = None
|
||||
if data.is_trashed is not None:
|
||||
message.is_trashed = data.is_trashed
|
||||
if data.is_trashed and not message.trashed_at:
|
||||
message.trashed_at = datetime.now(timezone.utc)
|
||||
elif not data.is_trashed:
|
||||
message.trashed_at = None
|
||||
|
||||
await db.commit()
|
||||
# Re-query con selectinload per evitare MissingGreenlet sui labels
|
||||
@@ -422,7 +442,13 @@ async def update_message(
|
||||
.options(selectinload(Message.labels))
|
||||
)
|
||||
message = refreshed.scalar_one()
|
||||
return MessageResponse.model_validate(messa
|
||||
return MessageResponse.model_validate(message)
|
||||
|
||||
|
||||
@router.get("/{message_id}/attachments", response_model=list[AttachmentResponse])
|
||||
async def list_attachments(
|
||||
message_id: uuid.UUID,
|
||||
current_user: CurrentUser,
|
||||
db: DB,
|
||||
) -> list[AttachmentResponse]:
|
||||
"""Elenca gli allegati di un messaggio."""
|
||||
@@ -473,7 +499,7 @@ async def download_attachment(
|
||||
secure=settings.minio_use_ssl,
|
||||
)
|
||||
|
||||
# storage_path è del tipo "tenant_id/attachments/filename"
|
||||
# storage_path e del tipo "tenant_id/attachments/filename"
|
||||
storage_path = attachment.storage_path
|
||||
response = await client.get_object(settings.minio_bucket, storage_path)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user