跳至主内容

FNS(fast-note-sync)+agent自动发布文章

· 6 分钟阅读 · …

因为我现在的博客是静态博客,不需要后端,所以发布文章就比较麻烦,每次都需要本地写了推送GitHub。一些无头cms的写作体验在手机上也不太好,再加上我的思维比较跳跃,很多时候想的东西只会停留在脑海,无法落地。于是我大部分的写作都无法坚持下来,多数是因为头脑一热写一下。而且写作的设备也不固定,所以我一直在寻找一个多端可以让我拿起来随手记了可以发布到博客的方法。

为什么不使用动态博客?

答:带后台存储有一定安全风险(曾有位学弟的博客被人sql注入后删库),并且对我来说有点臃肿。

最终思路:obsidian写作——FNS同步到nas——agent通过FNS读取文章——脚本上传文章图片到R2(如果有图片)——agent写入一份发布版文章——我审阅发布版文章并填写文章属性

整体链路

┌─────────────────────────────────────────────────────────────────┐
│ 写作层                                                           │
│   Obsidian (Mac) ─┐                                             │
│   Obsidian (iPhone)├──同步──▶ FNS 服务端(飞牛 NAS,REST API)    │
│   FNS 网页面板 ────┘      正文 Markdown + 附件原图                │
└───────────────────────────┬─────────────────────────────────────┘
                            │  agent 调用 note2blog(curl 访问 API)
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│ 阶段 1:生成发布版(--fns-copy,不碰博客仓库)                      │
│   1. 取正文 + 下载附件(保留服务端 mtime)                         │
│   2. 位图 → WebP(有损/无损各编一份取小者)                        │
│   3. 上传 R2,key = photo/<slug>/<年月>-<slug>-<日期>-<时分秒>.webp│
│   4. 改写正文:![[图]] → R2 外链;Obsidian 语法降级;MDX 转义      │
│   5. 生成 frontmatter(title/date/slug/tags/excerpt/...)        │
│   6. 产出「原名(发布版).md」写回 FNS 同目录                       │
└───────────────────────────┬─────────────────────────────────────┘
                            │  我在 Obsidian 里补审 featured / bgm 等
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│ 阶段 2:发布 + 归档(--fns-publish --git-push --archive)          │
│   1. 读 FNS 发布版,校验 frontmatter 必备字段                      │
│   2. 写入博客仓库 src/content/posts/<slug>.mdx                    │
│   3. git commit & push(中文 Conventional Commits)               │
│   4. 归档:原稿 + 发布版 + 原图 移入 归档/<日期>/<slug>/,更新 INDEX │
└───────────────────────────┬─────────────────────────────────────┘
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│ 上线层                                                           │
│   GitHub 仓库 ──push 触发──▶ Cloudflare Pages 构建静态站           │
│                (另有每日定时 Action 重建,刷新构建期数据)          │
│                                      │                           │
│                                      ▼                           │
│                            snarkm.com 上线                        │
└─────────────────────────────────────────────────────────────────┘

为什么是 FNS 做中转

在众多笔记软件中还是更喜欢obsidian,并且有FNS这个项目实现了多端同步问题。解决了我多设备obsidian同步的大难题。感兴趣的可以去试一试,项目地址:

https://github.com/haierkeys/fast-note-sync-service

FNS 是部署在局域网 NAS 上的笔记同步服务,三端入口的内容最终都同步到它上面,而且它自己提供 REST API——读笔记正文、列附件、下载附件都是现成接口。这样 agent 不需要碰任何一端的本地文件,直接从服务端取数。并且也保证我本地和FNS双向同步。

阶段 1:从笔记到发布版

它做四件事,全程不写博客仓库:

取数。 FNS 侧只用到三个接口:/note 取 Markdown 原文,/files 分页列出全库附件,/file 按路径下载字节。Obsidian 粘贴的图片引用只有文件名没有路径,所以要先把全库附件清单拉下来按 basename 反查;找不到就警告并保留原引用,不静默吞图。下载时保留服务端 mtime——图片 key 里的时分秒取自它,用「下载时刻」的话每跑一次都是一组新 key,R2 里会堆满重复对象。

压图。 所有位图转 WebP:宽度超过 1600 等比缩小,然后有损(q82)和无损各编一份、取体积小的那个。纯文字截图里色块和重复字形多,WebP 无损的 LZ 压缩极强,常常只有有损的八分之一;画面带照片或渐变时则有损明显更小。多编一次只发生在首次导入,之后命中本地指纹缓存(按「内容 + 上传去向」记的 sha256)就直接复用。

上传 R2。 压好的图传到 Cloudflare R2 的公开桶,key 按 photo/<slug>/<年月>-<slug>-<日期>-<时分秒>.webp 生成:年月、日期取文章日期,时分秒取源图 mtime——这两个输入对同一份文件是稳定的,所以同一篇重导得到同一个 key,重复跑只是覆盖、不会堆重复对象。上传走 wrangler(或 AWS S3 兼容端点),成功后按「公开域名 + key」拼出 HTTPS 外链回填进正文。本地另记一份内容指纹,没改过的图直接复用上次的结果跳过上传。

改写正文。 博客是 Astro + MDX,比普通 Markdown 挑:裸 { } 会被当表达式、裸 < 会被当 JSX,构建直接失败,所以统一转义——但代码块和行内代码先用占位符保护起来最后还原。Obsidian 语法逐个降级:callout 变普通引用块、==高亮== 转 <mark>、%%注释%% 删除、[[双链]] 转纯文本(博客的 [[锚点]] 是图谱连线语法,语义冲突不能硬转)、带宽度的图写成带 loading="lazy" 的 <img>。

生成 frontmatter。 标题、日期、摘要、标签、slug、draft 全部自动生成,正文第一块是图片时自动关掉首字下沉。产出的「发布版」写回 FNS 同目录,我可以在 Obsidian 里直接补 featured、bgm、graph 这些不好自动推导的字段。

阶段 2:发布与归档

以 FNS 里的发布版为唯一真源:校验 frontmatter 必备字段后写入,最后交给agent提交推送仓库,然后归档——原稿、发布版、用到的原图一起移入 归档/<文章日期>/<slug>/,并更新仓库 INDEX.md 把这篇从「待发布」挪到「已发布」。这里未发布的文章就都放在根目录的原因是因为粘贴的照片默认也是放在根目录,索性就一起放这里了。

总结

整个流程被我做成了一个skill,配合脚本。我只需要再obsidian中写好,在微信或者imessage给我的agent说一句生成发布版文章,我在obsidian审阅并填写属性,就可以发布到了博客。整个流程体验还是不错,无需借助一些无头CMS大致实现一个动态博客的效果。

评论

标签

/** * 文章图片点击放大(lightbox)。 * - 作用于 .prose 内的图片;点击在原图位置原地放大到视口内最大可显示尺寸 * - 再点任意处 / Esc 关闭;滚轮与拖拽平移查看细节 * - 纯展示层增强:无 JS 或图片加载失败时点击无副作用,不影响原排版 */