在自定义 New API 镜像里叠加 PR,修好邮箱绑定
前面写过一篇《给自用 New API 开放用户统计》,做法是在 Docker 构建时拉取官方源码,改三个地方,再编译成自己的镜像。这个方案一直在用,升级也只是修改 .env 里的 NEW_API_REF,没有另外维护一套仓库。
后来又碰到一个问题:New API 开启 Turnstile 后,个人设置里的邮箱无法绑定,已经绑定过的也无法改绑。前端点发送验证码没有结果,后端实际上在等 Turnstile token,但邮箱绑定界面没有把它带过去。
上游已经有人提交了 PR #6347:fix(profile): include Turnstile token for email binding。截至 2026 年 8 月 21 日,这个 PR 仍然是 open 状态,还没有进入官方主分支。我不想为了一个前端修复再维护长期分支,于是直接把它叠加进原来的补丁镜像。
这里说的“合并 PR”,不是把代码合并进官方仓库,而是在 Docker 构建阶段固定执行一次 cherry-pick --no-commit。
PR 修了什么
这个问题的修复范围不大,改了三个前端文件:
- 邮箱验证码请求必须接收 Turnstile token,并把
turnstile参数发给/api/verification; - 邮箱绑定窗口先完成 Turnstile 校验,再发送验证码;
- 请求完成后清空 token,重新创建 Turnstile 组件,避免重复使用过期 token;
- 给这段请求逻辑补了一份回归测试。
它没有修改 Go 后端、数据库、依赖和锁文件。之前用户统计补丁改的是仪表盘路由和 router/api-router.go,两边没有改同一个文件,直接叠加发生冲突的可能性不大。
修改实际参与构建的 Dockerfile
我的 Compose 覆盖文件仍然指向原来的补丁目录:
services: new-api: build: context: ./newapi-patched dockerfile: Dockerfile args: NEW_API_REF: ${NEW_API_REF} image: local/newapi-user-stats:latest pull_policy: build所以要改的是服务器上的:
/compose/new-api/newapi-patched/Dockerfile不是随便找一份源码在宿主机里执行 git cherry-pick。宿主机上的临时源码并不参与 Compose 构建,改了也不会进容器。
先备份原文件:
cp /compose/new-api/newapi-patched/Dockerfile \ /compose/new-api/newapi-patched/Dockerfile.bak然后在 Dockerfile 的 source 阶段加入 PR 地址和固定提交。原来拉取 NEW_API_REF 的部分改成下面这样:
FROM alpine/git:latest AS source
ARG NEW_API_REF=mainARG PR_REF=refs/pull/6347/headARG PR_SHA=89db50a3fab1beaa26bf6238b70225a914b13893
RUN echo "开始获取 new-api 源码..." \ && git init /src \ && git -C /src remote add origin https://github.com/QuantumNous/new-api.git \ && git -C /src fetch --depth=1 origin "${NEW_API_REF}" \ && git -C /src checkout --detach FETCH_HEAD \ && echo "基础源码获取完成: $(git -C /src rev-parse HEAD)" \ \ && echo "开始应用 PR #6347..." \ && git -C /src fetch --depth=2 origin "${PR_REF}" \ && test "$(git -C /src rev-parse FETCH_HEAD)" = "${PR_SHA}" \ && git -C /src cherry-pick --no-commit "${PR_SHA}" \ && git -C /src diff --check \ && echo "PR #6347 应用完成: ${PR_SHA}"
WORKDIR /src后面的用户统计补丁和编译阶段全部保留。执行顺序变成:
- 按
.env里的NEW_API_REF拉取官方基础版本; - 检查并应用 PR #6347;
- 继续执行原来的用户统计补丁;
- 编译前端和 Go 程序,生成最终镜像。
PR_SHA 固定为 89db50a3fab1beaa26bf6238b70225a914b13893,不是为了写得复杂。PR 还没有合并,作者以后可能继续推送提交。如果只拉 refs/pull/6347/head 而不校验 SHA,哪天重新构建时拿到的代码可能已经变了。现在只要 PR 的 head 发生变化,test 就会让构建立刻停止,至少不会悄悄换掉补丁。
不要把 .env 里的 NEW_API_REF 改成 refs/pull/6347/head。NEW_API_REF 是当前使用的官方基础版本,PR 只是叠加在它上面的一个修复。直接替换会把两件事混在一起,也可能把基础版本退回到 PR 作者当时使用的提交。
重新构建并替换容器
进入 Compose 目录,重新构建:
cd /compose/new-api
docker compose \ -f docker-compose.yml \ -f compose.override.yml \ build --no-cache new-api构建日志里应该依次出现基础源码提交、应用 PR #6347、用户统计补丁和后续编译过程。确认没有报错后,替换容器:
docker compose \ -f docker-compose.yml \ -f compose.override.yml \ up -d --force-recreate new-api检查容器状态和最近日志:
docker compose \ -f docker-compose.yml \ -f compose.override.yml \ ps
docker logs --tail=100 new-api两个补丁都要验证
容器正常运行不代表功能一定正常。我分别检查了原来的用户统计补丁和这次邮箱修复:
- 普通用户登录后仍然能看到
/dashboard/users; - 已登录普通用户请求
/api/data/users返回 200,未登录请求仍然被拒绝; - 管理员页面和原有功能正常;
- 开启 Turnstile 后,可以绑定新邮箱,也可以改绑已有邮箱;
- 浏览器开发者工具里,发送验证码的
/api/verification请求同时带有email和turnstile参数。
最后一项最直接。请求里只有邮箱、没有 turnstile,就说明页面仍然在使用旧前端,应该先检查镜像是否重新构建、Compose 是否真的替换了容器,不要先去折腾数据库。
如果以后发生冲突
升级 NEW_API_REF 后,如果构建报下面这种错误:
could not apply 89db50a... fix(profile): include Turnstile token for email binding说明新的官方源码已经和这个 PR 不兼容,可能是上游改了同一段代码,也可能是修复已经用另一种方式进入主分支。此时旧容器还在运行,构建失败不会自动把它替换掉。
不要删除 SHA 检查,也不要强行跳过冲突继续编译。先看上游是否已经正式解决邮箱绑定问题;如果仍然需要手工处理冲突,就该建一个自己维护的 Git 分支了。Dockerfile 里的临时 cherry-pick 适合这种文件不重叠、改动很小的补丁,不适合长期养一串冲突。
这次没有改 Compose、数据库和原来的用户统计补丁,只是在现有源码阶段多应用一个固定提交。原来的升级方式也不变:更新 NEW_API_REF,重新构建。如果 PR 后续合并进上游,再删除 PR_REF、PR_SHA 和对应的 fetch、cherry-pick 几行即可。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!




















