""" 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