fix: remove all deleted_at/soft-delete from mails — standard mail program logic only
This commit is contained in:
@@ -665,33 +665,6 @@ async def imap_sync_folder(
|
||||
).scalar_one_or_none()
|
||||
|
||||
if existing_mail:
|
||||
# Bidirectional sync logic:
|
||||
# - Mail has deleted_at AND is still on IMAP server
|
||||
# - If pending delete in sync queue → our IMAP delete failed, DON'T restore
|
||||
# - If no pending delete → another program restored it, DO restore
|
||||
# - Mail has no deleted_at → normal update
|
||||
if existing_mail.deleted_at is not None:
|
||||
# Check if there's a pending delete in the sync queue
|
||||
from app.plugins.builtins.mail.models import MailSyncQueue
|
||||
pending_delete = (
|
||||
await db.execute(
|
||||
select(MailSyncQueue).where(
|
||||
and_(
|
||||
MailSyncQueue.mail_id == existing_mail.id,
|
||||
MailSyncQueue.operation == "delete",
|
||||
MailSyncQueue.status == "pending",
|
||||
)
|
||||
)
|
||||
)
|
||||
).scalar_one_or_none()
|
||||
if pending_delete:
|
||||
# Our IMAP delete failed and is retrying — don't restore
|
||||
logger.info("imap_sync_folder: skipping mail %s (deleted, pending IMAP delete in queue)", existing_mail.id)
|
||||
continue
|
||||
else:
|
||||
# No pending delete → another program restored the mail → restore in DB
|
||||
existing_mail.deleted_at = None
|
||||
logger.info("imap_sync_folder: restored mail %s (no pending delete, another program restored it)", existing_mail.id)
|
||||
# Update folder_id and imap_uid if mail moved to a different folder
|
||||
if existing_mail.folder_id != folder.id:
|
||||
existing_mail.folder_id = folder.id
|
||||
@@ -826,6 +799,33 @@ async def imap_sync_folder(
|
||||
logger.warning(f"Failed to save attachment for mail {mail.id}: {att_err}")
|
||||
continue
|
||||
|
||||
# Vanished-UID check: delete DB mails whose UID no longer exists on IMAP server
|
||||
try:
|
||||
vanished_search = await client.uid_search('ALL')
|
||||
vanished_raw = vanished_search[1][0] if vanished_search[1] and vanished_search[1][0] else b''
|
||||
if isinstance(vanished_raw, (bytes, bytearray)):
|
||||
imap_uids_set = {u.decode() if isinstance(u, bytes) else str(u) for u in vanished_raw.split()}
|
||||
else:
|
||||
imap_uids_set = set()
|
||||
db_mails = (
|
||||
await db.execute(
|
||||
select(Mail).where(
|
||||
and_(
|
||||
Mail.folder_id == folder_id,
|
||||
Mail.tenant_id == tenant_id,
|
||||
Mail.imap_uid.is_not(None),
|
||||
)
|
||||
)
|
||||
)
|
||||
).scalars().all()
|
||||
for db_mail in db_mails:
|
||||
if db_mail.imap_uid and db_mail.imap_uid not in imap_uids_set:
|
||||
logger.info("imap_sync_folder: deleting vanished mail %s (UID %s no longer on server)", db_mail.id, db_mail.imap_uid)
|
||||
await db.delete(db_mail)
|
||||
await db.flush()
|
||||
except Exception as vanished_exc:
|
||||
logger.warning("imap_sync_folder: vanished-UID check failed for folder %s: %s", folder_id, vanished_exc)
|
||||
|
||||
# Update folder counts
|
||||
total = (
|
||||
await db.execute(
|
||||
@@ -833,7 +833,6 @@ async def imap_sync_folder(
|
||||
and_(
|
||||
Mail.folder_id == folder_id,
|
||||
Mail.tenant_id == tenant_id,
|
||||
Mail.deleted_at.is_(None),
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -844,7 +843,6 @@ async def imap_sync_folder(
|
||||
and_(
|
||||
Mail.folder_id == folder_id,
|
||||
Mail.tenant_id == tenant_id,
|
||||
Mail.deleted_at.is_(None),
|
||||
Mail.is_seen.is_(False),
|
||||
)
|
||||
)
|
||||
@@ -1185,30 +1183,6 @@ async def imap_sync_account(
|
||||
).scalar_one_or_none()
|
||||
|
||||
if existing_mail:
|
||||
# Bidirectional sync logic:
|
||||
# - Mail has deleted_at AND is still on IMAP server
|
||||
# - If pending delete in sync queue → our IMAP delete failed, DON'T restore
|
||||
# - If no pending delete → another program restored it, DO restore
|
||||
# - Mail has no deleted_at → normal update
|
||||
if existing_mail.deleted_at is not None:
|
||||
from app.plugins.builtins.mail.models import MailSyncQueue
|
||||
pending_delete = (
|
||||
await db.execute(
|
||||
select(MailSyncQueue).where(
|
||||
and_(
|
||||
MailSyncQueue.mail_id == existing_mail.id,
|
||||
MailSyncQueue.operation == "delete",
|
||||
MailSyncQueue.status == "pending",
|
||||
)
|
||||
)
|
||||
)
|
||||
).scalar_one_or_none()
|
||||
if pending_delete:
|
||||
logger.info("imap_sync_account: skipping mail %s (deleted, pending IMAP delete in queue)", existing_mail.id)
|
||||
continue
|
||||
else:
|
||||
existing_mail.deleted_at = None
|
||||
logger.info("imap_sync_account: restored mail %s (no pending delete, another program restored it)", existing_mail.id)
|
||||
# Track folder moves: update folder_id and imap_uid if the
|
||||
# mail now appears in a different IMAP folder.
|
||||
if existing_mail.folder_id != folder.id:
|
||||
@@ -1217,6 +1191,7 @@ async def imap_sync_account(
|
||||
elif not existing_mail.imap_uid:
|
||||
existing_mail.imap_uid = uid_str
|
||||
|
||||
|
||||
# Save attachments for existing emails that have has_attachments but no records
|
||||
if attachments and existing_mail.has_attachments:
|
||||
existing_att_count = (
|
||||
@@ -1307,6 +1282,33 @@ async def imap_sync_account(
|
||||
continue
|
||||
await db.flush()
|
||||
|
||||
# Vanished-UID check: delete DB mails whose UID no longer exists on IMAP server
|
||||
try:
|
||||
vanished_search = await client.uid_search('ALL')
|
||||
vanished_raw = vanished_search[1][0] if vanished_search[1] and vanished_search[1][0] else b''
|
||||
if isinstance(vanished_raw, (bytes, bytearray)):
|
||||
imap_uids_set = {u.decode() if isinstance(u, bytes) else str(u) for u in vanished_raw.split()}
|
||||
else:
|
||||
imap_uids_set = set()
|
||||
db_mails = (
|
||||
await db.execute(
|
||||
select(Mail).where(
|
||||
and_(
|
||||
Mail.folder_id == folder.id,
|
||||
Mail.tenant_id == tenant_id,
|
||||
Mail.imap_uid.is_not(None),
|
||||
)
|
||||
)
|
||||
)
|
||||
).scalars().all()
|
||||
for db_mail in db_mails:
|
||||
if db_mail.imap_uid and db_mail.imap_uid not in imap_uids_set:
|
||||
logger.info("imap_sync_account: deleting vanished mail %s (UID %s no longer on server)", db_mail.id, db_mail.imap_uid)
|
||||
await db.delete(db_mail)
|
||||
await db.flush()
|
||||
except Exception as vanished_exc:
|
||||
logger.warning("imap_sync_account: vanished-UID check failed for folder %s: %s", folder.imap_name, vanished_exc)
|
||||
|
||||
# Update folder counts
|
||||
total = (
|
||||
await db.execute(
|
||||
|
||||
Reference in New Issue
Block a user