mirror of
https://github.com/xtr-dev/payload-mailing.git
synced 2025-12-11 08:43:24 +00:00
BREAKING CHANGE: Remove sendEmailWorkflow, add immediate processing to sendEmailTask
- Remove entire workflows directory and sendEmailWorkflow - Factor out email processing logic into reusable utilities (emailProcessor.ts) - Add processImmediately option to sendEmailTask input schema - Update sendEmailTask to process emails immediately when requested - Update processEmailsTask to use shared processing utilities - Remove workflow-related exports and plugin configuration - Simplify documentation to focus on unified task approach - Export new email processing utilities (processEmailById, processAllEmails) - Bump version to 0.4.0 (breaking change - workflows removed) Migration: Use sendEmailTask with processImmediately: true instead of sendEmailWorkflow
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import type { PayloadRequest, Payload } from 'payload'
|
||||
import type { MailingService } from '../services/MailingService.js'
|
||||
import { processAllEmails } from '../utils/emailProcessor.js'
|
||||
|
||||
/**
|
||||
* Data passed to the process emails task
|
||||
@@ -14,18 +14,16 @@ export interface ProcessEmailsTaskData {
|
||||
*/
|
||||
export const processEmailsTaskHandler = async (
|
||||
job: { data: ProcessEmailsTaskData },
|
||||
context: { req: PayloadRequest; mailingService: MailingService }
|
||||
context: { req: PayloadRequest }
|
||||
) => {
|
||||
const { mailingService } = context
|
||||
const { req } = context
|
||||
const payload = (req as any).payload
|
||||
|
||||
try {
|
||||
console.log('🔄 Processing email queue (pending + failed emails)...')
|
||||
|
||||
// Process pending emails first
|
||||
await mailingService.processEmails()
|
||||
|
||||
// Then retry failed emails
|
||||
await mailingService.retryFailedEmails()
|
||||
// Use the shared email processing logic
|
||||
await processAllEmails(payload)
|
||||
|
||||
console.log('✅ Email queue processing completed successfully')
|
||||
} catch (error) {
|
||||
@@ -49,10 +47,10 @@ export const processEmailsTask = {
|
||||
throw new Error('Mailing plugin not properly initialized')
|
||||
}
|
||||
|
||||
// Use the existing mailing service from context
|
||||
// Use the task handler
|
||||
await processEmailsTaskHandler(
|
||||
job as { data: ProcessEmailsTaskData },
|
||||
{ req, mailingService: mailingContext.service }
|
||||
{ req }
|
||||
)
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { sendEmail } from '../sendEmail.js'
|
||||
import { BaseEmailDocument } from '../types/index.js'
|
||||
import { processEmailById } from '../utils/emailProcessor.js'
|
||||
|
||||
export interface SendEmailTaskInput {
|
||||
// Template mode fields
|
||||
@@ -20,6 +21,7 @@ export interface SendEmailTaskInput {
|
||||
replyTo?: string
|
||||
scheduledAt?: string | Date // ISO date string or Date object
|
||||
priority?: number
|
||||
processImmediately?: boolean // If true, process the email immediately instead of waiting for the queue
|
||||
|
||||
// Allow any additional fields that users might have in their email collection
|
||||
[key: string]: any
|
||||
@@ -44,8 +46,8 @@ function transformTaskInputToSendEmailOptions(taskInput: SendEmailTaskInput) {
|
||||
// Standard email fields that should be copied to data
|
||||
const standardFields = ['to', 'cc', 'bcc', 'from', 'fromName', 'replyTo', 'subject', 'html', 'text', 'scheduledAt', 'priority']
|
||||
|
||||
// Template-specific fields that should not be copied to data
|
||||
const templateFields = ['templateSlug', 'variables']
|
||||
// Fields that should not be copied to data
|
||||
const excludedFields = ['templateSlug', 'variables', 'processImmediately']
|
||||
|
||||
// Copy standard fields to data
|
||||
standardFields.forEach(field => {
|
||||
@@ -54,9 +56,9 @@ function transformTaskInputToSendEmailOptions(taskInput: SendEmailTaskInput) {
|
||||
}
|
||||
})
|
||||
|
||||
// Copy any additional custom fields that aren't template or standard fields
|
||||
// Copy any additional custom fields that aren't excluded or standard fields
|
||||
Object.keys(taskInput).forEach(key => {
|
||||
if (!templateFields.includes(key) && !standardFields.includes(key)) {
|
||||
if (!excludedFields.includes(key) && !standardFields.includes(key)) {
|
||||
sendEmailOptions.data[key] = taskInput[key]
|
||||
}
|
||||
})
|
||||
@@ -72,6 +74,15 @@ export const sendEmailJob = {
|
||||
slug: 'send-email',
|
||||
label: 'Send Email',
|
||||
inputSchema: [
|
||||
{
|
||||
name: 'processImmediately',
|
||||
type: 'checkbox' as const,
|
||||
label: 'Process Immediately',
|
||||
defaultValue: false,
|
||||
admin: {
|
||||
description: 'Process and send the email immediately instead of waiting for the queue processor'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'templateSlug',
|
||||
type: 'text' as const,
|
||||
@@ -171,7 +182,8 @@ export const sendEmailJob = {
|
||||
type: 'date' as const,
|
||||
label: 'Schedule For',
|
||||
admin: {
|
||||
description: 'Optional date/time to schedule email for future delivery'
|
||||
description: 'Optional date/time to schedule email for future delivery',
|
||||
condition: (data: any) => !data.processImmediately
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -195,6 +207,7 @@ export const sendEmailJob = {
|
||||
handler: async ({ input, payload }: any) => {
|
||||
// Cast input to our expected type
|
||||
const taskInput = input as SendEmailTaskInput
|
||||
const shouldProcessImmediately = taskInput.processImmediately || false
|
||||
|
||||
try {
|
||||
// Transform task input into sendEmail options using helper function
|
||||
@@ -203,22 +216,36 @@ export const sendEmailJob = {
|
||||
// Use the sendEmail helper to create the email
|
||||
const email = await sendEmail<BaseEmailDocument>(payload, sendEmailOptions)
|
||||
|
||||
// If processImmediately is true, process the email now
|
||||
if (shouldProcessImmediately) {
|
||||
console.log(`⚡ Processing email ${email.id} immediately...`)
|
||||
await processEmailById(payload, String(email.id))
|
||||
console.log(`✅ Email ${email.id} processed and sent immediately`)
|
||||
|
||||
return {
|
||||
output: {
|
||||
success: true,
|
||||
id: email.id,
|
||||
status: 'sent',
|
||||
processedImmediately: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
output: {
|
||||
success: true,
|
||||
id: email.id,
|
||||
status: 'queued',
|
||||
processedImmediately: false
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
// Preserve original error and stack trace
|
||||
const wrappedError = new Error(`Failed to queue email: ${error.message}`)
|
||||
wrappedError.stack = error.stack
|
||||
wrappedError.cause = error
|
||||
throw wrappedError
|
||||
throw new Error(`Failed to process email: ${error.message}`, { cause: error })
|
||||
} else {
|
||||
throw new Error(`Failed to queue email: ${String(error)}`)
|
||||
throw new Error(`Failed to process email: ${String(error)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user