版本二
今年夏天,我把这个博客的第一版推出门时写道,下一步是要把“一个不错的博客”变成“一个真正的自托管系统”。版本二就是这一步,现在完成了,所以这篇文章把整件事一次说完。

重建后的后台文章列表。
博客有了自己的邮局。 现在有一个小型邮件服务负责所有从站点发出的邮件,还带一个持久化的发件队列,这样即使 SMTP 某天抽风,邮件也不会丢。这一下子解锁了我盼了很久的一堆功能:带验证链接的注册邮件、联系表单的消息直接落到我的收件箱、可以直接在后台回复这些消息,现在还有了密码重置。如果你忘了密码,终于有了一个“忘记密码?”的链接。重置链接只能用一次,一小时后失效,而且请求重置不会透露某个邮箱地址是否在这里有账号,不管有没有,回应看起来都一样。
所有错误集中一处。 认证、博客和邮件之前各自维护自己的错误日志,后台界面得在浏览器里把它们拼到一起。现在有个小型日志服务统一收集所有信息。每个服务依然先在本地写入,再在后台分批发送,所以就算日志服务挂了也不会丢数据,日志条目只是等着,之后再发送。我故意把它停掉,往站点里扔了一堆错误,再重新启动它,看着一切都准确无误地到达一次。非常满足。
真正能跑起来的备份。 数据库和媒体文件按计划自动备份,后台面板里有恢复控制,还有一个健康检查,备份一旦过期就会报警。我测试过完整恢复,包括整个数据库都不见了的情况。你绝对不希望第一次恢复测试就是真枪实弹的那一次。
推送即部署。 现在合并到 main 分支会自动构建一切、跑测试,然后自己把线上站点换成新版本。走到这一步本身就是一段小小的历险:第一次部署因为缺少主机密钥失败,第二次因为缺少一个密钥失败,后来发现生产环境配置里有一个空变量,一直在悄悄地把所有邮件功能都关掉。那次真的挺疼的。现在一切都正常了,每次部署还会在后台留一份发布说明草稿,好让我不再忘记这次到底发布了什么。
后台面板长大了。 一年下来不断往里加东西,它变成了十三个并排的菜单项和三种不同的按钮风格。我把它重建了一遍:可以折叠的分组导航,统一风格的控件,表格里的删除按钮终于看起来真的很危险,用户管理也变成了一个对话框,而不是塞进表格行里的五个小部件。设置页面现在说人话了,不再是一堆裸露的配置键名。
三种语言的文章,加上一点帮助。 自从做了 i18n 之后,博客就一直是三语的,但手动让 en、no 和 es 保持同步,是那种会把人逼疯的活。现在编辑器里有一个翻译按钮,会把另一种语言预填成草稿,让我在发布前审核和修改。它不会自己运行,浏览页面也不会触发它,翻译只在我主动要求时发生一次,然后被保存下来。隐私政策清楚写明了什么内容会离开服务器、什么时候离开,因为这对我很重要。
一个相册。 我住在一座长这样的岛上,平时也经常徒步,所以博客现在也兼职当起了相册。批量上传、相册分类、缩略图(这样没人会为了看一张网格图就下载一张 12 MB 的原图),每张照片还有自己的页面和说明文字。原图在文章里依然可以正常使用。
系列文章和回复邮件。 文章现在可以归入一个有序的系列,带一个“第 2 部分,共 7 部分”的提示框和上一篇/下一篇链接,这正是那篇讲述搭建这个地方的七篇系列文章一直想要的样子。而且如果有人回复了你的评论,你会收到一封提醒邮件,就算五个人一起回复也只发一封,账号设置里还有开关可以关掉它。目前这些邮件只有英文版,因为站点故意不存储你的账号使用哪种语言。这一点我以后可能会重新考虑。
我最喜欢的小零件:一个部分唯一索引就完成了回复邮件的合并:
CREATE UNIQUE INDEX reply_notifications_unsent_parent_idx
ON reply_notifications (parent_comment_id) WHERE sent_at IS NULL;第一版是我学着搭建一个博客的过程。第二版是我学着运营一个博客的过程。两者的差距比我预想的要大得多,而且大部分都是看不见的,我现在觉得这正是重点所在。接下来大概会做一个订阅邮件,也许会搞联邦化,照片肯定会更多。