fix(attachments): purge R2 attachments on no-JS bulk email delete

The form-based bulk-delete fallback removed KV entries but left R2
attachments orphaned. Extract a shared deleteAttachmentsForEmails helper
and use it across single, JSON bulk, and form bulk delete paths.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Julien Herr
2026-05-23 17:57:31 +02:00
parent ddde0e26a2
commit 9141cf89bd
3 changed files with 112 additions and 23 deletions
+29 -1
View File
@@ -1,6 +1,34 @@
import { EmailData, FeedList, FeedListItem, FeedMetadata } from "../../types";
import {
EmailData,
EmailMetadata,
Env,
FeedList,
FeedListItem,
FeedMetadata,
} from "../../types";
import { FEEDS_LIST_KEY } from "../../config/constants";
import { logger } from "../../lib/logger";
import { getAttachmentBucket } from "../../utils/attachments";
// Delete the R2 attachments belonging to the given email keys. Call before the
// emails are removed from feed metadata, while `emails` still carries their
// attachmentIds.
export async function deleteAttachmentsForEmails(
env: Env,
emails: EmailMetadata[],
keys: Iterable<string>,
): Promise<void> {
const keySet = new Set(keys);
const attachmentIds = emails
.filter((e) => keySet.has(e.key))
.flatMap((e) => e.attachmentIds ?? []);
if (attachmentIds.length === 0) return;
const bucket = getAttachmentBucket(env);
if (!bucket) return;
await Promise.allSettled(attachmentIds.map((id) => bucket.delete(id)));
}
export async function deleteKeysWithConcurrency(
emailStorage: KVNamespace,