mirror of
https://github.com/xtr-dev/payload-mailing.git
synced 2025-12-10 00:03:23 +00:00
Remove email outbox collection and process job; refactor email templates with rich text support and slug generation
This commit is contained in:
224
README.md
224
README.md
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user