be81fe52cf
Check Cross-Plugin Imports / check (push) Has been cancelled
serializers.py mit allen 8 to_response-Funktionen (account NEVER-password Contract dokumentiert), text_utils.py mit extract_email_addresses+_strip_html als pure functions. Re-Export via noqa F401 in services.py — alle Consumer unveraendert. services.py jetzt 2786 Z. (von 3087). Beweis: mail+sig_label_routes 51/51 passed nach ruff --fix; ruff clean.
170 lines
5.8 KiB
Python
170 lines
5.8 KiB
Python
"""Response serializers for the Mail plugin ORM models.
|
|
|
|
Extracted from services.py as part of the God-object split (BUG-018 pilot).
|
|
Re-exported by ``app.plugins.builtins.mail.services``.
|
|
|
|
account_to_response NEVER includes passwords — security-critical contract.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
|
|
from app.plugins.builtins.mail.models import (
|
|
Mail,
|
|
MailAccount,
|
|
MailAttachment,
|
|
MailFolder,
|
|
MailLabel,
|
|
MailRule,
|
|
MailSignature,
|
|
MailTemplate,
|
|
)
|
|
from app.plugins.builtins.mail.text_utils import (
|
|
extract_email_addresses, # noqa: F401 — re-exported for backwards compat
|
|
)
|
|
|
|
|
|
def attachment_to_response(att: MailAttachment) -> dict:
|
|
"""Convert a MailAttachment ORM object to a response dict."""
|
|
return {
|
|
"id": str(att.id),
|
|
"mail_id": str(att.mail_id),
|
|
"filename": att.filename,
|
|
"mime_type": att.mime_type,
|
|
"size_bytes": att.size_bytes,
|
|
"size": att.size_bytes, # alias for frontend compatibility
|
|
"content_id": att.content_id,
|
|
"is_inline": bool(att.content_id),
|
|
"dms_file_id": str(att.dms_file_id) if att.dms_file_id else None,
|
|
}
|
|
|
|
|
|
def account_to_response(account: MailAccount) -> dict:
|
|
"""Convert MailAccount to response dict, NEVER including password."""
|
|
return {
|
|
"id": str(account.id),
|
|
"email": account.email_address,
|
|
"email_address": account.email_address,
|
|
"display_name": account.display_name,
|
|
"imap_host": account.imap_host,
|
|
"imap_port": account.imap_port,
|
|
"imap_ssl": account.imap_ssl,
|
|
"smtp_host": account.smtp_host,
|
|
"smtp_port": account.smtp_port,
|
|
"smtp_tls": account.smtp_tls,
|
|
"username": account.username,
|
|
"is_shared": account.is_shared,
|
|
"is_active": account.is_active,
|
|
"sent_folder_imap_name": account.sent_folder_imap_name,
|
|
"drafts_folder_imap_name": account.drafts_folder_imap_name,
|
|
"spam_folder_imap_name": account.spam_folder_imap_name,
|
|
"trash_folder_imap_name": account.trash_folder_imap_name,
|
|
"created_at": account.created_at,
|
|
"updated_at": account.updated_at,
|
|
}
|
|
|
|
|
|
def mail_to_response(
|
|
mail: Mail,
|
|
attachments: list[MailAttachment] | None = None,
|
|
labels: list[MailLabel] | None = None,
|
|
) -> dict:
|
|
"""Convert a Mail ORM object to a response dict."""
|
|
resp = {
|
|
"id": str(mail.id),
|
|
"account_id": str(mail.account_id),
|
|
"folder_id": str(mail.folder_id),
|
|
"message_id": mail.message_id,
|
|
"thread_id": mail.thread_id,
|
|
"in_reply_to": mail.in_reply_to,
|
|
"subject": mail.subject,
|
|
"from_address": mail.from_address,
|
|
"from_name": mail.from_address.split("<")[0].strip().strip('"') if "<" in mail.from_address else mail.from_address,
|
|
"to_addresses": extract_email_addresses(mail.to_addresses) if mail.to_addresses else [],
|
|
"cc_addresses": extract_email_addresses(mail.cc_addresses) if mail.cc_addresses else [],
|
|
"bcc_addresses": extract_email_addresses(mail.bcc_addresses) if mail.bcc_addresses else [],
|
|
"body_text": mail.body_text,
|
|
"body_html": mail.body_html if mail.body_html else None,
|
|
"body_html_sanitized": mail.body_html_sanitized,
|
|
"sanitized_html": mail.body_html_sanitized,
|
|
"date": mail.received_at.isoformat() if mail.received_at else (mail.sent_at.isoformat() if mail.sent_at else None),
|
|
"is_seen": mail.is_seen,
|
|
"is_flagged": mail.is_flagged,
|
|
"flag_type": getattr(mail, 'flag_type', None),
|
|
"is_draft": mail.is_draft,
|
|
"is_answered": mail.is_answered,
|
|
"is_forwarded": mail.is_forwarded,
|
|
"has_attachments": mail.has_attachments,
|
|
"size_bytes": mail.size_bytes,
|
|
"received_at": mail.received_at,
|
|
"sent_at": mail.sent_at,
|
|
"contact_id": str(mail.contact_id) if mail.contact_id else None,
|
|
"attachments": [],
|
|
"labels": [],
|
|
}
|
|
if attachments:
|
|
resp["attachments"] = [attachment_to_response(a) for a in attachments]
|
|
if labels:
|
|
resp["labels"] = [
|
|
{"id": str(lbl.id), "name": lbl.name, "color": lbl.color} for lbl in labels
|
|
]
|
|
return resp
|
|
|
|
|
|
def folder_to_response(folder: MailFolder) -> dict:
|
|
"""Convert MailFolder to response dict."""
|
|
return {
|
|
"id": str(folder.id),
|
|
"account_id": str(folder.account_id),
|
|
"name": folder.name,
|
|
"imap_name": folder.imap_name,
|
|
"parent_id": str(folder.parent_id) if folder.parent_id else None,
|
|
"is_standard": folder.is_standard,
|
|
"unread_count": folder.unread_count,
|
|
"total_count": folder.total_count,
|
|
}
|
|
|
|
|
|
def rule_to_response(rule: MailRule) -> dict:
|
|
"""Convert MailRule to response dict."""
|
|
return {
|
|
"id": str(rule.id),
|
|
"name": rule.name,
|
|
"account_id": str(rule.account_id) if rule.account_id else None,
|
|
"priority": rule.priority,
|
|
"is_active": rule.is_active,
|
|
"conditions": json.loads(rule.conditions) if rule.conditions else {},
|
|
"actions": json.loads(rule.actions) if rule.actions else {},
|
|
}
|
|
|
|
|
|
def template_to_response(template: MailTemplate) -> dict:
|
|
"""Convert MailTemplate to response dict."""
|
|
return {
|
|
"id": str(template.id),
|
|
"name": template.name,
|
|
"subject": template.subject,
|
|
"body_html": template.body_html,
|
|
}
|
|
|
|
|
|
def signature_to_response(sig: MailSignature) -> dict:
|
|
"""Convert MailSignature to response dict."""
|
|
return {
|
|
"id": str(sig.id),
|
|
"name": sig.name,
|
|
"body_html": sig.body_html,
|
|
"account_id": str(sig.account_id) if sig.account_id else None,
|
|
"is_default": sig.is_default,
|
|
}
|
|
|
|
|
|
def label_to_response(label: MailLabel) -> dict:
|
|
"""Convert MailLabel to response dict."""
|
|
return {
|
|
"id": str(label.id),
|
|
"name": label.name,
|
|
"color": label.color,
|
|
}
|