feat(远端): 恢复自建 Gitea 代码辅仓,OpenList 降为备选备份

用户定案:「自建 gitea 同步一下吧,openlist 也作为备选备份」。
于是把上一轮「撤销全部 git 辅仓」的决定部分回退:在线那一路回到自建 Gitea,
OpenList 上的加密 bundle 从「唯一异地备份」改为「备选异地备份」。

两层不是重复,而是失效模式不同:
  · 自建 Gitea(在线、可增量、可浏览)—— 不受任何第三方平台规则约束
  · 离线 bundle(离线、单一文件、完整历史)—— 平台全挂也能恢复

一、实际同步
- Gitea 位于 23.254.236.47:3001(Gitea 28.0.0),本机直连即可推,
  **不需要广州中转机**(旧结论「沙箱跑不通 git smart HTTP」只针对当时的代理)
- 远端停在 ed38f938(2026-10-04),落后 57 个提交,且是本地 HEAD 的**祖先**
  → 一次 fast-forward 追平,**未强推、未丢历史**
- 同步后 origin 与 gitea 均指向 5f27ad32

二、git 配置
- 恢复 remote `gitea`(凭据编在 URL 里,只落本机 .git/config,不入库)
- `pushall` 回到双推:
    !git push origin main; ec=$?; git remote | grep -qx gitea && git push gitea main; exit $ec
  · 用 `;` 而非 `&&` → 主仓挂了辅仓照样推
  · `exit $ec` → 退出码仍以主仓为准,辅仓成功不掩盖主仓失败
  · gitea 段带存在性前置判断 → 没配辅仓时静默跳过,不白报错

三、发布链路恢复 gitea(前一轮刚去掉,现按新定案加回)
- `deploy/editor-api/bootstrap.sh`
    · 新增 GITEA_URL / GITEA_USER / GITEA_PASS(凭据必须现给,不写死在脚本里)
    · PUSH_REMOTES 默认 → origin,gitea;没给 GITEA_PASS 则降级为 origin
    · 恢复「配 gitea 远端」一节;退役远端名单去掉 gitea(只留 gh / gitee)
      —— 否则新部署会把现行辅仓当成退役远端清掉
    · 结尾补一句「未启用 Gitea 辅仓」的提示
- `docker-compose.editor.yml`、`editor-api/server.mjs` → 默认 origin,gitea
- `editor-api/README.md` → 变量表同步;`editor-api/Dockerfile` → 注释补 http 情形

四、`scripts/setup-cnb-remotes.sh` 改回双远端模式
- 头部沿革、USAGE、凭据段全部重写;GITEA_PASS 走环境变量,不入库
- 退役远端循环只清 gh / gitee;pushall 写同一份定义

五、文档
- `架构总览.md`:§1.2 地址地图加「代码辅仓」行、备份行降为「备选」;
  §2 旅程图改双推;§5.1 推送重写(含别名实际定义与三条取舍);
  §5.2 远端改双行表 + Gitea 接入细节;§5.3 由「不走 git 远端」改为
  「远端与备份的分工」(三层各自的失效模式);§5.6 定位改备选;
  §6 待办 #2/#4/#7/#10 更新
- `CNB构建落地方案.md` §4.0 目标形态与定案块、§4.1 第 4 条
- `README.md` 发布流程图、推送说明、脚本表
- `scripts/backup-run.mjs` 头部注释:不再是「唯一备份」

实测:
  git push gitea main → ed38f938..5f27ad32  main -> main
  git pushall         → origin / gitea 双向同步,退出码 0
  bash -n 校验 bootstrap.sh 与 setup-cnb-remotes.sh 均通过
This commit is contained in:
zqlit committed 2026-10-06 21:42:08 +08:00
1 parent 5f27ad321d
commit 448b898e40
10 files changed
+221 -109

No files matched your search

+24 -12
View File
@@ -143,24 +143,32 @@ CNB 只替换「构建 + 搬运」这一段,顺带砍掉广州中转机上那
## 四、落地骨架
### 4.0 第一步:仓库迁移与远端配置(CNB 主仓,**无 git 辅仓**)
### 4.0 第一步:仓库迁移与远端配置(CNB 主仓 + 自建 Gitea 辅仓)
**目标形态**
```
origin → https://cnb.cool/<组织>/<仓库> ← **唯一远端**(fetch + push),CNB 构建由它触发
origin → https://cnb.cool/<组织>/<仓库> ← **主仓**(fetch + push),CNB 构建由它触发
gitea → http://23.254.236.47:3001/zqlit/blog ← **代码同步辅仓**(只推不拉,不参与构建)
```
> ⚠️ **2026-10-06 定案:git 远端只留 CNB 一个。**
> **2026-10-06 定案:CNB 主仓 + 自建 Gitea 辅仓,另加一份离线 bundle 备份。**
>
> - **GitHub**(原 `gh` remote):账号被平台标记、仓库被按 AUP 清空 → 移除
> | remote | 地址 | 角色 |
> |---|---|---|
> | `origin` | `https://cnb.cool/zqlit/blog.git` | **主仓** —— 唯一的 fetch 源,推送即触发 CNB 构建 |
> | `gitea` | `http://23.254.236.47:3001/zqlit/blog.git` | **代码同步辅仓** —— 只作代码留档与找回,不参与构建 |
>
> - **GitHub**(原 `gh` remote):账号被平台标记、仓库被按 AUP 清空 → 已彻底移除,**别再往回加**
> - **Gitee**:单仓 500MB / 单文件 50MB 两道硬门槛过不去(本仓 `bin/linux/hugo` 83.1MB)→ 未采用
> - **自建 Gitea**:发布链路早已不依赖 → 移除
>
> **异地备份改为「整仓加密 bundle → 中兴 F50 上的 OpenList」**,与 git 远端无关。
> **备选异地备份 = 「整仓加密 bundle → 中兴 F50 上的 OpenList」**,与 git 远端无关。
> 它和 Gitea 辅仓不是一回事、也不能互相替代:
> 辅仓保「随时能 `clone` 回来」,bundle 保「平台全挂/账号被封时仍有一份完整历史」。
> 选型依据见 `架构总览.md` §5.3,实现与恢复流程见 §5.6。
`git pushall` 仍保留为别名,但现在等价于 `git push origin main`(只有这一段)。
`git pushall` 别名 = `git push origin main; git push gitea main` ——
用 `;` 而非 `&&`,主仓失败时辅仓照样推;退出码以主仓为准(辅仓失败不阻断发布)。
**★ 为什么不用「一个 remote 挂多个 pushurl」**(这条教训仍然有效)
@@ -174,8 +182,8 @@ origin → https://cnb.cool/<组织>/<仓库> ← **唯一远端**(fetch + p
也就是说:**主仓推失败时,辅仓那份备份也不会更新** —— 备份的意义就没了。
所以当时才拆成两个独立 remote,别名里用 `;` 而不是 `&&`,保证互不阻塞。
现在只有一个远端,这层复杂性自然取消了 —— 但**如果将来要再加辅仓**,
必须回到「独立 remote + 别名」的写法,**不要**用 pushurl。
**现行方案正是回到这个写法**:`origin`(CNB) 与 `gitea`(自建) 各自独立成 remote,
别名 `;` 串联、退出码取主仓 —— 这就是现在的 `git pushall`。
**脚本**(已就位,可直接用)
@@ -184,8 +192,12 @@ bash scripts/setup-cnb-remotes.sh https://cnb.cool/<组织>/<仓库>
```
它会先把你现有的远端配置(含明文凭据)备份到 `.workbuddy-backup/git-remotes.<时间戳>.txt`,
再重建 `origin`、把 `pushall` 设为 `git push origin main`,
并顺手清掉 `gh` / `gitee` / `gitea` 这些已退役的远端 —— 全程可回退。
再重建 `origin`、把 `pushall` 设为 `git push origin main; git push gitea main`,
并顺手清掉 `gh` / `gitee` 这两个已退役的远端 —— 全程可回退。
> Gitea 的凭据**不写死在脚本里**,要现给:
> `GITEA_PASS='<Gitea 密码或访问令牌>' bash scripts/setup-cnb-remotes.sh <CNB_URL>`
> 没给就只配 `origin`(`pushall` 里的 gitea 段会被自动跳过)。
**★ CNB 不支持 SSH(官方明确)**
@@ -281,7 +293,7 @@ CMD ["hugo", "version"]
> 1. **hugo 二进制改为随仓库携带**(`bin/linux/hugo`,84MB,进 git 后仅 24MB)。实测国内节点直连 GitHub 拉这个 release 要 **127 秒**(本机只要 7 秒),而每次构建都要付这个成本 → 自带更稳。
> 2. 又拍云 `upx` 的下载源是 `collection.b0.upaiyun.com`(国内 CDN),直连稳定,保留在线安装。
> 3. **架构不能混用**:`E:\Hugo\hugo.exe` 是 `windows/amd64`(本地预览用),CNB 构建节点是 `linux/amd64`(`bin/linux/hugo`),两者互不替代。
> 4. ~~若以后主仓改选 Gitee(单仓 500MB),这 84MB 会挤占配额~~ —— **2026-10-06 定案:不走任何 git 辅仓**(异地备份改用 OpenList 上的加密 bundle,见 `架构总览.md` §5.6),这条约束自然消失,`bin/linux/hugo` 继续随仓库携带即可。
> 4. ~~若以后主仓改选 Gitee(单仓 500MB),这 84MB 会挤占配额~~ —— **2026-10-06 定案:不走 Gitee**(它卡单文件 50MB / 单仓 500MB 两道门槛)。现行辅仓是**自建 Gitea**(自己的机器,没有配额这回事,见 §4.0),离线备份走 OpenList 上的加密 bundle(`架构总览.md` §5.6)。这条约束自然消失,`bin/linux/hugo` 继续随仓库携带即可。
> 5. **apt 源改阿里云镜像**:CNB 官方只明示加速「Docker / NPM / Maven 镜像」,Debian 默认 apt 源不在承诺范围内,而构建节点在国内 → 换阿里云兜底,避免构建卡在 `apt-get update`(首轮构建时留意这一步耗时)。
### 4.2 `.cnb.yml`(流水线)
+8 -6
View File
@@ -83,20 +83,22 @@ hugo server -D # 含草稿
```
写作(write-server / write/)
│ git pushall
│ git pushall (= git push origin main ; git push gitea main)
▼
CNB 主仓 zqlit/blog ──触发──► CNB 流水线(国内节点,约 3.5 分钟)
origin → CNB 主仓 zqlit/blog ──触发──► CNB 流水线(国内节点,约 3.5 分钟)
├─ Hugo 构建
├─ 同步到又拍云(境内源站)
├─ 刷新又拍云 CDN
├─ 刷新多吉云 CDN
├─ 部署 EdgeOne Pages(境外线路)
└─ 邮件通知
gitea → 自建 Gitea(23.254.236.47:3001) 代码同步辅仓:只推不拉、不参与构建
```
- **推送**:`git push origin main`(`origin` = CNB 主仓,**唯一远端**,触发构建)
—— `git pushall` 是同义的别名
- **异地备份**:**不走 git 远端**。本机计划任务每天 03:30 把整仓 bundle
- **推送**:`git pushall` —— 依次推 `origin`(CNB 主仓,触发构建)与 `gitea`(自建 Gitea 代码同步辅仓)
;只推主仓用 `git push origin main`
- **备选异地备份**:**不走 git 远端**。本机计划任务每天 03:30 把整仓 bundle
加密后传到中兴 F50 上的 OpenList(见 `架构总览.md` §5.6)
- **触发**:推送到 `main`;另有每日 `0 9 * * *`(北京时间)定时构建
- **密钥**:全部来自 CNB 密钥仓库 `zqlit/blog-secrets`,经 `.cnb.yml` 的 `imports` 注入,
@@ -133,7 +135,7 @@ Hugo + Ying 主题。`hugo.toml` 控站点信息、永久链接、Artalk 地址
| `add_draft_to_hidden.py` | 构建前把 `status: hidden` 转成草稿 | **CI** |
| `refresh_cdn.js` | 刷新多吉云 CDN(零依赖) | **CI** |
| `send_mail.js` | 构建结果邮件通知(零依赖 SMTP) | **CI** |
| `setup-cnb-remotes.sh` | 切换/重建 git 远端(只有 CNB 一个;顺手清掉退役远端) | 本机 |
| `setup-cnb-remotes.sh` | 切换/重建 git 远端(CNB 主仓 + 自建 Gitea 辅仓;顺手清掉退役远端) | 本机 |
| `backup-run.mjs` | 备份的推荐入口:跑 `backup-bundle` + **失败时发告警邮件** | 本机/计划任务 |
| `backup-bundle.mjs` | 整仓 bundle → AES-256-GCM 加密 → WebDAV 传中兴 F50(见 架构总览 §5.6) | 本机/计划任务 |
| `backup-task.cmd` | 上面的计划任务入口(每天 03:30;**内容必须全 ASCII**) | 计划任务 |
+48 -16
View File
@@ -20,23 +20,30 @@
#
# 可选环境变量(都有默认值):
# CNB_URL 默认 https://cnb.cool/zqlit/blog.git
# GITEA_URL 默认 http://23.254.236.47:3001/zqlit/blog.git(自建 Gitea 辅仓)
# GITEA_USER 默认 zqlit
# GITEA_PASS 可选:Gitea 的密码 / 访问令牌。**给了才会把 gitea 加进推送远端**,
# 否则 PUSH_REMOTES 自动降级成只推 origin(CNB 主仓)
# BLOG_DIR /srv/blog 仓库工作区
# APP_DIR /srv/editor-api compose + .env
# TRASH_DIR /srv/editor-trash 删除文章的回收目录(故意放在仓库外)
# BIND_PORT 8017 只绑 127.0.0.1
# PUSH_REMOTES origin 发布时依次推送的远端(默认只有 CNB 主仓)
# PUSH_REMOTES origin,gitea 发布时依次推送的远端
# GIT_AUTHOR_NAME / GIT_AUTHOR_EMAIL / BLOG_BASE
#
# ★ 2026-10-06(定案):**异地备份不再走 git 远端**。
# - GitHub(GH_URL / GH_SSH_KEY / remote `gh`):账号被平台标记,仓库被按
# AUP 清空 → 已从本脚本与整个链路移除
# ★ 远端与备份定案(2026-10-06):
# - CNB(remote `origin`):**主仓**,唯一的 fetch 源,推送即触发 CNB 构建
# - 自建 Gitea(remote `gitea`,23.254.236.47:3001):**代码同步辅仓**,
# 只作代码留档与找回,不参与构建(.github/ 已删,Actions 早已停用)
# - GitHub(GH_URL / GH_SSH_KEY / remote `gh`):账号被平台标记、仓库被按
# AUP 清空 → 已彻底移除,别再往回加
# - Gitee:单仓 500MB / 单文件 50MB 两道硬门槛,本仓 `bin/linux/hugo`
# 是 83.1MB → 未采用
# - 自建 Gitea:已从远端列表移除
# 现行备份 = CNB 主仓 + OpenList 上的加密 bundle(scripts/backup-bundle.mjs,
# 由 Windows 计划任务 Blog-BundleBackup 每天 03:30 跑)。
# 所以 PUSH_REMOTES 默认只有 origin;将来若要再加辅仓,设
# PUSH_REMOTES=origin,<名字> 并在下面「远端配置」一节补上对应 remote。
# - OpenList(中兴 F50 上的加密 bundle,scripts/backup-bundle.mjs,
# Windows 计划任务 Blog-BundleBackup 每天 03:30):**备选异地备份**,
# 与 git 远端无关,独立于上面三者
# 推送语义见 editor-api/src/git.mjs:逐个远端推、**主仓成功即算发布成功**,
# 辅仓失败只警告不阻断 —— 所以 gitea 挂了不会让文章发不出去。
# =====================================================================
set -euo pipefail
@@ -48,6 +55,8 @@ die() { printf '\033[1;31m!! %s\033[0m\n' "$*" >&2; exit 1; }
: "${ADMIN_PASS:?必须设置 ADMIN_PASS(后台登录密码,账号固定 admin)}"
CNB_URL="${CNB_URL:-https://cnb.cool/zqlit/blog.git}"
GITEA_URL="${GITEA_URL:-http://23.254.236.47:3001/zqlit/blog.git}"
GITEA_USER="${GITEA_USER:-zqlit}"
BLOG_DIR="${BLOG_DIR:-/srv/blog}"
APP_DIR="${APP_DIR:-/srv/editor-api}"
TRASH_DIR="${TRASH_DIR:-/srv/editor-trash}"
@@ -55,7 +64,15 @@ BIND_PORT="${BIND_PORT:-8017}"
GIT_AUTHOR_NAME="${GIT_AUTHOR_NAME:-blog-editor}"
GIT_AUTHOR_EMAIL="${GIT_AUTHOR_EMAIL:-editor@usj.cc}"
BLOG_BASE="${BLOG_BASE:-}"
PUSH_REMOTES="${PUSH_REMOTES:-origin}"
PUSH_REMOTES="${PUSH_REMOTES:-origin,gitea}"
# 没给 Gitea 凭据就只推主仓 —— 宁可少推一个辅仓,
# 也不要让每次发布都对着一个认证不过的远端空转一遍。
# (这里是有意直接覆盖成 origin:默认列表就是 origin,gitea,
# 没有 gitea 凭据等于只剩 origin;要推别的远端请连同 gitea 一起显式写全。)
if [ -z "${GITEA_PASS:-}" ]; then
PUSH_REMOTES="origin"
fi
command -v docker >/dev/null || die "没有 docker"
docker compose version >/dev/null 2>&1 || die "没有 docker compose"
@@ -78,11 +95,22 @@ else
git clone --branch main "$AUTH_URL" "$BLOG_DIR"
fi
# 退役的远端:以前这一节会配 GitHub / Gitee 辅仓。2026-10-06 起异地备份改走
# OpenList 上的加密 bundle(与 git 远端无关),所以 git 远端只留 CNB 主仓。
# 若这份工作区是从旧部署续用的,顺手把残留的退役远端清掉,
# 免得 `git pushall` 或后端按 PUSH_REMOTES 推送时撞上已失效的远端。
for _retired_remote in gh gitee gitea; do
# 自建 Gitea 辅仓:只有给了 GITEA_PASS 才配(没给就只推 origin,见上面 PUSH_REMOTES 降级)。
# 凭据直接编进远端 URL —— 容器里没有交互终端、也没有 credential helper,
# 这是唯一能让 `git push gitea` 免交互跑通的写法。
# 明文只落在 $APP_DIR/.env 与容器内的 .git/config,不会进仓库。
if [ -n "${GITEA_PASS:-}" ]; then
_gitea_auth="$(printf '%s' "$GITEA_URL" | sed -E "s#^(https?://)#\1${GITEA_USER}:${GITEA_PASS}@#")"
git -C "$BLOG_DIR" remote remove gitea 2>/dev/null || true
git -C "$BLOG_DIR" remote add gitea "$_gitea_auth"
unset _gitea_auth
fi
# 退役的远端:以前这一节会配 GitHub / Gitee 辅仓,两者都已退役
# (GitHub 被平台按 AUP 清空、Gitee 卡体积门槛)。若这份工作区是从旧部署续用的,
# 顺手把残留的退役远端清掉,免得后端按 PUSH_REMOTES 推送时撞上已失效的远端。
# ★ gitea **不在**退役名单里 —— 它是现行辅仓,别跟着一起清掉。
for _retired_remote in gh gitee; do
git -C "$BLOG_DIR" remote remove "$_retired_remote" 2>/dev/null || true
done
@@ -142,7 +170,7 @@ services:
TRASH_DIR: $TRASH_IN_CT
BIND_HOST: 0.0.0.0
GIT_BRANCH: main
PUSH_REMOTES: \${PUSH_REMOTES:-origin}
PUSH_REMOTES: \${PUSH_REMOTES:-origin,gitea}
GIT_AUTHOR_NAME: \${GIT_AUTHOR_NAME:-blog-editor}
GIT_AUTHOR_EMAIL: \${GIT_AUTHOR_EMAIL:-editor@usj.cc}
BLOG_BASE: \${BLOG_BASE:-}
@@ -176,6 +204,10 @@ for i in $(seq 1 30); do
echo " · 宿主机 nginx(1Panel 站点)加反代:location ^~ / { proxy_pass http://127.0.0.1:$BIND_PORT; ... }"
echo " · Cloudflare Worker 变量:EDITOR_API_BASE=https://<指向本机的域名> EDITOR_TOKEN=$EDITOR_TOKEN"
echo " · 推送远端:PUSH_REMOTES=$PUSH_REMOTES"
if [ -z "${GITEA_PASS:-}" ]; then
echo " · 未提供 GITEA_PASS → 本次只推 origin,Gitea 辅仓未启用"
echo " (要启用:GITEA_PASS='<Gitea 密码或令牌>' 重跑本脚本)"
fi
exit 0
fi
sleep 2
+1 -1
View File
@@ -27,7 +27,7 @@ services:
# 容器内监听 0.0.0.0 没关系 —— 外面那层端口映射已经把它锁在宿主机回环上了
BIND_HOST: 0.0.0.0
GIT_BRANCH: main
PUSH_REMOTES: origin
PUSH_REMOTES: origin,gitea
GIT_AUTHOR_NAME: blog-editor
GIT_AUTHOR_EMAIL: editor@usj.cc
MAX_UPLOAD_MB: "20"
+1 -1
View File
@@ -9,7 +9,7 @@
FROM node:22-alpine
# git:发布用。ca-certificates:https 远端(CNB)要它
# git:发布用。ca-certificates:https 远端(CNB)要它(自建 Gitea 辅仓走 http,不需要)
#
# ★ apk 源默认换成阿里云镜像:国内机器直连 dl-cdn.alpinelinux.org 实测慢到不可用
# (单个包 30~40 秒,12 个包要 7 分钟),换源后几秒装完。
+1 -1
View File
@@ -67,7 +67,7 @@
| `BIND_HOST` | `0.0.0.0` | 容器内监听地址;靠 compose 的端口映射限成 127.0.0.1 |
| `TRASH_DIR` | `/app/trash` | 删除文章的回收目录(**在仓库外**,不进 git) |
| `GIT_BRANCH` | `main` | 工作分支 |
| `PUSH_REMOTES` | `origin` | 依次推送的远端;**主仓失败才算发布失败**,其余远端失败只警告。异地备份不在此列 —— 走 OpenList 上的加密 bundle(`scripts/backup-bundle.mjs`) |
| `PUSH_REMOTES` | `origin,gitea` | 依次推送的远端;**主仓失败才算发布失败**,其余远端失败只警告。`origin`=CNB 主仓(推送即触发构建),`gitea`=自建 Gitea 代码同步辅仓(需给 `GITEA_PASS` 才配得上,见 `bootstrap.sh`)。异地备份不在此列 —— 走 OpenList 上的加密 bundle(`scripts/backup-bundle.mjs`) |
| `GIT_AUTHOR_NAME` / `GIT_AUTHOR_EMAIL` | `blog-editor` | 自动提交的作者 |
| `DEFAULT_AUTHOR` | 空 | 新建文章时 front matter `author` 的默认值 |
| `MAX_UPLOAD_MB` | `20` | 单张图片上限 |
+1 -1
View File
@@ -29,7 +29,7 @@ const cfg = {
contentDir: path.resolve(env.BLOG_ROOT || '/blog', 'content', 'posts'),
trashDir: path.resolve(env.TRASH_DIR || '/app/trash'),
branch: env.GIT_BRANCH || 'main',
pushRemotes: (env.PUSH_REMOTES || 'origin').split(',').map((s) => s.trim()).filter(Boolean),
pushRemotes: (env.PUSH_REMOTES || 'origin,gitea').split(',').map((s) => s.trim()).filter(Boolean),
authorName: env.GIT_AUTHOR_NAME || 'blog-editor',
authorEmail: env.GIT_AUTHOR_EMAIL || 'editor@localhost',
defaultAuthor: env.DEFAULT_AUTHOR || '',
+8 -6
View File
@@ -1,14 +1,16 @@
#!/usr/bin/env node
/**
* 备份包装器 —— 给「唯一辅仓」加一道失败告警
* 备份包装器 —— 给「离线那一层」加一道失败告警
* =====================================================================
* 为什么需要它:
* OpenList(中兴 F50)现在是**唯一的异地备份**。而这类「每天自动跑」的任务
* 最典型的失败模式恰恰是**静默**的 —— F50 被带出门、换了网段、OpenList
* 容器重启后没起来、WebDAV 口令改过……这些都不会有人主动发现,
* 直到某天真的要用备份时才发现最近三个月一份都没成功。
* OpenList(中兴 F50)上这份加密 bundle 是**唯一不依赖任何 git 服务**的备份 ——
* 在线那一路虽有自建 Gitea 代码辅仓(contributor 机器上,推一次就在),
* 但平台全挂、服务器被回收这类事故只有它能挡。
* 而这类「每天自动跑」的任务最典型的失败模式恰恰是**静默**的 ——
* F50 被带出门、换了网段、OpenList 容器重启后没起来、WebDAV 口令改过……
* 这些都不会有人主动发现,直到某天真的要用备份时才发现最近三个月一份都没成功。
*
* CNB 主仓还在,所以这不会立刻致命;但「唯一辅仓悄悄失效」这件事本身
* 在线辅仓和主仓都还在,所以这不会立刻致命;但「离线备份悄悄失效」这件事本身
* 必须被告知。于是加这一层:跑备份 → 失败就发邮件。
*
* 它做什么:
+56 -22
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# =====================================================================
# 配置 git 远端 —— 本项目只有 CNB 一个远端
# 配置 git 远端 —— CNB 主仓 + 自建 Gitea 代码同步辅仓
# =====================================================================
# 历史沿革:
# 2026-10-04 从「GitHub + 自建 Gitea 双推」切到「CNB 主 + GitHub 备份」
@@ -8,16 +8,18 @@
# 2026-10-06 一度改为「CNB 主 + Gitee 辅」,但 Gitee 免费版卡在
# 单仓 500MB / 单文件 50MB 两道硬门槛(本仓 bin/linux/hugo 83.1MB),
# 云效 Codeup 虽宽松却同属国内大区 —— 多一层同类平台不如换一类介质
# ★ 定案:**异地备份不走 git 远端**。
# 备份 = 整仓 bundle + AES-256-GCM 加密 → 中兴 F50 上的 OpenList
# (scripts/backup-bundle.mjs,计划任务 Blog-BundleBackup 每天 03:30)
# 故 git 远端只剩 CNB origin,pushall 也只推 origin。
# 2026-10-06 一度定案「远端只剩 CNB,异地备份改走 OpenList 加密 bundle」
# ★ 2026-10-06 现行:**CNB 主仓 + 自建 Gitea 辅仓**,OpenList 上的加密
# bundle 退为**备选备份**。两层性质不同、互为补充:
# · git 辅仓 保「随时能 clone 回来、能按提交追溯」
# · 离线 bundle 保「平台全挂/账号被封时仍有一份完整历史」
# (整仓 + AES-256-GCM → 中兴 F50 上的 OpenList,
# scripts/backup-bundle.mjs,计划任务 Blog-BundleBackup 每天 03:30)
#
# 为什么当初不用「一个 remote 挂多个 pushurl」:
# 为什么不用「一个 remote 挂多个 pushurl」:
# 实测(2026-10-04)git 对多个 pushurl 是「顺序推、遇错即停」——
# 第一个失败,后面的远端一个都不会推,那就失去备份意义了。
# 所以真要加辅仓,得用「独立 remote + 别名」而不是 pushurl。
# (现行方案连这个都不需要了 —— 备份已经换介质。)
# 所以用「独立 remote + 别名」而不是 pushurl。
#
# 用法:
# bash scripts/setup-cnb-remotes.sh <CNB_URL>
@@ -28,6 +30,11 @@
# 附带令牌(直接写进 Windows 凭据管理器,以后不再输):
# CNB_TOKEN='你的令牌' bash scripts/setup-cnb-remotes.sh https://cnb.cool/zqlit/blog
#
# Gitea 辅仓默认按内置地址配,但**凭据必须现给**(不写进本文件):
# GITEA_PASS='Gitea 密码或访问令牌' \
# bash scripts/setup-cnb-remotes.sh https://cnb.cool/zqlit/blog
# 要换地址/换用户名时再带上 GITEA_URL / GITEA_USER。
#
# 回退:脚本会把改动前的配置备份到 .workbuddy-backup/git-remotes.<时间戳>.txt
# =====================================================================
@@ -43,7 +50,9 @@ if [ -z "$CNB_URL" ]; then
· CNB 仓库地址直接用仓库页地址,带不带 .git 都行
· CNB **不支持 SSH**,只能 HTTPS + 访问令牌(用户名固定填 cnb,密码填令牌)
令牌:「个人设置 → 访问令牌」,勾「代码仓库 → 读写」
· 本项目**没有** git 辅仓。异地备份走中兴 F50 上的 OpenList:
· 自建 Gitea 辅仓走 http://23.254.236.47:3001(账号 zqlit),**另需 GITEA_PASS**:
GITEA_PASS='Gitea 密码或令牌' bash scripts/setup-cnb-remotes.sh <CNB_URL>
· 备选异地备份走中兴 F50 上的 OpenList(与 git 远端无关):
scripts/backup-bundle.mjs 打包 + AES-256-GCM 加密 + 上传(含校验与恢复)
.workbuddy-backup/openlist-backup.env 连接与口令配置(被 git 忽略)
Windows 计划任务 Blog-BundleBackup 每天 03:30 自动跑
@@ -51,6 +60,11 @@ USAGE
exit 1
fi
# 自建 Gitea 辅仓(默认地址;GITEA_PASS 必须外部给,不写死在本文件里)
GITEA_URL="${GITEA_URL:-http://23.254.236.47:3001/zqlit/blog.git}"
GITEA_USER="${GITEA_USER:-zqlit}"
GITEA_PASS="${GITEA_PASS:-}"
case "$CNB_URL" in
https://cnb.cool/*) ;;
*) echo "地址看起来不是 CNB 的:$CNB_URL"; exit 1 ;;
@@ -74,26 +88,45 @@ mkdir -p .workbuddy-backup
} > "$BACKUP"
echo "原配置已备份:$BACKUP"
# 1) origin = CNB(唯一远端,fetch + push,触发构建)
# 1) origin = CNB(主仓,fetch + push,推送即触发构建)
git remote remove origin 2>/dev/null || true
git remote add origin "$CNB_URL"
# 2) 清理退役远端
# gh(GitHub) / gitee / gitea 都已不在备份体系内 —— 留着只会在
# `git pushall` 或后端按 PUSH_REMOTES 推送时撞上失效远端,白白报错。
for retired in gh gitee gitea; do
# 2) gitea = 自建 Gitea(代码同步辅仓)
# 凭据直接编进远端 URL —— 这是「无凭据助手也能免交互推」的唯一简单写法。
# 明文只落在本机 .git/config(不提交、不推送、不进仓库)。
# (口令里若含 # 或 & 会被 sed 吃掉,那种情况请改用访问令牌。)
if [ -n "$GITEA_PASS" ]; then
GITEA_AUTH="$(printf '%s' "$GITEA_URL" | sed -E "s#^(https?://)#\1${GITEA_USER}:${GITEA_PASS}@#")"
git remote remove gitea 2>/dev/null || true
git remote add gitea "$GITEA_AUTH"
echo "gitea 远端已配置:$GITEA_URL(凭据编入 URL,仅存于本机 .git/config)"
elif git remote | grep -qx gitea; then
echo "gitea 远端已存在,本次未给 GITEA_PASS → 保持原样"
else
echo "未提供 GITEA_PASS → 跳过 gitea 远端配置(只剩 origin 一个远端)"
echo " 需要时:GITEA_PASS='xxx' bash scripts/setup-cnb-remotes.sh $CNB_URL"
fi
# 3) 清理退役远端
# gh(GitHub) / gitee 都已不在远端列表里 —— 留着只会在 `git pushall`
# 或后端按 PUSH_REMOTES 推送时撞上失效远端,白白报错。
# ★ gitea **不在**名单里:它是现行辅仓,别跟着一起清掉。
for retired in gh gitee; do
if git remote | grep -qx "$retired"; then
git remote remove "$retired"
echo "已移除退役远端:$retired"
fi
done
# 3) pushall
# 现在只有一个远端,但仍保留这个别名 —— 文档与肌肉记忆都在用它,
# 将来若真要再加辅仓,改这一行即可。
git config alias.pushall '!git push origin main'
# 4) pushall —— CNB 与 Gitea 都推
# ★ 用 `;` 而不是 `&&`:主仓失败时辅仓照样得推上去,
# 备份的意义正在于「主仓出问题时它那儿还有」。
# 但退出码仍以主仓为准(exit $ec),免得辅仓推成功把主仓的失败盖掉。
# 先探一下 gitea 在不在,免得没配辅仓时每次 pushall 都白报一行错。
git config alias.pushall '!git push origin main; ec=$?; git remote | grep -qx gitea && git push gitea main; exit $ec'
# 4) 凭据
# 5) 凭据
#
# 本机全局 helper 是 GCM(git-credential-manager.exe)。实测它对 cnb.cool 这类
# 第三方 HTTPS 远端会**每次都弹窗**,而且 GCM_INTERACTIVE=never +
@@ -128,8 +161,9 @@ echo "=== 当前远端 ==="
git remote -v | sed -E 's#://[^@/]*@#://***@#g'
echo
echo "=== 下一步 ==="
echo " git push origin main # 推 CNB(触发构建)"
echo " git pushall # 同上(别名,现在只含这一段)"
echo " git pushall # 依次推 origin(CNB) 与 gitea(推荐)"
echo " git push origin main # 只推 CNB(触发构建)"
echo " git push gitea main # 只推 Gitea 辅仓"
echo
echo " 异地备份不在这里 —— 想立刻跑一次整仓加密备份:"
echo " 备选异地备份不在这里 —— 想立刻跑一次整仓加密备份:"
echo " node scripts/backup-bundle.mjs --verify --keep 7"
+73 -43
View File
@@ -28,8 +28,9 @@
| 角色 | 地址 / 位置 | 说明 |
|---|---|---|
| **代码主仓** | `cnb.cool/zqlit/blog` | CNB,**私有**;构建由它触发;**唯一 git 远端** |
| **异地备份** | 中兴 F50 上的 OpenList(`/本地/备份/blog-bundle/`) | 整仓加密 bundle,计划任务每天 03:30 自动跑,**不依赖任何 git 平台**(见 §5.6) |
| **代码主仓** | `cnb.cool/zqlit/blog` | CNB,**私有**;构建由它触发;唯一 fetch 源 |
| **代码辅仓** | 自建 Gitea `23.254.236.47:3001/zqlit/blog` | 只推不拉,纯代码留档;**不受第三方平台规则约束**(见 §5.2) |
| **异地备份** | 中兴 F50 上的 OpenList(`/本地/备份/blog-bundle/`) | **备选**异地备份:整仓加密 bundle,计划任务每天 03:30 自动跑,**不依赖任何 git 服务**(见 §5.6) |
| **构建 + 发布** | CNB 流水线(腾讯云国内节点,4 核 8G) | 托管,0 元(免费额度内) |
| **密钥仓库** | `cnb.cool/zqlit/blog-secrets` | CNB「密钥仓库」类型,10 项变量经 `imports` 注入 |
| **境内源站** | 又拍云对象存储 | 由 CNB 国内节点**直传**(原为 `if: false` 的死代码) |
@@ -48,9 +49,10 @@
```
① 写作 write-server(网页) 或 write/(本地 Windows)
│
② git push git push origin main (别名 git pushall,同一件事)
│ └─ origin → cnb.cool/zqlit/blog (**唯一远端**,触发构建)
│ ★ 异地备份不走 git 远端 —— 由本机计划任务每天 03:30
② git push git pushall = git push origin main ; git push gitea main
│ ├─ origin → cnb.cool/zqlit/blog (**主仓**,触发构建)
│ └─ gitea → 23.254.236.47:3001/zqlit/blog(自建 Gitea,代码同步辅仓)
│ ★ 备选异地备份不走 git 远端 —— 由本机计划任务每天 03:30
│ 跑整仓加密 bundle 传到 F50 上的 OpenList(§5.6)
▼
③ CNB 流水线(国内节点,约 3.5 分钟,7 个 stage 顺序执行)
@@ -113,40 +115,53 @@
### 5.1 推送
```bash
git push origin main # 推 CNB(触发构建)
git pushall # 完全等价 —— 它只是个别名,本仓只有这一个远端
git pushall # CNB 与 Gitea 都推 —— 发布用这个
git push origin main # 只推 CNB(触发构建)
git push gitea main # 只推 Gitea 辅仓
```
**本仓只有 `origin` 一个 git 远端。** 曾经的双推(`git push origin main; git push gh main`)
随 GitHub 退役而取消;后来一度改写成 `origin + gitee`,也随「异地备份改用 OpenList」
(§5.6)一并取消 —— 备份换了介质,就不再需要第二个 git 远端。
`git pushall` 的实际定义:
留着的两条教训:
```
!git push origin main; ec=$?; git remote | grep -qx gitea && git push gitea main; exit $ec
```
- **不要用「一个 remote 挂多个 pushurl」** —— 实测 git 是「顺序推、遇错即停」,
第一个失败后面的都不推,备份意义就没了。真要加辅仓,得用「独立 remote + 别名」。
- 别名当初写成 `;` 串联而非 `&&`,正是为了让一段失败不影响另一段。现在只有一个远端,
这层考虑自然消失。
**本仓有两个 git 远端:`origin`(CNB 主仓)+ `gitea`(自建 Gitea 代码同步辅仓)。**
演变路径:GitHub 双推 →(GitHub 被 AUP 清空)→ 一度只剩 `origin` → 现在回到双远端。
注意 Gitee 从头到尾**没真正建起来过**(卡体积门槛,见 §5.3),别把它记成一代。
三条设计上的取舍:
- **不用「一个 remote 挂多个 pushurl」** —— 实测 git 是「顺序推、遇错即停」,
第一个失败后面的都不推,备份意义就没了。所以用「独立 remote + 别名」。
- 别名用 `;` 串联而非 `&&`,正是为了让一段失败不影响另一段(主仓挂了辅仓照样推);
再 `exit $ec` 把退出码还给主仓,免得辅仓成功掩盖主仓失败。
- gitea 那一段带 `git remote | grep -qx gitea` 前置判断 ——
没配辅仓时静默跳过,而不是白报一行「'gitea' does not appear to be a git repository」。
### 5.2 远端
| remote | 地址 | 角色 |
|---|---|---|
| `origin` | `https://cnb.cool/zqlit/blog.git` | **唯一远端**(fetch + push,触发 CNB 构建) |
| remote | 地址 | 角色 | fetch | push |
|---|---|---|---|---|
| `origin` | `https://cnb.cool/zqlit/blog.git` | **主仓**(CNB) | ✅ 唯一 fetch 源 | ✅ 触发 CNB 构建 |
| `gitea` | `http://23.254.236.47:3001/zqlit/blog.git` | **代码同步辅仓**(自建,Gitea 28.0.0) | ⛔ 只推不拉 | ✅ 代码留档,失败不阻断 |
- **Gitea 凭据编在远端 URL 里**(`http://zqlit:<密码>@…`):容器/脚本环境没有交互终端、
也没有可用的凭据助手,这是让 `git push gitea` 免交互跑通的唯一简单写法。
明文只落在本机 `.git/config`(以及服务器上容器内的同名文件),**不进仓库、不随之推送**。
⚠️ 口令里若含 `#` 或 `&` 会被拼 URL 的 `sed` 吃掉 —— 那种情况请改用访问令牌。
- 自建 Gitea **不参与构建**:`.github/` 已删、Actions 早已停用,它纯粹是代码副本。
当前版本 `28.0.0`,走 HTTP + 3001 端口,本机直连即可(**不需要**广州中转机,
与当初「本机跑不通 git smart HTTP」的旧结论不同 —— 那次是沙箱代理的问题)。
- 接入时的同步基线:Gitea 停在 `ed38f938`(2026-10-04),落后 57 个提交,
且是本地 HEAD 的**祖先** → 一次 fast-forward 就追平,**无需强推、不丢历史**。
已移除的远端(改动前的完整配置快照留在 `.workbuddy-backup/git-remotes.*.txt`):
| remote | 地址 | 移除于 | 原因 |
|---|---|---|---|
| `gh` | `github.com/zqlit/blog` | 2026-10-06 | 账号被标记、仓库被按 AUP 清空(见下) |
| `gitee` | (从未真正建立过) | 2026-10-06 | 两条硬门槛过不去,改走 OpenList 离线备份(§5.3) |
| `gitea` | 自建 `23.254.236.47:3001` | 2026-10-06 | 发布链路早已不依赖;留着只会在推送时撞上落后 56 个提交的旧副本 |
> **远端仓库本身都没删,只是本地不再挂。** 想临时恢复自建 Gitea 副本:
> ```bash
> git remote add gitea http://<用户名>:<密码>@23.254.236.47:3001/zqlit/blog.git
> git push gitea main
> ```
| `gh` | `github.com/zqlit/blog` | 2026-10-06 | 账号被标记、仓库被按 AUP 清空(见下),**别再往回加** |
| `gitee` | (从未真正建立过) | — | 两条硬门槛过不去(§5.3) |
> ⚠️ **GitHub 退役记录(2026-10-06)**
>
@@ -167,14 +182,26 @@ git pushall # 完全等价 —— 它只是个别名,本仓只有
> (`bootstrap.sh` / `docker-compose.editor.yml` / `server.mjs` / `README.md`)、
> 以及 CI 定义本身(整个 `.github/` 目录,1268 行)。
### 5.3 异地备份的定案(2026-10-06)
### 5.3 远端与备份的分工(2026-10-06)
**结论:异地备份不走 git 远端 —— 改用整仓加密 bundle → OpenList(见 §5.6)。**
**结论:git 辅仓用「自建 Gitea」,备份用「整仓加密 bundle → OpenList」。两者都要,各管一段。**
一句话理由:备份要防的是「平台整体出问题」,而把副本放到**另一家同样性质的平台**上,
只是把鸡蛋从左边口袋挪到右边口袋。真正的第三层得**换介质**。
| 层 | 载体 | 保住什么 | 依赖 |
|---|---|---|---|
| 主仓 `origin` | CNB | 源码 + 触发构建 | CNB 平台 |
| 代码辅仓 `gitea` | 自建 `23.254.236.47:3001` | 随时 `clone` 回来、按提交追溯 | **自己的机器** |
| 离线备份 | F50/OpenList 上的加密 bundle(§5.6) | 完整历史 + 所有对象,平台全挂也能恢复 | 家里的局域网 |
下面这些实测数据保留下来 —— 下次再冒出「要不要加个 git 辅仓」的念头时,
两层的区别不是「多一份」,而是**失效模式不同**:辅仓是**在线、可增量、可浏览**的副本,
但仍在别人的磁盘上;bundle 是**离线、离线介质、单一文件**的全量快照,
连 git 服务都没有也能恢复。所以辅仓不能替 bundle,bundle 也不如辅仓顺手。
**为什么不选「另一家托管平台」当辅仓**(Gitee / 云效 Codeup / GitLab):
把副本放到**另一家同样性质的托管平台**上,只是把鸡蛋从左边口袋挪到右边口袋 ——
GitHub 那次 AUP 清空就是活证。自建 Gitea 是唯一「不受第三方规则约束」的在线选项,
它早在建站初期就在跑,本就该留着。
下面这些实测数据保留下来 —— 下次再冒出「换一家托管平台当辅仓」的念头时,
这几张表就是答案。
体积分布(HEAD,实测 457.4 MB / 2509 文件):
@@ -219,7 +246,8 @@ git pushall # 完全等价 —— 它只是个别名,本仓只有
> ★ **这些都只是「换一个篮子」,不是容灾。**
> CNB 与 Gitee/Codeup 同属国内大区;GitHub/GitLab 又执行同一套合规逻辑
> (本轮 GitHub 被按 AUP 清空即为例证)。
> 所以最终**一个 git 辅仓都没有采用** —— 改用不依赖任何平台账号的离线备份:见 §5.6。
> 所以**托管平台这条路一个都没采用** —— 在线辅仓回到**自建 Gitea**(§5.2,
> 不受第三方规则约束),另加一层不依赖任何 git 服务的离线备份(§5.6)。
**顺带一个副作用**:`bin/linux/hugo`「出库」这件事**不用做了**。
那个 83.1 MB 二进制之所以成为问题,只因为它卡在 Gitee 的单文件 50 MB 上限上。
@@ -239,10 +267,12 @@ git pushall # 完全等价 —— 它只是个别名,本仓只有
- ⚠️ CNB 的 `imports` 是**一层映射**(key 就是变量名本身);又拍云服务名在 CNB 里必须叫
**`UPYUN_SERVICE`**(旧 Gitea 里叫 `UPYUN_BUCKET`,照抄会「变量未定义」)
### 5.6 整仓离线备份(中兴 F50 / OpenList)★ 当前**唯一**的异地备份
### 5.6 整仓离线备份(中兴 F50 / OpenList)★ 备选异地备份
> **定位**:2026-10-06 起这一层就是本项目的异地备份本体 —— 不再有任何 git 辅仓(§5.3)。
> 所以它的**可靠性**与**失败可见性**都比以前更重要:它一旦悄悄失效,代码就只剩 CNB 一处。
> **定位**:2026-10-06 起这一层是**备选**异地备份 —— 在线那一路已有自建 Gitea 代码辅仓(§5.2)。
> 但它的地位并没有因此变轻:**它是唯一一层「不依赖任何 git 服务」的备份**,
> 平台全挂、账号被封、服务器被回收都影响不到它。所以它的**可靠性**与**失败可见性**
> 依然是最要紧的:一旦悄悄失效,能挡住「平台级事故」的就只剩它。
> 失败告警因此是必备件,不是加分项。
**目标介质**:中兴 F50 5G CPE 内置 256 GB 存储,上面跑 OpenList
@@ -297,9 +327,9 @@ node scripts/backup-bundle.mjs --decrypt <文件> [--out x.bundle] # 恢复用
| `SMTP_HOST` / `SMTP_PORT` / `SMTP_USER` / `SMTP_PASS` / `SMTP_TO` | 失败告警发信(复用 CNB 流水线那套 QQ 邮箱授权码) |
> ⚠️ `BACKUP_PASSPHRASE` 是恢复 bundle 的唯一钥匙,**必须另存进密码管理器**。
> 它不构成单点:CNB 主仓仍是明文副本,口令丢了只是少一份备份。
> 它不构成单点:CNB 主仓与自建 Gitea 辅仓都还是明文副本,口令丢了只是少一份备份。
**失败告警**(`scripts/backup-run.mjs`)—— 这是「唯一辅仓」的必备件。
**失败告警**(`scripts/backup-run.mjs`)—— 这是「离线那一层」的必备件。
跑完备份后,退出码非 0 就通过 `scripts/send_mail.js` 发一封告警邮件
(主题带 `★`,正文附日志尾部与常见原因清单)。成功时默认**不发**(避免每天一封的邮件疲劳),
加 `--notify-success` 才发。
@@ -316,7 +346,7 @@ node scripts/backup-bundle.mjs --decrypt <文件> [--out x.bundle] # 恢复用
日志追加到 `.workbuddy-backup/logs/backup.log`,超 5 MB 自动轮转。
> 保留 **7 份 = 一周窗口**(每份 605.5 MB ≈ 4.2 GB,F50 有 256 GB)。
> 原本是 3 份;升格为「唯一辅仓」后放宽 —— 空间不值钱,回溯窗口值钱。
> 原本是 3 份;定位成「离线那一层备份」后放宽 —— 空间不值钱,回溯窗口值钱。
★ `backup-task.cmd` **内容必须全 ASCII**:cmd.exe 按**当前代码页**(zh-CN 是 GBK)
解析批处理文件,而 node 输出 UTF-8;UTF-8 中文注释会吞掉 CR/LF、
@@ -350,15 +380,15 @@ git clone blog.bundle blog # 或 git fetch blog.bundle 'refs/*:refs/*'
| # | 项 | 说明 |
|---|---|---|
| 1 | `gh` remote(GitHub) | ✅ **已移除**(2026-10-06)。GitHub 账号被标记、仓库被 AUP 清空 —— **GitHub 已彻底退出本项目**:远端、推送别名、发布链路 4 处硬编码、CI 定义全部清干净 |
| 2 | **异地备份方案** | ✅ **已定案**(2026-10-06):**不走 git 远端**,改用整仓加密 bundle → F50/OpenList(§5.6)。`pushall` 已收回为「只推 origin」,`gitee` / `gitea` 远端均已移除。选型依据(Gitee 两道硬门槛、Codeup 对照)保留在 §5.3 备查 |
| 2 | **远端与备份定案** | ✅ **已定案**(2026-10-06):**CNB 主仓 + 自建 Gitea 代码辅仓 + OpenList 离线 bundle**(§5.2/§5.3/§5.6)。自建 Gitea 不受第三方规则约束,是唯一「平台出事也带不走」的在线副本;离线 bundle 保完整历史。选型依据(Gitee 两道硬门槛、Codeup 对照)保留在 §5.3 备查 |
| 3 | `bin/linux/hugo` 出库 | ✅ **不必做**。那个 83.1MB 二进制只卡 Gitee 的单文件上限;既然不走 Gitee,`deploy/Dockerfile` 的取 hugo 方式与 `.cnb.yml` 的 `by:` 字段一行都不用改 |
| 4 | `gitea` remote | ✅ **已移除**(2026-10-06)。远端仓库本身没删,需要时可 `git remote add` 恢复(命令见 §5.2) |
| 4 | `gitea` remote | ✅ **已恢复**(2026-10-06):作为代码同步辅仓重新挂上,`pushall` 恢复双推(`origin` + `gitea`)。接入时 Gitea 停在 `ed38f938`,一次 fast-forward 追平(+57 提交),**未强推、未丢历史**。发布链路 5 处默认值同步改回 `origin,gitea`(`bootstrap.sh` / `docker-compose.editor.yml` / `server.mjs` / `README.md` / `setup-cnb-remotes.sh`) |
| 5 | GitHub 侧旧 workflow | ✅ **已删除**(2026-10-06)。整个 `.github/` 目录移除(`deploy.yml` 1169 行 + `aliyun-backup.yml` + `cleanup.yml`),共 −1284 行 —— **GitHub 已不再是任何环节的依赖**。旧实现可在 git 历史中查 |
| 6 | 令牌 scope | 缺 `repo-cnb-history:r`(读构建日志);补上后 agent 可自行排错 |
| 7 | Gitea 主机 / 广州中转机 | 发布链路已不依赖;是否退役取决于其它用途 |
| 7 | Gitea 主机 / 广州中转机 | **Gitea 主机已回归**(2026-10-06):作为代码辅仓,本机直连 `23.254.236.47:3001` 即可推。广州中转机仍不参与 git 链路 |
| 8 | `README.md` | ✅ 已更新(去 GitHub 化 + 补上备份脚本说明) |
| 9 | **敏感文件仍在跟踪中** | `.env`、`GITEA_SECRETS.md`、`write-server/nginx/ssl/privkey.pem`、`content/posts/2024/.../setup-secrets.png` —— 若还要推任何新平台,先 `git rm --cached` 并轮换凭据。⚠️ 当初「不改写历史」的唯一顾虑是备份会分叉;**GitHub 那份已消失,这个顾虑没有了** → `git filter-repo` 现在是可行窗口(代价:全部提交 SHA 改变)。离线 bundle 已加密(§5.6),不受此影响 |
| 10 | **整仓离线备份** | ✅ **已上线**(2026-10-06),**且是当前唯一的异地备份**,见 §5.6。计划任务 `Blog-BundleBackup` 每天 03:30 跑,端到端已验证;失败会发告警邮件(已实测)。待补三件安全项:把 `BACKUP_PASSPHRASE` 抄进密码管理器;给 OpenList **改掉 admin 密码 + 开两步验证**(现 `otp: false`,且旧密码已出现在对话里);确认 5244 端口**未暴露到公网** |
| 10 | **整仓离线备份** | ✅ **已上线**(2026-10-06),现为**备选**异地备份(在线那一路是自建 Gitea 辅仓),见 §5.6。计划任务 `Blog-BundleBackup` 每天 03:30 跑,端到端已验证;失败会发告警邮件(已实测)。待补三件安全项:把 `BACKUP_PASSPHRASE` 抄进密码管理器;给 OpenList **改掉 admin 密码 + 开两步验证**(现 `otp: false`,且旧密码已出现在对话里);确认 5244 端口**未暴露到公网** |
| 11 | **F50 存储目录的使用约定** | `/本地/` 下的 `刷机` / `系统` / `资料` / `软件` / `驱动` / `备份` 都是用户自己在管的类别。**本项目的 bundle 只写 `备份/blog-bundle/` 子目录**,不与用户手工备份(`github-zqlit-*`、`local-repos-*`)混放 |
---