缘起

上一篇记录了博客系统的搭建:iStoreOS 小主机上,Hugo + Gitea + 在线编辑器的骨架立起来了。但那时的它还只是一个「能用的毛坯」:发布靠手动构建再拷贝到云服务器,编辑器只能写新文章,改已发布的内容要 SSH 上去改文件,图片更是没有像样的通道。

这篇记录的是接下来两天发生的事:它被升级成了一套全自动出版系统——而且这一次,大部分活不是我干的,是 AI 干的。

现在的流水线长什么样

写作(三选一)
  ① 在线编辑器  edit.blog.is-i7.cn  (公网,Basic Auth)
  ② Gitea 网页  :3000
  ③ 直接改 pi 上的源码后 git push
        ↓ push
  Gitea 钩子 deploy-blog
        ↓ hugo 构建(约 210ms)
        ↓ rsync --delete 走 WireGuard 隧道 → 云服务器
  预览站(:8888) 和 is-i7.cn 同步更新

从点「发布」到文章上线,全程不到 10 秒。构建在树莓派级别的小主机上完成,云端只做静态托管——3GB 内存的小盒子跑得毫不费力。

编辑器的进化

最初的编辑器只有标题、标签、正文三个框,发布按标题生成文件名——意味着改一个已发布的标题就会创建一篇新文章,而旧文永远无法修改。

升级后的编辑器:

  • 文章侧栏:列出全部已发布文章,点开即载入编辑器,发布按钮自动变为「更新原文」——保留原文件路径、保留原始发布日期、自动携带版本号
  • 图片按钮:选图自动上传图床,光标处插入直链,编辑器预览里也能直接看到
  • 断线续编:刷新页面会恢复「正在编辑哪篇」的状态,不会误当新文章发出去

这些功能上线前经过了一轮「编辑器链路测试」——你没看错,博客里那些名字朴素的测试文章(《编辑器测试》《编辑器链路测试》《测试在线编辑文稿》)就是那个阶段诞生的。它们是 AI 用真实浏览器一轮轮点出来的:新建、发布、从列表加载、修改、再发布、验证线上更新、删除。测试全绿之后,它们作为脚手架被撤下了——但 git 历史里永远留着,算是这段调试期的化石。

图床:一次 PHP 到 Rust 的改道

图片最初跟博客仓库放在一起,后来决定独立成图床。第一站是 PHP 写的 EasyImages——功能全,但 253MB 的容器镜像总觉得笨重。后来换成了 Rust 的 dufs

镜像体积    253MB  →  3MB
运行内存    ~50MB  → ~10MB

功能上做减法:不需要相册、不需要评论,只要「上传 + 直链 + 权限」。dufs 的权限模型刚好合适——匿名只读(博客外链直接可用),账号可写(上传走反代注入凭据,前端零暴露)。网页拖拽、命令行 curl -T、编辑器插图三条路全通。

顺带一提,pi 在国内拉不动 Docker Hub,镜像是在云服务器上拉好、打包、走 WireGuard 隧道送回来的——3MB 的包,隧道两秒送达。

隧道:这一切的地基

上面反复出现的「WireGuard 隧道」值得单独一节。家里的小主机在 NAT 后面,云服务器有公网 IP,于是组了一张星型小网:

                云服务器 (中心节点 100.72.0.1, UDP)
    ┌────────────┼────────────┬──────────┐
  小主机       Mac 笔记本    RackNerd    家里其他设备
 100.72.0.2   100.72.0.3   100.72.0.5      .4

博客发布走它,图床流量走它,SSH 运维走它。Mac 上的隧道做成了系统服务:开机自启、掉线 20 秒内自动拉起——在公司、在咖啡馆,ssh pi 永远直连。

踩坑实录(本篇技术含量最高的部分)

  • 闭合代码栅栏不能带语言标注。一次批量「修复」把 ``` 结尾也标成了 ```plain,Markdown 规范里带标注的栅栏只能开启代码块——于是整篇文章被吞进一个巨型代码块。修复用状态机:代码块开启状态下遇到的栅栏一律还原为裸栅栏。
  • OpenWrt 的 ssh 是 dropbear 的 dbclient,不认 -F 配置文件,密钥格式也和 OpenSSH 不通用。部署脚本突然连不上,装 openssh-client 才解决。
  • Docker bind mount 会遮住镜像内容。挂载空目录到 /app/web,应用代码直接消失,nginx 只剩 403。修法:从镜像临时容器里 docker cp 出完整应用再挂载。
  • dufs 的上传权限是两层:账号规则给 rw 之外,还得加全局 --allow-upload;匿名规则写 @/:ro,那个 ro 少个 o 都不行。
  • pkill -f 会自杀:模式串出现在自己的命令行里,把自己的 shell 也杀了,后面的清理命令全没执行。
  • nginx reload 有切换瞬间:旧 worker 还在用旧配置,紧跟着的请求会撞上 403/405,几秒后再测就好——不是每次报错都是真的报错。

每一个坑现在都沉淀在服务器文档里,下次不管是人还是 AI 接手,都不用再踩一遍。

关于「AI 的劳动」

这套系统升级期间,AI 通过 SSH 和浏览器完成了大部分实操:改配置、写钩子、调前端、起容器、压测权限、修被改坏的 markdown、最后还把测试文章清理下线。人负责做决定——换 Rust、删测试帖、标题改成什么——AI 负责把决定变成现实,并把过程写进文档。

所以这篇也算一份工作记录:编辑部现在有两个人(类),一个出主意,一个出手。下一篇不知道谁执笔,但流水线已经不挑作者了。


落日余晖,记录折腾与思考——现在也记录一点人机协作。