跳至内容
ORTHANT 博客技术架构详解

ORTHANT 博客技术架构详解

从文章保存到网页上线,梳理 ORTHANT 的发布与阅读流程,以及登录授权、搜索、评论和浏览统计的实现方案。

  • 系统架构
  • Hugo
  • Cloudflare Pages
  • GitHub App
  • 工程实践

ORTHANT 的核心是提前生成网页,按需处理交互:文章保存为文本文件,发布时生成可阅读的网页;读者访问时获取这些页面,登录、在线写作、阅读授权和浏览统计则由服务端程序处理。

理解这套架构,可以沿着一篇文章的过程展开:它存在哪里,如何发布,读者如何访问,以及评论和统计如何附着在页面上。

1. 整体架构:内容、网页与服务如何配合

文章的原始内容使用 Markdown 保存,也就是带有标题、列表、代码块等标记的文本。Git 记录这些文件及图片的修改历史,GitHub 托管仓库,让本地写作和网页编辑使用同一份内容来源。

但文章文件还不是完整的网站。发布时,Hugo 会读取文章、导航配置和页面模板,生成浏览器可以显示的 HTML 网页。这个过程称为构建。Hextra 是 Hugo 使用的主题,提供文章排版、专题导航和目录等界面。

生成好的网页交给 Cloudflare Pages 托管,让读者通过网址访问。将生成好的页面和资源更新到网站上的过程称为部署。

内容从写作到阅读的主线如下:

代码块 Text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
文章与图片
    ↓ Git 记录版本,GitHub 保存仓库
    ↓ Hugo 使用 Hextra 生成网页
    ↓ Cloudflare Pages 部署生成的网页

浏览器访问网站
    ├─ 页面与附件 → Functions 检查 → Pages 交付
    ├─ 登录与保存 → Functions → GitHub
    ├─ 浏览统计 → Functions → D1
    ├─ 搜索 → 浏览器内查询
    └─ 评论 → giscus → GitHub Discussions

图中的 Pages Functions 是运行在 Cloudflare 上的服务端程序,负责处理需要即时判断或写入的请求。它与 Pages 配合:Pages 提供已生成的内容,Functions 处理访问时发生的操作。

D1 是保存动态数据的数据库,GitHub Discussions 是保存评论的讨论区。它们不保存文章正文,各自的用途在后面的互动功能中展开。

2. 一篇文章如何发布

本地编辑、GitHub 网页编辑和站内编辑器,最终都会修改同一个仓库。区别在于写作界面,而不是内容存储位置;文章不需要在几套正文数据库之间同步。

以站内编辑为例,Vditor 提供 Markdown 与可视化编辑界面。打开文章编辑时,系统读取仓库里的最新内容;点击“保存并发布”后,依次完成以下步骤:

  1. 检查修改:服务端验证站长身份、内容格式,并检查文章是否已经被其他入口更新。
  2. 保存版本:将正文与本次新上传的图片放进同一个 Git 提交,避免新文章与所需图片分开发布。
  3. 生成网页:Cloudflare 检测到生产分支更新,运行 Hugo,重新生成页面、导航和搜索数据。
  4. 更新网站:Pages 部署生成结果,让新内容开始对外提供访问。
  5. 确认上线:编辑页面核对线上文章的内容版本,确认读者已经可以获得本次修改。

这里有两个不同的完成时刻:保存成功,表示内容已进入仓库;上线成功,表示新网页已经部署。 构建排队或失败时,已经保存的内容仍在仓库中,但网站可能还是旧版,需要处理构建后再发布。

多个写作入口也带来冲突的可能。例如,在 GitHub 上改过文章后,再用之前打开的旧编辑页面保存,旧内容就可能覆盖新修改。ORTHANT 会检查版本,遇到冲突时停止写入,要求重新读取和核对,而不是自动覆盖。

因此,整个发布过程围绕同一个内容版本推进:先保存可追踪的修改,再生成网站,最后确认交付结果。

3. 读者打开文章时发生什么

发布完成后,文章标题、段落、表格和代码已经存在于生成的网页中。读者访问时,不需要临时从 GitHub 读取 Markdown,再重新生成整篇文章。

一次访问可以分为三个步骤:

  1. 检查访问范围:请求先经过 Functions。公开页面继续交付;项目案例及其附件先检查阅读权限。
  2. 返回现成页面:Pages 提供已经构建的 HTML、样式、图片和脚本,浏览器据此显示文章。
  3. 加载交互功能:页面再按需要读取登录状态、更新浏览次数,或加载评论与搜索。

这里的“静态”指的是正文在发布时已经生成,并不表示页面不能交互。文章内容没有改变时,多次访问可以使用同一份生成结果;新增评论或浏览次数则单独写入对应服务,不必重新生成正文。

交互资源也不需要全部挤在首次打开页面时加载。评论接近正文末尾才加载,搜索在使用时准备查询数据,编辑器则在进入写作时加载。普通阅读因此不必承担完整写作界面的加载开销。

这种分工让正文与附加功能相对独立:评论或统计服务异常时,已经显示的文章仍可阅读。不过,本站的请求经过 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。

阅读到这里时加载评论。