Obsidian 笔记发布 — Quartz + CF Pages + Zero Trust

TL;DR

Obsidian 笔记通过 Quartz 生成静态网站,部署到 Cloudflare Pages,用 Zero Trust Access 做邮箱白名单验证。GitHub 私有仓库存储源文件,改完笔记跑 bash sync.sh 一键同步。老师浏览器打开 notes.getgrowth.app,邮箱验证后直接看笔记。

现状:✅ 已跑通,notes.getgrowth.app 正常访问。


🎯 背景:为什么需要这个

  • 需求:把 Obsidian 笔记分享给老师看,但不能把 50+ 个 .md 文件直接发过去——太多、不好传、没有导航和搜索
  • 约束:笔记不能公开,只能特定人能看;不能花钱(CF Pages 免费额度内搞定)
  • 被否决的方案:Obsidian Publish($8/月,每次发布手动操作)、直接发 .md 文件(老师没法浏览双链和 Mermaid 图)

✅ 前置条件

跑这篇笔记之前,你手上应该已经有:

  • Obsidian vault(笔记已经在里面)
  • GitHub 账号 + 已安装 gh CLI 并登录
  • Cloudflare 托管域名(getgrowth.app
  • Node.js ≥ v20(本机 v24 验证通过)
  • WSL2 环境(Windows)

🏗️ 最终架构

Obsidian 写笔记 (本地 vault)
    │  bash sync.sh
    ▼
rsync 复制到 ~/quartz-site/content/
    │  npx quartz build
    ▼
生成 public/ (HTML)
    │  git push
    ▼
GitHub 私有仓库 (miaoQ777/knowledge-base)
    │  CF Pages 检测到 push,自动构建
    ▼
Cloudflare Pages (全球 CDN)
    │  Zero Trust Access 拦截
    ▼
邮箱验证页 → 白名单通过 → notes.getgrowth.app

和”一开始想的软链接方案”比,省了 CF 构建时找不到本地 vault 的坑(404)。笔记真实存入 Git 仓库,CF 能正常克隆和构建。


🛠️ 部署步骤

关于这部分的阅读方式

每一步先给”抄了就能跑”的命令,命令下面折叠的 Callout 是详细解释——不想看原理的话,跳过折叠框直接抄下一条命令即可。

第 1 步:克隆 Quartz v4

cd ~
git clone --branch v4 --depth 1 https://github.com/jackyzha0/quartz.git quartz-site
cd quartz-site
npm install

第 2 步:配置 quartz.config.ts

编辑 ~/quartz-site/quartz.config.ts,改以下字段:

pageTitle: "Growth App 知识库",
locale: "zh-CN",
baseUrl: "notes.getgrowth.app",
ignorePatterns: ["private", "templates", ".obsidian", "账号", "*.excalidraw"],
analytics: null,

第 3 步:初始化 content 目录

cd ~/quartz-site
mkdir -p content

先不用 rsync,等写完 sync.sh 再同步。先在 vault 根目录创建首页:

/mnt/e/KnowledgeBase/index.md

---
title: Growth App 知识库
---
 
# Growth App 主权架构知识库
 
欢迎。
 
## 入口
- [[主权架构/最新架构]] — 当前基础设施架构
- [[press/维护]] — Press 日常运维
 
## 目录
- **主权架构** — 架构设计与演化
- **press** — Frappe Press 部署与运维
- **Cloudflare** — CF 各项功能

第 4 步:创建同步脚本

~/quartz-site/sync.sh

#!/bin/bash
set -e
source ~/.nvm/nvm.sh
nvm use 24
 
echo "=== 同步笔记 ==="
rsync -a \
  --exclude='.obsidian/' --exclude='.git/' \
  --exclude='*.excalidraw' --exclude='账号.md' --exclude='*.canvas' \
  /mnt/e/KnowledgeBase/ content/
 
echo "=== 构建 ==="
npx quartz build
 
echo "=== 推送 ==="
git add -A
git commit -m "sync: $(date +%Y-%m-%d)" || echo "无新内容"
git push
 
echo "=== 完成: https://notes.getgrowth.app ==="
chmod +x ~/quartz-site/sync.sh
bash ~/quartz-site/sync.sh

第 5 步:创建 GitHub 私有仓库并推送

cd ~/quartz-site
gh repo create miaoQ777/knowledge-base --private --source=. --remote=origin --push

第 6 步:Cloudflare Pages 连接 GitHub

  1. dash.cloudflare.comWorkers & PagesCreatePagesConnect to Git
  2. 授权 GitHub → 选 miaoQ777/knowledge-base

构建设置:

字段
Production branchmain
Build commandnpx quartz build
Build output directorypublic

环境变量:

变量
NODE_VERSION22

第 7 步:Cloudflare Zero Trust 邮箱验证

  1. Cloudflare Dashboard → Zero TrustAccessApplicationsAdd an application
  2. Self-hosted(中文可能叫”私人目的地”)
  3. 填 subdomain: notes,domain: getgrowth.app,Path 留空
  4. 点 Create → 进入刚创建的应用 → Policies 标签 → Create new policy
  5. Policy name: Allowed,Action: Allow
  6. Add Rule → Include → Emails → 填老师和自己的邮箱
  7. 所有开关(MFA、Purpose justification、Temporary auth)全关着不动
  8. Save policy

第 8 步:绑自定义域名

Pages 项目 → Custom domainsSet up a custom domain → 填 notes.getgrowth.appContinueActivate domain


↩️ 回滚 / 卸载

  • 停止网站:CF Pages → 项目 Settings → Delete project(源文件和 GitHub 仓库不受影响)
  • 去掉邮箱验证:Zero Trust → Access → Applications → 删除 KnowledgeBase 应用
  • 清理本机rm -rf ~/quartz-site
  • 清理 GitHubgh repo delete miaoQ777/knowledge-base
  • 回滚后验证notes.getgrowth.app 不再解析或返回 CF 错误页

🔍 验证方法

# 1. 本地构建成功
cd ~/quartz-site && npx quartz build
# → Done processing 54 files in Xs
 
# 2. 网站可访问
curl -s -o /dev/null -w "%{http_code}" https://notes.getgrowth.app
# → 不应返回 200(被 Zero Trust 拦截,返回 CF 登录页,状态码 302 或 403)
 
# 3. 邮箱验证后可访问(浏览器测试)
# 打开 https://notes.getgrowth.app → 输入邮箱 → 收到验证码 → 进入笔记首页

如果 curl 返回 404 → CF Pages 构建失败或 content 目录为空(见故障排查 #1)。


🧠 涉及到的代码逻辑

文件做了什么
~/quartz-site/quartz.config.tsQuartz 配置:标题、语言、排除目录、域名
~/quartz-site/sync.sh一键同步脚本:rsync → build → git push
~/quartz-site/.gitignore排除 node_modules、public、.quartz 不进仓库
/mnt/e/KnowledgeBase/index.md网站首页

流程简述

  1. 用户在 Obsidian 写笔记 → vault 文件更新
  2. bash sync.sh → rsync 复制到 content/(排除敏感文件)
  3. npx quartz build → Markdown 编译为 public/ 目录的 HTML
  4. git push → 推到 GitHub 私有仓库
  5. CF Pages 检测到 push → 自动 clone → npx quartz build → 部署到 CDN
  6. 用户访问 notes.getgrowth.app → Zero Trust 拦截 → 邮箱验证 → 放行

关键细节(如果只说一件事):CF Pages 构建环境没有你的本地 vault。软链接方案在 CF 上会 404,必须把笔记真实文件存入仓库。


💡 总结经验

  • 这次最大的教训:软链接在本地能工作,在 CI/CD 环境里就是空目录。先本地验证再推送到远程构建的步骤不能省
  • 如果再来一次,我会先测试 CF Pages 的构建环境能不能访问我的文件,而不是直接把软链接推上去然后看到 404
  • 这个问题的本质其实是”构建环境和开发环境不一致”,不是 Quartz 或 CF 的 bug
  • 以后排查类似问题,第一时间应该看的三个东西:1) CF Pages 构建日志 2) content 目录是否为空 3) public/ 是否生成成功

🕳️ 踩过的坑 & 被否决的方案


🩺 故障排查决策树

症状 1:notes.getgrowth.app 返回 404
  → 查 CF Pages 构建日志(Dashboard → Pages → knowledge-base → Deployments)
    → 如果是 "content/index.md not found" → content 目录是空的,检查 sync.sh 是否成功运行
    → 如果是构建命令失败 → 检查 quartz.config.ts 语法

症状 2:sync.sh 报错
  → npx quartz build 失败 → 检查 Node 版本(需要 ≥20)
  → rsync 报权限错误 → 检查 /mnt/e/KnowledgeBase 是否可访问
  → git push 报认证错误 → 运行 gh auth status

症状 3:邮箱验证后页面白屏
  → 浏览器 F12 → Network 标签 → 看哪个资源加载失败
    → CSS/JS 404 → CF Pages 构建不完整,重新 push 触发构建
    → 所有资源正常但页面空白 → quartz.config.ts 的 baseUrl 配置错误

⚠️ 已知限制 / 副作用

  • 每次改完笔记要手动跑 sync.sh。不像 Obsidian Publish 一键发布。但一行命令的成本可以接受
  • Excalidraw 文件被排除*.excalidraw),如果以后想发布 Excalidraw 图,需要先导出 PNG 再嵌入笔记
  • LaTeX 数学公式里不能有中文。中文出现在 $...$ 里会触发警告(不影响构建,但渲染可能异常)
  • GitHub 私有仓库源文件对平台运维人员理论上可见。真正的商业机密(密钥、合同、客户数据)不建议放
  • CF Zero Trust 保护的是域名访问层。如果有人在 CDN 节点层面直接访问构建产物,理论上能绕过邮箱验证

🚨 监控 / 告警

  • 健康检查curl -s -o /dev/null -w "%{http_code}" https://notes.getgrowth.app → 返回 302/403 说明 Zero Trust 在线;返回 200 说明 Zero Trust 掉了(立刻查)
  • CF Pages 构建失败:GitHub push 后等 1-2 分钟,去 CF Dashboard → Pages → knowledge-base → Deployments 看状态。CF 不会主动通知你构建失败
  • 如果彻底挂了:CF 或 GitHub 宕机(概率极低)。恢复后 push 一次即可触发重新构建

🔒 安全 & 成本速查

  • 密钥/Token:GitHub OAuth Token(gh auth 管理),CF Deploy Token(自动生成,Dashboard 可见)。权限:GitHub→仓库读写,CF→Pages 部署
  • 谁能访问:GitHub 仓库:你 + collaborator;网站:Zero Trust 邮箱白名单
  • 月度成本:$0(CF Pages 免费:无限站点/请求/带宽,每月 500 次构建;GitHub 免费:无限私有仓库;Zero Trust:50 用户免费)
  • 如果要下线:CF Pages → 删项目;Zero Trust → 删 Access 应用;GitHub → 删仓库

📌 待办 / 未来优化方向

  • 给 sync.sh 加自动触发:Obsidian 保存文件时自动同步(用 inotifywait 监听 vault 目录变化)
  • 考虑在笔记 frontmatter 里加 publish: true/false 控制是否发布(Quartz v4 的 RemoveDrafts 插件支持)
  • 老师如果想编辑笔记:给他 GitHub collaborator 权限 + 装 Obsidian,克隆仓库后在本地 Obsidian 打开

📋 速查命令合集(复制专用,无解释)

# 日常发布笔记
bash ~/quartz-site/sync.sh
 
# 本地预览(http://localhost:8080)
source ~/.nvm/nvm.sh && nvm use 24 && cd ~/quartz-site && npx quartz build --serve
 
# 查看构建日志
gh browse -R miaoQ777/knowledge-base
 
# 检查网站状态
curl -s -o /dev/null -w "%{http_code}" https://notes.getgrowth.app

📝 更新日志

日期改动原因
2026-07-13首次创建,全流程跑通