验证码邮件系统

系统概览

本系统为多个网站 / 模块提供统一的验证码邮件能力。每个接入方是一个「应用」,拥有:

业务流程:

业务系统 --POST /api/verify/send--> 本系统 --SMTP--> 用户邮箱
业务系统 --POST /api/verify/check--> 本系统 --校验 Redis--> 返回结果

一、创建应用

方式一:管理页面(推荐)

1
打开 模板管理,右上角输入 Admin Key(即 .env 的 ADMIN_KEY,未配置则用 API_KEY),点「连接」。
2
点左侧「+ 新建应用」,填写应用名称(显示在邮件里)、应用标识(英文/数字)、初始模板风格。
3
创建成功后得到 ak_ 开头的 API Key,复制保存给接入方。
应用名称可以随时在左侧列表点「编辑」修改,改完立即影响新发出的邮件显示名。

方式二:管理 API

curl -X POST http://你的服务器:8000/api/apps \
  -H "X-Admin-Key: 你的管理密钥" \
  -H "Content-Type: application/json" \
  -d '{"name":"XX商城","app_key":"shop","preset":"vibrant-orange"}'

二、接入发信

接入方只需调用两个接口,请求头带自己的 X-API-Key。

发送验证码

POST /api/verify/send
X-API-Key: ak_xxxxxxxxxxxxxxxx
Content-Type: application/json

{"email": "user@example.com", "scene": "register"}

校验验证码

POST /api/verify/check
X-API-Key: ak_xxxxxxxxxxxxxxxx
Content-Type: application/json

{"email": "user@example.com", "scene": "register", "code": "123456"}

scene 取值:

值场景
register注册
login登录
reset找回密码
change变更 / 绑定

校验成功返回 {"ok":true,"message":"验证码校验成功"},即可放行业务操作。

同一个应用的发信与校验必须使用同一个 API Key。验证码按应用隔离,换 Key 校验会失败。

Python 调用示例

import httpx

BASE = "http://你的服务器:8000"
KEY = "ak_xxxxxxxxxxxxxxxx"
HEADERS = {"X-API-Key": KEY}

def send_code(email: str, scene: str = "register"):
    r = httpx.post(f"{BASE}/api/verify/send",
                   json={"email": email, "scene": scene},
                   headers=HEADERS, timeout=10)
    r.raise_for_status()
    return r.json()

def check_code(email: str, code: str, scene: str = "register") -> bool:
    r = httpx.post(f"{BASE}/api/verify/check",
                   json={"email": email, "scene": scene, "code": code},
                   headers=HEADERS, timeout=10)
    return r.status_code == 200 and r.json().get("ok")

Node.js 调用示例

const BASE = "http://你的服务器:8000";
const KEY = "ak_xxxxxxxxxxxxxxxx";

async function sendCode(email, scene = "register") {
  const res = await fetch(`${BASE}/api/verify/send`, {
    method: "POST",
    headers: { "Content-Type": "application/json", "X-API-Key": KEY },
    body: JSON.stringify({ email, scene }),
  });
  return res.json();
}

三、自定义模板

每个应用的 4 个场景各有一份模板,可独立修改,不影响其他应用。

1
打开 模板管理,选择左侧应用。
2
切换场景标签(注册 / 登录 / 找回密码 / 变更绑定)。
3
编辑标题、HTML 正文、纯文本正文。
4
点「预览」查看渲染效果(验证码固定 123456),确认后点「保存模板」。

四、模板变量

模板使用 Jinja2 语法,渲染时自动转义变量,防止注入。

变量含义示例
{{ company }}应用名称XX商城
{{ scene_name }}场景中文名注册
{{ code }}验证码123456
{{ ttl_minutes }}有效期(分钟)10

还支持 Jinja2 控制语句,例如:

{% if code %}您的验证码是 {{ code }}{% endif %}

五、内置模板

preset名称风格
minimal-blue简约蓝白底蓝字,清爽通用(默认)
business-gray商务灰深色头部,正式稳重
vibrant-orange活力橙橙色主调,活泼年轻
「套用预设样式」会覆盖该应用 4 个场景的模板,自定义过的内容会被替换。

六、隔离说明

维度隔离方式
验证码Redis key 含应用 ID,A 应用发的码 B 应用校验不到
限流按「应用 + 邮箱」计数,互不影响
模板按应用 + 场景存储,各改各的
日志发送日志带应用 ID,可按应用筛选

七、常见问题

Q1:接入方只换 API Key 就行吗?

是。接口地址、请求参数、返回格式都不变,只需要把 X-API-Key 换成新应用的 key。注意收发要用同一个 key。

Q2:邮件里的品牌名怎么改?

改应用名称即可。在模板管理页左侧应用列表点「编辑」改名,新发出的邮件显示名立即生效。

Q3:能不能让接入方自己登录改模板?

当前用统一的 Admin Key,适合内部使用。若要对外开放自助管理,需要再加接入方账号体系。

Q4:应用被删除了验证码还在吗?

系统不提供删除应用,避免误删。如需停用,可将数据库 app.status 改为 disabled,对应 API Key 立即失效。

Q5:内网应用怎么接入?

用内网地址调用即可,例如 http://172.21.109.5:8000,无需走公网。