From 9a996a33e59d4866b97158d88ff9eab67204b07e Mon Sep 17 00:00:00 2001 From: Bas van den Aakster Date: Sun, 14 Sep 2025 21:01:35 +0200 Subject: [PATCH] Add afterChange hook to auto-schedule jobs for pending emails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✨ Smart Job Scheduling: - Automatically creates processing jobs for pending emails - Prevents orphaned emails that bypass sendEmail() function - Checks for existing jobs to avoid duplicates - Respects scheduledAt for delayed sending - Handles both create and update operations intelligently 🔍 Logic: - Only triggers for emails with status 'pending' - Skips if email was already pending (prevents duplicate jobs) - Queries existing jobs to avoid creating duplicates - Uses mailing config queue or defaults to 'default' - Graceful error handling (logs but doesn't fail email operations) 📈 Benefits: - Complete email processing coverage - Works for emails created via admin interface - Handles manual status changes back to pending - Maintains scheduling for delayed emails - Zero-configuration auto-recovery --- src/collections/Emails.ts | 52 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/collections/Emails.ts b/src/collections/Emails.ts index 503f9aa..172ec67 100644 --- a/src/collections/Emails.ts +++ b/src/collections/Emails.ts @@ -183,6 +183,58 @@ const Emails: CollectionConfig = { }, }, ], + hooks: { + afterChange: [ + async ({ doc, previousDoc, req, operation }) => { + // Only process if this is a pending email and we have jobs configured + if (doc.status !== 'pending' || !req.payload.jobs) { + return + } + + // Skip if this is an update and status didn't change to pending + if (operation === 'update' && previousDoc?.status === 'pending') { + return + } + + try { + // Check if a processing job already exists for this email + const existingJobs = await req.payload.find({ + collection: 'payload-jobs', + where: { + 'input.emailId': { + equals: String(doc.id), + }, + task: { + equals: 'process-email', + }, + }, + limit: 1, + }) + + // If no job exists, create one + if (existingJobs.totalDocs === 0) { + const mailingContext = (req.payload as any).mailing + const queueName = mailingContext?.config?.queue || 'default' + + await req.payload.jobs.queue({ + queue: queueName, + task: 'process-email', + input: { + emailId: String(doc.id) + }, + // If scheduled, set the waitUntil date + waitUntil: doc.scheduledAt ? new Date(doc.scheduledAt) : undefined + }) + + console.log(`Auto-scheduled processing job for email ${doc.id}`) + } + } catch (error) { + console.error(`Failed to auto-schedule job for email ${doc.id}:`, error) + // Don't throw - we don't want to fail the email creation/update + } + } + ] + }, timestamps: true, // indexes: [ // {