From cba7e05d9873913d22d1d2f55dcf089a3ea54d14 Mon Sep 17 00:00:00 2001 From: Til Wegener <38760774+tilwegener@users.noreply.github.com> Date: Thu, 14 Aug 2025 08:10:58 +0000 Subject: [PATCH] fix: handle attachment cleanup errors safely and surface messages --- .../controllers/archived-email.controller.ts | 7 ++- .../src/services/ArchivedEmailService.ts | 53 +++++++++++++------ .../archived-emails/[id]/+page.svelte | 8 ++- 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/packages/backend/src/api/controllers/archived-email.controller.ts b/packages/backend/src/api/controllers/archived-email.controller.ts index 3faf007..3c2b704 100644 --- a/packages/backend/src/api/controllers/archived-email.controller.ts +++ b/packages/backend/src/api/controllers/archived-email.controller.ts @@ -47,8 +47,11 @@ export class ArchivedEmailController { return res.status(204).send(); } catch (error) { console.error(`Delete archived email ${req.params.id} error:`, error); - if (error instanceof Error && error.message === 'Archived email not found') { - return res.status(404).json({ message: error.message }); + if (error instanceof Error) { + if (error.message === 'Archived email not found') { + return res.status(404).json({ message: error.message }); + } + return res.status(500).json({ message: error.message }); } return res.status(500).json({ message: 'An internal server error occurred' }); } diff --git a/packages/backend/src/services/ArchivedEmailService.ts b/packages/backend/src/services/ArchivedEmailService.ts index 2c87afc..d374479 100644 --- a/packages/backend/src/services/ArchivedEmailService.ts +++ b/packages/backend/src/services/ArchivedEmailService.ts @@ -158,10 +158,9 @@ export class ArchivedEmailService { const storage = new StorageService(); - // Load attachments before deleting the email - let emailAttachmentsResult: { attachmentId: string; storagePath: string }[] = []; + // Load and handle attachments before deleting the email itself if (email.hasAttachments) { - emailAttachmentsResult = await db + const emailAttachmentsResult = await db .select({ attachmentId: attachments.id, storagePath: attachments.storagePath, @@ -169,24 +168,46 @@ export class ArchivedEmailService { .from(emailAttachments) .innerJoin(attachments, eq(emailAttachments.attachmentId, attachments.id)) .where(eq(emailAttachments.emailId, emailId)); + + try { + for (const attachment of emailAttachmentsResult) { + const [refCount] = await db + .select({ count: count(emailAttachments.emailId) }) + .from(emailAttachments) + .where(eq(emailAttachments.attachmentId, attachment.attachmentId)); + + if (refCount.count === 1) { + await storage.delete(attachment.storagePath); + await db + .delete(emailAttachments) + .where( + and( + eq(emailAttachments.emailId, emailId), + eq(emailAttachments.attachmentId, attachment.attachmentId) + ) + ); + await db + .delete(attachments) + .where(eq(attachments.id, attachment.attachmentId)); + } else { + await db + .delete(emailAttachments) + .where( + and( + eq(emailAttachments.emailId, emailId), + eq(emailAttachments.attachmentId, attachment.attachmentId) + ) + ); + } + } + } catch { + throw new Error('Failed to delete email attachments'); + } } // Delete the email file from storage await storage.delete(email.storagePath); - // Handle attachments: delete only if not referenced elsewhere - for (const attachment of emailAttachmentsResult) { - const [refCount] = await db - .select({ count: count(emailAttachments.emailId) }) - .from(emailAttachments) - .where(eq(emailAttachments.attachmentId, attachment.attachmentId)); - - if (refCount.count === 1) { - await storage.delete(attachment.storagePath); - await db.delete(attachments).where(eq(attachments.id, attachment.attachmentId)); - } - } - const searchService = new SearchService(); await searchService.deleteDocuments('emails', [emailId]); diff --git a/packages/frontend/src/routes/dashboard/archived-emails/[id]/+page.svelte b/packages/frontend/src/routes/dashboard/archived-emails/[id]/+page.svelte index e19c00d..0482bb9 100644 --- a/packages/frontend/src/routes/dashboard/archived-emails/[id]/+page.svelte +++ b/packages/frontend/src/routes/dashboard/archived-emails/[id]/+page.svelte @@ -48,9 +48,13 @@ method: 'DELETE' }); if (!response.ok) { - throw new Error('Failed to delete email'); + const errorData = await response.json().catch(() => null); + const message = errorData?.message || 'Failed to delete email'; + console.error('Delete failed:', message); + alert(message); + return; } - await goto('/dashboard/archived-emails'); + await goto('/dashboard/archived-emails', { invalidateAll: true }); } catch (error) { console.error('Delete failed:', error); } finally {