OpenBitFun 生产源站恢复手册
给换机后的人和 AI Agent:clone BitFun 之后按本文把当前 ssh lwb 这台源站
搬到新机器。命令默认从 BitFun 仓库根目录执行。
这是 已有源站搬迁,不是从零装一套空服务。不要对仍在服务的 lwb 重跑
clone、crontab 整表安装、市场 first-install 或空 volume 的 deploy.sh。
不要再发明一份脱离仓库的同步脚本副本。历史上的
/root/repos/BitFun-AutoUpdate/ 只是旧主机上的运行拷贝,换机后不要重建。
当前源站只靠 cron,不必恢复 webhook listener。
Agent 先读这个
- 根目录
AGENTS.md,再读本文件。 - MiniApp / Skin 分别只走它们自己的手册,不要从本目录重建那两个容器。
- 不要
cat、下载或打印/etc/bitfun-*/market.env、New API 数据目录、账号密码。 - 不要在
/root/repos/BitFun或/root/repos/BitFun-Website上跑git clean。 共享 BitFun checkout 上也禁止无确认的git reset --hard(与 MiniApp 手册一致)。 该 checkout 里可能有 Relay 的未跟踪.env。 - 本机 Nginx 只听 80。HTTPS 在前面的云 WAF / 负载均衡终止,不要在这台机器上 擅自申请 Let's Encrypt 或改 443。
- 未知 Host 必须落到
00-default-server.conf的 404,不能落到 New API 或 Relay。 - 每步失败立刻停。先做旧机只读导出,再在新机导入。
这台机器上有什么
| 产品 | 公网 | 源站 | 仓库内入口 |
|---|---|---|---|
| 官网 + 下载页 | https://openbitfun.com/ 、/download |
Nginx → BitFun-Website/dist |
本文「官网」 |
| Release 镜像 | https://openbitfun.com/release/ |
cron → /srv/bitfun-release |
本文「Release 镜像」;脚本在 scripts/openbitfun-release-sync.sh |
| Relay | https://remote.openbit.fun/relay |
bitfun-relay:9700 |
src/apps/relay-server/README.md + 本文 Nginx |
| MiniApp 市场 | https://market.openbitfun.com/miniapp/ |
127.0.0.1:9710 |
../miniapp-market/README.md |
| Skin 市场 | https://market.openbitfun.com/skin/ |
127.0.0.1:9720 |
../skin-market/README.md |
| New API | https://api.openbit.fun/ |
0.0.0.0:33292 |
不在 BitFun 仓库。见本文「New API」 |
当前生产 SSH 别名是 lwb(root)。下文旧机用 OLD_HOST=lwb,新机用
NEW_HOST。换机后可以把 lwb 指到新主机,但导出/导入期间必须同时能 SSH
到两台。
生产路径约定:
| 路径 | 用途 |
|---|---|
/root/repos/BitFun |
BitFun checkout。Relay 静态页和同步脚本从这里读 |
/root/repos/BitFun-Website |
官网独立仓库 GCWing/BitFun-Website |
/srv/bitfun-release |
GitHub Release 镜像(禁止放进 Website dist/) |
/srv/bitfun-miniapp-market |
MiniApp 专用 checkout / 数据 / 备份 |
/srv/bitfun-skin-market |
Skin 专用 checkout / 数据 / 备份 |
/etc/bitfun-miniapp-market/market.env |
MiniApp secrets,root:root 0600 |
/etc/bitfun-skin-market/market.env |
Skin secrets,root:root 0600 |
/root/repos/new_api_bak |
New API 镜像 tar + data/ 卷。BitFun 不管 |
Git remote 不要混用:
- 换机新 clone
https://github.com/GCWing/BitFun.git之后,唯一 remote 是origin,跟踪origin/main。 - 当前仍在服务的
lwb是origin=bobleer/BitFun、upstream=GCWing/BitFun。只在这台旧机上才使用upstream。
脚本里的 OPENBITFUN_BASE_URL 写死为 https://openbitfun.com/release。
域名变了要改 scripts/openbitfun-release-sync.sh,不能只改 Nginx。
A. 旧机只读导出
在能 SSH 到旧机的机器上执行。默认 OLD_HOST=lwb。导出目录在旧机
/root/bitfun-host-export,不要写进 Website dist/ 或 BitFun checkout。
OLD_HOST=lwb
ssh "$OLD_HOST" 'set -eu
EXPORT=/root/bitfun-host-export
rm -rf "$EXPORT"
install -d -m 0700 "$EXPORT"
git -C /root/repos/BitFun remote -v >"$EXPORT/bitfun.remotes"
git -C /root/repos/BitFun rev-parse HEAD >"$EXPORT/bitfun.rev"
git -C /root/repos/BitFun-Website rev-parse HEAD >"$EXPORT/website.rev"
git -C /srv/bitfun-miniapp-market/app rev-parse HEAD >"$EXPORT/miniapp.rev"
git -C /srv/bitfun-skin-market/app rev-parse HEAD >"$EXPORT/skin.rev"
crontab -l >"$EXPORT/root.crontab"
uname -a >"$EXPORT/uname.txt"
docker ps --format "{{.Names}} {{.Image}} {{.Status}}" >"$EXPORT/docker.ps"
node -v >"$EXPORT/node.version" 2>/dev/null || true
docker run --rm \
-v relay-server_relay-db:/data \
-v "$EXPORT":/backup \
alpine tar czf /backup/relay-server_relay-db.tar.gz -C /data .
docker run --rm \
-v relay-server_room-web:/data \
-v "$EXPORT":/backup \
alpine tar czf /backup/relay-server_room-web.tar.gz -C /data .
test -s "$EXPORT/bitfun.rev"
test -s "$EXPORT/relay-server_relay-db.tar.gz"
test -s "$EXPORT/relay-server_room-web.tar.gz"
ls -la "$EXPORT"'
然后把导出目录、Release 镜像、市场数据、New API 和 secrets 拷到新机。
rsync 不要加 -v 去扫 market.env 内容。
OLD_HOST=lwb
NEW_HOST=new-openbitfun # 换成新机器 SSH 别名
ssh "$NEW_HOST" 'install -d -m 0700 /root/bitfun-host-export /root/repos /srv'
rsync -aH --numeric-ids "$OLD_HOST:/root/bitfun-host-export/" \
"$NEW_HOST:/root/bitfun-host-export/"
rsync -aH --numeric-ids "$OLD_HOST:/srv/bitfun-release/" \
"$NEW_HOST:/srv/bitfun-release/"
rsync -aH --numeric-ids \
"$OLD_HOST:/srv/bitfun-miniapp-market/" \
"$NEW_HOST:/srv/bitfun-miniapp-market/"
rsync -aH --numeric-ids \
"$OLD_HOST:/srv/bitfun-skin-market/" \
"$NEW_HOST:/srv/bitfun-skin-market/"
rsync -aH --numeric-ids \
"$OLD_HOST:/etc/bitfun-miniapp-market/" \
"$NEW_HOST:/etc/bitfun-miniapp-market/"
rsync -aH --numeric-ids \
"$OLD_HOST:/etc/bitfun-skin-market/" \
"$NEW_HOST:/etc/bitfun-skin-market/"
rsync -aH --numeric-ids \
"$OLD_HOST:/root/repos/new_api_bak/" \
"$NEW_HOST:/root/repos/new_api_bak/"
验收:新机上 test -s /root/bitfun-host-export/relay-server_relay-db.tar.gz、
test -d /srv/bitfun-release/0.2.18、test -f /etc/bitfun-miniapp-market/market.env。
不要 cat 任何 market.env。
B. 新机导入
下面 ssh 目标都是 $NEW_HOST。先装 Docker、Nginx、cron,再按顺序做。
每块做完就跑该块验证。
1. 基础软件和 Node
生产官网构建用 Node v20.20.2,装在 /root/.nvm。非交互 SSH 默认不加载
nvm,所以每条 npm 命令都要先 source。
NEW_HOST=new-openbitfun
ssh "$NEW_HOST" 'set -eu
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y docker.io nginx cron rsync curl ca-certificates python3
systemctl enable --now docker nginx cron
command -v docker
command -v nginx
systemctl is-active cron
export NVM_DIR="$HOME/.nvm"
if [ ! -s "$NVM_DIR/nvm.sh" ]; then
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
fi
# shellcheck disable=SC1091
. "$NVM_DIR/nvm.sh"
nvm install 20.20.2
nvm alias default 20.20.2
node -v
test "$(node -v)" = "v20.20.2"'
2. BitFun checkout
新机从 GCWing/BitFun clone,只用 origin。checkout 导出记录的 commit,
不要猜 upstream/main。
NEW_HOST=new-openbitfun
ssh "$NEW_HOST" 'set -eu
BITFUN_REV="$(cat /root/bitfun-host-export/bitfun.rev)"
test -n "$BITFUN_REV"
if [ ! -d /root/repos/BitFun/.git ]; then
git clone https://github.com/GCWing/BitFun.git /root/repos/BitFun
fi
cd /root/repos/BitFun
git remote -v
git fetch origin
git checkout --detach "$BITFUN_REV"
test "$(git rev-parse HEAD)" = "$BITFUN_REV"
test -x scripts/openbitfun-release-sync.sh'
git remote -v 应只有 origin → github.com/GCWing/BitFun.git。
3. Nginx 兜底(先拆 Ubuntu default)
新装 Nginx 自带 sites-enabled/default,也是 default_server。不删掉的话,
未知 Host 不会落到市场手册里的 404 兜底。
NEW_HOST=new-openbitfun
ssh "$NEW_HOST" 'set -eu
rm -f /etc/nginx/sites-enabled/default
install -m 0644 \
/root/repos/BitFun/deploy/miniapp-market/nginx-default-server.conf \
/etc/nginx/sites-available/00-default-server.conf
ln -sfn /etc/nginx/sites-available/00-default-server.conf \
/etc/nginx/sites-enabled/00-default-server.conf
nginx -t
systemctl reload nginx
code="$(curl -sS -o /dev/null -w "%{http_code}" \
-H "Host: unconfigured.openbitfun.com" http://127.0.0.1/)"
test "$code" = "404"'
4. Release 镜像
先用旧机 rsync 过来的目录(保留 0.2.14 起的多版本,供旧 Desktop / Dispatch), 再跑一次 in-repo 同步补最新版。不要对空目录只 sync 一次就当完成。
Windows 网页安装包文件名以 GitHub latest.json 的 manual_installers 为准
(现在是 BitFun_${version}_windows-x86_64-installer.exe)。不要再写死
bitfun-installer.exe。
release-sync.cron 就是这台机器的整份 root crontab。crontab 该文件会
替换所有 root cron。先备份,确认没有其它任务再装。
NEW_HOST=new-openbitfun
ssh "$NEW_HOST" 'set -eu
test -d /srv/bitfun-release
test -f /srv/bitfun-release/downloads.json
touch /var/log/bitfun-release-sync.log
chmod 644 /var/log/bitfun-release-sync.log
crontab -l > /root/bitfun-host-export/new-host.crontab.bak 2>/dev/null || true
crontab /root/repos/BitFun/deploy/openbitfun-host/release-sync.cron
crontab -l
/root/repos/BitFun/scripts/openbitfun-release-sync.sh
python3 -c "import json; print(json.load(open(\"/srv/bitfun-release/downloads.json\"))[\"version\"])"'
downloads.json 的 version 必须等于
https://github.com/GCWing/BitFun/releases/latest/download/latest.json 的
version。日志里若再出现 Failed to download bitfun-installer.exe,说明跑到了
旧脚本,停下来改 cron,不要手工改清单。
锁文件默认是 /var/lock/bitfun-release-sync.lock,不要再指向
/root/repos/BitFun-AutoUpdate/sync.lock,也不要放进 /srv/bitfun-release
(该目录由 Nginx /release/ 对外提供)。
可选 beta:
BITFUN_RELEASE_CHANNEL=beta /root/repos/BitFun/scripts/openbitfun-release-sync.sh
5. 官网
官网不在 BitFun 里。源码是 https://github.com/GCWing/BitFun-Website。
npm run build 会清空 dist/,所以 Release 镜像绝不能放进 dist/release。
NEW_HOST=new-openbitfun
ssh "$NEW_HOST" 'set -eu
WEBSITE_REV="$(cat /root/bitfun-host-export/website.rev)"
test -n "$WEBSITE_REV"
if [ ! -d /root/repos/BitFun-Website/.git ]; then
git clone https://github.com/GCWing/BitFun-Website.git /root/repos/BitFun-Website
fi
cd /root/repos/BitFun-Website
git fetch origin
git checkout --detach "$WEBSITE_REV"
export NVM_DIR="$HOME/.nvm"
# shellcheck disable=SC1091
. "$NVM_DIR/nvm.sh"
nvm use 20.20.2
npm install
npm run build
test -f dist/index.html
install -m 0644 \
/root/repos/BitFun/deploy/openbitfun-host/nginx-openbit.fun.conf \
/etc/nginx/sites-available/openbit.fun
ln -sfn /etc/nginx/sites-available/openbit.fun /etc/nginx/sites-enabled/openbit.fun
nginx -t
systemctl reload nginx
curl -fsS -o /dev/null -w "%{http_code}\n" \
-H "Host: openbitfun.com" http://127.0.0.1/'
更新官网:在 Website 仓库拉代码后只跑 npm run build,不要动 /srv/bitfun-release。
6. Relay
必须先恢复两个 volume,再启动容器。先跑 deploy.sh 会建空卷,账号、同步和
Pages 资产都会丢。不要在空库上用 relay-admin 新建用户来“代替”恢复。
当前生产 .env 只有镜像开关(BITFUN_USE_CN_MIRROR 等)。新机用
BITFUN_MIRROR=auto,让 deploy.sh 按主机网络自己选。不要写死
--global-mirror。不要 cat 旧 .env。
NEW_HOST=new-openbitfun
ssh "$NEW_HOST" 'set -eu
EXPORT=/root/bitfun-host-export
test -s "$EXPORT/relay-server_relay-db.tar.gz"
test -s "$EXPORT/relay-server_room-web.tar.gz"
docker volume create relay-server_relay-db
docker volume create relay-server_room-web
docker run --rm \
-v relay-server_relay-db:/data \
-v "$EXPORT":/backup \
alpine tar xzf /backup/relay-server_relay-db.tar.gz -C /data
docker run --rm \
-v relay-server_room-web:/data \
-v "$EXPORT":/backup \
alpine tar xzf /backup/relay-server_room-web.tar.gz -C /data
cd /root/repos/BitFun/src/apps/relay-server
BITFUN_MIRROR=auto bash deploy.sh
install -m 0644 \
/root/repos/BitFun/deploy/openbitfun-host/nginx-remote.openbit.fun.conf \
/etc/nginx/sites-available/remote.openbit.fun
ln -sfn /etc/nginx/sites-available/remote.openbit.fun \
/etc/nginx/sites-enabled/remote.openbit.fun
nginx -t
systemctl reload nginx
curl -fsS http://127.0.0.1:9700/health
curl -fsS -o /dev/null -w "%{http_code}\n" \
-H "Host: remote.openbit.fun" http://127.0.0.1/relay/health
docker exec bitfun-relay /app/relay-admin --db /app/data/bitfun_relay.db list-users'
list-users 必须能看到旧机已有账号(当前是 bowen628、wgq@2012)。
若只有空表,停下来,不要 add-user。
7. MiniApp / Skin(搬迁,不是首次安装)
不要跑市场手册里的「从 market.env.example 生成新 secret」。旧
market.env、SQLite、artifacts 已经 rsync 过来。
- 确认
/etc/bitfun-*-market/market.env权限仍是root:root0600,不要打开文件。 - 确认
/srv/bitfun-*-market/{data,artifacts,backups}在。 - 专用 checkout 回到导出的 commit,再按对应手册 只 recreate 容器,不要 当绿场初始化。
- Skin 仍从 MiniApp 专用 checkout 的契约出发,不要用
/root/repos/BitFun当 Skin 的运行 checkout。 - 按市场手册安装它们自己的 Nginx 与 backup timer。
NEW_HOST=new-openbitfun
ssh "$NEW_HOST" 'set -eu
MINIAPP_REV="$(cat /root/bitfun-host-export/miniapp.rev)"
SKIN_REV="$(cat /root/bitfun-host-export/skin.rev)"
test -n "$MINIAPP_REV"
test -n "$SKIN_REV"
stat -c "%U:%G %a %n" /etc/bitfun-miniapp-market/market.env \
/etc/bitfun-skin-market/market.env
test -f /srv/bitfun-miniapp-market/data/market.sqlite
test -f /srv/bitfun-skin-market/data/market.sqlite
git -C /srv/bitfun-miniapp-market/app checkout --detach "$MINIAPP_REV"
git -C /srv/bitfun-skin-market/app checkout --detach "$SKIN_REV"
test "$(git -C /srv/bitfun-miniapp-market/app rev-parse HEAD)" = "$MINIAPP_REV"
test "$(git -C /srv/bitfun-skin-market/app rev-parse HEAD)" = "$SKIN_REV"'
然后只按:
- MiniApp:../miniapp-market/README.md 的构建 / recreate / 验证,不要走「初次开放市场」生成新 OAuth。
- Skin:../skin-market/README.md 同样只 recreate。
8. New API
这是改过的 new-api 镜像,BitFun clone 恢复不了。旧机事实:
- 镜像:
new-api:v1.0.0-rc.14-openbitfun-mod - 离线包:
/root/repos/new_api_bak/new-api-v1.0.0-rc.14-openbitfun-mod.tar - 数据:
/root/repos/new_api_bak/data→ 容器/data - 启动:
/root/repos/new_api_bak/start.sh(宿主33292→ 容器3000)
NEW_HOST=new-openbitfun
ssh "$NEW_HOST" 'set -eu
test -f /root/repos/new_api_bak/new-api-v1.0.0-rc.14-openbitfun-mod.tar
test -d /root/repos/new_api_bak/data
/root/repos/new_api_bak/load_image.sh
/root/repos/new_api_bak/start.sh
install -m 0644 \
/root/repos/BitFun/deploy/openbitfun-host/nginx-api.openbit.fun.conf \
/etc/nginx/sites-available/api.openbit.fun
ln -sfn /etc/nginx/sites-available/api.openbit.fun \
/etc/nginx/sites-enabled/api.openbit.fun
nginx -t
systemctl reload nginx
docker inspect --format "{{.State.Status}}" new-api'
没有 tar 和 data 就不要猜镜像、不要空库上线。
9. 一次性对齐本目录的 Nginx
若前面分步已装过官网 / Relay / API vhost,这一节只用来核对文件是否与仓库一致。
先 nginx -t 再 reload。市场 vhost 仍按市场手册安装,不要用本目录文件覆盖
market.openbitfun.com.conf。
NEW_HOST=new-openbitfun
ssh "$NEW_HOST" 'set -eu
rm -f /etc/nginx/sites-enabled/default
install -m 0644 \
/root/repos/BitFun/deploy/miniapp-market/nginx-default-server.conf \
/etc/nginx/sites-available/00-default-server.conf
ln -sfn /etc/nginx/sites-available/00-default-server.conf \
/etc/nginx/sites-enabled/00-default-server.conf
install -m 0644 \
/root/repos/BitFun/deploy/openbitfun-host/nginx-openbit.fun.conf \
/etc/nginx/sites-available/openbit.fun
ln -sfn /etc/nginx/sites-available/openbit.fun /etc/nginx/sites-enabled/openbit.fun
install -m 0644 \
/root/repos/BitFun/deploy/openbitfun-host/nginx-remote.openbit.fun.conf \
/etc/nginx/sites-available/remote.openbit.fun
ln -sfn /etc/nginx/sites-available/remote.openbit.fun \
/etc/nginx/sites-enabled/remote.openbit.fun
install -m 0644 \
/root/repos/BitFun/deploy/openbitfun-host/nginx-api.openbit.fun.conf \
/etc/nginx/sites-available/api.openbit.fun
ln -sfn /etc/nginx/sites-available/api.openbit.fun \
/etc/nginx/sites-enabled/api.openbit.fun
nginx -t
systemctl reload nginx'
10. 切 DNS / WAF
本机没有 443。把云 WAF / 负载均衡的源站指到新机器后,再跑公网验收。
整机验收
在源站本机用 Host 头打 80,不要依赖这台机器能否解析公网 DNS。
NEW_HOST=new-openbitfun
ssh "$NEW_HOST" 'set -eu
curl -fsS -o /dev/null -w "openbitfun.com/ %{http_code}\n" \
-H "Host: openbitfun.com" http://127.0.0.1/
python3 - <<PY
import json, urllib.request
req = urllib.request.Request(
"http://127.0.0.1/release/downloads.json",
headers={"Host": "openbitfun.com"},
)
with urllib.request.urlopen(req, timeout=15) as resp:
data = json.load(resp)
print("downloads.json", data["version"])
PY
curl -fsS -o /dev/null -w "remote /relay/health %{http_code}\n" \
-H "Host: remote.openbit.fun" http://127.0.0.1/relay/health
curl -fsS -o /dev/null -w "unknown host %{http_code}\n" \
-H "Host: unconfigured.openbitfun.com" http://127.0.0.1/
docker ps --format "{{.Names}} {{.Status}}"
crontab -l
docker exec bitfun-relay /app/relay-admin --db /app/data/bitfun_relay.db list-users'
预期:官网 200;downloads.json 版本等于 GitHub latest;Relay health 200;
未知 Host 404;bitfun-relay healthy;cron 指向
/root/repos/BitFun/scripts/openbitfun-release-sync.sh;list-users 含旧账号。
公网验收(从能解析 DNS 的机器):
https://openbitfun.com/download显示的版本 =downloads.jsonhttps://openbitfun.com/release/latest.json与 GitHub latest 同版本https://remote.openbit.fun/relay/healthhttps://market.openbitfun.com/miniapp/api/v1/healthhttps://market.openbitfun.com/skin/(按 Skin 手册)
日常更新(不是换机)
在 当前 lwb 上,GCWing 叫 upstream。在按本文新 clone 的机器上,GCWing
叫 origin。先 git remote -v 再选命令。
| 要做的事 | 怎么做 |
|---|---|
| 更新 BitFun 源码 | 先 cp -a src/apps/relay-server/.env /tmp/relay-server.env.bak(若存在)。git fetch 对应 remote,再 git merge --ff-only <remote>/main。不要默认 reset --hard,不要 git clean。 |
| 更新 Relay | 源码更新后 cd src/apps/relay-server && BITFUN_MIRROR=auto bash deploy.sh。账号 volume 会留下。 |
| 更新 Release 镜像脚本 | 只改仓库里的 scripts/openbitfun-release-sync.sh。cron 已经跑这份文件。 |
| 更新官网 | 在 BitFun-Website 拉代码,source ~/.nvm/nvm.sh && nvm use 20.20.2 && npm run build。 |
| 更新市场 | 只用对应市场手册。 |
更新 BitFun 源码不会自动更新官网、New API 或市场容器。