@bernierllc/电子邮件服务
综合电子邮件服务组织模板管理、多供应商交付、跟踪和用户管理.
特征
- 多服务支持 - 发送Grid、Mailgun、AWSSES和SMTP
- 模板管理 - 创建、更新和提供可变替代的电子邮件模板
- 魔法链接认证 - 无密码登录的安全、有时间限制的认证链接
- 自动重试逻辑 - 暂时性故障时与焦急的对照
- 电子邮件验证 - 发送前验证收件人地址(语法、域名、MX记录)
- 交货跟踪 - 跟踪电子邮件发送,打开、点击和回放
- 订户管理 - 管理订阅者、名单和偏好
- 队列集成 - 背景安排和发送电子邮件
- Webhook 处理 - 流程提供商为交付活动提供网点
- 数据库持久性 - 储存模板、交付记录和订户
- 分析 - 全面交付统计和报告
安装
npm install @bernierllc/email-service
同伴依赖关系
安装您需要的电子邮件提供者 :
# SendGrid
npm install @sendgrid/mail
# SMTP (included)
npm install nodemailer
使用量
快速启动
import { EmailService } from '@bernierllc/email-service';
const service = new EmailService({
providers: [
{
type: 'smtp',
smtp: {
host: 'smtp.gmail.com',
port: 587,
secure: false,
auth: { user: '[email protected]', pass: 'password' }
}
}
],
defaultProvider: 'smtp',
database: {
type: 'sqlite',
database: './data/email.db'
},
templates: {
cacheEnabled: true,
cacheTTL: 3600
},
tracking: {
enabled: true,
domain: 'yourdomain.com'
}
});
await service.initialize();
// Send an email
const result = await service.send({
from: '[email protected]',
to: ['[email protected]'],
subject: 'Welcome!',
html: '<h1>Hello!</h1><p>Welcome to our service.</p>'
});
console.log(`Email sent: ${result.messageId}`);
核心概念
1. 多服务电子邮件发送
// Configure multiple providers
const service = new EmailService({
providers: [
{
type: 'sendgrid',
apiKey: process.env.SENDGRID_API_KEY
},
{
type: 'mailgun',
apiKey: process.env.MAILGUN_API_KEY,
domain: 'mg.yourdomain.com'
},
{
type: 'ses',
region: 'us-east-1',
apiKey: process.env.AWS_ACCESS_KEY,
secretKey: process.env.AWS_SECRET_KEY
},
{
type: 'smtp',
smtp: {
host: 'smtp.gmail.com',
port: 587,
secure: false,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS
}
}
}
],
defaultProvider: 'sendgrid',
database: { type: 'sqlite', database: './email.db' }
});
// Send via specific provider
await service.send({
from: '[email protected]',
to: ['[email protected]'],
subject: 'Test',
html: '<p>Test</p>',
provider: 'mailgun' // Override default
});
2. 模板管理
// Create template
const template = await service.createTemplate({
name: 'welcome-email',
subject: 'Welcome {{name}}!',
html: `
<h1>Hello {{name}}</h1>
<p>Welcome to {{company}}!</p>
<p>Your account email is: {{email}}</p>
`,
text: 'Hello {{name}}, welcome to {{company}}!'
});
// Send using template
await service.send({
from: '[email protected]',
to: ['[email protected]'],
templateId: template.id,
templateData: {
name: 'John Doe',
company: 'Acme Corp',
email: '[email protected]'
}
});
// Update template
await service.updateTemplate(template.id, {
subject: 'Welcome to {{company}}, {{name}}!',
html: '<h1>Welcome {{name}}!</h1>'
});
// Get template
const retrieved = await service.getTemplate(template.id);
console.log(retrieved.variables); // ['name', 'company', 'email']
// Delete template
await service.deleteTemplate(template.id);
3. 魔法链接认证
发送安全,有时间限制的认证链接用于无密码登录,电子邮件验证,密码重置等.
// Configure magic link support
const service = new EmailService({
providers: [/* ... */],
database: {/* ... */},
magicLink: {
secret: process.env.MAGIC_LINK_SECRET, // Required: Secret key for signing tokens
expirationMinutes: 30, // Optional: Default 60 minutes
issuer: 'YourApp' // Optional: Token issuer name
}
});
await service.initialize();
// Send magic link for passwordless login
const result = await service.sendMagicLink({
recipient: '[email protected]',
purpose: 'sign in',
redirectUrl: 'https://app.example.com/auth/verify',
subject: 'Your Login Link', // Optional: Custom subject
expirationMinutes: 15 // Optional: Override default expiration
});
console.log(result.magicLink); // https://app.example.com/auth/verify?token=eyJ...
console.log(result.token); // eyJ... (signed JWT token)
console.log(result.expiresAt); // 2025-01-15T12:45:00.000Z
// Send with custom data in token
const resetResult = await service.sendMagicLink({
recipient: '[email protected]',
purpose: 'reset your password',
redirectUrl: 'https://app.example.com/reset-password',
customData: {
userId: '12345',
action: 'password-reset'
}
});
// Use custom template for magic link email
const welcomeResult = await service.sendMagicLink({
recipient: '[email protected]',
purpose: 'verify your email',
redirectUrl: 'https://app.example.com/verify',
template: 'welcome-verification', // Use existing template
templateData: {
userName: 'John Doe',
companyName: 'Acme Corp'
}
});
魔法链接特性数字 :
- 使用安全签名符
@bernierllc/crypto-utils - * 配置到期时间
- QQ 自定义数据嵌入符号
- QQ 默认专业电子邮件模板( 或使用自己的)
- QQ 自动 URL 参数处理( 已保存查询参数)
- QQ 对所有电子邮件提供者的支持
正在校验魔法链接键数字 :
import { verifySignedToken } from '@bernierllc/crypto-utils';
// In your auth/verify endpoint
app.get('/auth/verify', async (req, res) => {
const token = req.query.token as string;
try {
const payload = await verifySignedToken(
token,
process.env.MAGIC_LINK_SECRET!
);
// Token is valid - log user in
console.log(payload.email); // [email protected]
console.log(payload.purpose); // 'sign in'
console.log(payload.customData); // Any custom data you included
// Create session and redirect
req.session.userId = payload.email;
res.redirect('/dashboard');
} catch (error) {
// Token expired, invalid, or tampered with
res.status(401).send('Invalid or expired link');
}
});
4. 重试逻辑和电子邮件验证
自动重试以指数回放和电子邮件地址验证 .
const service = new EmailService({
providers: [/* ... */],
database: {/* ... */},
// Retry configuration (optional)
retry: {
maxRetries: 3, // Number of retry attempts
initialDelayMs: 1000, // Initial delay between retries
maxDelayMs: 30000, // Maximum delay
jitter: true, // Add randomness to delays
enableMetrics: true // Track retry metrics
},
// Email validation (optional)
validation: {
validateRecipients: true, // Validate email addresses before sending
strict: true // Use strict validation rules
}
});
// Emails will automatically retry on transient failures
const result = await service.send({
to: '[email protected]',
subject: 'Test',
html: '<p>This will retry up to 3 times if it fails</p>'
});
console.log(result.retryCount); // Number of retries that occurred
重试特性数字 :
- * * 与焦躁相呼应,避免雷鸣群
- QQ 可配置的最大重试和延迟
- QQ 在瞬态失败时自动重试
- * 重试指标跟踪(尝试、成功、失败)
- 与
@bernierllc/retry-policy, (中文(简体) ),retry-state,以及retry-metrics
验证特征数字 :
- 语法验证(RFC 5322遵守)
- QQ域验证( 启用严格模式时的 MX 记录检查)
- QQ 可处理的电子邮件检测
- ++ 多个收件人验证
- 与
@bernierllc/email-validator
5. 交付跟踪
// Send email
const result = await service.send({
from: '[email protected]',
to: ['[email protected]'],
subject: 'Order Confirmation',
html: '<h1>Thanks for your order!</h1>'
});
// Get delivery record
const delivery = await service.getDelivery(result.deliveryId);
console.log(delivery.status); // 'sent', 'delivered', 'opened', 'clicked', etc.
// Track opens
await service.trackOpen(result.deliveryId);
// Track clicks
await service.trackClick(result.deliveryId);
// Get statistics
const stats = await service.getDeliveryStats();
console.log(`Open rate: ${stats.openRate}%`);
console.log(`Click rate: ${stats.clickRate}%`);
console.log(`Bounce rate: ${stats.bounceRate}%`);
// Get statistics for date range
const monthStats = await service.getDeliveryStats(
new Date('2025-01-01'),
new Date('2025-01-31')
);
4. 订户管理
// Create subscriber
const subscriber = await service.createSubscriber({
email: '[email protected]',
name: 'John Doe',
lists: ['newsletter', 'updates'],
tags: ['premium', 'early-adopter'],
metadata: {
source: 'website',
signupDate: new Date()
}
});
// Get subscriber
const found = await service.getSubscriber(subscriber.id);
// Update subscriber
await service.updateSubscriber(subscriber.id, {
name: 'John Smith',
tags: ['premium', 'verified']
});
// Unsubscribe
await service.unsubscribe('[email protected]');
// Create subscriber list
const list = await service.createList(
'Monthly Newsletter',
'Our monthly product updates and news'
);
5. 排定的电子邮件
// Configure queue
const service = new EmailService({
providers: [/* ... */],
database: { type: 'sqlite', database: './email.db' },
queue: {
backend: new MemoryBackend(),
concurrency: 5,
retryPolicy: {
maxAttempts: 3,
backoff: { type: 'exponential', delay: 1000 }
}
}
});
// Schedule for later
await service.send({
from: '[email protected]',
to: ['[email protected]'],
subject: 'Reminder',
html: '<p>This is your reminder</p>',
scheduledAt: new Date(Date.now() + 24 * 60 * 60 * 1000) // 24 hours
});
// Send with priority
await service.send({
from: '[email protected]',
to: ['[email protected]'],
subject: 'URGENT: System Alert',
html: '<p>Critical alert</p>',
priority: 'high' // high, normal, low
});
6. 散装发送
const results = await service.sendBulk([
{
from: '[email protected]',
to: ['[email protected]'],
subject: 'Personalized for User 1',
html: '<p>Hello User 1</p>'
},
{
from: '[email protected]',
to: ['[email protected]'],
subject: 'Personalized for User 2',
html: '<p>Hello User 2</p>'
}
]);
results.forEach((result, index) => {
console.log(`Email ${index + 1}: ${result.success ? 'sent' : 'failed'}`);
});
7. 网络垃圾处理
// Register webhook handler
service.registerWebhookHandler('delivered', async (event) => {
console.log(`Email ${event.messageId} was delivered`);
// Update your database, trigger workflows, etc.
});
service.registerWebhookHandler('opened', async (event) => {
console.log(`Email ${event.messageId} was opened`);
// Track user engagement
});
service.registerWebhookHandler('clicked', async (event) => {
console.log(`Link clicked in ${event.messageId}`);
// Track click-through rates
});
service.registerWebhookHandler('bounced', async (event) => {
console.log(`Email ${event.messageId} bounced`);
// Update subscriber status
await service.unsubscribe(event.data.email);
});
// Process webhook from provider
await service.handleWebhook({
provider: 'sendgrid',
event: 'delivered',
messageId: 'msg_123',
timestamp: new Date(),
data: { /* provider-specific data */ }
});
API 参考
电子邮件服务
构造器
new EmailService(config: EmailServiceConfig)
配置 :
interface EmailServiceConfig {
providers: ProviderConfig[];
defaultProvider?: EmailProvider;
database: DatabaseConfig;
queue?: QueueOptions;
templates?: {
cacheEnabled?: boolean;
cacheTTL?: number;
};
tracking?: {
enabled?: boolean;
domain?: string;
};
compliance?: {
unsubscribeLink?: boolean;
footerRequired?: boolean;
};
}
interface ProviderConfig {
type: 'sendgrid' | 'mailgun' | 'ses' | 'smtp';
apiKey?: string;
domain?: string;
region?: string;
smtp?: SMTPConfig;
rateLimit?: RateLimitConfig;
}
方法
电子邮件发送
send(request: EmailRequest): Promise<SendEmailResult>- 发送电子邮件sendBulk(requests: EmailRequest[]): Promise<SendEmailResult[]>- 发送多封电子邮件
模板管理
createTemplate(request: TemplateCreateRequest): Promise<EmailTemplate>- 创建模板getTemplate(id: string): Promise<EmailTemplate | null>- 获取模板updateTemplate(id: string, update: TemplateUpdateRequest): Promise<EmailTemplate>- 更新模板deleteTemplate(id: string): Promise<boolean>- 删除模板
交货跟踪
getDelivery(id: string): Promise<DeliveryRecord | null>- 拿到交货记录trackOpen(deliveryId: string): Promise<void>- 跟踪电子邮件打开trackClick(deliveryId: string): Promise<void>- 点击音轨链接getDeliveryStats(startDate?: Date, endDate?: Date): Promise<DeliveryStats>- 获取统计数据
订户管理
createSubscriber(request: SubscriberCreateRequest): Promise<Subscriber>- 创建订阅者getSubscriber(id: string): Promise<Subscriber | null>- 获取订阅者updateSubscriber(id: string, update: SubscriberUpdateRequest): Promise<Subscriber>- 更新用户unsubscribe(email: string): Promise<boolean>- 取消订阅电子邮件createList(name: string, description?: string): Promise<SubscriberList>- 创建列表
Webhook 处理
registerWebhookHandler(event: string, handler: WebhookHandler): void- 登记员handleWebhook(event: WebhookEvent): Promise<void>- 进程网点
生命周期
initialize(): Promise<void>- 服务初始化shutdown(): Promise<void>- 清理和关闭
类型定义
interface EmailRequest {
from: EmailAddress | string;
to: EmailAddress[] | string[];
cc?: EmailAddress[] | string[];
bcc?: EmailAddress[] | string[];
subject: string;
html?: string;
text?: string;
templateId?: string;
templateData?: TemplateContext;
attachments?: EmailAttachment[];
headers?: Record<string, string>;
tags?: string[];
metadata?: Record<string, any>;
priority?: 'high' | 'normal' | 'low';
scheduledAt?: Date;
provider?: EmailProvider;
}
interface SendEmailResult {
success: boolean;
messageId?: string;
deliveryId?: string;
provider?: EmailProvider;
error?: string;
timestamp: Date;
queuedForLater?: boolean;
}
interface DeliveryStats {
total: number;
sent: number;
delivered: number;
opened: number;
clicked: number;
bounced: number;
failed: number;
openRate: number;
clickRate: number;
bounceRate: number;
}
enum DeliveryStatus {
QUEUED = 'queued',
SENDING = 'sending',
SENT = 'sent',
DELIVERED = 'delivered',
OPENED = 'opened',
CLICKED = 'clicked',
BOUNCED = 'bounced',
FAILED = 'failed',
UNSUBSCRIBED = 'unsubscribed'
}
最佳做法
处理错误
try {
await service.send(emailRequest);
} catch (error) {
if (error instanceof ProviderError) {
// Switch to backup provider
console.error('Provider failed:', error.provider);
} else if (error instanceof TemplateError) {
// Template rendering failed
console.error('Template error:', error.message);
} else if (error instanceof DeliveryError) {
// Delivery failed
console.error('Delivery error:', error.message);
}
}
模板变量
// Always validate template data matches template variables
const template = await service.getTemplate(templateId);
const missingVars = template.variables.filter(
v => !(v in templateData)
);
if (missingVars.length > 0) {
throw new Error(`Missing template variables: ${missingVars.join(', ')}`);
}
限制费率
// Configure per-provider rate limits
const service = new EmailService({
providers: [
{
type: 'sendgrid',
apiKey: process.env.SENDGRID_API_KEY,
rateLimit: {
maxPerSecond: 100,
maxPerMinute: 5000,
maxPerHour: 100000
}
}
],
// ...
});
监测
// Regular health monitoring
setInterval(async () => {
const stats = await service.getDeliveryStats();
if (stats.bounceRate > 5) {
console.warn('High bounce rate detected:', stats.bounceRate);
}
if (stats.failed > 100) {
console.error('High failure count:', stats.failed);
}
}, 60000); // Every minute
依赖关系
此套件的管弦乐 :
- @bernierllc/电子邮件发送器 - 多供应商电子邮件发送
- @bernierllc/电子邮件-parser - 电子邮件内容解析
- @ bernierllc/ template- 引擎 - 模板渲染
- @bernierllc/队列管理器 - 背景工作处理
- @bernierllc/webhook-验证器 - 网页记录验证
- @bernierllc/数据库调整器 - 数据持久性
- @ bernierllc/ 拖放器 - 结构化伐木
- @bernierlc/ 配置管理器 - 配置管理
一体化状况
- 锁定器: 集成 - 用于结构化记录的用法@ bennellc/logger
- 多克套装: 准备 - 有标记文档
- NeverHub 整合: 计划 - 服务发现和事件总线集成@bernierllc/neverhub-adapter
实例
见 示例目录 用于完整的工作示例:
basic-smtp.ts- 基本SMTP电子邮件发送templates.ts- 模板管理和渲染tracking.ts- 交货跟踪和分析subscribers.ts- 订户管理webhooks.ts- 网页浏览处理multi-provider.ts- 多供应商配置
许可证
版权(c)2025 Bernier有限责任公司。 版权所有.
这个软件包是专有软件,只许可在它交付的项目范围内使用.