Fix vari frontend

This commit is contained in:
2026-03-27 14:20:59 +01:00
parent bb2060c1ae
commit ab6db28449
8 changed files with 276 additions and 22 deletions
+97
View File
@@ -21,6 +21,8 @@ from app.schemas.mailbox import (
MailboxCreateRequest,
MailboxListResponse,
MailboxResponse,
MailboxSyncResponse,
MailboxUnreadCountsResponse,
MailboxUpdateRequest,
)
from app.services.mailbox_service import MailboxService
@@ -123,6 +125,52 @@ async def list_mailboxes(
)
@router.get("/unread-counts", response_model=MailboxUnreadCountsResponse)
async def get_unread_counts(
current_user: CurrentUser,
db: DB,
) -> MailboxUnreadCountsResponse:
"""
Restituisce il numero di messaggi non letti per ciascuna casella accessibile.
Usato dalla sidebar per mostrare i badge per casella.
- Admin: conta su tutte le caselle del tenant.
- Operatori: solo le caselle con permesso can_read.
"""
from sqlalchemy import func, select
from app.models.message import Message
# Determina le caselle visibili
if current_user.is_admin:
visible_ids = None # nessun filtro
else:
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 MailboxUnreadCountsResponse(counts={})
q = (
select(Message.mailbox_id, func.count().label("cnt"))
.where(
Message.tenant_id == current_user.tenant_id,
Message.is_read == False, # noqa: E712
Message.direction == "inbound",
Message.is_trashed == False, # noqa: E712
Message.is_archived == False, # noqa: E712
Message.parent_message_id.is_(None),
)
.group_by(Message.mailbox_id)
)
if visible_ids is not None:
from app.models.mailbox import Mailbox
q = q.where(Message.mailbox_id.in_(visible_ids))
rows = (await db.execute(q)).all()
counts = {str(row.mailbox_id): row.cnt for row in rows}
return MailboxUnreadCountsResponse(counts=counts)
@router.get("/{mailbox_id}", response_model=MailboxResponse)
async def get_mailbox(
mailbox_id: uuid.UUID,
@@ -200,3 +248,52 @@ async def test_mailbox_connection(
tenant_id=current_user.tenant_id,
data=data,
)
@router.post(
"/{mailbox_id}/sync",
response_model=MailboxSyncResponse,
)
async def force_sync_mailbox(
mailbox_id: uuid.UUID,
current_user: AdminUser,
db: DB,
) -> MailboxSyncResponse:
"""
Forza una sincronizzazione IMAP immediata della casella.
Accoda il job sync_mailbox nel worker tramite arq/Redis.
Utile dopo un errore di connessione o per forzare un aggiornamento.
Richiede ruolo **admin**.
"""
from app.core.exceptions import NotFoundError
svc = _svc(db)
mailbox = await svc.get_mailbox(mailbox_id, current_user.tenant_id)
if mailbox.status == "deleted":
raise NotFoundError("Casella non trovata o eliminata")
try:
from arq.connections import RedisSettings, create_pool as arq_create_pool
from app.config import get_settings
cfg = get_settings()
arq_settings = RedisSettings.from_dsn(cfg.redis_url)
arq_redis = await arq_create_pool(arq_settings)
await arq_redis.enqueue_job("sync_mailbox", str(mailbox_id))
await arq_redis.aclose()
except Exception as exc:
from app.core.logging import get_logger
logger = get_logger(__name__)
logger.error(f"[force_sync] Impossibile accodare job per {mailbox_id}: {exc}")
return MailboxSyncResponse(
status="error",
mailbox_id=mailbox_id,
message=f"Impossibile accodare il job: {exc}",
)
return MailboxSyncResponse(
status="enqueued",
mailbox_id=mailbox_id,
message=f"Sincronizzazione avviata per {mailbox.email_address}",
)
+12
View File
@@ -116,3 +116,15 @@ class ConnectionTestResult(BaseModel):
message: str
latency_ms: float | None = None
capabilities: list[str] | None = None # Solo per IMAP
class MailboxSyncResponse(BaseModel):
"""Risposta all'accodamento di un job di sincronizzazione manuale."""
status: str
mailbox_id: uuid.UUID
message: str
class MailboxUnreadCountsResponse(BaseModel):
"""Conteggio messaggi non letti per casella."""
counts: dict[str, int]