Logo文档

邮件系统

学习如何在 NEXTDEVKIT 中使用 React Email 发送邮件和管理时事通讯

NEXTDEVKIT 包含一个强大的邮件系统,基于 React EmailResend 提供商构建,提供美观、响应式的邮件模板,支持国际化,完美适用于交易邮件、时事通讯和用户通信。

🏗️ 邮件系统架构

NEXTDEVKIT 的邮件系统结构如下:

src/
├── mail/
│   ├── index.ts                    # 主要邮件发送函数
│   ├── types.ts                    # 邮件接口和类型
│   ├── actions.ts                  # 时事通讯服务器操作
│   ├── templates/
│   │   ├── index.ts               # 模板注册表
│   │   ├── forgot-password.tsx    # 密码重置模板
│   │   ├── email-verification.tsx # 邮件验证模板
│   │   ├── newsletter-signup.tsx  # 时事通讯注册模板
│   │   └── contact-form.tsx       # 联系表单模板
│   ├── providers/
│   │   ├── index.ts               # 提供商注册表
│   │   └── resend.ts              # Resend 提供商
│   └── components/
│       ├── layout.tsx             # 邮件布局组件
│       └── email-button.tsx       # 邮件按钮组件
├── config/
│   └── index.ts                   # 邮件配置
└── messages/
    ├── en.json                    # 英文邮件翻译
    └── zh.json                    # 中文邮件翻译

⚙️ 邮件配置

应用配置

邮件设置在 src/config/index.ts 中配置:

export const appConfig = {
  mail: {
    provider: "resend",              // 邮件提供商
    from: "noreply@nextdevkit.com",  // 默认发送者地址
    contact: "contact@nextdevkit.com", // 联系邮箱地址
  },
  // ... 其他配置
} as const;

环境变量

设置所需的环境变量:

# 对于 Resend
RESEND_API_KEY=re_your_resend_api_key
RESEND_AUDIENCE_ID=your_audience_id  # 用于时事通讯

提供商设置

Resend 设置

  1. 创建 Resend 账户

    • 访问 resend.com
    • 创建账户并验证您的域名
  2. 获取 API 密钥

    • 转到 API Keys 部分
    • 创建具有发送权限的新 API 密钥
    • 添加到您的环境变量
  3. 时事通讯设置(可选):

    • 在 Resend 中创建受众
    • 获取受众 ID 用于时事通讯功能

📧 邮件模板

可用模板

NEXTDEVKIT 包含预构建的邮件模板:

模板描述上下文属性
forgotPassword密码重置name: string, url: string
emailVerification邮件验证name: string, url: string
newsletterSignup时事通讯确认name: string, unsubscribeUrl: string
contactForm联系表单通知name: string, email: string, message: string

模板结构

每个模板都是一个支持国际化的 React 组件:

src/mail/templates/welcome.tsx
import { Layout } from "@/mail/components/layout";
import { EmailButton } from "@/mail/components/email-button";
import type { i18nEmailProps } from "@/mail/types";

interface WelcomeEmailProps extends i18nEmailProps {
  name: string;
}

export function Welcome({ name, locale, messages }: WelcomeEmailProps) {
  const t = createTranslator({
    locale,
    messages,
    namespace: "mail.welcome",
  });

  return (
    <Layout>
      <Text>{t("greeting", { name })}</Text>
      <Text>{t("body")}</Text>
      <EmailButton href="https://nextdevkit.com/docs">
        {t("getStarted")}
      </EmailButton>
    </Layout>
  );
}

模板注册表

模板在 src/mail/templates/index.ts 中注册:

export const mailTemplates = {
  forgotPassword: ForgotPassword,
  newsletterSignup: NewsletterSignup,
  emailVerification: EmailVerification,
  contactForm: ContactForm,
} as const;

export type TemplateKey = keyof typeof mailTemplates;

📤 发送邮件

基本邮件发送

使用 sendEmail 函数发送邮件:

import { sendEmail } from "@/mail";

// 使用模板发送
await sendEmail({
  to: "user@example.com",
  templateKey: "forgotPassword",
  context: {
    name: "John Doe",
    url: "https://example.com/reset-password",
  },
  locale: "en", // 可选,默认为配置默认值
});

// 发送原始 HTML 邮件
await sendEmail({
  to: "user@example.com",
  subject: "欢迎使用 NEXTDEVKIT",
  html: "<h1>欢迎!</h1><p>感谢您加入我们。</p>",
  text: "欢迎!感谢您加入我们。",
});

服务器操作示例

src/app/actions/send-reset-password-email.ts
"use server";

import { sendEmail } from "@/mail";
import { getSession } from "@/lib/auth/server";

export async function sendResetPasswordEmail(resetUrl: string) {
  const session = await getSession();
  
  if (!session?.user) {
    throw new Error("未验证");
  }

  const success = await sendEmail({
    to: session.user.email,
    templateKey: "forgotPassword",
    context: {
      name: session.user.name || "用户",
      url: resetUrl,
    },
  });

  if (!success) {
    throw new Error("发送重置密码邮件失败");
  }
}

📰 时事通讯管理

时事通讯操作

NEXTDEVKIT 为时事通讯管理提供服务器操作:

import { 
  subscribeToNewsletter, 
  unsubscribeFromNewsletter, 
  isSubscribedToNewsletter 
} from "@/mail/actions";

// 订阅时事通讯
await subscribeToNewsletter({ email: "user@example.com" });

// 取消订阅时事通讯
await unsubscribeFromNewsletter({ email: "user@example.com" });

// 检查订阅状态
const isSubscribed = await isSubscribedToNewsletter({ email: "user@example.com" });

🌐 国际化

邮件翻译

邮件翻译存储在消息文件中:

英文(messages/en.json

{
  "mail": {
    "forgotPassword": {
      "subject": "Reset your password",
      "greeting": "Hi {name}!",
      "body": "Click the button below to reset your password.",
      "resetPassword": "Reset Password"
    },
    "emailVerification": {
      "subject": "Verify your email",
      "greeting": "Hi {name}!",
      "body": "Click the button below to verify your email address.",
      "verifyEmail": "Verify Email"
    }
  }
}

中文(messages/zh.json

{
  "mail": {
    "forgotPassword": {
      "subject": "重置密码",
      "greeting": "你好 {name}!",
      "body": "点击下面的按钮重置你的密码。",
      "resetPassword": "重置密码"
    },
    "emailVerification": {
      "subject": "验证邮箱",
      "greeting": "你好 {name}!",
      "body": "点击下面的按钮验证你的邮箱地址。",
      "verifyEmail": "验证邮箱"
    }
  }
}

本地化邮件发送

// 发送本地化邮件
await sendEmail({
  to: "user@example.com",
  templateKey: "forgotPassword",
  context: {
    name: "张三",
    url: "https://example.com/reset-password",
  },
  locale: "zh", // 中文语言
});

🔧 创建自定义模板

1. 创建模板组件

src/mail/templates/custom-template.tsx
import { Text } from "@react-email/components";
import { createTranslator } from "use-intl/core";
import { Layout } from "@/mail/components/layout";
import { EmailButton } from "@/mail/components/email-button";
import type { i18nEmailProps } from "@/mail/types";

interface CustomTemplateProps extends i18nEmailProps {
  userName: string;
  actionUrl: string;
}

export function CustomTemplate({ 
  userName, 
  actionUrl, 
  locale, 
  messages 
}: CustomTemplateProps) {
  const t = createTranslator({
    locale,
    messages,
    namespace: "mail.customTemplate",
  });

  return (
    <Layout>
      <Text>{t("greeting", { name: userName })}</Text>
      <Text>{t("body")}</Text>
      <EmailButton href={actionUrl}>
        {t("action")}
      </EmailButton>
    </Layout>
  );
}

2. 注册模板

src/mail/templates/index.ts
import { CustomTemplate } from "./custom-template";

export const mailTemplates = {
  // ... 现有模板
  customTemplate: CustomTemplate,
} as const;

3. 添加翻译

// messages/zh.json
{
  "mail": {
    "customTemplate": {
      "subject": "自定义邮件主题",
      "greeting": "你好 {name}!",
      "body": "这是一个自定义邮件模板。",
      "action": "执行操作"
    }
  }
}

4. 使用模板

await sendEmail({
  to: "user@example.com",
  templateKey: "customTemplate",
  context: {
    userName: "John Doe",
    actionUrl: "https://nextdevkit.com/action",
  },
});

🔌 自定义邮件提供商

创建自定义提供商

src/mail/providers/custom-provider.ts
import type { MailProvider, SendParams } from "@/mail/types";

export class CustomMailProvider implements MailProvider {
  private apiKey: string;

  constructor() {
    this.apiKey = process.env.CUSTOM_MAIL_API_KEY!;
  }

  async sendEmail(params: SendParams): Promise<void> {
    const response = await fetch("https://api.customprovider.com/send", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${this.apiKey}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        to: params.to,
        subject: params.subject,
        html: params.html,
        text: params.text,
      }),
    });

    if (!response.ok) {
      throw new Error("发送邮件失败");
    }
  }

  async subscribe(email: string): Promise<void> {
    // 订阅时事通讯的实现
  }

  async unsubscribe(email: string): Promise<void> {
    // 取消订阅时事通讯的实现
  }

  async isSubscribed(email: string): Promise<boolean> {
    // 检查订阅状态的实现
    return false;
  }
}

注册提供商

src/mail/providers/index.ts
import { CustomMailProvider } from "./custom-provider";

const providers = {
  resend: ResendMailProvider,
  custom: CustomMailProvider, // 添加自定义提供商
} as const;

更新配置

src/config/index.ts
export const appConfig = {
  mail: {
    provider: "custom", // 使用自定义提供商
    from: "noreply@nextdevkit.com",
    contact: "contact@nextdevkit.com",
  },
  // ... 其他配置
};

🔗 相关资源


🎯 下一步

现在您了解了邮件系统,请探索这些相关功能: