Menu

邮件通知

NEXTY.DEV 已经内置了邮件发送抽象层,支持 ResendCloudflare Email Sending 两个服务商,并封装好了常用工具函数和邮件模板。你无需从零配置,只需要按照本文步骤,就可以:

  • 新增自己的邮件模板(例如订单通知、会员到期提醒等)
  • 在服务端 Action 中发送邮件
  • 保证发件人信息、退订链接等符合最佳实践

前置步骤

使用邮件通知功能前,请先完成任一邮件服务商配置:

配置完成后,通过环境变量 DEFAULT_EMAIL_PROVIDER 切换默认服务商,业务代码无需改动。

一、整体架构概览

在 NEXTY.DEV 中,邮件相关的核心文件有:

  • 统一邮件入口lib/mail/index.ts
  • 邮件服务商实现lib/mail/providers/resend.tslib/mail/providers/cloudflare.ts
  • React 邮件模板渲染lib/mail/render.tsx
  • 联系人管理lib/mail/contacts.ts
  • 具体业务场景 Action 示例actions/newsletter/index.tsactions/stripe/index.ts
  • React 邮件模板emails/*.tsx

推荐你把所有邮件发送调用都统一走 lib/mailsendEmail,不要在业务里自己 new Resend() 或实例化 Cloudflare SDK,这样可以:

  • 统一管理 API Key、发件人邮箱、退订逻辑
  • 自动处理 React 模板渲染和纯文本 fallback
  • 支持在 Resend 和 Cloudflare 之间无缝切换
  • 自动把收件人加入联系人列表(Resend 会同步到 Contacts,Cloudflare 会记录到 Redis)

二、环境变量与基础配置

在开始写代码之前,请确保以下环境变量已经配置:

环境变量说明
DEFAULT_EMAIL_PROVIDER默认邮件服务商:resendcloudflare,默认 resend
DEFAULT_ADMIN_NAME发件人名称(例如:NEXTY.DEV Team
DEFAULT_ADMIN_EMAIL发件人邮箱(例如:[email protected]
NEXT_PUBLIC_SITE_URL站点完整域名,用于生成邮件中的绝对链接

如果你使用 Resend,还需要:

  • RESEND_API_KEY:你的 Resend API Key

如果你使用 Cloudflare Email Sending,还需要:

  • CLOUDFLARE_EMAIL_API_TOKEN:Cloudflare API Token
  • CLOUDFLARE_ACCOUNT_ID:Cloudflare 账号 ID

在本地开发环境中,你可以在 .env.local 中这样配置(以 Cloudflare 为例):

DEFAULT_EMAIL_PROVIDER=cloudflare
DEFAULT_ADMIN_NAME="NEXTY.DEV Team"
DEFAULT_ADMIN_EMAIL="[email protected]"
NEXT_PUBLIC_SITE_URL=https://yourdomain.com
 
CLOUDFLARE_EMAIL_API_TOKEN=your_cf_email_api_token
CLOUDFLARE_ACCOUNT_ID=your_cf_account_id

三、核心发送工具:sendEmail

lib/mail 提供了统一的 sendEmail 方法,参数定义如下(已简化说明):

interface SendEmailOptions {
  to: string | string[];                         // 收件人邮箱
  from: string | { email: string; name?: string }; // 发件人
  subject: string;                               // 邮件标题
  html?: string;                                 // HTML 内容(与 react 二选一)
  text?: string;                                 // 纯文本内容(可选)
  replyTo?: string | { email: string; name?: string }; // 回复邮箱
  cc?: string | string[];                        // 抄送
  bcc?: string | string[];                       // 密送
  headers?: Record<string, string>;              // 自定义邮件头
  attachments?: EmailAttachment[];               // 附件
  react?: React.ComponentType<any> | React.ReactElement; // React 邮件模板
  reactProps?: Record<string, any>;              // 传给模板的 props
  addToContacts?: boolean;                       // 发送成功后是否加入联系人列表
}

内部会帮你做这些事情:

  • 如果 to 为空,直接返回错误
  • 如果环境变量没配置好,返回错误
  • 如果使用 React 模板,自动渲染为 HTML,并生成纯文本 fallback
  • 使用 DEFAULT_ADMIN_NAME + DEFAULT_ADMIN_EMAIL 组合出发件人(你也可以在调用时覆盖)
  • 根据 DEFAULT_EMAIL_PROVIDER 路由到对应的服务商
  • 发送失败时按 HTTP 状态码自动重试一次

显式指定服务商

sendEmail 的第二个参数可以显式指定服务商,常用于 A/B 测试或需要同时走不同渠道的场景:

import { sendEmail } from "@/lib/mail";
 
// 使用默认服务商
await sendEmail({ ...options });
 
// 显式使用 Resend
await sendEmail({ ...options }, "resend");
 
// 显式使用 Cloudflare
await sendEmail({ ...options }, "cloudflare");

四、内置示例:订阅 Newsletter 的欢迎邮件

actions/newsletter/index.ts 展示了一个完整的示例,流程大致为:

  • 对用户输入的邮箱做归一化和校验
  • 做频率限制(防止恶意刷接口)
  • 准备好邮件标题和退订链接
  • 调用 sendEmail 发送 NewsletterWelcomeEmail 模板

片段逻辑说明(伪代码级别):

await sendEmail({
  to: normalizedEmail,
  from: {
    email: process.env.DEFAULT_ADMIN_EMAIL!,
    name: process.env.DEFAULT_ADMIN_NAME || siteConfig.name,
  },
  subject: `Welcome to ${siteConfig.name} Newsletter!`,
  react: NewsletterWelcomeEmail,
  reactProps: {
    email: normalizedEmail,
    unsubscribeLink,
  },
  headers: {
    "List-Unsubscribe": `<${unsubscribeLink}>`,
    "List-Unsubscribe-Post": "List-Unsubscribe=One-Click",
  },
  addToContacts: true,
});

你在自己业务中可以完全参考这套写法,只需要替换:

  • 模板组件react 字段)
  • 模板 propsreactProps 字段)
  • subject 文案

五、联系人管理

lib/mail/contacts.ts 提供了统一的联系人管理接口:

  • addContact(email, provider):发送成功后调用,Resend 会同步到 Resend Contacts,Cloudflare 会记录到 Redis 集合。
  • removeContact(email):退订时调用,会同时清理 Resend Contacts 和 Redis 记录,保证切换服务商后退订仍然有效。
  • isContact(email):判断邮箱是否在 Redis 联系人集合中。

业务代码一般不需要直接调用这些方法,sendEmailaddToContacts: true 会自动处理添加联系人。

六、注意事项

  • 新建邮件模板时,需保持不同邮件模板风格一致,这样对你的品牌形象最有利。
  • 邮件模板内品牌信息使用了 siteConfig.namesiteConfig.url,你需要确保 config/site.ts 中的配置正确。
  • 邮件模板无法使用 lucide-react,请使用 SVG 图标或者图片绝对链接。
  • 邮件模板无法使用 Tailwind CSS,请使用内联样式。
  • 邮件模板内不能使用相对链接,请使用绝对链接。
  • Resend 免费计划可以保存 1000 个联系人,每个月可以发送 3000 封,如果无法满足你的需求,你需要升级计划。
  • Cloudflare Email Sending 要求发件人域名已经通过 DNS 验证,且当前 main 分支通过 REST API 发送,适用于服务器或 Vercel 部署;Cloudflare Workers 部署请使用 cf-pg 分支

新建邮件模板时,让 AI 参照已有邮件模板新建模版即可,无需人工检查各个细节。