fix: IMAP sync - use imap_uid, add sync queue with retry, restore deleted on sync, upload sent mails

- imap_delete_mail: use stored imap_uid instead of Message-ID search, raise on failure
- imap_move_mail: same imap_uid fix, raise on failure
- New MailSyncQueue model + migration 0007 for pending IMAP operations
- delete_mail route: queues failed IMAP delete for retry
- move_mail route: queues failed IMAP move for retry
- process_sync_queue: retries pending delete/move operations in auto-sync loop
- Auto-sync loop: processes sync queue before pulling new mails
- Restore soft-deleted mails if they still exist on IMAP server during sync
- Upload sent mails to IMAP Sent folder via APPEND after SMTP send
- Plugin version bumped to 1.2.0
This commit is contained in:
Agent Zero
2026-07-20 11:31:17 +02:00
parent 2a33b5706a
commit c24a86bc90
5 changed files with 210 additions and 45 deletions
+25 -4
View File
@@ -31,6 +31,7 @@ from app.plugins.builtins.mail.models import (
MailLabelAssignment,
MailRule,
MailSignature,
MailSyncQueue,
MailTemplate,
PgpKey,
)
@@ -1531,12 +1532,22 @@ async def delete_mail(
# Soft-delete: set deleted_at = now()
mail.deleted_at = datetime.now(UTC)
await db.flush()
# IMAP delete: non-critical, errors logged but DB soft-delete still succeeds
# IMAP delete: queue for retry if it fails
try:
await mail_services.imap_delete_mail(db, m_id, tenant_id)
except Exception as exc:
import logging
logging.getLogger(__name__).warning("IMAP delete failed for mail %s: %s", m_id, exc)
logging.getLogger(__name__).warning("IMAP delete failed for mail %s: %s, queuing for retry", m_id, exc)
queue_entry = MailSyncQueue(
tenant_id=tenant_id,
mail_id=m_id,
operation="delete",
payload={},
status="pending",
last_error=str(exc),
)
db.add(queue_entry)
await db.flush()
@router.post("/{mail_id}/move")
@@ -1570,12 +1581,22 @@ async def move_mail(
# Update mail.folder_id
mail.folder_id = target_f_id
await db.flush()
# IMAP move: non-critical
# IMAP move: queue for retry if it fails
try:
await mail_services.imap_move_mail(db, m_id, target_f_id, tenant_id)
except Exception as exc:
import logging
logging.getLogger(__name__).warning("IMAP move failed for mail %s: %s", m_id, exc)
logging.getLogger(__name__).warning("IMAP move failed for mail %s: %s, queuing for retry", m_id, exc)
queue_entry = MailSyncQueue(
tenant_id=tenant_id,
mail_id=m_id,
operation="move",
payload={"target_folder_id": str(target_f_id)},
status="pending",
last_error=str(exc),
)
db.add(queue_entry)
await db.flush()
return mail_to_response(mail)