8abe93d17a0ed336937e17282867ae6258f870b8
背景:GitHub 被按 AUP 清空后,用户提出自己的中兴 F50(5G CPE + 内置 256GB)
上跑着 OpenList,想用它当第三层备份。实测可行,已落地并跑通。
为什么是 bundle 而不是直接推 git:
WebDAV 不支持原子的 rename/lock,bare repo 挂上去 push 会让对象写坏 ——
表面成功、实际随机损坏,可能几个月后才发现。bundle 是单文件顺序写,安全。
为什么加密(用户一度想省掉):
历史里含 .env、TLS 私钥、GITEA_SECRETS.md。介质是随身设备的内部存储,
明文 = 把密钥放在一台可能丢失/刷机/送修的机器上。
OpenList 的登录只保护「访问通道」,不保护「存储介质」——拆机就能读。
加密成本实测仅 1.9~2.9 秒、体积不变;且 CNB/Gitea 仍是明文副本,
口令丢失只是少一份备份,不构成单点。
实现(scripts/backup-bundle.mjs,零 npm 依赖):
- git bundle create --all → AES-256-GCM(Node 内置 crypto)
- 布局 magic(8)|salt(16)|iv(12)|密文|tag(16),scrypt(N=32768,r=8,p=1) 派生密钥
- 为什么不用 gpg:本机 gpg 2.4.9 在 Windows 下已损坏(反复 stale lockfile,
node spawn 直接 EBUSY);换内置 crypto 后零外部依赖且带认证标签
- 上传后可选 --verify:下载回来比对 sha256,端到端闭环
- --keep 控制远端保留份数;--decrypt 恢复;--list 盘点;--dry 不上传
定时任务(scripts/backup-task.cmd + Windows 计划任务 Blog-BundleBackup):
- 每天 03:30 本地时间,默认 --verify --keep 3
- InteractiveToken + LeastPrivilege、StartWhenAvailable、1h 超时、IgnoreNew 防重入
- 日志追加到 .workbuddy-backup/logs/backup.log,超 5MB 轮转
★ backup-task.cmd 内容必须全 ASCII:
cmd.exe 按当前代码页(zh-CN 是 GBK)解析批处理文件,而 node 输出 UTF-8。
UTF-8 中文注释会吞掉 CR/LF 并把下一行当命令执行 —— 实测踩到(一条 rem 被当命令跑)。
ASCII 是 UTF-8 子集,纯英文注释与 node 的中文输出混写不会乱。
同理不能用 %date%(含本地化星期),改用系统时间 API 取 ISO 格式时间。
日志轮转的 for 语句必须加 if exist 守卫,否则首次运行报「系统找不到指定的路径」。
实测(由计划任务实际拉起,非手工执行):
bundle 6.8~9.9s(605.5MB)/ 加密 1.9~2.9s / 上传 19.4~20.6s(29.4~31.3 MB/s)
/ 下载回读 sha256 一致,端到端退出码 0
恢复链路已演练:--decrypt → git bundle verify 报 "records a complete history"
→ 1008 提交完整一致
文档(架构总览.md):
- §5.3 从「Gitee 两条硬约束」扩写为「辅仓选型」,补入云效 Codeup 基础版对照
(Git 5GiB + 单文件命令行 200MB)—— 选它则 bin/linux/hugo 不必出库、
构建链路一行不用改
- 新增 §5.6 整仓离线备份(介质 / 为什么 bundle / 为什么加密 / 加密格式 / 用法 /
配置 / 定时任务 / 恢复流程 / 实测数据)
- §6 待办:#2 改为「辅仓选型未定」并说明 pushall 现状;#3 标注只有选 Gitee 才必须做;
#9 补记「不改写历史」的唯一障碍已随 GitHub 消失;新增 #10 备份已上线 + 三项安全待办
优世界博客(usj.cc)
Hugo 静态博客 + 自研评论后端 + 写作后台。 构建与发布跑在腾讯云 CNB(国内节点),单次发布约 3.5 分钟,境内/境外两条线路一次推完。
架构全貌、迁移前后对比、运维要点 →
架构总览.md
一、项目构成
| 子系统 | 位置 | 技术栈 |
|---|---|---|
| 内容 | content/、themes/Ying/ |
Hugo 0.128.2 extended + Ying 主题 |
| 评论后端 | blog-admin/ |
artalk-cf:Cloudflare Workers + D1 + KV(api.200181.xyz) |
| 写作后台 | write-server/(线上 post.usj.cc)、write/(本地 Windows) |
Next.js |
| 发布 | .cnb.yml、deploy/ |
CNB 流水线 + Dockerfile |
二、目录结构
blog/
├── .cnb.yml # ★ CNB 流水线(push + 每日定时)
├── deploy/Dockerfile # 构建镜像(hugo 二进制由 bin/linux/hugo 提供)
├── bin/linux/hugo # Hugo extended 0.128.2(linux/amd64,供 CNB 构建用)
├── content/
│ ├── posts/<年>/<日期>-<标题>/ # 文章(Page Bundle,index.md + 图片)
│ ├── about.md / links.md / circles.md / archives.md
├── themes/Ying/ # 主题(layout / assets / data)
├── static/ # 原样复制进产物(emotion 表情、image、js …)
├── blog-admin/ # 评论后端(artalk-cf)
├── write-server/ # 线上写作后台
├── write/ # 本地写作前端
├── scripts/ # 各类工具脚本(见第六节)
├── hugo.toml # Hugo 主配置
└── 架构总览.md # ★ 架构文档
三、内容写作
文章结构
每篇文章是一个 Page Bundle:
content/posts/2024/2024-05-01-文章标题/
├── index.md # 正文
└── 配图.jpg # 同目录图片(可用相对路径引用)
URL 规则
由 front matter 的 slug 决定(hugo.toml 里 permalinks.post = "/:slug"):
---
title: "我的文章"
date: 2024-05-01
slug: "my-post"
---
生成 https://usj.cc/my-post.html(uglyURLs,带 .html)。
隐藏文章
在 front matter 加 status: hidden。构建前 scripts/add_draft_to_hidden.py 会把它转成
draft: true,不出现在列表里,但直达链接仍可访问。
本地预览
hugo server -D # 含草稿
四、发布流程
写作(write-server / write/)
│ git pushall
▼
CNB 主仓 zqlit/blog ──触发──► CNB 流水线(国内节点,约 3.5 分钟)
├─ Hugo 构建
├─ 同步到又拍云(境内源站)
├─ 刷新又拍云 CDN
├─ 刷新多吉云 CDN
├─ 部署 EdgeOne Pages(境外线路)
└─ 邮件通知
- 推送:
git pushall=git push origin main; git push gitee main(origin= CNB 主仓,触发构建;gitee= 辅仓,异地备份、不构建) - 触发:推送到
main;另有每日0 9 * * *(北京时间)定时构建 - 密钥:全部来自 CNB 密钥仓库
zqlit/blog-secrets,经.cnb.yml的imports注入, 仓库里没有任何明文密钥 - 改动
main即自动上线,本地无需构建
五、三个子系统
内容系统
Hugo + Ying 主题。hugo.toml 控站点信息、永久链接、Artalk 地址、弹幕等。
评论系统(blog-admin/)
自研的 Artalk v2 兼容服务端,跑在 Cloudflare Workers + D1(SQLite)+ KV:
- 前端用官方 Artalk 客户端(
themes/Ying/assets/js/libs/Artalk.js,本地打包,非 CDN) - 后端 API 基址
https://api.200181.xyz(评论/api/v2/*与 RSS 订阅/api/*同一 Worker) - 部署:
cd blog-admin && npm run deploy(详细步骤见blog-admin/README.md、部署清单.md)
写作后台
write-server/:线上版(Next.js),部署在独立主机write/:本地 Windows 版
六、常用脚本(scripts/)
| 脚本 | 用途 | 在哪跑 |
|---|---|---|
add_draft_to_hidden.py |
构建前把 status: hidden 转成草稿 |
CI |
refresh_cdn.js |
刷新多吉云 CDN(零依赖) | CI |
send_mail.js |
构建结果邮件通知(零依赖 SMTP) | CI |
setup-cnb-remotes.sh |
切换/重建 git 远端(CNB 主仓 + GitHub 备份) | 本机 |
optimize_images.js |
图片批量压缩优化 | 本机 |
generate_circle_data.js |
抓友链 RSS 生成朋友圈数据 | 本机 |
update_link_lite_json.ps1 |
友链 links.yaml → JSON |
本机 |
add_ancient_chars.py / check_ancient_chars.py / merge_chars.py |
字体生僻字增补与校验 | 本机 |
cleanup_duplicates.js / migrate_slugs.js |
一次性维护脚本 | 本机 |
deploy_*.sh(又拍云 / EdgeOne / 定时)是本机手动部署的旧入口,日常已不需要—— 推送main由 CNB 自动完成。
七、相关文档
| 文档 | 内容 |
|---|---|
架构总览.md |
当前架构全貌(子系统、发布链路、迁移前后对比、运维要点) |
CNB构建落地方案.md |
迁 CNB 的实施方案与实测数据 |
代码源与构建平台选型.md |
平台对比(CNB / Gitee / GitLab / EdgeOne / 阿里云 ESA) |
砍COS改造步骤.md |
腾讯云 COS 下线记录 |
blog-admin/README.md |
评论后端完整说明 |
blog-admin/部署清单.md |
评论后端部署步骤 |
Languages
TypeScript
42%
JavaScript
32.2%
CSS
13.8%
HTML
6.2%
Shell
3.4%
Other
2.4%