Files
leocrm/app/plugins/builtins/mail/schemas.py
T
Agent Zero 9da04057b9 feat: mail list font size, remove Termin toolbar, rename Flagge to Markierung with symbol picker
- Mail list: title and subject font text-xs -> text-sm
- Remove Termin (create-event) toolbar button
- Rename Flagge -> Markierung with select dropdown (star, flag, bookmark, important, question)
- Backend: add flag_type column to Mail model + migration 0006
- Backend: flag_type in MailFlagsUpdate schema, update_flags route, mail_to_response
- Frontend: flag_type in Mail interface, FlagUpdatePayload, MailList symbol display
2026-07-20 10:39:16 +02:00

383 lines
9.2 KiB
Python

"""Pydantic schemas for the Mail plugin."""
from __future__ import annotations
from datetime import datetime
from pydantic import BaseModel, ConfigDict, Field
# ─── Mail Accounts ───
class MailAccountCreate(BaseModel):
model_config = ConfigDict(populate_by_name=True)
email_address: str = Field(..., min_length=1, max_length=255, alias="email")
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(default="", min_length=0, max_length=255)
password: str = Field(..., min_length=1, max_length=512)
is_shared: bool = False
sent_folder_imap_name: str | None = Field(None, max_length=255)
drafts_folder_imap_name: str | None = Field(None, max_length=255)
spam_folder_imap_name: str | None = Field(None, max_length=255)
trash_folder_imap_name: str | None = Field(None, max_length=255)
class MailAccountUpdate(BaseModel):
model_config = ConfigDict(populate_by_name=True)
email_address: str | None = Field(None, min_length=1, max_length=255, alias="email")
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
sent_folder_imap_name: str | None = Field(None, max_length=255)
drafts_folder_imap_name: str | None = Field(None, max_length=255)
spam_folder_imap_name: str | None = Field(None, max_length=255)
trash_folder_imap_name: str | None = Field(None, max_length=255)
class MailAccountResponse(BaseModel):
id: str
email: 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
sent_folder_imap_name: str | None = None
drafts_folder_imap_name: str | None = None
spam_folder_imap_name: str | None = None
trash_folder_imap_name: str | None = None
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
flag_type: str | 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
flag_type: str | None = None
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 MailMoveRequest(BaseModel):
target_folder_id: str
class MailDraftCreate(BaseModel):
account_id: str
to: list[str] = Field(default_factory=list)
cc: list[str] = Field(default_factory=list)
bcc: list[str] = Field(default_factory=list)
subject: str = ""
body_text: str = ""
body_html: str = ""
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