mcpField
C
cloudflare-email-routing

cloudflare-email-routing

github.com/jackspace云与 DevOps1655 次安装12 周前更新
代码仓库

关于 cloudflare-email-routing

面向 Cloudflare Workers 开发者的 Email Routing 实践指南,介绍收取、解析、转发和发送邮件的配置与代码模式。还覆盖常见部署与投递问题的预防和排查。

主要功能

  1. 01Email Workers 收取与处理邮件
  2. 02解析邮件内容和附件
  3. 03按规则过滤与转发
  4. 04Worker 发送邮件
  5. 05配置 DNS 与邮件绑定

适用场景

  1. 01将来信存入 D1 或 KV
  2. 02按主题路由到不同收件地址
  3. 03为支持请求发送自动回复
  4. 04从 Worker 发送通知邮件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jackspace/claudeskillz --skill cloudflare-email-routing

安装后在 Claude Code 中输入/cloudflare-email-routing确认已加载。

附带文件README.mdSKILL.json

详细介绍

Cloudflare Email Routing

状态:已可用于生产环境 ✅ 最后更新:2025-10-23 最新版本:[email protected],[email protected]


什么是 Cloudflare Email Routing?

Cloudflare Email Routing 提供两项互补功能:

  1. Email Workers - 使用自定义逻辑(允许列表、阻止列表、转发、解析、回复)接收和处理收到的邮件
  2. Send Email - 从 Workers 向已验证的目标地址发送邮件(通知、警报、确认信息)

这两项功能均为免费,并可协同工作,为 Cloudflare Workers 提供完整的电子邮件功能。


快速开始(10 分钟)

第 1 部分:启用 Email Routing(控制面板)

前提条件:域名必须使用 Cloudflare DNS

  1. 登录 Cloudflare 控制面板 → 选择你的域名
  2. 前往 Email > Email Routing
  3. 选择 Enable Email Routing → Add records and enable
    • 这会自动向 DNS 添加 MX 记录、SPF 和 DKIM
  4. 创建目标地址:
  5. ✅ 基本电子邮件转发现已启用

你刚刚完成的操作:配置了 DNS 和基本转发。现在,让我们为自定义逻辑添加 Workers。


第 2 部分:使用 Email Workers 接收邮件
1. 安装依赖项
npm install [email protected] [email protected]

为什么需要这些软件包:

  • postal-mime - 解析收到的邮件(标头、正文、附件)
  • mimetext - 创建用于发送或回复的邮件
2. 创建 Email Worker

创建 src/email.ts:

import { EmailMessage } from 'cloudflare:email';
import PostalMime from 'postal-mime';

export default {
  async email(message, env, ctx) {
    // Parse the incoming message
    const parser = new PostalMime.default();
    const email = await parser.parse(await new Response(message.raw).arrayBuffer());

    console.log('From:', message.from);
    console.log('To:', message.to);
    console.log('Subject:', email.subject);

    // Forward to verified destination
    await message.forward('[email protected]');
  },
};
3. 配置 Wrangler

更新 wrangler.jsonc:

{
  "name": "email-worker",
  "main": "src/email.ts",
  "compatibility_date": "2025-10-11"
}
4. 部署并绑定
npx wrangler deploy

# In Cloudflare Dashboard:
# Email > Email Routing > Email Workers
# Select your worker → Create route → Enter address (e.g., [email protected])

你刚刚完成的操作:创建了一个用于记录并转发邮件的 Worker。


第 3 部分:从 Workers 发送邮件
1. 配置 Send Email 绑定

更新 wrangler.jsonc:

{
  "name": "my-worker",
  "main": "src/index.ts",
  "compatibility_date": "2025-10-11",
  "send_email": [
    {
      "name": "EMAIL",
      "destination_address": "[email protected]"
    }
  ]
}

重要:destination_address 必须是:

  • 已启用 Email Routing 的域名
  • Email Routing 设置中已验证的目标地址
2. 从 Worker 发送邮件
import { EmailMessage } from 'cloudflare:email';
import { createMimeMessage } from 'mimetext';

export default {
  async fetch(request, env, ctx) {
    // Create email message
    const msg = createMimeMessage();
    msg.setSender({ name: 'My App', addr: '[email protected]' });
    msg.setRecipient('[email protected]');
    msg.setSubject('Welcome to My App');
    msg.addMessage({
      contentType: 'text/plain',
      data: 'Thank you for signing up!',
    });

    // Send via binding
    const message = new EmailMessage(
      '[email protected]',
      '[email protected]',
      msg.asRaw()
    );

    await env.EMAIL.send(message);

    return new Response('Email sent!');
  },
};
3. 部署
npx wrangler deploy

你刚刚完成的操作:将 Worker 配置为向已验证的地址发送邮件。


Email Workers:完整指南

运行时 API
EmailEvent 处理程序
export default {
  async email(message: ForwardableEmailMessage, env: Env, ctx: ExecutionContext) {
    // Process email here
  },
};

参数:

  • message - ForwardableEmailMessage 对象
  • env - 环境绑定(KV、D1、机密等)
  • ctx - 执行上下文(用于异步操作的 waitUntil)
ForwardableEmailMessage 属性
interface ForwardableEmailMessage {
  readonly from: string;          // Sender email
  readonly to: string;            // Recipient email
  readonly headers: Headers;      // Email headers
  readonly raw: ReadableStream;   // Raw email message
  readonly rawSize: number;       // Size in bytes

  // Methods
  setReject(reason: string): void;
  forward(rcptTo: string, headers?: Headers): Promise<void>;
  reply(message: EmailMessage): Promise<void>;
}

常见模式

模式 1:允许列表

仅接受来自获准发件人的邮件:

export default {
  async email(message, env, ctx) {
    const allowList = [
      '[email protected]',
      '[email protected]',
      '[email protected]',
    ];

    if (!allowList.includes(message.from)) {
      message.setReject('Address not on allowlist');
      return;
    }

    await message.forward('[email protected]');
  },
};

适用场景:联系表单、私人电子邮件地址、团队收件箱


模式 2:阻止列表

拒绝来自特定发件人或域名的邮件:

export default {
  async email(message, env, ctx) {
    const blockList = [
      '[email protected]',
      '@suspicious-domain.com', // Block entire domain
    ];

    const isBlocked = blockList.some(pattern =>
      message.from.includes(pattern)
    );

    if (isBlocked) {
      message.setReject('Sender blocked');
      return;
    }

    await message.forward('[email protected]');
  },
};

适用场景:垃圾邮件过滤、屏蔽骚扰者、按域名屏蔽


模式 3:解析并存储

提取邮件内容并存储到 D1 或 KV 中:

import PostalMime from 'postal-mime';

export default {
  async email(message, env, ctx) {
    // Parse email
    const parser = new PostalMime.default();
    const rawEmail = new Response(message.raw);
    const email = await parser.parse(await rawEmail.arrayBuffer());

    // Store in D1
    await env.DB.prepare(
      'INSERT INTO emails (from_addr, subject, text, received_at) VALUES (?, ?, ?, ?)'
    ).bind(
      message.from,
      email.subject,
      email.text,
      new Date().toISOString()
    ).run();

    // Forward to inbox
    await message.forward('[email protected]');
  },
};

适用场景:邮件归档、工单系统、支持收件箱、审计日志


模式 4:自动回复

使用自定义逻辑发送自动回复:

import PostalMime from 'postal-mime';
import { createMimeMessage } from 'mimetext';
import { EmailMessage } from 'cloudflare:email';

export default {
  async email(message, env, ctx) {
    // Parse incoming email
    const parser = new PostalMime.default();
    const email = await parser.parse(await new Response(message.raw).arrayBuffer());

    // Create reply
    const msg = createMimeMessage();
    msg.setSender({ name: 'Support Team', addr: '[email protected]' });
    msg.setRecipient(message.from);
    msg.setHeader('In-Reply-To', message.headers.get('Message-ID'));
    msg.setSubject(`Re: ${email.subject}`);
    msg.addMessage({
      contentType: 'text/plain',
      data: `Thank you for your message about "${email.subject}". We'll respond within 24 hours.`,
    });

    // Send reply
    const replyMessage = new EmailMessage(
      '[email protected]',
      message.from,
      msg.asRaw()
    );

    await message.reply(replyMessage);

    // Also forward to team inbox
    await message.forward('[email protected]');
  },
};

适用场景:不在办公室时的自动回复、支持工单确认、自动化响应


模式 5:条件路由

根据邮件内容将邮件路由到不同目标地址:

import PostalMime from 'postal-mime';

export default {
  async email(message, env, ctx) {
    const parser = new PostalMime.default();
    const email = await parser.parse(await new Response(message.raw).arrayBuffer());

    const subject = email.subject.toLowerCase();

    // Route based on subject keywords
    if (subject.includes('urgent') || subject.includes('critical')) {
      await message.forward('[email protected]');
    } else if (subject.includes('invoice') || subject.includes('payment')) {
      await message.forward('[email protected]');
    } else if (subject.includes('support') || subject.includes('help')) {
      await message.forward('[email protected]');
    } else {
      await message.forward('[email protected]');
    }
  },
};

适用场景:部门路由、优先级筛选、按类别划分的收件箱


Send Email:完整指南

配置
单个目标地址(简单)
{
  "send_email": [
    {
      "name": "EMAIL",
      "destination_address": "[email protected]"
    }
  ]
}

行为:通过 env.EMAIL 发送的所有邮件都会发往此地址。

多个目标地址(灵活)
{
  "send_email": [
    {
      "name": "EMAIL",
      "allowed_destination_addresses": [
        "[email protected]",
        "[email protected]",
        "[email protected]"
      ]
    }
  ]
}

行为:可以向列表中的任意地址发送邮件。

多个绑定(便于管理)
{
  "send_email": [
    {
      "name": "NOTIFICATIONS",
      "destination_address": "[email protected]"
    },
    {
      "name": "ALERTS",
      "destination_address": "[email protected]"
    }
  ]
}

行为:将不同绑定用于不同用途。


发送邮件
基本文本邮件
import { EmailMessage } from 'cloudflare:email';
import { createMimeMessage } from 'mimetext';

const msg = createMimeMessage();
msg.setSender({ name: 'My App', addr: '[email protected]' });
msg.setRecipient('[email protected]');
msg.setSubject('Welcome!');
msg.addMessage({
  contentType: 'text/plain',
  data: 'Welcome to our service!',
});

const email = new EmailMessage(
  '[email protected]',
  '[email protected]',
  msg.asRaw()
);

await env.EMAIL.send(email);
HTML 邮件
import { EmailMessage } from 'cloudflare:email';
import { createMimeMessage } from 'mimetext';

const msg = createMimeMessage();
msg.setSender({ name: 'My App', addr: '[email protected]' });
msg.setRecipient('[email protected]');
msg.setSubject('Welcome!');

// Add both plain text and HTML versions
msg.addMessage({
  contentType: 'text/plain',
  data: 'Welcome to our service!',
});

msg.addMessage({
  contentType: 'text/html',
  data: '<h1>Welcome!</h1><p>Thanks for joining us.</p>',
});

const email = new EmailMessage(
  '[email protected]',
  '[email protected]',
  msg.asRaw()
);

await env.EMAIL.send(email);
带自定义标头的邮件
import { EmailMessage } from 'cloudflare:email';
import { createMimeMessage } from 'mimetext';

const msg = createMimeMessage();
msg.setSender({ name: 'My App', addr: '[email protected]' });
msg.setRecipient('[email protected]');
msg.setSubject('Password Reset');

// Add custom headers
msg.setHeader('X-Priority', '1');
msg.setHeader('X-Application-ID', 'my-app-123');

msg.addMessage({
  contentType: 'text/plain',
  data: 'Click here to reset your password...',
});

const email = new EmailMessage(
  '[email protected]',
  '[email protected]',
  msg.asRaw()
);

await env.EMAIL.send(email);

DNS 配置

自动设置(推荐)

在仪表板中启用 Email Routing 时,Cloudflare 会自动添加:

  1. MX 记录 - 将邮件直接发送到 Cloudflare 的服务器

    yourdomain.com. 300 IN MX 13 amir.mx.cloudflare.net.
    yourdomain.com. 300 IN MX 86 linda.mx.cloudflare.net.
    yourdomain.com. 300 IN MX 24 isaac.mx.cloudflare.net.
    
  2. SPF 记录 - 授权 Cloudflare 代表你发送邮件

    yourdomain.com. 300 IN TXT "v=spf1 include:_spf.mx.cloudflare.net ~all"
    
  3. DKIM 记录 - 为外发邮件签名

    Automatically configured per domain
    
手动设置(高级)

如果你需要从其他服务提供商迁移:

  1. 前往 Email > Email Routing > Settings
  2. 选择 Start disabling > Unlock records and continue
  3. 根据需要编辑 DNS 记录
  4. 准备就绪后,选择 Lock DNS records 以保护 Email Routing

警告:更改 MX 记录会导致 Email Routing 无法使用。仅在迁移服务提供商时执行此操作。


预防已知问题

此技能可预防 8 个已记录的问题:

问题 #1:“此 Workers 不支持 Email Trigger”

错误:测试邮件 Workers 时,失败并显示“此 Workers 不支持 Email Trigger”

来源:workers-sdk #3751

原因:Wrangler dev 并不完全支持邮件触发器;必须通过已部署的 Workers 进行测试

预防措施:

  • 测试前务必部署邮件 Workers
  • 使用 wrangler tail 进行实时调试
  • 使用仪表板的“Test Email Event”功能(可用时)

问题 #2:目标地址验证错误

错误:已验证的目标地址在 Email Worker 转发中显示为“未验证”

来源:社区报告(Cloudflare Community)

原因:仪表板存在错误,只有在常规路由规则中也使用这些地址时,才会将其显示为已验证

预防措施:

  • 先为每个目标地址创建常规转发规则
  • 然后在 Email Workers 中使用相同的地址
  • 部署 Workers 前验证地址

问题 #3:Gmail 速率限制

错误:“421:我们的系统检测到来自你 IP 地址的未经请求邮件发送速率异常”

来源:社区报告

原因:由于共享基础设施,Gmail 可能会将 Cloudflare 的 IP 范围标记为可疑

预防措施:

  • 正确配置 SPF/DKIM/DMARC 记录
  • 不要通过 Email Routing 发送批量邮件
  • 大量发送时使用事务性邮件服务(例如 SendGrid、Mailgun)
  • 限制发送速率(个人用途每小时最多发送 50-100 封邮件)

问题 #4:使用 MailChannels 时出现 SPF permerror

错误:通过 MailChannels 路由时出现 SPF permerror

来源:社区讨论

原因:通过多个服务转发时,SPF 记录链会中断

预防措施:

  • 使用 Email Routing 的原生发送功能,而不是 MailChannels
  • 如果使用 MailChannels,请正确配置 SPF include
  • 使用 MXToolbox SPF checker 进行测试

问题 #5:免费套餐的日志记录有限

错误:无法查看 Workers 日志或邮件处理详情

来源:社区报告

原因:免费套餐的日志保留和流式传输功能有限

预防措施:

  • 开发期间使用 wrangler tail 查看实时日志
  • 在邮件 Workers 中广泛使用 console.log()
  • 将关键数据存储在 D1/KV 中以便调试
  • 升级到 Workers 付费套餐以获得更好的可观测性

问题 #6:活动日志不一致

错误:即使邮件已成功转发,在活动日志中仍显示为“已丢弃”

来源:社区报告

原因:仪表板错误地显示了状态

预防措施:

  • 检查邮件是否实际送达,而不要只依赖仪表板
  • 使用 wrangler tail 验证处理情况
  • 在 D1/KV 中实现自己的日志记录
  • 使用真实邮件进行测试,确认邮件送达

问题 #7:测试邮件事件一直处于加载状态

错误:Dashboard 中的“Test Email Event”按钮一直处于加载状态

来源:workers-sdk #9195

原因:Dashboard 测试界面存在 Bug(截至 2025-10 尚未解决)

预防措施:

  • 不要依赖 Dashboard 测试功能
  • 改用 curl 配合本地开发(参见“本地开发”部分)
  • 部署后使用真实邮件进行测试
  • 使用 wrangler tail 监控处理情况

问题 #8:Worker 调用失败

错误:“Rejected reason: Unknown error: failed to call worker: Worker call failed for 3 times, aborting…”

来源:workers-sdk #9069,社区报告

原因:Worker 因运行时错误、超时或内存问题而崩溃

预防措施:

  • 使用 try/catch 添加全面的错误处理
  • 为外部 API 调用设置超时
  • 在拒绝请求前将错误记录到 D1/KV
  • 对非关键操作使用 ctx.waitUntil()
  • 使用各种邮件格式进行测试(纯文本、HTML、附件)

本地开发

接收邮件

Wrangler 通过 HTTP POST 模拟邮件接收:

# Start dev server
npx wrangler dev

# In another terminal, send test email
curl http://localhost:8787 -X POST \
  --data-binary @- << EOF
From: [email protected]
To: [email protected]
Subject: Test Email

This is a test email body.
EOF

结果:Wrangler 会记录邮件处理过程,并显示转发邮件的目标位置。

发送邮件

Wrangler 会将已发送的邮件写入本地 .eml 文件:

// Your worker code
await env.EMAIL.send(message);

终端输出:

[wrangler:inf] send_email binding called with the following message:
  /tmp/miniflare-abc123/files/email/message-123.eml

查看邮件:

cat /tmp/miniflare-abc123/files/email/message-123.eml

配置文件参考

完整的 wrangler.jsonc(接收 + 发送)
{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "email-worker",
  "main": "src/email.ts",
  "account_id": "YOUR_ACCOUNT_ID",
  "compatibility_date": "2025-10-11",
  "observability": {
    "enabled": true
  },

  // Send email binding
  "send_email": [
    {
      "name": "NOTIFICATIONS",
      "destination_address": "[email protected]"
    },
    {
      "name": "ALERTS",
      "allowed_destination_addresses": [
        "[email protected]",
        "[email protected]"
      ]
    }
  ],

  // Optional: Add other bindings
  "d1_databases": [
    {
      "binding": "DB",
      "database_name": "email-archive",
      "database_id": "YOUR_DATABASE_ID"
    }
  ],

  "kv_namespaces": [
    {
      "binding": "EMAIL_CACHE",
      "id": "YOUR_KV_ID"
    }
  ]
}

TypeScript 类型

环境绑定
interface Env {
  // Send email bindings
  EMAIL: SendEmail;
  NOTIFICATIONS: SendEmail;
  ALERTS: SendEmail;

  // Other bindings
  DB: D1Database;
  EMAIL_CACHE: KVNamespace;
}

interface SendEmail {
  send(message: EmailMessage): Promise<void>;
}
完整类型定义
import { EmailMessage } from 'cloudflare:email';

interface ForwardableEmailMessage {
  readonly from: string;
  readonly to: string;
  readonly headers: Headers;
  readonly raw: ReadableStream;
  readonly rawSize: number;

  setReject(reason: string): void;
  forward(rcptTo: string, headers?: Headers): Promise<void>;
  reply(message: EmailMessage): Promise<void>;
}

declare module 'cloudflare:email' {
  export class EmailMessage {
    constructor(from: string, to: string, raw: string | ReadableStream);
  }
}

完整设置检查清单

邮件路由设置
  • 域名已添加到 Cloudflare DNS
  • 已在 Dashboard 中启用 Email Routing
  • 已自动添加 MX、SPF、DKIM 记录
  • 至少有一个目标地址已通过验证
  • 使用自定义地址测试基本转发功能
Email Workers(接收)
  • 已安装 [email protected]
  • 已安装 [email protected]
  • 已创建包含 async email() 处理程序的邮件 Worker
  • 已部署 Worker:npx wrangler deploy
  • 已在 Dashboard 中将 Worker 绑定到邮件路由
  • 向路由地址发送真实邮件进行测试
  • 使用 wrangler tail 验证日志
发送邮件
  • 已在 wrangler.jsonc 中配置 send_email 绑定
  • 已指定 destination_address 或 allowed_destination_addresses
  • 所有目标地址都已在 Email Routing 中通过验证
  • Worker 代码使用 env.EMAIL.send()
  • 已部署 Worker:npx wrangler deploy
  • 通过 Worker 端点测试发送邮件
  • 确认邮件已送达收件人

故障排除

问题:“Email Trigger not available to this workers”

解决方案:

  • 部署 Worker:npx wrangler deploy
  • 使用真实邮件进行测试,不要使用本地模拟
  • 使用 wrangler tail 监控处理情况

问题:“Destination address not verified”

解决方案:

  • 在 Dashboard 中检查 Email Routing > Destination addresses
  • 如有需要,点击“Resend verification”
  • 先创建常规转发规则(作为 Bug 的变通方案)
  • 在部署 Worker 前验证所有地址

问题:Gmail 因 421 错误拒收邮件

解决方案:

  • 确认已配置 SPF/DKIM 记录(使用 Email Routing 时会自动配置)
  • 降低发送频率(个人使用时每小时最多 50-100 封)
  • 不要发送未经请求的邮件或群发邮件
  • 高发送量场景可考虑使用事务性邮件服务

问题:邮件未从 Email Worker 转发

解决方案:

  • 检查 Dashboard 中是否已将 Worker 绑定到正确的邮件路由
  • 确认目标地址已通过验证
  • 使用 wrangler tail 查看处理日志
  • 检查 Worker 代码中的错误(在 forward() 外围使用 try/catch)
  • 确认 MX 记录仍指向 Cloudflare

问题:无法查看 Worker 日志

解决方案:

  • 使用 wrangler tail --format pretty 查看实时日志
  • 在 Worker 中添加大量 console.log() 语句
  • 将调试信息存储在 D1 或 KV 中,以便稍后检查
  • 升级到 Workers 付费计划,以延长日志保留时间

问题:Worker 因“failed to call worker”而崩溃

解决方案:

  • 为所有操作添加 try/catch 错误处理
  • 为外部 API 调用设置超时时间
  • 使用不同的邮件格式(纯文本、HTML、附件)进行测试
  • 检查 Worker 是否超出 CPU/内存限制
  • 对非阻塞操作使用 ctx.waitUntil()

高级主题

解析邮件附件
import PostalMime from 'postal-mime';

export default {
  async email(message, env, ctx) {
    const parser = new PostalMime.default();
    const email = await parser.parse(await new Response(message.raw).arrayBuffer());

    // Access attachments
    if (email.attachments && email.attachments.length > 0) {
      for (const attachment of email.attachments) {
        console.log('Attachment:', attachment.filename);
        console.log('Type:', attachment.mimeType);
        console.log('Size:', attachment.content.length);

        // Store in R2
        await env.BUCKET.put(
          `emails/${Date.now()}-${attachment.filename}`,
          attachment.content
        );
      }
    }

    await message.forward('[email protected]');
  },
};

基于邮件创建任务
import PostalMime from 'postal-mime';

export default {
  async email(message, env, ctx) {
    const parser = new PostalMime.default();
    const email = await parser.parse(await new Response(message.raw).arrayBuffer());

    // Extract task from email subject
    const taskMatch = email.subject.match(/\[TASK\](.*)/i);

    if (taskMatch) {
      const taskDescription = taskMatch[1].trim();

      // Create task in D1
      await env.DB.prepare(
        'INSERT INTO tasks (description, created_by, created_at) VALUES (?, ?, ?)'
      ).bind(
        taskDescription,
        message.from,
        new Date().toISOString()
      ).run();

      // Send confirmation
      await message.reply(new EmailMessage(
        '[email protected]',
        message.from,
        `Task created: ${taskDescription}`
      ));
    }
  },
};

由邮件触发的工作流
export default {
  async email(message, env, ctx) {
    // Trigger Cloudflare Workflow based on email
    if (message.from.endsWith('@trusted-domain.com')) {
      await env.WORKFLOW.create({
        params: {
          emailFrom: message.from,
          emailTo: message.to,
          receivedAt: new Date().toISOString(),
        },
      });
    }

    await message.forward('[email protected]');
  },
};

依赖项

必需:

内置:

  • cloudflare:email - EmailMessage 类(无需安装)

可选:

  • @cloudflare/workers-types - TypeScript 类型定义

官方文档


软件包版本(已于 2025-10-23 验证)

{
  "dependencies": {
    "postal-mime": "^2.5.0",
    "mimetext": "^3.0.27"
  },
  "devDependencies": {
    "@cloudflare/workers-types": "^4.20251014.0",
    "wrangler": "^4.44.0"
  }
}

有疑问?遇到问题?

  1. 查看 references/common-errors.md,了解详细的故障排除信息
  2. 查看 references/dns-setup.md,获取 DNS 配置帮助
  3. 查看 references/local-development.md,了解测试模式
  4. 查看官方文档:https://developers.cloudflare.com/email-routing/
  5. 使用 wrangler tail 进行实时调试
  6. 在控制面板中确认所有目标地址均已验证

评论

支持 Markdown