"""Pydantic schemas for the Mail plugin.""" from __future__ import annotations from datetime import datetime from pydantic import BaseModel, Field # ─── Mail Accounts ─── class MailAccountCreate(BaseModel): email_address: str = Field(..., min_length=1, max_length=255) display_name: str = Field(default="", max_length=255) imap_host: str = Field(..., min_length=1, max_length=255) imap_port: int = Field(default=993, ge=1, le=65535) imap_ssl: bool = True smtp_host: str = Field(..., min_length=1, max_length=255) smtp_port: int = Field(default=587, ge=1, le=65535) smtp_tls: bool = True username: str = Field(..., min_length=1, max_length=255) password: str = Field(..., min_length=1, max_length=512) is_shared: bool = False class MailAccountUpdate(BaseModel): email_address: str | None = Field(None, min_length=1, max_length=255) display_name: str | None = Field(None, max_length=255) imap_host: str | None = Field(None, min_length=1, max_length=255) imap_port: int | None = Field(None, ge=1, le=65535) imap_ssl: bool | None = None smtp_host: str | None = Field(None, min_length=1, max_length=255) smtp_port: int | None = Field(None, ge=1, le=65535) smtp_tls: bool | None = None username: str | None = Field(None, min_length=1, max_length=255) password: str | None = Field(None, min_length=1, max_length=512) is_shared: bool | None = None is_active: bool | None = None class MailAccountResponse(BaseModel): id: str email_address: str display_name: str imap_host: str imap_port: int imap_ssl: bool smtp_host: str smtp_port: int smtp_tls: bool username: str is_shared: bool is_active: bool created_at: datetime | None = None updated_at: datetime | None = None class SharedMailboxUserAssign(BaseModel): user_ids: list[str] = Field(default_factory=list) class DelegateCreate(BaseModel): delegate_user_id: str access_level: str = Field("read", pattern="^(read|full)$") class SendPermissionCreate(BaseModel): user_id: str # ─── Mail Folders ─── class MailFolderCreate(BaseModel): account_id: str name: str = Field(..., min_length=1, max_length=255) imap_name: str = Field(..., min_length=1, max_length=255) parent_id: str | None = None class MailFolderUpdate(BaseModel): name: str | None = Field(None, min_length=1, max_length=255) class MailFolderResponse(BaseModel): id: str account_id: str name: str imap_name: str parent_id: str | None = None is_standard: bool unread_count: int total_count: int # ─── Mails ─── class MailSendRequest(BaseModel): account_id: str to: list[str] = Field(..., min_length=1) cc: list[str] = Field(default_factory=list) bcc: list[str] = Field(default_factory=list) subject: str = Field(default="", max_length=512) body_html: str = "" body_text: str = "" in_reply_to: str | None = None references_header: str | None = None signature_id: str | None = None template_id: str | None = None template_vars: dict[str, str] = Field(default_factory=dict) attachments: list[str] = Field(default_factory=list) class MailReplyRequest(BaseModel): body_html: str = "" body_text: str = "" reply_to_all: bool = False signature_id: str | None = None class MailForwardRequest(BaseModel): to: list[str] = Field(..., min_length=1) cc: list[str] = Field(default_factory=list) body_html: str = "" body_text: str = "" signature_id: str | None = None class MailFlagsUpdate(BaseModel): is_seen: bool | None = None is_flagged: bool | None = None is_draft: bool | None = None is_answered: bool | None = None is_forwarded: bool | None = None class MailResponse(BaseModel): id: str account_id: str folder_id: str message_id: str thread_id: str in_reply_to: str | None = None subject: str from_address: str to_addresses: str cc_addresses: str bcc_addresses: str body_text: str body_html_sanitized: str is_seen: bool is_flagged: bool is_draft: bool is_answered: bool is_forwarded: bool has_attachments: bool size_bytes: int received_at: datetime | None = None sent_at: datetime | None = None contact_id: str | None = None company_id: str | None = None attachments: list[dict] = Field(default_factory=list) labels: list[dict] = Field(default_factory=list) class MailListResponse(BaseModel): mails: list[MailResponse] total: int page: int page_size: int class MailLinkRequest(BaseModel): contact_id: str | None = None company_id: str | None = None class MailCreateEventRequest(BaseModel): calendar_id: str title: str | None = None start: str | None = None end: str | None = None description: str | None = None class ThreadResponse(BaseModel): thread_id: str subject: str mail_count: int mails: list[MailResponse] = Field(default_factory=list) # ─── Templates ─── class MailTemplateCreate(BaseModel): name: str = Field(..., min_length=1, max_length=255) subject: str = Field(default="", max_length=512) body_html: str = "" class MailTemplateResponse(BaseModel): id: str name: str subject: str body_html: str class TemplateSubstituteRequest(BaseModel): template_id: str variables: dict[str, str] = Field(default_factory=dict) # ─── Signatures ─── class MailSignatureCreate(BaseModel): name: str = Field(..., min_length=1, max_length=255) body_html: str = "" account_id: str | None = None is_default: bool = False class MailSignatureResponse(BaseModel): id: str name: str body_html: str account_id: str | None = None is_default: bool # ─── Rules ─── class MailRuleCreate(BaseModel): name: str = Field(..., min_length=1, max_length=255) account_id: str | None = None priority: int = 0 is_active: bool = True conditions: dict = Field(default_factory=dict) actions: dict = Field(default_factory=dict) class MailRuleResponse(BaseModel): id: str name: str account_id: str | None = None priority: int is_active: bool conditions: dict actions: dict # ─── Vacation ─── class VacationConfig(BaseModel): account_id: str is_enabled: bool = True subject: str = Field(default="Out of Office", max_length=512) body_text: str = "" body_html: str = "" start_date: str | None = None end_date: str | None = None class VacationResponse(BaseModel): is_enabled: bool subject: str body_text: str body_html: str # ─── PGP ─── class PgpKeyImport(BaseModel): private_key_armored: str = Field(..., min_length=1) passphrase: str = Field(default="", max_length=255) class PgpKeyResponse(BaseModel): id: str key_id: str public_key_armored: str class ContactPgpKeyCreate(BaseModel): public_key_armored: str = Field(..., min_length=1) class ContactPgpKeyResponse(BaseModel): id: str contact_id: str public_key_armored: str key_id: str class PgpEncryptRequest(BaseModel): recipient_email: str plaintext: str class PgpDecryptRequest(BaseModel): ciphertext: str passphrase: str = "" # ─── Labels ─── class MailLabelCreate(BaseModel): name: str = Field(..., min_length=1, max_length=100) color: str = Field(default="#808080", max_length=20) class MailLabelResponse(BaseModel): id: str name: str color: str class MailLabelAssign(BaseModel): label_id: str # ─── Search ─── class MailSearchResponse(BaseModel): results: list[MailResponse] total: int # ─── Connection Test ─── class ConnectionTestRequest(BaseModel): imap_host: str imap_port: int = 993 smtp_host: str smtp_port: int = 587 username: str password: str class ConnectionTestResponse(BaseModel): imap_ok: bool smtp_ok: bool message: str