ORTHANT 博客技术架构详解
从文章保存到网页上线,梳理 ORTHANT 的发布与阅读流程,以及登录授权、搜索、评论和浏览统计的实现方案。
ORTHANT 的核心是提前生成网页,按需处理交互:文章保存为文本文件,发布时生成可阅读的网页;读者访问时获取这些页面,登录、在线写作、阅读授权和浏览统计则由服务端程序处理。
理解这套架构,可以沿着一篇文章的过程展开:它存在哪里,如何发布,读者如何访问,以及评论和统计如何附着在页面上。
1. 整体架构:内容、网页与服务如何配合
文章的原始内容使用 Markdown 保存,也就是带有标题、列表、代码块等标记的文本。Git 记录这些文件及图片的修改历史,GitHub 托管仓库,让本地写作和网页编辑使用同一份内容来源。
但文章文件还不是完整的网站。发布时,Hugo 会读取文章、导航配置和页面模板,生成浏览器可以显示的 HTML 网页。这个过程称为构建。Hextra 是 Hugo 使用的主题,提供文章排版、专题导航和目录等界面。
生成好的网页交给 Cloudflare Pages 托管,让读者通过网址访问。将生成好的页面和资源更新到网站上的过程称为部署。
内容从写作到阅读的主线如下:
| |
图中的 Pages Functions 是运行在 Cloudflare 上的服务端程序,负责处理需要即时判断或写入的请求。它与 Pages 配合:Pages 提供已生成的内容,Functions 处理访问时发生的操作。
D1 是保存动态数据的数据库,GitHub Discussions 是保存评论的讨论区。它们不保存文章正文,各自的用途在后面的互动功能中展开。
2. 一篇文章如何发布
本地编辑、GitHub 网页编辑和站内编辑器,最终都会修改同一个仓库。区别在于写作界面,而不是内容存储位置;文章不需要在几套正文数据库之间同步。
以站内编辑为例,Vditor 提供 Markdown 与可视化编辑界面。打开文章编辑时,系统读取仓库里的最新内容;点击“保存并发布”后,依次完成以下步骤:
- 检查修改:服务端验证站长身份、内容格式,并检查文章是否已经被其他入口更新。
- 保存版本:将正文与本次新上传的图片放进同一个 Git 提交,避免新文章与所需图片分开发布。
- 生成网页:Cloudflare 检测到生产分支更新,运行 Hugo,重新生成页面、导航和搜索数据。
- 更新网站:Pages 部署生成结果,让新内容开始对外提供访问。
- 确认上线:编辑页面核对线上文章的内容版本,确认读者已经可以获得本次修改。
这里有两个不同的完成时刻:保存成功,表示内容已进入仓库;上线成功,表示新网页已经部署。 构建排队或失败时,已经保存的内容仍在仓库中,但网站可能还是旧版,需要处理构建后再发布。
多个写作入口也带来冲突的可能。例如,在 GitHub 上改过文章后,再用之前打开的旧编辑页面保存,旧内容就可能覆盖新修改。ORTHANT 会检查版本,遇到冲突时停止写入,要求重新读取和核对,而不是自动覆盖。
因此,整个发布过程围绕同一个内容版本推进:先保存可追踪的修改,再生成网站,最后确认交付结果。
3. 读者打开文章时发生什么
发布完成后,文章标题、段落、表格和代码已经存在于生成的网页中。读者访问时,不需要临时从 GitHub 读取 Markdown,再重新生成整篇文章。
一次访问可以分为三个步骤:
- 检查访问范围:请求先经过 Functions。公开页面继续交付;项目案例及其附件先检查阅读权限。
- 返回现成页面:Pages 提供已经构建的 HTML、样式、图片和脚本,浏览器据此显示文章。
- 加载交互功能:页面再按需要读取登录状态、更新浏览次数,或加载评论与搜索。
这里的“静态”指的是正文在发布时已经生成,并不表示页面不能交互。文章内容没有改变时,多次访问可以使用同一份生成结果;新增评论或浏览次数则单独写入对应服务,不必重新生成正文。
交互资源也不需要全部挤在首次打开页面时加载。评论接近正文末尾才加载,搜索在使用时准备查询数据,编辑器则在进入写作时加载。普通阅读因此不必承担完整写作界面的加载开销。
这种分工让正文与附加功能相对独立:评论或统计服务异常时,已经显示的文章仍可阅读。不过,本站的请求经过 Functions,页面也由 Pages 托管,这两个环节仍然是网站正常访问的依赖。
4. 登录与阅读授权如何区分
博客里的“登录”并不是一种统一权限。管理文章、阅读项目案例和发表评论,分别对应不同的授权范围:
| 身份 | 如何获得权限 | 可以做什么 |
|---|---|---|
| 站长 | GitHub 站长授权 | 管理文章、阅读案例、管理评论 |
| 授权读者 | 验证阅读授权码 | 阅读案例及附件 |
| 评论用户 | giscus 评论授权 | 参与公开文章讨论 |
站长授权由 GitHub App 连接博客与 GitHub,服务端确认账号身份和仓库权限后建立会话。会话用于在一段时间内识别已登录的站长,浏览器通过 Cookie 随请求携带凭证;真正的写入权限仍由服务端校验,不取决于页面上是否显示编辑按钮。
阅读授权码通过验证后,也会得到浏览器凭证,有效期为八小时;有效站长会话可直接阅读案例。评论授权则由 giscus 独立处理,不会授予案例阅读或文章编辑权限。
案例保护发生在正文交付之前。 未授权请求取得的是授权提示,而不是先下载正文再用界面遮住。案例附件采用同样的检查,正文不进入公共搜索,受保护响应也不进入公共缓存。
阅读码属于共享授权,不是逐人账号系统。获得阅读权的人仍然可以复制内容,因此案例本身需要脱敏,访问控制不能替代内容处理。
5. 搜索、评论与浏览统计如何接入
这三项功能都出现在文章页面上,但运行位置和数据去向不同。
搜索:查询在浏览器内完成。
Hugo 构建时生成公开文章的搜索数据,包含标题、章节、标签和正文。读者使用搜索时,浏览器加载这些数据,由 FlexSearch 建立便于查找的索引并查询,不需要每输入一个词就请求服务端全文检索。
结果按文章合并,标题匹配优先,摘要帮助读者判断是否值得打开。项目案例不进入这份公开数据,因此不能通过搜索绕过阅读授权。
评论:页面显示讨论,GitHub 保存内容。
giscus 将 GitHub Discussions 的讨论嵌入文章页面。ORTHANT 使用独立的公共评论仓库,与私有内容仓库分开;每篇文章按访问地址关联讨论,评论更新不触发网站重新构建。
这种方式复用了现有的讨论与账号体系,但也意味着发言需要 GitHub 授权,评论加载依赖外部服务。项目案例不开放评论,文章迁移地址时则需要考虑原讨论的关联。
浏览统计:服务端写入数据库。
页面向 Functions 发出计数请求,服务端判断是否应该计入,再将结果写入 Cloudflare D1 数据库。同一浏览器、同一文章三十分钟内只计一次,有效站长会话下的访问和编辑预览不计入。
计数通过稳定的文章标识关联,修改标题或重新部署不会清零。它反映去重后的浏览次数,不等于精确访客人数。
三者体现了同一条分工原则:文章与图片需要版本历史,保存在 Git;评论保存在 Discussions;频繁变化的计数及限流记录保存在 D1。
6. 这套架构的取舍
这套组合适合个人维护、以阅读为主的技术博客。它把文章保留为可管理的文本,将正文生成放在发布阶段,并把必要的动态处理交给托管服务。
内容容易追踪,发布需要等待。 Git 让每次修改都有版本记录,也允许更换写作工具;相应地,内容更新需要经过构建和部署,不会在点击保存后立即出现在所有读者面前。
日常运行减少了服务器维护,但仍有平台依赖。 无需自行维护一台持续运行的应用服务器,仍要维护 Functions、授权配置和依赖组件,并关注托管服务的配额与费用。GitHub 或评论服务异常也会影响相应功能。
访问控制覆盖交付过程,但不是逐人权限系统。 共享阅读码适合有限范围的案例分享;若需要按人分配或撤销权限,就需要另一套身份模型。授权服务不可用时应拒绝受保护访问,预览和历史部署也需要遵循同样的保护要求。
文章版本可以恢复,全站数据不能一起回滚。 Git 历史覆盖文章与图片,不会自动恢复评论、浏览统计和部署配置。这些数据保存在不同系统中,备份和恢复必须分别考虑。
因此,ORTHANT 的核心并不是把所有功能都做成静态页面,而是明确分工:正文提前生成,发布按版本推进,权限在服务端判断,互动数据独立更新。
评论
使用 GitHub 登录后参与讨论,评论将公开保存在 GitHub。
站长管理删除会同步到 GitHub,无法撤销。
阅读到这里时加载评论。