Nozomi Relay 是一个基于 Go 的轻量级邮件中转服务,用于从下游 SMTP 转发到腾讯云 SES 等多个邮件推送服务 API。
由于严格的风控政策,目前大量邮件提供商已经不再支持使用 SMTP 发送邮件,而是强制使用自家的 API。但是,常见的下游应用程序(例如 Rauthy, Gitea 等)仍然只能支持 SMTP 发信。本服务支持接收 SMTP 邮件后用 JavaScript 规则提取变量(例如验证码),再调用上游模板 Sendmail 接口。
- Go + Gin + SQLite 后端
- 内置 SMTP relay,支持配置多个下游 SMTP 账号
- 腾讯云 SES / Resend / Brevo API 支持
- 同步腾讯云模板列表与模板变量
- JavaScript 规则脚本提取下游邮件内容并映射模板变量
- 支持按时区设置不同邮件提供商的每日额度重置时间,避免超额扣费
- 发送历史、错误日志、腾讯云回调事件记录
- Ant Design 管理面板,含统计、配置、模板、规则、账号、日志
后端:
cd backend
cp .env.example .env
go run ./cmd/server前端:
cd Nozomi-Admin
npm run dev默认地址:
- 管理面板:http://127.0.0.1:5173
- 后端 API:http://127.0.0.1:5000
- SMTP relay:127.0.0.1:2525
如果你希望把管理面板挂到子路径,例如 https://auth.kirari.fun/nozomi,可以设置:
NOZOMI_WEB_BASE_PATH=/nozomi/这个值会同时影响:
- 管理面板静态资源前缀
- 前端路由前缀
- API 路径前缀,例如
/nozomi/api/... - 登录 cookie 的
Path
默认管理员账号来自 backend/.env,默认下游 SMTP 账号为 anonymous(空密码,仅在显式启用无认证 SMTP 时使用)。
数据库默认使用 SQLite,也可以切换到 PostgreSQL:
NOZOMI_DB_DRIVER=postgresql
NOZOMI_DB_DSN=postgres://nozomi:password@postgres:5432/nozomi?sslmode=disable嵌入式 Docker 镜像的前端资源路径必须与运行时路径一致,例如:
docker build --build-arg NOZOMI_WEB_BASE_PATH=/nozomi/ -t nozomi .嵌入式构建会把该路径写入二进制;若运行时覆盖为不同值,服务会拒绝启动并报告配置不一致。
将现有 SQLite 数据一次性导入空的 PostgreSQL 数据库:
./nozomi-relay \
--migrate-sqlite /data/nozomi.sqlite3 \
--postgres-dsn 'postgres://nozomi:password@postgres:5432/nozomi?sslmode=disable'这是离线迁移命令,执行前必须停止对源 SQLite 的写入。迁移要求目标表为空,并在一个事务中创建目标 schema、复制全部数据和重置 sequence;任一表失败都会回滚。
如果你从局域网 IP 访问前端,比如 http://192.168.31.221:5173,需要在后端 .env 里把 NOZOMI_CORS_ORIGINS 加上对应的完整 origin,例如:
NOZOMI_CORS_ORIGINS=http://localhost:5173,http://127.0.0.1:5173,http://192.168.31.221:5173开发模式下,前端 npm run dev 仍然是通过 Vite 代理把 /api 转发到 http://127.0.0.1:5000,不是直接从浏览器打 5000。
项目支持两种构建模式:
只构建 Go 后端,前端单独部署或用 Vite 开发服务运行:
./scripts/build.sh separate
./dist/nozomi-relay这是默认的 API-only 二进制,不包含前端静态文件。
先构建前端,再把 Nozomi-Admin/dist 嵌入 Go 二进制,由 Go 同时提供 API 和管理面板静态服务:
NOZOMI_WEB_BASE_PATH=/nozomi/ \
./scripts/build.sh embedded
./dist/nozomi-relay-embedded启动后访问后端 HTTP 地址即可打开管理面板,例如默认的 http://127.0.0.1:5000;如果设置了 NOZOMI_WEB_BASE_PATH=/nozomi/,则访问 http://127.0.0.1:5000/nozomi。/api/* 会跟随这个前缀,例如 /nozomi/api/*;其它路径会优先返回静态文件,未命中时回退到 index.html 以支持前端路由。
注意:
- 这里必须使用带尾部斜杠的
NOZOMI_WEB_BASE_PATH=/nozomi/ - 如果写成
/nozomi,Vite 构建出的静态资源路径可能仍然是根路径/assets/... - 挂在反向代理子路径下时,这会直接导致管理面板白屏
运行时可以用命令行参数控制是否启用内置静态服务:
./dist/nozomi-relay-embedded -web auto
./dist/nozomi-relay-embedded -web off普通分离模式二进制即使使用 -web auto 也不会提供前端静态文件,因为构建时没有嵌入 dist。
项目根目录提供了单容器 Dockerfile。它会先构建 Nozomi-Admin,再把前端嵌入 Go 二进制,启动后同时提供管理面板、HTTP API 和 SMTP relay。
docker build -t nozomi-relay .
docker run --rm -p 5000:5000 -p 2525:2525 -v nozomi-data:/data nozomi-relay如果放在反向代理后面,HTTP 通常只暴露给内部网络;SMTP 2525 仍然需要单独映射到宿主机,供下游应用连接。
规则脚本运行在 Go 内嵌 JavaScript 引擎中,后端提供 input:
{
from,
to,
subject,
text,
html,
headers,
raw
}脚本返回 null 表示不匹配;匹配时返回:
({
templateId: 100001, // 腾讯云模板 ID
subject: input.subject, // 邮件主题
variables: { // 模板变量映射
code: "123456",
action: "登录"
}
})后端会检查 variables 是否填满腾讯云模板中解析到的 {{变量名}}。
在腾讯云 SES 控制台配置回调地址:
https://你的域名/nozomi/api/callback/tencent
如果没有设置 NOZOMI_WEB_BASE_PATH,则仍然是根路径下的 /api/callback/tencent。
当前版本记录 delivered、bounce、dropped、open、click 等事件,并根据 bulkId / messageId 关联发送历史。
