new-api 邮箱注册与 SMTP 配置 (Email Registration and SMTP Setup)
目标
new-api 的「邮箱验证」(Email Verification) 开启后,用户注册必须先收到一封验证码邮件。本篇记录它如何工作、如何配置发信、以及几个会让人误判的坑。
机制:new-api 自带发信,但邮箱要你自己给
new-api 自带完整的发信实现,不需要装插件、不需要额外脚本:
- 发信代码:
common/email.go的SendEmail(subject, receiver, content),用 Go 标准库net/smtp自己实现 SMTP 客户端(支持 SSL 直连 465 与 STARTTLS)。 - 触发接口:
GET /api/verification?email=...→controller/misc.go的SendEmailVerification。 - 前端:注册页的「获取验证码」按钮(
web/src/features/auth/hooks/use-email-verification.ts)。
但它不会替你提供邮箱。必须给它一个发信邮箱 + 授权码(authorization code)——授权码由邮箱服务商签发,与账号密码不同。所以「要不要自己授权」的答案是:要。
配置项(options 表)
| Key | 说明 | QQ 邮箱取值 |
|---|---|---|
SMTPServer | SMTP 服务器 | smtp.qq.com |
SMTPPort | 端口 | 465 |
SMTPSSLEnabled | 直连 SSL | true(465 用) |
SMTPStartTLSEnabled | STARTTLS | false(587 才用) |
SMTPAccount | 发信账号 | xxx@qq.com |
SMTPFrom | 发件人地址(也用于生成 Message-ID 的域名) | 同 SMTPAccount |
SMTPToken | 授权码(不是登录密码) | 16 位字母 |
EmailVerificationEnabled | 是否要求邮箱验证 | true |
EmailDomainRestrictionEnabled | 是否限定邮箱域名 | true |
EmailDomainWhitelist | 允许的域名列表,逗号分隔 | 见下方坑 |
EmailAliasRestrictionEnabled | 是否禁止 + / . 别名 | 默认关 |
端口分支逻辑(common/email.go 的 newSMTPClient):SMTPSSLEnabled 为真,或 SMTPPort == 465 且未开 STARTTLS 时,走 tls.Dial;否则走明文 smtp.Dial 再按需 StartTLS。
写入方式:直接改库最省事,改完不用重启——
INSERT INTO options(key,value) VALUES('SMTPServer','smtp.qq.com')
ON CONFLICT(key) DO UPDATE SET value=excluded.value;坑
1. ApiError 返回的是 HTTP 200,日志里的 200 不等于成功
common/gin.go:
func ApiError(c *gin.Context, err error) {
c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
}所以 GIN 访问日志里 GET /api/verification ... 200 完全不能证明邮件发出去了。必须看响应体里的 success 字段,或者用 curl 实测。
判断依据:{"message":"invalid SMTP account","success":false} = SMTPFrom/SMTPAccount 是空的;{"message":"SMTP 服务器未配置","success":false} = 服务器和账号都空。
2. 邮箱域名白名单是精确匹配整个域名,不是后缀匹配
service/email_binding.go:
allowed := slices.Contains(common.EmailDomainWhitelist, parts[1])parts[1] 是 @ 后面的完整域名。所以白名单里写 outlook 不会放行 outlook.com——只会放行一个字面量域名叫 outlook 的地址,等于永远不匹配。曾因此在线上实际挡掉真实用户。抄白名单时每个域名都要写全。
Go 默认白名单(common/constants.go)里有 gmail.com / qq.com / 163.com / 126.com / hotmail.com / foxmail.com 等;一旦 options 表写了 EmailDomainWhitelist,就以表里的为准,默认值被整体覆盖。
3. 改 options 表后要等同步周期(默认 60 秒)
common/init.go 的 SyncFrequency = GetEnvOrDefault("SYNC_FREQUENCY", 60),loadOptionsFromDatabase() 按这个周期把表里的值刷进内存变量。日志里能看到 [SYS] ... | syncing options from database。改完立刻测会失败,等一个周期再测。
4. EmailVerificationEnabled=true + SMTP 未配置 = 所有人注册不了
这是最容易踩的组合:验证开着、发信没配,前端「获取验证码」一直失败,但接口返回 HTTP 200,看日志看不出问题。排查时第一件事就是实测一次 /api/verification 看响应体。
5. QQ 个人邮箱有每日发信上限
约 100 封/天(QQ 会员约 500 封/天),且采用动态风控;短时间高频发信(每分钟上限约 20–40 封)会被临时阻断。用授权码不会提升额度。注册量一大就会顶到天花板,届时需换 Resend / Brevo 这类专业发信服务。
验证方法
# 1) 服务端直接发一封(不用浏览器)
curl -s "http://127.0.0.1:3000/api/verification?email=你的邮箱@qq.com"
# 期望:{"message":"","success":true}
# 响应耗时有几秒 = 真的连上了 SMTP 服务器并投递
# 2) 确认同步周期已加载配置
journalctl -u new-api --since "-3 min" | grep "syncing options"
# 3) 看服务端有无报错
journalctl -u new-api --since "-90 sec" | grep -iE "smtp|email"适用边界
- 本篇针对 new-api v1.0.0-rc.40(
QuantumNous/new-api);字段名与端口分支逻辑可能随版本变化。 - 邮箱验证开启时,
controller/user.go的Register要求user.Email和user.VerificationCode都非空;关闭后注册只校验用户名密码。 - QQ 邮箱 SMTP 从境外服务器(如 LA 小鸡)连接正常,465 端口 TLS 握手可通,未见 IP 风控拦截。
本次实例:6767 API 配 QQ 邮箱(2026-09-24)
- 站点
ai.6767.chat(LA 小鸡,/opt/relay/na/one-api.db)。 - 原状态:
EmailVerificationEnabled=true,但options表没有任何 SMTP 行 → 所有用户注册卡在拿不到验证码;当天已有真实用户在试(qq.com / gmail.com / outlook.com)全部失败。 - 另发现
EmailDomainWhitelist写成了qq.com,163.com,126.com,gmail.com,outlook(漏.com),outlook 用户被白名单挡掉。 - 处理:备份库到
/root/one-api.db.bak-20260924_144336→ 写入SMTPServer=smtp.qq.com/SMTPPort=465/SMTPSSLEnabled=true/SMTPAccount/SMTPFrom/SMTPToken(授权码)→ 修白名单 → 等服务一个同步周期 → 实测{"success":true},耗时 3.3s。
关联
- new-api 前端定制与重编译部署 (Frontend Customization and Rebuild Deploy)
- new-api 批量生成兑换码 (Batch Generate Redemption Codes)
本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/AI apps