一个基于 NextJS + Cloudflare 技术栈构建的可爱临时邮箱服务🎉
简体中文 | English
在线演示 • 文档 • 特性 • 技术栈 • 本地运行 • 部署 • 邮箱域名配置 • 权限系统 • 系统设置 • 发件功能 • Webhook 集成 • OpenAPI • CLI 工具 • MCP 服务器 • 环境变量 • Github OAuth App 配置 • Google OAuth App 配置 • 贡献 • 许可证 • 交流群 • 支持



完整文档: https://docs.moemail.app
文档站点包含详细的使用指南、API 文档、部署教程等完整信息。
git clone https://github.com/beilunyang/moemail.git
cd moemail
pnpm install
cp wrangler.example.json wrangler.json
cp wrangler.email.example.json wrangler.email.json
cp wrangler.cleanup.example.json wrangler.cleanup.json
设置 Cloudflare D1 数据库名以及数据库 ID
cp .env.example .env.local
设置 AUTH_GITHUB_ID, AUTH_GITHUB_SECRET, AUTH_SECRET
pnpm db:migrate-local
pnpm dev
pnpm deploy:email
pnpm dev:cleanup
pnpm test:cleanup
pnpm generate-test-data
https://www.bilibili.com/video/BV19wrXY2ESM/
cp .env.example .env
在 .env 文件中设置环境变量
运行部署脚本
pnpm dlx tsx ./scripts/deploy/index.ts
本项目可使用 GitHub Actions 实现自动化部署。支持以下触发方式:
CLOUDFLARE_API_TOKEN: Cloudflare API 令牌CLOUDFLARE_ACCOUNT_ID: Cloudflare 账户 IDAUTH_GITHUB_ID: GitHub OAuth App IDAUTH_GITHUB_SECRET: GitHub OAuth App SecretAUTH_SECRET: NextAuth Secret,用来加密 session,请设置一个随机字符串CUSTOM_DOMAIN: 网站自定义域名,用于访问 MoeMail (可选, 如果不填, 则会使用 Cloudflare Pages 默认域名)PROJECT_NAME: Pages 项目名 (可选,如果不填,则为 moemail) DATABASE_NAME: D1 数据库名称 (可选,如果不填,则为 moemail-db)KV_NAMESPACE_NAME: Cloudflare KV namespace 名称,用于存储网站配置 (可选,如果不填,则为 moemail-kv)
选择触发方式:
方式一:推送 tag 触发 ```bash # 创建新的 tag git tag v1.0.0
# 推送 tag 到远程仓库 git push origin v1.0.0 ```
方式二:手动触发 - 进入仓库的 Actions 页面 - 选择 "Deploy" workflow - 点击 "Run workflow"
v 开头(例如:v1.0.0)在 MoeMail 个人中心页面,可以配置网站的邮箱域名,支持多域名配置,多个域名用逗号分隔

为了使邮箱域名生效,还需要在 Cloudflare 控制台配置邮件路由,将收到的邮件转发给 Email Worker 处理。



路由规则旁边的目标地址, 进去绑定一个邮箱本项目采用基于角色的权限控制系统(RBAC)。
新用户默认角色由皇帝在个人中心的网站设置中配置: - 公爵:新用户将获得临时邮箱、Webhook 配置权限以及 API Key 管理权限 - 骑士:新用户将获得临时邮箱和 Webhook 配置权限 - 平民:新用户无任何权限,需要等待皇帝册封为骑士或公爵
系统包含四个角色等级:
每个站点只能有一个皇帝
公爵(Duke)
可以被皇帝贬为骑士或平民
骑士(Knight)
可以被皇帝贬为平民或册封为公爵
平民(Civilian)
/api/roles/init-emperor 接口的用户将成为皇帝,即网站所有者站点已有皇帝后,无法再提升其他用户为皇帝
角色变更
系统设置存储在 Cloudflare KV 中,包括以下内容:
DEFAULT_ROLE: 新注册用户默认角色,可选值为 CIVILIAN、KNIGHT、DUKEEMAIL_DOMAINS: 支持的邮箱域名,多个域名用逗号分隔ADMIN_CONTACT: 管理员联系方式MAX_EMAILS: 每个用户可创建的最大邮箱数量皇帝角色可以在个人中心页面设置
MoeMail 支持使用临时邮箱发送邮件,基于 Resend 服务。
| 角色 | 每日发件限制 | 说明 |
|---|---|---|
| 皇帝 (Emperor) | 无限制 | 网站管理员,无发件限制 |
| 公爵 (Duke) | 5封/天 | 默认每日可发送5封邮件 |
| 骑士 (Knight) | 2封/天 | 默认每日可发送2封邮件 |
| 平民 (Civilian) | 禁止发件 | 无发件权限 |
💡 提示:皇帝可以在个人中心的邮件服务配置中自定义公爵和骑士的每日发件限制。
复制 API Key 供后续配置使用
配置发件服务
点击保存配置
验证配置
在邮箱页面创建一个新的临时邮箱
发送邮件
点击"发送"按钮
查看发送记录
当收到新邮件时,系统会向用户配置并且已启用的 Webhook URL 发送 POST 请求。
Content-Type: application/json
X-Webhook-Event: new_message
{
"emailId": "email-uuid",
"messageId": "message-uuid",
"fromAddress": "sender@example.com",
"subject": "邮件主题",
"content": "邮件文本内容",
"html": "邮件HTML内容",
"receivedAt": "2024-01-01T12:00:00.000Z",
"toAddress": "your-email@moemail.app"
}
项目提供了一个简单的测试服务器, 可以通过如下命令运行:
pnpm webhook-test-server
测试服务器会在本地启动一个 HTTP 服务器,监听 3001 端口(http://localhost:3001), 并打印收到的 Webhook 消息详情。
如果需要进行外网测试,可以通过 Cloudflare Tunnel 将服务暴露到外网:
pnpx cloudflared tunnel --url http://localhost:3001
本项目提供了 OpenAPI 接口,支持通过 API Key 进行访问。API Key 可以在个人中心页面创建(需要是公爵或皇帝角色)。
在请求头中添加 API Key:
X-API-Key: YOUR_API_KEY
GET /api/config
返回响应:
{
"defaultRole": "CIVILIAN",
"emailDomains": "moemail.app,example.com",
"adminContact": "admin@example.com",
"maxEmails": "10"
}
响应字段说明:
- defaultRole: 新用户默认角色,可选值:CIVILIAN(平民)、KNIGHT(骑士)、DUKE(公爵)
- emailDomains: 支持的邮箱域名,多个域名用逗号分隔
- adminContact: 管理员联系方式
- maxEmails: 每个用户可创建的最大邮箱数量
POST /api/emails/generate
Content-Type: application/json
{
"name": "test",
"expiryTime": 3600000,
"domain": "moemail.app"
}
参数说明:
- name: 邮箱前缀,可选
- expiryTime: 有效期(毫秒),可选值:3600000(1小时)、86400000(1天)、604800000(7天)、0(永久)
- domain: 邮箱域名,可通过 /api/config 接口获取
返回响应:
{
"id": "email-uuid-123",
"email": "test@moemail.app"
}
响应字段说明:
- id: 邮箱的唯一标识符
- email: 创建的邮箱地址
GET /api/emails?cursor=xxx
参数说明:
- cursor: 分页游标,可选
返回响应:
{
"emails": [
{
"id": "email-uuid-123",
"address": "test@moemail.app",
"createdAt": "2024-01-01T12:00:00.000Z",
"expiresAt": "2024-01-02T12:00:00.000Z",
"userId": "user-uuid-456"
}
],
"nextCursor": "encoded-cursor-string",
"total": 5
}
响应字段说明:
- emails: 邮箱列表数组
- nextCursor: 下一页游标,用于分页请求
- total: 邮箱总数量
GET /api/emails/{emailId}?cursor=xxx
参数说明:
- emailId: 邮箱的唯一标识符,必填
- cursor: 分页游标,可选
返回响应:
{
"messages": [
{
"id": "message-uuid-789",
"from_address": "sender@example.com",
"subject": "邮件主题",
"received_at": 1704110400000
}
],
"nextCursor": "encoded-cursor-string",
"total": 3
}
响应字段说明:
- messages: 邮件列表数组
- nextCursor: 下一页游标,用于分页请求
- total: 邮件总数量
DELETE /api/emails/{emailId}
参数说明:
- emailId: 邮箱的唯一标识符,必填
返回响应:
{
"success": true
}
响应字段说明:
- success: 删除操作是否成功
GET /api/emails/{emailId}/{messageId}
参数说明:
- emailId: 邮箱的唯一标识符,必填
- messageId: 邮件的唯一标识符,必填
返回响应:
{
"message": {
"id": "message-uuid-789",
"from_address": "sender@example.com",
"subject": "邮件主题",
"content": "邮件文本内容",
"html": "
邮件HTML内容
",
"received_at": 1704110400000
}
}
响应字段说明:
- message: 邮件详细信息对象
- id: 邮件的唯一标识符
- from_address: 发件人邮箱地址
- subject: 邮件主题
- content: 邮件纯文本内容
- html: 邮件HTML内容
- received_at: 接收时间(时间戳)
POST /api/emails/{emailId}/share
Content-Type: application/json
{
"expiresIn": 86400000
}
参数说明:
- emailId: 邮箱的唯一标识符,必填
- expiresIn: 分享链接有效期(毫秒),0 表示永久有效,可选
返回响应:
{
"id": "share-uuid-123",
"emailId": "email-uuid-123",
"token": "abc123def456",
"expiresAt": "2024-01-02T12:00:00.000Z",
"createdAt": "2024-01-01T12:00:00.000Z"
}
响应字段说明:
- id: 分享记录的唯一标识符
- emailId: 关联的邮箱 ID
- token: 分享链接的访问令牌
- expiresAt: 分享链接过期时间,null 表示永久有效
- createdAt: 创建时间
分享链接访问地址:https://your-domain.com/shared/{token}
GET /api/emails/{emailId}/share
参数说明:
- emailId: 邮箱的唯一标识符,必填
返回响应:
{
"shares": [
{
"id": "share-uuid-123",
"emailId": "email-uuid-123",
"token": "abc123def456",
"expiresAt": "2024-01-02T12:00:00.000Z",
"createdAt": "2024-01-01T12:00:00.000Z"
}
],
"total": 1
}
响应字段说明:
- shares: 分享链接列表数组
- total: 分享链接总数
DELETE /api/emails/{emailId}/share/{shareId}
参数说明:
- emailId: 邮箱的唯一标识符,必填
- shareId: 分享记录的唯一标识符,必填
返回响应:
{
"success": true
}
响应字段说明:
- success: 删除操作是否成功
POST /api/emails/{emailId}/messages/{messageId}/share
Content-Type: application/json
{
"expiresIn": 86400000
}
参数说明:
- emailId: 邮箱的唯一标识符,必填
- messageId: 邮件的唯一标识符,必填
- expiresIn: 分享链接有效期(毫秒),0 表示永久有效,可选
返回响应:
{
"id": "share-uuid-456",
"messageId": "message-uuid-789",
"token": "xyz789ghi012",
"expiresAt": "2024-01-02T12:00:00.000Z",
"createdAt": "2024-01-01T12:00:00.000Z"
}
响应字段说明:
- id: 分享记录的唯一标识符
- messageId: 关联的邮件 ID
- token: 分享链接的访问令牌
- expiresAt: 分享链接过期时间,null 表示永久有效
- createdAt: 创建时间
分享链接访问地址:https://your-domain.com/shared/message/{token}
GET /api/emails/{emailId}/messages/{messageId}/share
参数说明:
- emailId: 邮箱的唯一标识符,必填
- messageId: 邮件的唯一标识符,必填
返回响应:
{
"shares": [
{
"id": "share-uuid-456",
"messageId": "message-uuid-789",
"token": "xyz789ghi012",
"expiresAt": "2024-01-02T12:00:00.000Z",
"createdAt": "2024-01-01T12:00:00.000Z"
}
],
"total": 1
}
响应字段说明:
- shares: 分享链接列表数组
- total: 分享链接总数
```http DELETE /api/emails/{emailId}/messages/{messageId}/share/{shareId}
$ claude mcp add moemail \
-- python -m otcore.mcp_server <graph>