返回
用 Cloudflare 免费套餐搭一个 AI 时代的个人博客
5 分钟

用 Cloudflare 免费套餐搭一个 AI 时代的个人博客

“Workers + D1 + R2 + Hono,服务端渲染、TDD 开发,零成本上线。”


这个博客本身就是一次「和 AI 助手一起做项目」的练习:需求用中文描述,AI 助手写代码、跑测试、部署,我负责提需求和验收。下面记录它的技术选型和实现要点。

为什么选 Cloudflare 免费套餐

个人博客的流量很小,最怕的是维护成本。Cloudflare 免费套餐把需要的东西都包了:

组件 作用 免费额度(大致)
Workers 运行代码、渲染页面 每天 10 万次请求
D1 SQLite 数据库,存文章/项目/关于 每天 500 万行读
R2 对象存储,存图片 10 GB,出站流量免费

不需要服务器,也不需要自己续 HTTPS 证书,先用免费的 *.workers.dev 域名,以后再绑定自己的域名。

架构

整个站点是一个 Worker:

  • Hono 负责路由,页面全部在服务端渲染成 HTML;
  • D1 存内容,Markdown 在保存时就渲染成 HTML,读文章只查一次库;
  • R2 存后台上传的图片,/images/... 带一年的 immutable 缓存;
  • 后台 /admin 用一个 Worker Secret 作为口令,登录态是 HMAC 签名的 Cookie。
// wrangler.jsonc(节选)
{
  "name": "pf-blog",
  "main": "src/index.ts",
  "d1_databases": [{ "binding": "DB", "database_name": "pf-blog-db" }],
  "r2_buckets": [{ "binding": "BUCKET", "bucket_name": "pf-blog-media" }]
}

快和轻

  • 前台没有框架运行时,唯一的脚本是不到 1 KB 的深浅色切换;
  • CSS 内联,配合 ETag,重复访问直接 304;
  • CSP 只允许同源脚本,Markdown 里的原始 HTML 一律转义。

搜索

数据量不大,搜索直接用 D1 的 LIKE 查标题、摘要和正文,注意把用户输入里的 % 和 _ 转义掉:

const pattern = "%" + q.replace(/[\\%_]/g, (c) => "\\" + c) + "%";
db.prepare(
  "SELECT slug, title FROM posts WHERE status='published' AND title LIKE ?1 ESCAPE '\\'",
).bind(pattern);

用 TDD 约束 AI 写代码

让 AI 写代码,最大的风险是「看起来能跑」。所以每次改动都先写失败的测试,再让实现通过:

  1. 用 @cloudflare/vitest-plugin 在真实的 workerd 运行时里跑测试,D1/R2 都是本地模拟;
  2. 测试覆盖页面、草稿不可见、登录、CSRF、图片上传限制、搜索转义等;
  3. 每次部署前跑一遍测试和 tsc --noEmit。
it("draft posts are 404 publicly", async () => {
  const res = await get(`/posts/${draftSlug}`);
  expect(res.status).toBe(404);
});

测试也是给 AI 的「验收标准」:需求说不清的地方,用测试说清楚。

部署

部署脚本把一次性的事情都做了:创建 D1、创建 R2 桶、执行迁移、发布。R2 第一次使用需要先在控制台开通;没开通时脚本会先不绑定 R2 部署,开通后再跑一次即可。

npx wrangler d1 migrations apply pf-blog-db --remote
npx wrangler deploy

样式

界面参考了开源主题 astro-theme-pure(Apache-2.0),在 Hono 模板里用手写 CSS 重新实现,只保留文章、项目、关于和搜索。