Remove email outbox collection and process job; refactor email templates with rich text support and slug generation

This commit is contained in:
2025-09-13 12:11:35 +02:00
parent ed9d979d3e
commit 3868e74770
34 changed files with 2674 additions and 374 deletions

224
README.md
View File

@@ -2,6 +2,8 @@
📧 **Template-based email system with scheduling and job processing for PayloadCMS**
⚠️ **Pre-release Warning**: This package is currently in active development (v0.0.x). Breaking changes may occur before v1.0.0. Not recommended for production use.
## Features
**Template System**: Create reusable email templates with Handlebars syntax
@@ -52,9 +54,9 @@ export default buildConfig({
```typescript
import { sendEmail, scheduleEmail } from '@xtr-dev/payload-mailing'
// Send immediately
// Send immediately using template slug
const emailId = await sendEmail(payload, {
templateId: 'welcome-email',
templateSlug: 'welcome-email',
to: 'user@example.com',
variables: {
firstName: 'John',
@@ -64,7 +66,7 @@ const emailId = await sendEmail(payload, {
// Schedule for later
const scheduledId = await scheduleEmail(payload, {
templateId: 'reminder-email',
templateSlug: 'reminder-email',
to: 'user@example.com',
scheduledAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours
variables: {
@@ -82,13 +84,17 @@ const scheduledId = await scheduleEmail(payload, {
interface MailingPluginConfig {
collections?: {
templates?: string // default: 'email-templates'
outbox?: string // default: 'email-outbox'
emails?: string // default: 'emails'
}
defaultFrom?: string
transport?: Transporter | MailingTransportConfig
queue?: string // default: 'default'
retryAttempts?: number // default: 3
retryDelay?: number // default: 300000 (5 minutes)
emailWrapper?: EmailWrapperHook // optional email layout wrapper
richTextEditor?: RichTextField['editor'] // optional custom rich text editor
onReady?: (payload: any) => Promise<void> // optional callback after plugin initialization
initOrder?: 'before' | 'after' // default: 'before'
}
```
@@ -125,34 +131,117 @@ import nodemailer from 'nodemailer'
2. Navigate to **Mailing > Email Templates**
3. Create a new template with:
- **Name**: Descriptive name for the template
- **Slug**: Unique identifier for the template (auto-generated)
- **Subject**: Email subject (supports Handlebars)
- **HTML Template**: HTML content with Handlebars syntax
- **Text Template**: Plain text version (optional)
- **Variables**: Define available variables
- **Content**: Rich text editor with Handlebars syntax (automatically generates HTML and text versions)
### Template Example
**Subject**: `Welcome to {{siteName}}, {{firstName}}!`
**HTML Template**:
```html
<h1>Welcome {{firstName}}!</h1>
<p>Thanks for joining {{siteName}}. We're excited to have you!</p>
**Content** (using rich text editor with Handlebars):
```
# Welcome {{firstName}}! 🎉
{{#if isPremium}}
<p><strong>Premium Benefits:</strong></p>
<ul>
<li>Priority support</li>
<li>Advanced features</li>
<li>Monthly reports</li>
</ul>
{{/if}}
Thanks for joining {{siteName}}. We're excited to have you!
<p>Your account was created on {{formatDate createdAt 'long'}}.</p>
<p>Visit your dashboard: <a href="{{dashboardUrl}}">Get Started</a></p>
**What you can do:**
• Create beautiful emails with rich text formatting
• Use the emailWrapper hook to add custom layouts
• Queue and schedule emails effortlessly
<hr>
<p>Best regards,<br>The {{siteName}} Team</p>
Your account was created on {{formatDate createdAt "long"}}.
Best regards,
The {{siteName}} Team
```
## Advanced Features
### Email Wrapper Hook
Use the `emailWrapper` hook to apply consistent layouts to all emails:
```typescript
mailingPlugin({
// ... other config
emailWrapper: (email) => {
const wrappedHtml = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>${email.subject}</title>
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
.container { max-width: 600px; margin: 0 auto; background: white; }
.header { background: #007bff; color: white; padding: 20px; }
.content { padding: 30px; }
.footer { background: #f8f9fa; padding: 15px; text-align: center; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>My Company</h1>
</div>
<div class="content">
${email.html}
</div>
<div class="footer">
© 2024 My Company. All rights reserved.
</div>
</div>
</body>
</html>
`
return {
...email,
html: wrappedHtml,
text: `MY COMPANY\n\n${email.text}\n\n© 2024 My Company`
}
}
})
```
### Custom Rich Text Editor
Override the rich text editor used for templates:
```typescript
import { lexicalEditor } from '@payloadcms/richtext-lexical'
import { FixedToolbarFeature, HeadingFeature } from '@payloadcms/richtext-lexical'
mailingPlugin({
// ... other config
richTextEditor: lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
FixedToolbarFeature(),
HeadingFeature({ enabledHeadingSizes: ['h1', 'h2', 'h3'] }),
// Add more features as needed
],
})
})
```
### Initialization Hooks
Control plugin initialization order and add post-initialization logic:
```typescript
mailingPlugin({
// ... other config
initOrder: 'after', // Initialize after main Payload onInit
onReady: async (payload) => {
// Called after plugin is fully initialized
console.log('Mailing plugin ready!')
// Custom initialization logic here
await setupCustomEmailSettings(payload)
}
})
```
## Handlebars Helpers
@@ -172,20 +261,20 @@ Send an email immediately:
```typescript
const emailId = await sendEmail(payload, {
templateId: 'order-confirmation', // optional
to: 'customer@example.com',
cc: 'manager@example.com', // optional
bcc: 'archive@example.com', // optional
from: 'orders@yoursite.com', // optional, uses default
replyTo: 'support@yoursite.com', // optional
subject: 'Custom subject', // required if no template
html: '<h1>Custom HTML</h1>', // required if no template
text: 'Custom text version', // optional
variables: { // template variables
templateSlug: 'order-confirmation', // optional - use template slug
to: ['customer@example.com'], // string or array of emails
cc: ['manager@example.com'], // optional - array of emails
bcc: ['archive@example.com'], // optional - array of emails
from: 'orders@yoursite.com', // optional, uses default
replyTo: 'support@yoursite.com', // optional
subject: 'Custom subject', // required if no template
html: '<h1>Custom HTML</h1>', // required if no template
text: 'Custom text version', // optional
variables: { // template variables
orderNumber: '12345',
customerName: 'John Doe'
},
priority: 1 // optional, 1-10 (1 = highest)
priority: 1 // optional, 1-10 (1 = highest)
})
```
@@ -195,7 +284,7 @@ Schedule an email for later delivery:
```typescript
const emailId = await scheduleEmail(payload, {
templateId: 'newsletter',
templateSlug: 'newsletter',
to: ['user1@example.com', 'user2@example.com'],
scheduledAt: new Date('2024-01-15T10:00:00Z'),
variables: {
@@ -205,12 +294,13 @@ const emailId = await scheduleEmail(payload, {
})
```
### processOutbox(payload)
### processEmails(payload)
Manually process pending emails:
```typescript
await processOutbox(payload)
import { processEmails } from '@xtr-dev/payload-mailing'
await processEmails(payload)
```
### retryFailedEmails(payload)
@@ -218,31 +308,31 @@ await processOutbox(payload)
Manually retry failed emails:
```typescript
import { retryFailedEmails } from '@xtr-dev/payload-mailing'
await retryFailedEmails(payload)
```
## Job Processing
The plugin automatically processes emails using PayloadCMS jobs:
The plugin automatically adds a unified email processing job to PayloadCMS:
- **Outbox Processing**: Every 5 minutes
- **Failed Email Retry**: Every 30 minutes
- **Job Name**: `process-email-queue`
- **Function**: Processes both pending emails and retries failed emails
- **Trigger**: Manual via admin panel or API call
Ensure you have jobs configured in your Payload config:
The job is automatically registered when the plugin initializes. To trigger it manually:
```typescript
export default buildConfig({
jobs: {
// Configure your job processing
tasks: [],
// ... other job config
},
// Queue the job for processing
await payload.jobs.queue({
task: 'process-email-queue',
input: {}
})
```
## Email Status Tracking
All emails are stored in the outbox collection with these statuses:
All emails are stored in the emails collection with these statuses:
- `pending` - Waiting to be sent
- `processing` - Currently being sent
@@ -251,7 +341,7 @@ All emails are stored in the outbox collection with these statuses:
## Monitoring
Check the **Mailing > Email Outbox** collection in your admin panel to:
Check the **Mailing > Emails** collection in your admin panel to:
- View email delivery status
- See error messages for failed sends
@@ -278,14 +368,46 @@ import {
MailingPluginConfig,
SendEmailOptions,
EmailTemplate,
OutboxEmail
QueuedEmail,
EmailObject,
EmailWrapperHook
} from '@xtr-dev/payload-mailing'
```
## Recent Changes
### v0.0.x (Latest)
**🔄 Breaking Changes:**
- Removed email layouts system in favor of `emailWrapper` hook for better flexibility
- Email fields (`to`, `cc`, `bcc`) now use `hasMany: true` for proper array handling
- Templates now use slug-based lookup instead of ID-based for developer-friendly API
- Email collection renamed from "outbox" to "emails"
- Unified job processing: single `process-email-queue` job handles both pending and failed emails
**✨ New Features:**
- Rich text editor with automatic HTML/text conversion
- Template slugs for easier template reference
- `emailWrapper` hook for consistent email layouts
- Custom rich text editor configuration support
- Initialization hooks (`onReady`, `initOrder`) for better plugin lifecycle control
- Improved Handlebars variable interpolation with defensive programming
**🐛 Bug Fixes:**
- Fixed text version uppercase conversion in headings
- Fixed Handlebars interpolation issues in text version
- Improved plugin initialization order to prevent timing issues
**💡 Improvements:**
- Better admin UI with proper array input controls
- More robust error handling and logging
- Enhanced TypeScript definitions
- Simplified template creation workflow
## License
MIT
## Contributing
Issues and pull requests welcome at [GitHub repository](https://github.com/xtr-dev/payload-mailing)
Issues and pull requests welcome at [GitHub repository](https://github.com/xtr-dev/payload-mailing)