个人 NotesChapter IV · Vol. MMXXVI
IV.Chapter 4 · 个人 Notes
仓库同步于 2026年5月21日
走一遍:这篇博客是怎么从文件夹跑到你屏幕上的
用一次「云散步」看懂:仓库目录、校验脚本、Supabase、GitHub Actions,以及和 Cursor Skills 里那些「先想清楚再动手」有什么共同点。
**太长不看**:你在 `content/posts/` 里放一篇「文件夹文章」→ 推上 `main` → GitHub Actions 用**服务端密钥**把正文 upsert 进 Supabase → 网站用**公开密钥**只读拉表;**正文以数据库为准**(仓库内不再内置演示章节)。 --- ## 1. 为什么要搞这么一圈? 很多人第一次听说「文章不进数据库、却又要数据库」会愣一下。这里的拆法其实很常见: - **Git**:负责版本、评审、回滚、谁改了哪一行——人类协作的舒适区。 - **Supabase(Postgres)**:负责给已经上线的站点一个**稳定、可查询**的正文来源,不必每次改个字就重新打一整包前端。 - **可选的少量本地条目**:`staticPostsCatalog` 现为空数组;若将来要在代码里放极少数固定页,仍可与远程按 `slug` 合并,远程优先。 一句话:**Git 管「怎么生产」,数据库管「线上读什么」,前端再管「怎么展示」。** --- ## 2. 你在仓库里唯一能「摸得着」的那一层 约定很简单(也是本仓库真实在用的): - 根目录下有 `content/posts/`。 - **每一个子文件夹 = 一篇文章**,文件夹名必须等于 `post.json` 里的 `slug`。 - 元数据在 `post.json`,正文在 `body.md`(默认 Markdown)或 `body.html`(当 `content_format` 为 `html` 时)。 - 以下划线 `_` 或点 `.` 开头的目录会被发布脚本**跳过**(例如留给模板的 `_template`)。 你可以把 `post.json` 想成「书的扉页信息」,把 `body.md` 想成「正文手稿」。 --- ## 3. 中间那只「看不见的手」:同步脚本在干什么? `tools/publish/sync-posts.mjs` 会做几件朴素但重要的事: 1. 扫描 `content/posts` 下每个可发布目录。 2. 校验必填字段、日期格式、`category` 是否落在站点支持的枚举里。 3. 把 `post.json` + 正文文件拼成一行记录,通过 PostgREST **按 `slug` upsert**(有则更新、无则插入)。 本地想先看会生成什么数据,可以跑 **`npm run publish:content:dry`**:只打印 JSON,**不写库**,适合当「预演」。 真正写入需要 **`SUPABASE_SERVICE_ROLE_KEY`**(以及项目 URL)。这一点和「浏览器里用的 publishable / anon 密钥」是故意分开的——下面一节说原因。 --- ## 4. 两把钥匙:为什么「能读」不等于「能发」? Supabase 里这张 `published_posts` 表开了 **RLS(行级安全)**:对匿名访问通常只放开 **SELECT**。也就是说: - **站点访客用的密钥**:足够把文章读出来渲染。 - **写入 / 更新**:走的是 CI 或你本机脚本,用的是 **service role**,绕过 RLS 做 upsert。 这不是刁难,而是安全基线:**能在用户浏览器里出现的密钥,就不该拥有删库灌库的权限。** 如果你把「能发帖」的权限也塞进前端环境变量,等于把家门钥匙复印贴在小区公告栏上。 --- ## 5. GitHub Actions:把「推代码」变成「发版」 当 `main`(或 `master`)上 `content/posts/**` 有变更时,工作流会跑同一个同步脚本。仓库里需要在 Secrets 里配置: - 项目根 URL(与 `VITE_SUPABASE_URL` 一致,**不要**带 `/rest/v1/` 尾巴)。 - **Service role** 密钥(只放在 GitHub Secret,绝不进仓库)。 于是流程变成:**你写的是 Markdown / HTML,Git 记的是 diff,线上读者看到的是合并后的章节列表。** 这才是「博客发布」在工程上的完整闭环。 --- ## 6. 和 Cursor Skills 的一点「隔空击掌」 你如果在 Cursor 里开过一些 **Agent Skills**,会发现它们和这条发布链在气质上很像——都是**把冲动拆成步骤、把证据放在断言前面**: - **Brainstorming / Writing plans** 一类技能,强调的是:先弄清要写什么、再动手;这和「先 `dry-run` 看 JSON,再真写入」是同一类习惯——**减少「我以为对了」带来的返工**。 - **Verification before completion** 的精神是:声称「好了」之前,用命令或检查清单对照现实;对应到这里就是:**合并后本地 `build`、CI 绿、必要时再打开站点确认远程 slug 已出现在目录中**。 - **Product craft** 里常提的「默认路径要安全」:RLS + 双密钥,就是把「默认只能读」设成安全路径,把「能写」收窄到自动化和你本机。 这些不是教条;它们和一条好的发布流水线一样,都在回答同一句话:**系统默认应该站在「会犯错的人类」这一边。** --- ## 7. 你接下来可以怎么玩 1. 复制 `_template` 成新目录,改 `slug` 与 `post.json` 对齐。 2. `npm run publish:content:dry` 看一眼输出。 3. 配好密钥后 `npm run publish:content`,或推 `main` 交给 Actions。 4. 打开站点对应 `/posts/<slug>`,确认列表与正文。 若某篇只想留在 Git、暂时不上线,**不要**跑同步,或不要在 Supabase 里插入该行即可;若线上想撤下一篇远程文,需要在表里删对应 `slug`(当前脚本不做「仓库删目录就自动删库」的反向同步)。 --- *本文即一篇按上述约定放在 `content/posts/git-to-supabase-one-tour/` 的说明稿;同步进 Supabase 后,与其它条目一样由站点读取。*
❦走一遍:这篇博客是怎么从文件夹跑到你屏幕上的— 4 —
// comments
0 threads登录 后可留言、回复。
- 还没有留言,来做第一个。