Files
PecHub/backend/app/schemas/mailbox.py
T
2026-03-27 14:20:59 +01:00

131 lines
4.5 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.
"""
Schemas Pydantic per le caselle PEC (mailboxes).
Le credenziali vengono accettate in chiaro dalla API e cifrate nel service.
"""
import uuid
from datetime import datetime
from typing import Literal
from pydantic import BaseModel, EmailStr, Field, field_validator
# ─── Request schemas ──────────────────────────────────────────────────────────
class MailboxCreateRequest(BaseModel):
"""Dati per creare una nuova casella PEC."""
email_address: EmailStr = Field(..., description="Indirizzo email PEC")
display_name: str | None = Field(None, max_length=255, description="Nome visualizzato")
provider: str | None = Field(None, max_length=100, description="Provider PEC (aruba, namirial...)")
# Credenziali IMAP (in chiaro, cifrate prima della persistenza)
imap_host: str = Field(..., min_length=1, max_length=255, description="Host IMAP")
imap_port: int = Field(993, ge=1, le=65535, description="Porta IMAP")
imap_user: str = Field(..., min_length=1, max_length=255, description="Username IMAP")
imap_pass: str = Field(..., min_length=1, description="Password IMAP")
imap_use_ssl: bool = Field(True, description="Usa SSL/TLS per IMAP")
# Credenziali SMTP (in chiaro, cifrate prima della persistenza)
smtp_host: str = Field(..., min_length=1, max_length=255, description="Host SMTP")
smtp_port: int = Field(465, ge=1, le=65535, description="Porta SMTP")
smtp_user: str = Field(..., min_length=1, max_length=255, description="Username SMTP")
smtp_pass: str = Field(..., min_length=1, description="Password SMTP")
smtp_use_tls: bool = Field(True, description="Usa TLS per SMTP")
@field_validator("imap_port", "smtp_port")
@classmethod
def validate_port(cls, v: int) -> int:
if v not in range(1, 65536):
raise ValueError("Porta non valida")
return v
class MailboxUpdateRequest(BaseModel):
"""Aggiornamento parziale di una casella PEC."""
display_name: str | None = Field(None, max_length=255)
provider: str | None = Field(None, max_length=100)
status: Literal["active", "paused"] | None = None
# Aggiornamento credenziali IMAP (opzionale)
imap_host: str | None = Field(None, min_length=1, max_length=255)
imap_port: int | None = Field(None, ge=1, le=65535)
imap_user: str | None = Field(None, min_length=1, max_length=255)
imap_pass: str | None = None
imap_use_ssl: bool | None = None
# Aggiornamento credenziali SMTP (opzionale)
smtp_host: str | None = Field(None, min_length=1, max_length=255)
smtp_port: int | None = Field(None, ge=1, le=65535)
smtp_user: str | None = Field(None, min_length=1, max_length=255)
smtp_pass: str | None = None
smtp_use_tls: bool | None = None
# ─── Response schemas ─────────────────────────────────────────────────────────
class MailboxResponse(BaseModel):
"""Risposta API casella PEC NON include mai le credenziali in chiaro."""
id: uuid.UUID
tenant_id: uuid.UUID
email_address: str
display_name: str | None
provider: str | None
# Info IMAP senza credenziali
imap_host: str
imap_port: int
imap_use_ssl: bool
# Info SMTP senza credenziali
smtp_host: str
smtp_port: int
smtp_use_tls: bool
# Stato sync
status: str
last_sync_at: datetime | None
last_sync_uid: int | None
sync_error_msg: str | None
sync_error_count: int
created_by: uuid.UUID | None
created_at: datetime
updated_at: datetime
model_config = {"from_attributes": True}
class MailboxListResponse(BaseModel):
items: list[MailboxResponse]
total: int
page: int
page_size: int
class ConnectionTestRequest(BaseModel):
"""Test connessione IMAP/SMTP per una casella esistente o per credenziali nuove."""
protocol: Literal["imap", "smtp"] = Field("imap", description="Protocollo da testare")
class ConnectionTestResult(BaseModel):
success: bool
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]