用户:「动手 推送吧」—— 动手处理两个有死线的遗留。
一、writeapi.usj.cc 的证书(12 月会断)→ 已解决
现状复核推翻了上一轮的判断:
· 上一轮记的「它的 ssl_certificate 引用不在 /www/sites/ 树里」是**错的** ——
/www/conf.d/writeapi.usj.cc.conf 里明确写着
ssl_certificate /www/sites/writeapi.usj.cc/ssl/fullchain.pem;
· 真正原因是**它在 1Panel 面板上根本不是一个网站**(面板共 11 个站点,没有它),
所以 `findWebsite('writeapi.usj.cc')` 必然抛「找不到网站」→
加进 one_panel_sites 只会让**每次续期都报错**(而且只警告不阻断,长期没人发现)
· 实测确认:redeploy 时其余 5 个站点正常,只有它失败
解法:**不写一行代码** —— 它与 artalk.usj.cc 本来就同用一张 *.usj.cc 证书,
让它用**相对软链接跟随 artalk** 即可,随续期自动更新:
ln -sfn ../../artalk.usj.cc/ssl/fullchain.pem /1panel/1panel/www/sites/writeapi.usj.cc/ssl/fullchain.pem
★★ 踩到的坑:软链接**必须相对路径**
宿主 /1panel/1panel/www/sites/ ↔ 容器内 /www/sites/(1Panel 的 openresty 跑在容器
`1Panel-openresty-Kmh2`)。第一版写成宿主绝对路径 → **容器侧断链** →
nginx -s reload **静默失败**(保留旧配置、服务不中断、握手照旧拿旧证书)→
表面全绿,真实后果是「等某天 nginx 重启,writeapi 的 HTTPS 直接挂」。
唯一线索是 worker 进程的启动时间没变 —— **所以验收必须看 worker 是否真的重启**。
验收(全部实测):
替换前 CN=usj.cc / LiteSSL RSA CA / 2026-12-07(mtime 10-04 21:50)
替换后 CN=*.usj.cc / LiteSSL ECC CA / 2027-01-04
容器内 head -1 → -----BEGIN CERTIFICATE----- / -----BEGIN PRIVATE KEY-----
预检 syntax is ok / test is successful
reload → signal process started;**worker 22:51:39 重启**(证明软链接真被读了)
握手 SNI=writeapi.usj.cc → *.usj.cc / Jan 4 2027;实访 /health → HTTP 200
新增 deploy/cn-certkeeper/src/dump-cert.mjs:把 KV 里(AES-GCM 加密)的证书导成明文 PEM,
补上「KV ↔ 手工 vhost 磁盘文件」之间原先不存在的桥。只读,用完即删(含明文私钥)。
操作层的两个坑(写进文档):
· 1Panel 文件 API **拒绝写 .mjs**(返回 500「目标路径不存在」,实为可执行扩展名过滤);
往新建的 root:root 700 目录写也会失败 → 临时脚本用
`docker exec -i … sh -c 'cat > 路径' < 本地文件` 送
· cnrun.py(1Panel 计划任务通道)偶发「退出码 0 但零输出」;
加 `timeout 30 docker exec …` 包一层即稳定,判断状态优先看容器内直接证据
二、配置回退 + 文档更正
· certkeeper-config.mjs:把 writeapi 从 one_panel_sites 移除,换成一段注释说明
「它不是 1Panel 站点,别加进来」+ 软链接做法 + 必须相对的警告;
线上 config.kv 同步回退(sha256 与改动前完全一致 ccac2530…)
· 更正「3 个手工 vhost」的说法:vaultwarden **是**正常纳管的站点(面板 #13,
主域名 vw.usj.cc,别名才是 vaultwarden)→ 手工 vhost 实际只剩 writeapi + dnsapi
· dnsapi.usj.cc 重新定性为**不做**:它是 cn-dns-helper 的反代入口
(proxy_pass 127.0.0.1:8018,仅 HTTP),去留应与 cn-dns-helper 一起决定,
不该单独给一个待退役的服务加 HTTPS
三、pushall 加固:先 fetch + rebase 再推(本次真实撞到的问题)
推 CNB 时被 rejected —— 因为线上写作后台(editor-api)发文章会**直接推 main**,
本机两笔提交与之分叉。这不是异常,是**日常**。原别名直接 push,必然反复撞。
· 新增 scripts/pushall.sh:fetch → 已在远端之后则 rebase → 推所有远端
- 冲突时**停在 rebase 中途**交人工(不强推、不丢东西)
- 工作区不干净时 git 自己会拒绝 rebase(不会吞改动)
- 任一远端失败**不改判另一个**(主仓失败辅仓照样推),退出码以第一次失败为准
- ⚠️ 修了自己写的一处疏漏:`if ! cmd; then ec=$?` 的 $? 是**取反后**的结果(0),
不是 git 的退出码 —— 必须显式 ec=1(靠 set -o pipefail 保证管道退出码不被 sed 掩盖)
· setup-cnb-remotes.sh 的别名改指向脚本;README 脚本表补这一行
四、文档
· docs/证书管家.md:速览表「已知遗留」→ 已解决;§9.14 遗留段重写(含纠正自己写错的判据
「路径像不像不构成判据,要直接查面板站点清单」);**新增 §9.15**
「手工 vhost 的证书:用相对软链接跟随已纳管站点」(机制 + 路径坑 + 三条验收 + dump-cert 用法)
· docs/架构精简候选.md:候选 5 标记完成,补上「为什么 A 走不通、B 未采用」;
执行顺序里划掉它
· 架构总览.md:§5.7「已知遗留」重写(2 个手工 vhost + vaultwarden 更正);
§6 待办 #13 改为已解决、#14 标注完成
532 lines
37 KiB
Markdown
532 lines
37 KiB
Markdown
# 优世界博客 · 架构总览
|
||
|
||
> 更新时间:2026-10-06
|
||
> 状态:**迁移已落地并跑通**(四线路全绿,单次发布约 3.5 分钟)
|
||
> 本文档只描述**现状**;决策期的一次性评估与已完结方案见 [`docs/archive/`](docs/archive/)。
|
||
> 文档地图见 [`docs/README.md`](docs/README.md)。
|
||
|
||
---
|
||
|
||
## 0. 一句话
|
||
|
||
**五个子系统**,一条 **3 步**的托管发布链路。构建与发布已从「自建 Gitea + act_runner + 广州中转机」迁到
|
||
**腾讯云 CNB(cnb.cool)** —— 发布链路上**需要自己运维的机器从 3 台降到 0 台**
|
||
(另有一台国内机跑写作后台与证书签发,但都不在构建链路里)。
|
||
|
||
---
|
||
|
||
## 1. 全景
|
||
|
||
### 1.1 五个子系统
|
||
|
||
| # | 子系统 | 技术栈 | 跑在哪 | 状态 |
|
||
|---|---|---|---|---|
|
||
| **1** | **内容系统** | Hugo 0.128.2 extended + Ying 主题,138 篇 md;产物约 3200 文件 / 424MB | 产物分发到 3 个 CDN | 正常 |
|
||
| **2** | **评论系统** | `blog-admin/` = artalk-cf(Artalk v2 兼容服务端)+ RSS 机器人 | Cloudflare Workers + D1 + KV → `api.200181.xyz` | ✅ 零服务器零运维 |
|
||
| **3** | **写作系统** | `write-server/`(Next.js 16,`post.usj.cc`)<br>`write/`(本地 Windows 前端)<br>`editor-api/`(国内机轻量后端,实为线上那条发布入口) | 独立 Docker 主机/本机/国内机 | 可用(两套前端功能重叠) |
|
||
| **4** | **发布基础设施** | **CNB 流水线**(`.cnb.yml`)+ 又拍云 + 多吉云 + EdgeOne | 腾讯云 CNB(**托管**) | **已跑通** |
|
||
| **5** | **证书系统** | 证书管家(自研):ACME 签发 + DNS-01 + 多目标部署(多吉云 / 1Panel) | **国内机 Docker `cn-certkeeper`**(签发+部署)<br>+ CF Worker(**只读**:监控/后台/探针) | ✅ 已上线,每日 04:10 自动续期(详见 §5.7) |
|
||
|
||
### 1.2 服务与地址地图
|
||
|
||
| 角色 | 地址 / 位置 | 说明 |
|
||
|---|---|---|
|
||
| **代码主仓** | `cnb.cool/zqlit/blog` | CNB,**私有**;构建由它触发;唯一 fetch 源 |
|
||
| **代码辅仓** | 自建 Gitea `23.254.236.47:3001/zqlit/blog` | 只推不拉,纯代码留档;**不受第三方平台规则约束**(见 §5.2) |
|
||
| **写作后台** | 国内机 `119.29.215.187` → `127.0.0.1:8017`(容器 `editor-api`) | 轻量 Node 后端(零 npm 依赖),经 CF Worker 转发;发布时**推 `origin`(CNB) + `gitea`**(见 §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` 的死代码) |
|
||
| **国内加速** | 多吉云 CDN | 回源又拍云 |
|
||
| **境外线路** | EdgeOne Pages(项目 `hugo-blog`,`--area overseas`) | 腾讯 EdgeOne 国际站 |
|
||
| **评论后端** | `api.200181.xyz` | Cloudflare Workers |
|
||
| **证书签发** | 国内机 `119.29.215.187` → `127.0.0.1:8019`(容器 `cn-certkeeper`) | ACME 签发 + DNS-01 + 多吉云/1Panel 部署;`deploy/cn-certkeeper/`,每日 04:10 续期 |
|
||
| **证书只读面** | `api.200181.xyz/api/v2/ssl*` | CF Worker 上的监控 / 后台 / 探针,**只读**(`/ssl/issue` 已改 501 硬拒绝,见 §5.7) |
|
||
| **通知** | QQ 邮件(`imql@qq.com`) | 已收敛为**单一通道**;同时承担备份失败告警(§5.6) |
|
||
|
||
> 关键点:境内、境外仍是**两条独立线路**,但**由同一条流水线一次推完** ——
|
||
> 这是本次迁移最大的结构性改善。
|
||
|
||
---
|
||
|
||
## 2. 一次发布的完整旅程(3 步)
|
||
|
||
```
|
||
① 写作 write-server(网页) 或 write/(本地 Windows)
|
||
│
|
||
② 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 顺序执行)
|
||
├─ 1. Hugo 构建 草稿隐藏预处理 → hugo --minify --gc → 算构建哈希
|
||
├─ 2. 同步到又拍云 upx sync(境内直传,非阻断但如实上报)
|
||
├─ 3. 刷新又拍云 CDN purge 首页 + sitemap/rss/archives/posts 等固定入口
|
||
├─ 4. 刷新多吉云 CDN node scripts/refresh_cdn.js
|
||
├─ 5. 部署 EdgeOne edgeone pages deploy --area overseas
|
||
├─ 6. 上报部署状态 POST api.200181.xyz/api/deploy-status
|
||
└─ 7. 邮件通知 成功 → stages 末尾;失败 → failStages
|
||
```
|
||
|
||
**对照**:迁移前是 **7 步,中间有 3 个环节是你自己运维的服务器**。
|
||
|
||
> **两条推送入口,同一份语义**:上面第 ② 步是**本机**的 `git pushall`;
|
||
> **线上写作后台**(国内机 `editor-api` 容器)走的是同一套 ——
|
||
> `PUSH_REMOTES=origin,gitea`,在 `/srv/blog` 工作区里逐个远端推,
|
||
> **主仓(CNB)成功即算发布成功**,辅仓(Gitea)失败只警告不阻断。
|
||
> 两边都要遵守同一条铁律:**任何提交都必须先落到 CNB** ——
|
||
> pull 源只有 `origin`,只推 gitea 的提交后台看不见(见 §5.2)。
|
||
|
||
---
|
||
|
||
## 3. 流水线细节(`.cnb.yml`,337 行)
|
||
|
||
| 触发 | 条件 | 说明 |
|
||
|---|---|---|
|
||
| push | 推送到 `main` | 主链路 |
|
||
| crontab | `0 9 * * *`(Asia/Shanghai) | 每天 09:00,与 push **共用同一组 stage**(YAML 锚点) |
|
||
|
||
| 机制 | 实现 |
|
||
|---|---|
|
||
| 构建镜像 | `deploy/Dockerfile`,hugo 二进制**随仓库携带**(`bin/linux/hugo`,linux/amd64) |
|
||
| ⚠️ docker 上下文 | `by: [bin/linux/hugo]` —— CNB 的 docker build **只看得见 Dockerfile + by 列出的文件**,漏写会报 `not found` |
|
||
| 密钥注入 | `imports: https://cnb.cool/zqlit/blog-secrets/-/blob/main/secrets.yml` |
|
||
| stage 间传状态 | 落盘 `.ci_status`(原版靠 `needs.*.outputs`) |
|
||
| 并发控制 | `lock.cancel-in-progress`(对应原 `concurrency`) |
|
||
| 非阻断 | 又拍云三步 + 状态上报 + 邮件用 `allowFailure: true`,失败仍写状态如实上报 |
|
||
| 资源 | `cpus: 4`(4 核 8G) |
|
||
|
||
---
|
||
|
||
## 4. 迁移前后对比
|
||
|
||
| 维度 | 迁移前(Gitea 自建) | 迁移后(CNB) |
|
||
|---|---|---|
|
||
| 发布链路环节 | **7 步** | **3 步** |
|
||
| 自维护机器 / 服务 | **3 台**(Gitea 主机 + act_runner + 广州中转机) | **0**(构建托管) |
|
||
| 流水线定义 | GitHub Actions **1169 行** | `.cnb.yml` **284 行** |
|
||
| 产物传递 | 打 `tar.zst` → artifact → 中转机跨境下载 | 同一 pipeline **共享工作目录**,无需传递 |
|
||
| 又拍云直传 | `if: false`(境外 runner 必挂,**从未跑通**) | **已跑通**(国内节点直传) |
|
||
| COS 中转层 | 需要(存储 + 流量账单) | **已砍** |
|
||
| `github.server_url` 兼容分支 | **8 处** | **0** |
|
||
| 通知 | 3 套(TG / 飞书 / 邮件),约 400 行 | **1 套**(邮件) |
|
||
| 图片优化 + 反向 commit | CI 内每轮白跑(15 张老图处理不掉) | **未迁**(如需保留,照搬 `scripts/optimize_images.js`) |
|
||
| 构建环境 | 境外 VPS | 腾讯云**国内节点** 4 核 8G |
|
||
| 费用 | VPS 月租 | **0 元**(100GiB 仓库 + 160 核时/月) |
|
||
| 单次发布耗时 | — | **约 3.5 分钟** |
|
||
|
||
> 原链路里那些补丁(COS 中转 / `sync.sh` 每分钟轮询 / artifact 打包 / 兼容分支)
|
||
> **唯一根因是「runner 在境外」**。CNB 节点在国内,这一整层随之消失。
|
||
|
||
---
|
||
|
||
## 5. 运维要点
|
||
|
||
### 5.1 推送
|
||
|
||
```bash
|
||
git pushall # CNB 与 Gitea 都推 —— 发布用这个
|
||
git push origin main # 只推 CNB(触发构建)
|
||
git push gitea main # 只推 Gitea 辅仓
|
||
```
|
||
|
||
`git pushall` 的实际定义:
|
||
|
||
```
|
||
!git push origin main; ec=$?; git remote | grep -qx gitea && git push gitea main; exit $ec
|
||
```
|
||
|
||
**本仓有两个 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 | 地址 | 角色 | 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 就追平,**无需强推、不丢历史**。
|
||
|
||
**线上 editor-api 容器的推送目标**(2026-10-06 起,国内机 `119.29.215.187`)
|
||
—— 「CNB 主仓 + Gitea 备份仓 + OpenList 文件备份」这条定案在容器侧的落点:
|
||
|
||
```ini
|
||
# /srv/editor-api/.env
|
||
PUSH_REMOTES=origin,gitea
|
||
```
|
||
|
||
- 容器挂载 `/srv`,仓库工作区 `/srv/blog`,同一份 `.git/config` 里挂着 `origin`(CNB) 与 `gitea`
|
||
- 发布语义(`editor-api/src/git.mjs`):**逐个远端推,主仓成功即算发布成功**,
|
||
辅仓失败只在返回里带一条 warning、不阻断 —— 所以 Gitea 挂了不会让文章发不出去
|
||
- **pull 源永远只有 `origin`**:所以在别处**只推了 gitea** 的提交,后台是看不见的
|
||
(`git pull` 不会从它拉),下次发布会因 non-fast-forward 被拒。
|
||
→ **铁律:任何提交都必须先落到 CNB。**
|
||
- 国内机实测可直连自建 Gitea(`200` / 0.38s),**不需要中转机**
|
||
- 验证方式(改完必做):`curl -s http://127.0.0.1:8017/health` 看 `remotes` 字段,
|
||
容器日志里也有一行 `[editor-api] 分支 main 推送远端 origin, gitea`;
|
||
写权限用「推临时分支 → `ls-remote` 确认 → 删分支」实测,别用 `--dry-run`(会因落后误报)
|
||
|
||
已移除的远端(改动前的完整配置快照留在 `.workbuddy-backup/git-remotes.*.txt`):
|
||
|
||
| remote | 地址 | 移除于 | 原因 |
|
||
|---|---|---|---|
|
||
| `gh` | `github.com/zqlit/blog` | 2026-10-06 | 账号被标记、仓库被按 AUP 清空(见下),**别再往回加** |
|
||
| `gitee` | (从未真正建立过) | — | 两条硬门槛过不去(§5.3) |
|
||
|
||
> ⚠️ **GitHub 退役记录(2026-10-06)**
|
||
>
|
||
> 起因:GitHub 账号被平台标记,随后仓库被按 AUP 合规条款清空 ——
|
||
> 远端留下一条**孤立提交**(无父提交):
|
||
>
|
||
> ```
|
||
> c21d5669 2026-10-06 20:08:32 +0800 zqlit <zqlit@users.noreply.github.com>
|
||
> chore: remove repository content (AUP compliance)
|
||
> ```
|
||
>
|
||
> 同时远端 `main` 的历史与本地**完全分叉**(两边只共享 2024-07-11 的 `Initial commit`,
|
||
> 之后每个提交 SHA 都不同;远端那份是 989 提交、剥离了 `.env`/私钥/大二进制的变体)。
|
||
> 教训:**别把「备份」寄托在会对内容做合规处置的平台上**;
|
||
> 且一旦某次历史被重写,SHA 从此再也对不上。
|
||
>
|
||
> 本项目随后清掉了 GitHub 的**全部**痕迹:remote、推送别名、发布链路里 4 处硬编码
|
||
> (`bootstrap.sh` / `docker-compose.editor.yml` / `server.mjs` / `README.md`)、
|
||
> 以及 CI 定义本身(整个 `.github/` 目录,1268 行)。
|
||
|
||
### 5.3 远端与备份的分工(2026-10-06)
|
||
|
||
**结论:git 辅仓用「自建 Gitea」,备份用「整仓加密 bundle → OpenList」。两者都要,各管一段。**
|
||
|
||
| 层 | 载体 | 保住什么 | 依赖 |
|
||
|---|---|---|---|
|
||
| 主仓 `origin` | CNB | 源码 + 触发构建 | CNB 平台 |
|
||
| 代码辅仓 `gitea` | 自建 `23.254.236.47:3001` | 随时 `clone` 回来、按提交追溯 | **自己的机器** |
|
||
| 离线备份 | F50/OpenList 上的加密 bundle(§5.6) | 完整历史 + 所有对象,平台全挂也能恢复 | 家里的局域网 |
|
||
|
||
两层的区别不是「多一份」,而是**失效模式不同**:辅仓是**在线、可增量、可浏览**的副本,
|
||
但仍在别人的磁盘上;bundle 是**离线、离线介质、单一文件**的全量快照,
|
||
连 git 服务都没有也能恢复。所以辅仓不能替 bundle,bundle 也不如辅仓顺手。
|
||
|
||
**为什么不选「另一家托管平台」当辅仓**(Gitee / 云效 Codeup / GitLab):
|
||
把副本放到**另一家同样性质的托管平台**上,只是把鸡蛋从左边口袋挪到右边口袋 ——
|
||
GitHub 那次 AUP 清空就是活证。自建 Gitea 是唯一「不受第三方规则约束」的在线选项,
|
||
它早在建站初期就在跑,本就该留着。
|
||
|
||
下面这些实测数据保留下来 —— 下次再冒出「换一家托管平台当辅仓」的念头时,
|
||
这几张表就是答案。
|
||
|
||
体积分布(HEAD,实测 457.4 MB / 2509 文件):
|
||
|
||
| 目录 | 体积 | 说明 |
|
||
|---|---|---|
|
||
| `content/`(含 `posts/`) | ≈ 308 MB | 博客正文与媒体(mp4/mp3/大图),**搬不走** |
|
||
| `bin/linux/hugo` | **83.1 MB** | CNB 构建用的 hugo 二进制,**可移出 git** |
|
||
| `static/` | ≈ 38 MB | 站点静态资源 |
|
||
| `themes/` | 20.3 MB | Ying 主题 |
|
||
| 其余全部 | ≈ 8 MB | 代码、文档、脚本 |
|
||
|
||
**Gitee 的两条硬约束 —— 都不满足:**
|
||
|
||
| 约束 | Gitee 免费版 | 本仓库当前 | 判定 |
|
||
|---|---|---|---|
|
||
| 单仓库容量 | **≤ 500 MB** | `.git` **620 MB** | ❌ 超 24% |
|
||
| 单文件大小 | **≤ 50 MB** | `bin/linux/hugo` **83.1 MB** | ❌ **必删** |
|
||
| 用户总仓库容量 | 5 GB | 620 MB | ✅ |
|
||
| 私有仓协作人数 | 5 人 | 个人 | ✅ 无关 |
|
||
|
||
`bin/linux/hugo` 是**硬门槛**:**不管怎么瘦身历史,只要它还跟踪在 HEAD 里,Gitee 一律拒收**。
|
||
→ 移出后 HEAD ≈ **374 MB**,才有一份余量。
|
||
|
||
**对照:阿里云效 Codeup 基础版 —— 两条硬约束一条都不存在:**
|
||
|
||
| 项 | Gitee 免费版 | 云效 Codeup 基础版 |
|
||
|---|---|---|
|
||
| 单库容量 | ≤ 500 MB | **Git 5 GiB + LFS 5 GiB** |
|
||
| 单文件 | ≤ 50 MB | **Web 50 MB / 命令行 200 MB** |
|
||
| 620 MB 的 `.git` | 超 24%,推不上 | 占 **12.1%** ✅ |
|
||
| 83.1 MB 的 hugo | 超 66%,一律拒收 | 占 **41.6%** ✅ |
|
||
| 价格 | 免费 | 0 元/人/年,不限人数、不限仓库数 |
|
||
|
||
→ 若选 Codeup,`bin/linux/hugo` **不必出库**,`deploy/Dockerfile` 与 `.cnb.yml` 的 `by:`
|
||
字段**一行都不用改**(首推 620 MB 也在其 60 分钟推送超时内)。
|
||
注意两点:① 默认「中心组织」**只支持阿里云 RAM 账号登录、且必须绑钉钉**,
|
||
不接受的可选 2025-12 上线的「地域组织」(华东2上海,支持自建账号密码);
|
||
② 基础版无「删库保护」,容量到 90% 提醒、**超限直接禁止写操作**(连删文件都做不了)。
|
||
已有的阿里云 OSS AccessKey **不能**用来建 Codeup 仓库 —— git 推送走独立的克隆账号密码或 SSH Key。
|
||
|
||
> ★ **这些都只是「换一个篮子」,不是容灾。**
|
||
> CNB 与 Gitee/Codeup 同属国内大区;GitHub/GitLab 又执行同一套合规逻辑
|
||
> (本轮 GitHub 被按 AUP 清空即为例证)。
|
||
> 所以**托管平台这条路一个都没采用** —— 在线辅仓回到**自建 Gitea**(§5.2,
|
||
> 不受第三方规则约束),另加一层不依赖任何 git 服务的离线备份(§5.6)。
|
||
|
||
**顺带一个副作用**:`bin/linux/hugo`「出库」这件事**不用做了**。
|
||
那个 83.1 MB 二进制之所以成为问题,只因为它卡在 Gitee 的单文件 50 MB 上限上。
|
||
既然不走 Gitee,`deploy/Dockerfile` 的取 hugo 方式与 `.cnb.yml` 的 `by:` 字段
|
||
**一行都不用改**。
|
||
|
||
### 5.4 凭据
|
||
|
||
- 令牌存**仓库外**:`~/.workbuddy/secrets/cnb-token`
|
||
- 本仓 `credential.helper` 指向一个自定义脚本;`.git/config` **无明文**
|
||
- ⚠️ 本机全局 helper 是 **GCM**(会弹窗、对第三方 HTTPS 远端还会挂死);wincred 对 cnb.cool 有过「幽灵记录」删不掉 → 故用自定义 helper 绕开
|
||
|
||
### 5.5 密钥
|
||
|
||
- 修改密钥 → 编辑 CNB 密钥仓库的 `secrets.yml`(**网页编辑,禁 clone**)
|
||
- 本地 `cnb-secrets.yml` 只是粘贴草稿(已 gitignore,**不入库**)
|
||
- ⚠️ CNB 的 `imports` 是**一层映射**(key 就是变量名本身);又拍云服务名在 CNB 里必须叫
|
||
**`UPYUN_SERVICE`**(旧 Gitea 里叫 `UPYUN_BUCKET`,照抄会「变量未定义」)
|
||
|
||
### 5.6 整仓离线备份(中兴 F50 / OpenList)★ 备选异地备份
|
||
|
||
> **定位**:2026-10-06 起这一层是**备选**异地备份 —— 在线那一路已有自建 Gitea 代码辅仓(§5.2)。
|
||
> 但它的地位并没有因此变轻:**它是唯一一层「不依赖任何 git 服务」的备份**,
|
||
> 平台全挂、账号被封、服务器被回收都影响不到它。所以它的**可靠性**与**失败可见性**
|
||
> 依然是最要紧的:一旦悄悄失效,能挡住「平台级事故」的就只剩它。
|
||
> 失败告警因此是必备件,不是加分项。
|
||
|
||
**目标介质**:中兴 F50 5G CPE 内置 256 GB 存储,上面跑 OpenList
|
||
(`http://192.168.0.1:5244`,WebDAV 端点**必须带 `/dav` 前缀**)。7×24 常开、走局域网、
|
||
**不依赖任何 git 平台账号** —— 这是它比「再选一个代码托管平台」更值钱的地方。
|
||
|
||
**存放位置**:`/本地/备份/blog-bundle/` —— **专属子目录,不是 `/本地/备份/` 根下**。
|
||
根目录由用户自己在用(放着 `github-zqlit-*`、`local-repos-*` 等手工备份),
|
||
把文件混进去既容易被误删,也让「清理旧份」多一层风险。
|
||
(早期版本确实直接写在根目录下,实测发现文件已被清掉,故改为子目录隔离。)
|
||
|
||
**为什么是 bundle 而不是直接推 git**:WebDAV 不支持原子的 rename/lock,
|
||
把 bare repo 挂上去直接 `git push` 会让对象写坏 —— 表面成功、实际随机损坏,
|
||
可能几个月后才发现。bundle 是**单文件顺序写**,没有这个问题。
|
||
|
||
**为什么加密**:本仓库历史里含 `.env`、TLS 私钥、`docs/GITEA_SECRETS.md`。
|
||
介质是一台随身设备的内部存储,明文等于把密钥放在可能丢失 / 刷机 / 送修的设备上。
|
||
OpenList 的登录只保护**访问通道**,不保护**存储介质** —— F50 丢了拆开就能读。
|
||
|
||
**为什么不用 gpg**:本机 gpg 2.4.9 在 Windows 下已损坏(反复 `removing stale lockfile`,
|
||
node spawn 直接 `EBUSY`)。改用 Node 内置 `crypto` 的 **AES-256-GCM**:
|
||
零外部依赖、带认证标签(能检测篡改/截断)、可流式处理 600 MB 不爆内存。
|
||
|
||
**★ 打包前必须先 `git fetch --all`**(2026-10-06 补,此前是个静默漏洞):
|
||
|
||
`git bundle create --all` 取的是**本地已知**的 ref —— 其中 `refs/remotes/origin/main`
|
||
停在上一次 fetch/pull 的位置。而这份备份跑在**家里那台机器**上,它的工作区
|
||
并不会随写作后台(editor-api)的发布自动更新。不 fetch 就打 bundle,等于
|
||
**把过期的快照当备份**:后台最近发的文章一篇都不在里面,而且**备份照样报"成功"**。
|
||
→ 所以现在先 fetch 再打包;**fetch 失败不致命**(离线也得出得来备份),
|
||
只降级为「用本地已有 ref 打包」并在日志里显著告警、失败邮件里也点名这一条。
|
||
跳过用 `--no-fetch`。
|
||
|
||
日志里那句 `快照 origin/main = <sha> <日期> <标题>` 是这份备份的"封面" ——
|
||
恢复时第一件要确认的就是它,别只看"备份成功"。
|
||
|
||
**加密文件布局**:`magic(8) | salt(16) | iv(12) | 密文(...) | GCM tag(16)`,
|
||
密钥由 `scrypt(N=32768, r=8, p=1)` 从口令派生。
|
||
|
||
**用法**(`scripts/backup-bundle.mjs`,零 npm 依赖):
|
||
|
||
```bash
|
||
# 推荐入口:带失败告警的包装器(计划任务用的就是它)
|
||
node scripts/backup-run.mjs # = backup-bundle 的默认参数
|
||
node scripts/backup-run.mjs --verify --keep 7 # 计划任务的默认组合
|
||
node scripts/backup-run.mjs --dry --notify-success # 试跑并强制发一封成功邮件
|
||
|
||
# 底层脚本(backup-run.mjs 就是转发到它)
|
||
node scripts/backup-bundle.mjs # fetch + 加密 + 上传 + 比对字节数
|
||
node scripts/backup-bundle.mjs --verify # 额外下载回来比对 sha256(端到端最可靠)
|
||
node scripts/backup-bundle.mjs --dry # 只打包加密,不上传
|
||
node scripts/backup-bundle.mjs --no-fetch # 跳过 fetch(离线/调试)
|
||
node scripts/backup-bundle.mjs --keep 7 # 远端保留最近 7 份(默认值)
|
||
node scripts/backup-bundle.mjs --list # 列出远端现有备份
|
||
node scripts/backup-bundle.mjs --decrypt <文件> [--out x.bundle] # 恢复用
|
||
```
|
||
|
||
**配置**(按序查找,先找到生效):环境变量 → `~/.openlist-backup.env`
|
||
→ `.workbuddy-backup/openlist-backup.env`(**已在 .gitignore 内**)。
|
||
|
||
| 键 | 用途 |
|
||
|---|---|
|
||
| `OPENLIST_URL` / `OPENLIST_USER` / `OPENLIST_PASS` | OpenList 地址与账号 |
|
||
| `OPENLIST_DIR` | 远端目录,当前 `/本地/备份/blog-bundle` |
|
||
| `BACKUP_PASSPHRASE` | bundle 加密口令 |
|
||
| `SMTP_HOST` / `SMTP_PORT` / `SMTP_USER` / `SMTP_PASS` / `SMTP_TO` | 失败告警发信(复用 CNB 流水线那套 QQ 邮箱授权码) |
|
||
|
||
> ⚠️ `BACKUP_PASSPHRASE` 是恢复 bundle 的唯一钥匙,**必须另存进密码管理器**。
|
||
> 它不构成单点:CNB 主仓与自建 Gitea 辅仓都还是明文副本,口令丢了只是少一份备份。
|
||
|
||
**失败告警**(`scripts/backup-run.mjs`)—— 这是「离线那一层」的必备件。
|
||
跑完备份后,退出码非 0 就通过 `scripts/send_mail.js` 发一封告警邮件
|
||
(主题带 `★`,正文附日志尾部与常见原因清单)。成功时默认**不发**(避免每天一封的邮件疲劳),
|
||
加 `--notify-success` 才发。
|
||
|
||
> 为什么非做不可:「每天自动跑」的任务最典型的失败模式恰恰是**静默**的 ——
|
||
> F50 被带出门、换了网段、OpenList 重启后没起来、WebDAV 口令改过……
|
||
> 没人会主动发现,直到真要用备份那天。
|
||
> 发信逻辑独立于备份逻辑:**发信失败不改判备份的退出码**,通知不该掩盖真正的故障。
|
||
|
||
**定时任务**:Windows 计划任务 `Blog-BundleBackup`,每天 **03:30(本地)**
|
||
执行 `scripts/backup-task.cmd`(默认 `--verify --keep 7`)。
|
||
设置:`InteractiveToken` + `LeastPrivilege`、`StartWhenAvailable`(错过就补跑)、
|
||
`ExecutionTimeLimit=PT1H`、`MultipleInstances=IgnoreNew`(防重入)。
|
||
日志追加到 `.workbuddy-backup/logs/backup.log`,超 5 MB 自动轮转。
|
||
|
||
> 保留 **7 份 = 一周窗口**(每份 605.5 MB ≈ 4.2 GB,F50 有 256 GB)。
|
||
> 原本是 3 份;定位成「离线那一层备份」后放宽 —— 空间不值钱,回溯窗口值钱。
|
||
|
||
★ `backup-task.cmd` **内容必须全 ASCII**:cmd.exe 按**当前代码页**(zh-CN 是 GBK)
|
||
解析批处理文件,而 node 输出 UTF-8;UTF-8 中文注释会吞掉 CR/LF、
|
||
把下一行当命令执行(实测踩到:一条 `rem` 被当命令跑了)。
|
||
ASCII 是 UTF-8 子集,纯英文注释与 node 的中文输出混写不会乱。
|
||
|
||
**恢复流程**:
|
||
|
||
```bash
|
||
node scripts/backup-bundle.mjs --decrypt blog-<时间戳>.bundle.enc --out blog.bundle
|
||
git bundle verify blog.bundle # 应报 "records a complete history"
|
||
git clone blog.bundle blog # 或 git fetch blog.bundle 'refs/*:refs/*'
|
||
```
|
||
|
||
**实测数据**(2026-10-06):
|
||
|
||
| 阶段 | 耗时 |
|
||
|---|---|
|
||
| `git bundle create --all` | 6.8 ~ 9.9 s(605.5 MB) |
|
||
| AES-256-GCM 加密 | 1.3 ~ 2.9 s(体积不变) |
|
||
| WebDAV 上传 | 19.2 ~ 20.6 s → **29.4 ~ 31.6 MB/s** |
|
||
| 下载回读 + sha256 比对 | ≈ 30 s,**一致** |
|
||
|
||
端到端退出码 0;恢复链路已演练(解密 → `git bundle verify` → 1008 提交完整一致);
|
||
失败告警的邮件链路也已实测(发出一封「备份成功」验证信到 `imql@qq.com`)。
|
||
|
||
**覆盖范围(够到哪儿、够不到哪儿)—— 别把「备份成功」当成"什么都备了"**
|
||
|
||
| 内容 | 在不在这一层里 |
|
||
|---|---|
|
||
| 博客正文与附件 `content/**` | ✅ 在 —— 打包的是**仓库全量对象** |
|
||
| 通过写作后台(editor-api)发布的文章 | ✅ 在 —— 前提是**打包前 fetch 成功**(见上,这正是加 fetch 的原因) |
|
||
| `.env` / TLS 私钥等历史里的敏感文件 | ✅ 在(**这才需要加密**) |
|
||
| 国内机 `/srv/editor-api/{.env,docker-compose.yml}` | ❌ **不在** —— 仓库外,且国内机够不到家里的 OpenList |
|
||
|
||
最后一行是**已知缺口**,但影响可控:那几个文件是**可重建的**,不是不可替代的数据 ——
|
||
`docker-compose.yml` 由 `deploy/editor-api/bootstrap.sh` 生成(在仓库里),
|
||
`EDITOR_TOKEN` 丢了就重新生成并同步到 Worker,`ADMIN_PASS` / Gitea 口令都是可轮换的。
|
||
真正不可再生的只有 **git 历史(含 content 全量)**,而它已经在里面了。
|
||
(若哪天想连这几个文件也一起备,走 1Panel API 抓回来塞进同一份上传即可 —— 家庭机抓得到国内机。)
|
||
|
||
---
|
||
|
||
### 5.7 证书管家(子系统 5)
|
||
|
||
**职责划分 —— 一句话:签发与部署在国内机,Worker 只有只读。**
|
||
|
||
| 层 | 职责 | 跑在哪 |
|
||
|---|---|---|
|
||
| 签发 + 部署 | ACME(LiteSSL,EAB 继承 certimate 账户)· DNS-01 · 写多吉云 CDN · 写 1Panel | **国内机 Docker `cn-certkeeper`**,只绑 `127.0.0.1:8019`,每日 **04:10** 自动续期 |
|
||
| 只读 | 监控 / 后台页面 / 探针 | CF Worker(`blog-admin/`),路由 `/api/v2/ssl*` |
|
||
|
||
**为什么非要这么分**:CF Worker **免费版 CPU 硬顶 10ms/请求**(cron 同样 10ms),
|
||
签发要跑十来次 JWS 签名,**根本跑不动**;而 `[limits] cpu_ms` 在免费版会让**整个部署失败**(code 100328)。
|
||
用户否决了 CF Paid($5/月 ≈ ¥36/月 —— 比这台本来就在跑的国内机还贵)。
|
||
|
||
**三条不能破的红线**:
|
||
|
||
1. **Worker 不得签发** —— `POST /ssl/issue` 已改成 **501 硬拒绝**,证书 cron 已从 `wrangler.toml` 移除
|
||
2. **证书路由只认 Bearer 会话**,绝不用 `isAdminRequest` —— 后者有 Artalk 老客户端兜底
|
||
(`?name=<管理员名>&email=<管理员邮箱>` 即视为管理员),而这俩是**公开信息**。
|
||
一旦混用,任何人拼 query 就能读走 TLS 私钥。
|
||
3. **角色判定必须正着枚举放行**(`role === 'admin' || role === 'ssl'`),
|
||
禁止写成 `role !== 'editor'` —— 以后新增角色会**静默获得私钥权限**。
|
||
(角色体系见 `blog-admin/`;`ssl` 与 `editor` **平级、互不包含**)
|
||
|
||
**纳管域名**(唯一事实源 `blog-admin/tools/certkeeper-config.mjs`):
|
||
|
||
| 域名 | 部署目标 | 备注 |
|
||
|---|---|---|
|
||
| `usj.cc` | 多吉云 CDN + 1Panel | SAN 为 `usj.cc;*.usj.cc` |
|
||
| `t-t.live` | 1Panel(5 个站点) | 本项目**已接管**,certimate 的竞争工作流已停用 |
|
||
| `200181.xyz` | 1Panel | |
|
||
|
||
**与 certimate 的关系**:certimate 容器仍在机器上,但**凡「会签发并部署」的工作流都已停用**
|
||
(`enabled=0`)—— 否则它会用 RSA 证书覆盖本项目签的 ECC 证书,且**60 天后才爆、爆得静默**。
|
||
保留的只有三条**纯监控告警**工作流。
|
||
|
||
**已知遗留**:
|
||
|
||
- **手工 nginx vhost**(不归 1Panel 站点管理,`/websites/{id}/https` 碰不到)目前**只剩 2 个**:
|
||
- `writeapi.usj.cc`(写作后台 API 入口)—— ✅ **已解决**(2026-10-06):证书改用
|
||
**相对软链接跟随 `artalk.usj.cc`**(两者本就同用 `*.usj.cc` 那张证书),随续期自动更新。
|
||
⚠️ 软链接**必须相对路径**(宿主 `/1panel/1panel/www/…` vs 容器 `/www/…`,绝对路径会在容器内断链,
|
||
且断链后 nginx reload 会**静默失败**);验收要看 **worker 进程是否真的重启**,光看握手会被骗。
|
||
机制与坑见 [`docs/证书管家.md`](docs/证书管家.md) §9.15
|
||
- `dnsapi.usj.cc` —— 其实是 `cn-dns-helper` 的反代入口(`proxy_pass 127.0.0.1:8018`,**仅 HTTP**)。
|
||
它的去留应与 `cn-dns-helper` 一起决定,**不单独**给它加 HTTPS(见 [`docs/架构精简候选.md`](docs/架构精简候选.md) 候选 2)
|
||
- 注:`vaultwarden` **不是**手工 vhost —— 它是面板 #13 站点(主域名 `vw.usj.cc`),正常纳管。
|
||
(本文档早先把它误算作手工 vhost,2026-10-06 更正)
|
||
- 动代码前必看 [`docs/证书管家.md`](docs/证书管家.md) 的 **§8.4 / §9.9 / §9.11 / §9.12** 四节
|
||
「易误判、勿回退」的坑(ACME 客户端 bug、CSR 三层 SEQ、`ssl/update` 不物化站点文件、
|
||
1Panel HTTPS 字段名是大写 `SSL` 等)
|
||
|
||
---
|
||
|
||
## 6. 遗留待办
|
||
|
||
| # | 项 | 说明 |
|
||
|---|---|---|
|
||
| 1 | `gh` remote(GitHub) | ✅ **已移除**(2026-10-06)。GitHub 账号被标记、仓库被 AUP 清空 —— **GitHub 已彻底退出本项目**:远端、推送别名、发布链路 4 处硬编码、CI 定义全部清干净 |
|
||
| 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):作为代码同步辅仓重新挂上,`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`)。**线上 editor-api 容器也已切双推**(见 #12) |
|
||
| 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 主机到期** | **2026 年 11 月**,境外 VPS `23.254.236.47` 到期 —— 迁移是**有死线的待办**,不是可选项。现在迁移面已大幅缩小(**只需搬 Gitea 本体,runner / upyun-sync / 中转机都不需要**,因为构建已在 CNB),完整步骤与「本仓 9 处写死旧地址」的清单见 [`docs/Gitea迁移指南.md`](docs/Gitea迁移指南.md)。⚠️ 那篇第九节还给出了「干脆不留这个辅仓」的选项与判断依据 |
|
||
| 8 | `README.md` | ✅ 已更新(去 GitHub 化 + 补上备份脚本说明) |
|
||
| 9 | **敏感文件仍在跟踪中** | `.env`、`docs/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),现为**备选**异地备份(在线那一路是自建 Gitea 辅仓),见 §5.6。计划任务 `Blog-BundleBackup` 每天 03:30 跑,端到端已验证;失败会发告警邮件(已实测)。**2026-10-06 补**:打包前加 `git fetch --all` —— 原来备的是「本机已知 ref」的快照,后台新发的文章可能一份都不在里面,且备份照样报成功(§5.6)。待补三件安全项:把 `BACKUP_PASSPHRASE` 抄进密码管理器;给 OpenList **改掉 admin 密码 + 开两步验证**(现 `otp: false`,且旧密码已出现在对话里);确认 5244 端口**未暴露到公网** |
|
||
| 11 | **F50 存储目录的使用约定** | `/本地/` 下的 `刷机` / `系统` / `资料` / `软件` / `驱动` / `备份` 都是用户自己在管的类别。**本项目的 bundle 只写 `备份/blog-bundle/` 子目录**,不与用户手工备份(`github-zqlit-*`、`local-repos-*`)混放 |
|
||
| 12 | **editor-api 容器的三层留存** | ✅ **已落地**(2026-10-06,国内机 `119.29.215.187`):`PUSH_REMOTES=origin,gitea`(CNB 主仓 + 自建 Gitea 备份仓),文件层走 §5.6 的加密 bundle。实测:容器内推 `origin` ✅ / 推 `gitea` ✅(临时分支推完即删),`/health` 返回 `remotes:["origin","gitea"]`,三处 `main` 对齐 `448b898e`。接入时 `/srv/blog` 落后 10 个提交,一次 fast-forward 追平 |
|
||
| 13 | ★ **证书管家遗留** | ✅ **① 已解决**(2026-10-06):`writeapi.usj.cc` 是**手工 vhost**(面板 11 个网站里没有它,所以按站点名绑定永远找不到目标)→ 改用**相对软链接跟随 `artalk.usj.cc`**,随续期自动更新。证书从 `CN=usj.cc`/RSA/**2026-12-07** 换成 `CN=*.usj.cc`/ECC/**2027-01-04**,容器内可读、预检通过、**worker 已重启**、`/health` 200;机制与「必须用相对路径」的坑见 [`docs/证书管家.md`](docs/证书管家.md) §9.15。<br>**② `dnsapi.usj.cc` 重新定性为「不做」**:它是 `cn-dns-helper` 的反代入口(`proxy_pass 127.0.0.1:8018`,仅 HTTP),去留应与 `cn-dns-helper` 一起决定。<br>(附带澄清:`200181.xyz` 公网走 Cloudflare,看到的 LE 证书是 **CF 自家的边缘证书**,与本项目无关;源站 `ssh.200181.xyz` 已是本项目签的 `*.200181.xyz` / 2027-01-04 ✅) |
|
||
| 14 | **架构复杂度盘点** | 「感觉架构还是复杂了」的正面回应 → 已盘点:**跨 6 个环境,其中 4 个要自己维护**,这才是复杂度的来源;国内机 9 个容器里属于本项目的只有 3 个,能动的只有 2 个。**5 个精简候选 + 建议执行顺序**见 [`docs/架构精简候选.md`](docs/架构精简候选.md):certimate 容器可停、`cn-dns-helper` 大概率是遗留、Gitea 迁 or 不留、两套写作前端收敛、~~writeapi 证书纳管~~(✅ 2026-10-06 已完成,见 #13) |
|
||
|
||
---
|
||
|
||
## 附:相关文档
|
||
|
||
文档只有两个入口在根目录(`README.md`、本文);其余在 `docs/`,一次性的评估与已完结方案在 `docs/archive/`。
|
||
完整地图见 [`docs/README.md`](docs/README.md)。
|
||
|
||
| 文档 | 内容 |
|
||
|---|---|
|
||
| `docs/证书管家.md` | ★ 证书管家(签发在国内机,Worker 只读):现状 + 决策沿革 + 勿回退的坑 |
|
||
| `docs/Gitea迁移指南.md` | ★ 自建 Gitea 搬迁时**必须同步改的全部位置** + 验证清单 |
|
||
| `docs/架构精简候选.md` | 复杂度盘点 + 5 个可精简候选(待决策,含收益/代价/风险) |
|
||
| `docs/CNB构建落地方案.md` | 迁 CNB 的实施方案与实测数据 |
|
||
| `docs/GITEA_SECRETS.md` | Gitea / 相关凭据清单(**含明文,勿外传**) |
|
||
| `docs/archive/` | 决策期评估与已完结方案(Gitee / EdgeOne / ESA / 精简方案 / COS 下线 / 评论修复 / 诊断报告 …) |
|
||
|
||
**证据出处(现行)**:`.cnb.yml`(284 行)、`deploy/Dockerfile`、`bin/linux/hugo`、
|
||
`scripts/send_mail.js`、`scripts/refresh_cdn.js`、`scripts/setup-cnb-remotes.sh`、
|
||
`scripts/backup-bundle.mjs`、`scripts/backup-run.mjs`、`scripts/backup-task.cmd`。
|
||
原 GitHub Actions 流水线(`.github/workflows/deploy.yml`,1169 行)已于 2026-10-06 删除,
|
||
需要查请翻 git 历史。
|