静态博客最大的短板是没有动态能力——评论、点赞、浏览量,全都要服务端。第三方评论服务(Disqus、Giscus 之流)要么国内访问困难,要么强依赖 GitHub 账号。这次干脆自己造:Cloudflare D1 做存储,Worker 做 API,三个静态站共用一套后端。
需求拆解
- 全球数据同步:国内用户从 Gitee Pages 发评论,海外用户从 GitHub Pages / www.520691.xyz 看到的必须是同一条
- 零依赖静态站:博客本身还是纯 HTML,不引入构建工具
- 树形评论:支持楼中楼回复
- 互动统计:点赞(toggle)、浏览量(UV 级)、评论数
- 防滥用:访客指纹限流,无需登录
架构总览
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ www.520691.xyz │ │ gjsvip.github.io│ │ gitee 镜像 │
│ (CF Pages) │ │ /blog/ │ │ (Gitee Pages) │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
└─────────────────────┼─────────────────────┘
▼
https://dujiecone.520691.xyz/api/blog/*
(Cloudflare Worker)
│
┌──────────┴──────────┐
│ BLOG_DB (D1) │
│ comments / likes │
│ views / says │
└─────────────────────┘
关键决策:三个静态站的前端调用同一个 API 根地址(API_BASE = 'https://dujiecone.520691.xyz'),数据天然同步。Worker 已按 zone 路由绑定到主域,无需额外域名。
数据模型
| 表 | 字段要点 |
|---|---|
| comments | slug + parent_id 树形结构、status 审核位、visitor 指纹 |
| likes | UNIQUE(visitor, comment_id) 保证一人一赞,重复提交即取消 |
| views | UNIQUE(slug, visitor) UPSERT 累加,UV 级浏览统计 |
| says | 说说流,独立于文章的轻内容 |
API 契约
统一信封 {code: 0, data}:
GET /api/blog/comments?slug=xxx # 树形列表 + total
POST /api/blog/comments # {slug, nickname, email?, content, parent_id?}
POST /api/blog/like # {slug?} 或 {comment_id} — toggle 语义
POST /api/blog/view # {slug, visitor}
GET /api/blog/stats?slugs=a,b,c # 批量 views/likes/comments
GET /api/blog/says # 说说流
几个实现细节
1. 访客指纹与限流
不登录就要防刷。用 sha256(IP + User-Agent + salt) 生成匿名指纹,评论限流 2 条/分钟(KV 桶),UV 去重(D1 UNIQUE 约束),前端再持久化一个 visitorId 到 localStorage 保持点赞状态。
2. Gravatar 头像
邮箱(选填)走 MD5 取 Gravatar 哈希,未填则显示昵称首字。MD5 用公有领域实现,零依赖。
3. 回复的层级校验
楼中楼只允许两层:回复必须挂在同一 slug 下已审核的评论上,防止跨文章串楼。
4. 亮暗双主题 + 移动端适配
设计系统用 CSS 变量驱动,[data-theme] 切换即全站换肤,选择记忆在 localStorage。640px 以下导航折叠为抽屉菜单。
前端功能清单
- Ctrl+K 搜索弹窗(标题/标签/摘要模糊匹配,键盘上下选择)
- 阅读进度条 + 返回顶部 + 滚动入场动画
- 文章页 TOC 目录(IntersectionObserver 滚动高亮)
- 代码块一键复制 + 轻量语法高亮
- 点赞 / 浏览 / 评论数实时展示
- emoji 选择器、昵称记忆、回复楼中楼
- 归档时间线、标签云、友链、说说页
效果
三端任意入口发布的评论、点赞、浏览,在其余入口实时可见——因为它们本来就读写同一套 D1。静态站的纯粹性与动态互动的丰富度,这次都要了。
API 已开源在 gjsvip/dujiecode 的
worker/routes/blog.ts,欢迎围观。