Outbox

aifans.fan
← 返回发信台

AIFans Outbox

使用手册 & 实现文档

涵盖日常使用方法、架构设计、文件说明、部署流程以及后续扩展指南。随时翻阅,无需查外部资料。

目录

  1. 日常使用
    1. 登录
    2. 撰写 & 发送邮件
    3. 管理发件人 & 域名
    4. 桌面/手机 App(PWA)
  2. 架构总览
  3. 文件说明
  4. 部署 & 更新
  5. 密钥管理
  6. PWA 实现细节
  7. 绑定自定义域名
  8. 未来扩展
  9. 多域名发信(Resend + SES)
  10. 迁移 & 交接
01

日常使用

登录

访问 aifans-outbox.pages.dev(或已绑定的自定义域名),输入访问密码进入发信台。密码保存在浏览器 sessionStorage,关闭标签页即自动清除。

忘记密码? 密码(APP_PASSWORD)存储在 Cloudflare 后台 → Workers & Pages → aifans-outbox → Settings → Variables and Secrets,可在那里查看或修改。

撰写 & 发送邮件

  1. 选择发件人从「发件人」下拉框选一个已验证的地址;选「+ 自定义…」可临时输入任意 aifans.fan 地址。
  2. 填写收件人支持逗号、分号或空格分隔多个地址,下方会实时显示「N 个收件人 ✓」。
  3. 填写主题和正文右侧面板实时预览渲染效果。正文支持 HTML(默认)和纯文本两种模式,通过右上角切换。
  4. 点击「发送」成功后显示绿色回执 ID(来自 Resend),页面底部会追加到本次会话记录。
完整发送历史Resend 后台 → 可查看,包含时间、状态、详细日志。

管理发件人 & 新增域名

点击页面右上角「+ 管理发件域名」,弹出面板后:

添加新域名(第一步)

前往 Resend 域名管理,添加域名并按提示加 DNS 记录,等 Verified 后即可发信。

添加发件人地址(第二步)

切换到「发件人列表」标签,输入 名称 <you@example.com>,点添加——无需重新部署,立即生效(存 localStorage)。

桌面 / 手机 App(PWA)

Windows 11

Edge 或 Chrome 打开网址 → 地址栏右侧「安装此应用」图标 → 安装 → 开始菜单出现 Outbox 独立窗口图标。

iPhone / iPad

Safari 打开网址 → 底部分享按钮 → 「添加到主屏幕」→ 主屏出现图标,打开效果近似原生 App。

Windows 快捷键(可选):安装 AutoHotkey v2,新建 outbox.ahk,写入 ^!m::Run "https://aifans-outbox.pages.dev",双击运行后 Ctrl+Alt+M 随时打开。
02

架构总览

整个系统托管在 Cloudflare Pages 上,由两部分组成:

浏览器 / PWA / 命令行 填表或请求 → POST /api/send(带密码头) │ ▼ Cloudflare Pages(aifans-outbox.pages.dev) ┌─────────────────────────────────────────────────────┐ │ 静态资源(Edge CDN 全球分发) │ │ index.html · docs · sw.js · manifest · icons │ │ │ │ Pages Function:functions/api/send.js │ │ ├─ 校验 APP_PASSWORD │ │ └─ 按发件域名路由提供商 ↓ │ └─────────────────────────────────────────────────────┘ │ ├─ aifans.fan ──────────▶ Resend(resend.com) └─ 其它域名(sinosurg.com…) ▶ Amazon SES(SigV4 · v2 API)

设计要点

03

文件说明

aifans-outbox/ ├── index.html # 完整前端:登录 + 发信台 + 预览 + 域名管理面板 ├── docs.html # 本文档页面 ├── manifest.webmanifest # PWA 清单(图标/名称/显示模式) ├── sw.js # Service Worker(缓存外壳,/api/* 不缓存) ├── icon-192.png # PWA 图标 192×192 ├── icon-512.png # PWA 图标 512×512 ├── icon-maskable-512.png # Maskable 图标(Android 自适应图标用) ├── apple-touch-icon.png # iOS 主屏图标 180×180 ├── icon.svg # 图标源文件(深色底 + 琥珀圆环) ├── gen-icons.mjs # 一次性脚本:SVG → PNG(需 sharp) ├── README.md # 项目简介(部署说明) ├── MIGRATION.md # 迁移/交接手册(换账号、从零重建) ├── .gitignore # git 排除:node_modules/.wrangler/填好的密钥脚本 ├── scripts/ # 交接脚本模板 │ ├── set-secrets.example.ps1 # 批量设置全部 Secret │ └── add-domain-dns.example.ps1 # 给新 SES 域名写 DKIM/DMARC DNS └── functions/ # Cloudflare Pages Functions(自动识别) └── api/ ├── send.js # 后端:校验密码 + 按域名路由 Resend / SES └── _aws4fetch.js # 内置 AWS SigV4 签名库(调 SES 用,下划线=非路由)

关键文件详解

文件职责改动频率
index.html全部前端逻辑(单文件 SPA),含登录、撰写、实时预览、发件人 localStorage 管理功能迭代时改
functions/api/send.js唯一后端入口,校验密码,按发件域名路由到 Resend 或 SES,组装邮件体并发送发信逻辑/提供商变更时改
functions/api/_aws4fetch.jsAWS Signature V4 签名(aws4fetch,MIT),用 Web Crypto 在 Worker 内给 SES 请求签名几乎不动
manifest.webmanifestPWA 元数据:名称、图标、启动 URL、显示模式改应用名/图标时改
sw.jsService Worker:网络优先缓存外壳,保证可安装 + 离线打开缓存策略变更时改
04

部署 & 更新

项目通过 Wrangler CLI 部署到 Cloudflare Pages,无需 git 仓库。已通过 OAuth 登录,无需每次输入账号密码。

首次部署(已完成,留作记录)

npm i -g wrangler
wrangler pages project create aifans-outbox --production-branch main
wrangler pages deploy . --project-name aifans-outbox --branch main

日常更新流程

修改完任意文件后,在项目目录执行:

cd D:\projects\resend
wrangler pages deploy . --project-name aifans-outbox --branch main

30 秒左右全球生效,无需手动刷新 CDN。

更新 Secret(密钥)

# 更新 Resend API Key
echo "re_新密钥" | wrangler pages secret put RESEND_API_KEY --project-name aifans-outbox

# 更新登录密码
echo "新密码" | wrangler pages secret put APP_PASSWORD --project-name aifans-outbox
注意:用 echo "..." | 写入 Secret 时,部分系统会带换行符导致密钥匹配失败。本项目后端已在两处加 .trim() 规避此问题。

查看 Wrangler 登录状态

wrangler whoami

显示 labladodog@163.com 及账号 ID 即表示已登录,权限涵盖 Pages Write。

05

密钥管理

Secret 名用途在哪里查看/修改
RESEND_API_KEY调用 Resend API 发信,格式 re_...Resend 后台 → API Keys;Cloudflare Pages → Settings → Variables and Secrets
APP_PASSWORD网页登录密码(自定义),校验头 x-app-passwordCloudflare Pages → Settings → Variables and Secrets
RESEND_DOMAINS走 Resend 的域名白名单(逗号分隔),当前 aifans.fan;其余域名自动走 SESCloudflare Pages → Variables and Secrets
AWS_ACCESS_KEY_IDSES 鉴权用 IAM access key,格式 AKIA...AWS IAM → Users → 该用户 → Security credentials
AWS_SECRET_ACCESS_KEYSES 鉴权用 IAM secret(仅创建时显示一次)AWS IAM(丢失只能重新生成)
AWS_SES_REGIONSES 区域,当前 us-east-2(必须与域名验证所在区一致)AWS SES 控制台右上角区域

Resend API Key Rotate(安全建议)

  1. 生成新 Key登录 resend.com/api-keys,Create API Key,复制新 key。
  2. 写入 Cloudflareecho "re_新key" | wrangler pages secret put RESEND_API_KEY --project-name aifans-outbox
  3. 重新部署wrangler pages deploy . --project-name aifans-outbox --branch main
  4. 删除旧 Key回 Resend 后台删掉旧 key,彻底失效。

AWS IAM 密钥 Rotate

  1. 新建 access keyAWS IAM → Users → 该用户 → Security credentials → Create access key(用途选 Application outside AWS)。
  2. 写入 Cloudflareecho "AKIA新..." | wrangler pages secret put AWS_ACCESS_KEY_ID --project-name aifans-outbox,secret 同理写 AWS_SECRET_ACCESS_KEY
  3. 重新部署wrangler pages deploy . --project-name aifans-outbox --branch main
  4. 停用旧 key回 IAM 把旧 access key 设为 Inactive 并删除。
06

PWA 实现细节

PWA(Progressive Web App)让网页可以像原生应用一样安装到设备。本项目用了三个标准组件:

① Web App Manifest(manifest.webmanifest)

告诉浏览器「这个网站可以被安装」,描述名称、图标、启动行为:

{
  "name": "AIFans Outbox",
  "short_name": "Outbox",
  "start_url": "/",
  "display": "standalone",      // 独立窗口,无浏览器地址栏
  "background_color": "#16130f",
  "theme_color": "#16130f",
  "icons": [
    { "src": "/icon-192.png", "sizes": "192x192", "purpose": "any" },
    { "src": "/icon-512.png", "sizes": "512x512", "purpose": "any" },
    { "src": "/icon-maskable-512.png", "sizes": "512x512", "purpose": "maskable" }
  ]
}

② Service Worker(sw.js)

浏览器在后台运行的脚本,拦截网络请求实现缓存。本项目策略:

③ 图标生成

原始设计是 SVG(深色背景 + 琥珀色同心圆),通过 sharp 库渲染成不同尺寸 PNG:

node gen-icons.mjs  // 需先 npm install sharp
Maskable 图标(icon-maskable-512.png)用于 Android 自适应图标,设计时内容留有约 20% 安全边距,确保圆形裁切后图案居中完整。

在 index.html 中的引入方式

<link rel="manifest" href="/manifest.webmanifest" />
<meta name="theme-color" content="#16130f" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />

// 脚本末尾注册 Service Worker
if ("serviceWorker" in navigator) {
  window.addEventListener("load", () => {
    navigator.serviceWorker.register("/sw.js").catch(() => {});
  });
}
07

绑定自定义域名

目前通过 aifans-outbox.pages.dev 访问。如需绑定如 mail.aifans.fan

  1. 命令行方式wrangler pages domain add aifans-outbox --domain mail.aifans.fan——因 aifans.fan 已在本账号的 Cloudflare,DNS 记录自动添加。
  2. 或后台操作Cloudflare → Workers & Pages → aifans-outbox → Custom domains → Set up a domain → 填入域名确认。
  3. 等待生效通常 1–5 分钟,SSL 证书自动签发。之后用自定义域名访问即可,*.pages.dev 地址同时保留有效。
08

未来扩展

Telegram 机器人入口 设计已完成,待实施

在 Pages 项目里新增 functions/telegram/webhook.js,复用已有的 RESEND_API_KEY。在 Telegram 发 /send 收件人 || 主题 || 正文 即可触发发信,有回执推送。需额外 3 个 Secret:TELEGRAM_BOT_TOKENTELEGRAM_ALLOWED_CHAT_ID(白名单)、TELEGRAM_WEBHOOK_SECRET

邮件中转(发邮件→自动转发)未来备选

用 Cloudflare Email Routing 接收发往魔法地址(如 relay@aifans.fan)的邮件,Worker 解析后用 Resend 重新以 aifans.fan 域名发出。适合"转发已有内容"场景,Wrangler token 已有 email_routing 权限。

Cloudflare Access 替代密码登录 安全加固

Zero Trust → Access → Applications → Self-hosted,绑定此网址,加一条 Allow 策略(你的邮箱地址)。之后访问自动跳 Cloudflare 邮件验证,无需维护 APP_PASSWORD

09

多域名发信:Resend + SES 混合架构 已实现

Resend 免费版仅允许 1 个自定义域名。为合法、低成本地支持多域名,后端改为按发件域名自动路由aifans.fan 继续走 Resend 免费,新域名走 Amazon SES(域名不限、按量计费 $0.10/1000 封,个人量级近乎免费)。

路由规则

后端读取 RESEND_DOMAINS 白名单:命中的域名走 Resend,其余一律走 SES。 所以新增 SES 域名时无需改这个变量,也无需改代码。

当前状态

域名通道状态
aifans.fanResend✅ 正常发信(不受改造影响)
sinosurg.comAmazon SESDKIM 已加 DNS(验证中→Verified);路由 + SigV4 签名已通;SES 生产访问审核中
SES 沙盒期:生产访问审批通过前,SES 域名只能发给已验证的收件地址、200 封/天。 审批通过(AWS 1–3 工作日,邮件通知)后自动解除,可发任意收件人。

以后新增一个 SES 域名(5 步,无需改代码)

  1. SES 建身份AWS SES(区域 us-east-2)→ Identities → Create identity → Domain → 填域名,得到 3 条 DKIM CNAME。
  2. 加 DNS 记录把那 3 条 CNAME(+ 可选 DMARC TXT)加到该域名的 Cloudflare DNS。可让我用一个临时的「Edit zone DNS」API Token 自动写入。
  3. 等验证SES 中域名状态变 Verified(几分钟~最多 72 小时)。
  4. 网页加发件人发信台「+ 管理发件域名」→ 发件人列表 → 加 名称 <you@新域名>
  5. 直接发选该发件人发信即可——后端自动识别为非 Resend 域名、走 SES。无需改代码或重新部署。

技术实现

为什么不用其他方案

10

迁移 & 交接

为支持「换账号 / 换电脑 / 换人接手」,项目已做到完全自包含、可复现。完整步骤见仓库根目录的 MIGRATION.md,这里是要点速览。

命脉:全部 Secret / 变量(名称必须一致)

后端依赖以下 6 项(存于 Cloudflare Pages → Settings → Variables and Secrets)。 真实值只在各平台后台,代码内无明文密钥。

名称用途
APP_PASSWORD网页登录密码
RESEND_API_KEYResend 发信 key
RESEND_DOMAINS走 Resend 的域名白名单(当前 aifans.fan),其余走 SES
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEYSES 鉴权 IAM 密钥
AWS_SES_REGIONSES 区域(当前 us-east-2)
改任何 Secret 后必须重新部署一次才生效:wrangler pages deploy . --project-name aifans-outbox --branch main

换账号场景(详见 MIGRATION.md §3)

换 Cloudflare 账号

新账号 wrangler login → create project → deploy → 重设 6 个 Secret → 再 deploy。若域名 DNS 也迁移,需重建 DKIM/SPF。

换 AWS 账号(SES)

重验证域名(新 DKIM CNAME)→ 新建 IAM key → 更新 3 个 AWS Secret → 重新申请生产访问(新账号默认沙盒)。

换 Resend 账号

新账号验证 aifans.fan → 建新 API Key → 更新 RESEND_API_KEY → 重新部署。

换电脑 / 接手人

账号不变时最简单:装 wrangler → wrangler login → 拷贝项目目录(或 git clone)→ 直接 deploy,Secret 已在云端无需重设。

交接物料清单


AIFans Outbox · Cloudflare Pages + Resend + Amazon SES · 最后更新 2026-05-30