跳转到正文

memos 随记自动同步博客短文 #ai

memos 是我的随手记,博客短文区是 mars-blog 的时间线。想让两者打通:在 memos 里发一条公开随记,1-2 分钟后短文自动出现在博客上;删掉随记,短文也跟着消失。靠 memos 的 webhook 和 Cloudflare Pages Function 实现,中间踩了两个文档没写的坑,记下来。

链路:memos 到一篇短文

memos(ECS 上的 docker 容器)在随记创建、更新、删除时会 POST 到配置的 webhook URL。把这个 URL 指向博客的 /api/memos-hook(Pages Function),函数校验合法后把内容写成 notes/memos-<id>.md 放进 R2,再触发 Deploy Hook 重建静态站,短文就上线了。

数据流全貌:

memos 发公开随记
  → POST webhook(带 URL token)
  → /api/memos-hook(Pages Function)
  → 校验 token + 过滤非公开
  → 写 notes/memos-<id>.md 到 R2
  → 触发 Deploy Hook
  → astro build 重新上线

坑一:0.29.1 的 webhook 不签名

官方文档写的是 Standard Webhooks 规范:投递带 webhook-idwebhook-timestampwebhook-signature 三个头,接收方用创建 webhook 时生成的 whsec_ 密钥验签。第一版代码就按这个写——结果 memos 投递的请求全部被我拒掉,日志里全是 Failed to dispatch webhook ... status code: 401

抓包(临时把 webhook URL 指到 webhook.site)发现真相:memos 0.29.1 的投递只有 content-type 一个头,没有任何签名。签名机制是 0.29 之后的新版本才加的,我这个镜像的版本刚好处在新旧之间:webhook 能发,但发的是裸请求。文档描述的是新版行为,和 0.29.1 对不上。

坑二:payload 的字段和直觉不一样

抓到的请求体长这样:

{
  "url": "...",
  "activityType": "memos.memo.created",
  "creator": "users/wujinxing718",
  "memo": {
    "name": "memos/DMp3VSztSZCkhWuB6EQzTo",
    "create_time": { "seconds": 1787739699 },
    "content": "你好,这是通过 memos webhook 发来的一条随记",
    "visibility": 3
  }
}

三个地方和直觉不同:

  • visibility数字枚举:3 才是 PUBLIC,1 是 PRIVATE,2 是 PROTECTED。不能用字符串 "PUBLIC" 去比
  • create_time{seconds} 对象,不是 ISO 字符串,要 new Date(seconds * 1000) 转换
  • memo.name 形如 memos/DMp3VSzt...,id 是 UUID 不是自增数字,slug 直接取 / 后面的部分就行

鉴权:URL token 兜底

0.29.1 不签名,webhook 端点又必须鉴权(否则任何人 POST 都能往博客塞内容)。方案:memos 的 webhook URL 里带一个 query 参数:

https://blog.wujinxing.site/api/memos-hook?token=whsec_xxx

函数先校验 token 是否等于环境变量里的 MEMOS_WEBHOOK_SECRET;同时保留新版 HMAC 签名校验作为第二通道——将来升级 memos 后签名请求也能过,两条路并存不用返工。

幂等:slug 就是 memo id

短文文件名用 notes/memos-<id>.md,id 来自 memo.name。这样天然幂等:

  • 同一个 memo 反复投递、编辑覆盖写同一个文件,不会刷出重复短文
  • memos.memo.deleted 事件到达时删同一个文件,随记和短文一一对应
  • 只在 visibility = 3(PUBLIC) 时写,PRIVATE/PROTECTED 的随记不会漏到公开博客

验证

改动后验证分两步。第一步模拟:用 webhook.site 捕获一条真实投递,确认 payload 结构和请求头;第二步实测:在 memos 发一条公开随记,等重建完成,博客短文区出现对应内容,R2 里也多出 notes/memos-<id>.md

实测抓包比读文档快得多。memos 版本跨度大、行为差异明显,遇到 401 别急着怀疑鉴权逻辑,先确认投递的真实长相。