From 881f4817beb6d0fa355cd55755f18ba8b4ac8dbf Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Thu, 16 Jul 2026 23:08:37 +0200 Subject: [PATCH] fix(mail): save attachments for existing emails during sync - IMAP sync now saves attachments for existing emails that have has_attachments=true but no attachment records in the database (retroactive attachment saving) - Replace silent exception catching with logger.warning for attachment save failures - Add logging module import and logger instance - This fixes the issue where 62 emails had has_attachments=true but 0 attachment records --- app/plugins/builtins/mail/services.py | 39 +++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/app/plugins/builtins/mail/services.py b/app/plugins/builtins/mail/services.py index 60abb21..79f476a 100644 --- a/app/plugins/builtins/mail/services.py +++ b/app/plugins/builtins/mail/services.py @@ -4,6 +4,7 @@ from __future__ import annotations import base64 import json +import logging import mimetypes import os import re @@ -39,6 +40,8 @@ from app.plugins.builtins.mail.models import ( VacationSentLog, ) +logger = logging.getLogger(__name__) + # ─── Attachment Storage Helpers ─── MAX_ATTACHMENT_SIZE = 25 * 1024 * 1024 # 25 MB @@ -754,6 +757,38 @@ async def imap_sync_account( ) ).scalar_one_or_none() if existing_mail: + # Save attachments for existing emails that have has_attachments but no records + if attachments and existing_mail.has_attachments: + existing_att_count = ( + await db.execute( + select(text("COUNT(*)")).where( + and_( + MailAttachment.mail_id == existing_mail.id, + MailAttachment.tenant_id == tenant_id, + ) + ) + ) + ).scalar() or 0 + if existing_att_count == 0: + for att_data in attachments: + try: + storage_path = await _save_attachment_to_storage( + existing_mail.id, att_data["filename"], att_data["content"] + ) + attachment = MailAttachment( + tenant_id=tenant_id, + mail_id=existing_mail.id, + filename=_sanitize_filename(att_data["filename"]), + mime_type=att_data["mime_type"], + size_bytes=att_data["size"], + storage_path=storage_path, + content_id=att_data.get("content_id"), + ) + db.add(attachment) + except Exception as att_err: + logger.warning(f"Failed to save attachment for existing mail {existing_mail.id}: {att_err}") + continue + await db.flush() continue mail = Mail( @@ -801,8 +836,8 @@ async def imap_sync_account( content_id=att_data.get("content_id"), ) db.add(attachment) - except Exception: - # Skip attachments that fail to save + except Exception as att_err: + logger.warning(f"Failed to save attachment for new mail {mail.id}: {att_err}") continue await db.flush()