post / 2026.08.18

LeanCloud要停了,我把Waline评论搬到了MongoDB Atlas

LeanCloud要停了,我把Waline评论搬到了MongoDB Atlas

我的博客评论系统是 Hugo + Waline,数据一直托管在 LeanCloud。几个月前,LeanCloud发邮件告诉我“快要停服了”,但我转头就把这事忘了。最近翻阅友链,辛未羊在评论区说waline托管平台要关了,我才想起来这码事。

LeanCloud、Vercel托管、Waline评论管理,只有建站的时候才配置过。有些东西平时感觉不到,真要搬起来还挺麻烦。

起因

Waline 本身只是一个评论服务端,存储后端其实可以换。LeanCloud 只是其中一个选项。

官方支持的后端大概有:

  • MongoDB
  • MySQL / TiDB
  • PostgreSQL
  • SQLite
  • 腾讯云 CloudBase
  • GitHub

一开始我也纠结过要不要趁机换个评论系统,比如 Giscus、Twikoo、Artalk 什么的。但想了想,换评论系统意味着历史评论、回复关系、用户数据都要重新处理,迁移成本太高了。而且我只需要把数据从 LeanCloud 挪走,没必要连 Waline 一起换掉。

所以最后决定:继续用 Waline,只换数据库。

为什么选 MongoDB

MongoDB 最大的优势是:LeanCloud 的 BSON 备份可以直接恢复进 MongoDB。

也就是说,不用把评论一条条转成 SQL,不用重新设计表结构,Comment、Users、Counter 这些数据基本能原样搬过去。

而如果选 MySQL/PostgreSQL,虽然 Waline 官方提供了转换工具,但主要处理 Comment,Users、Counter、Tag 这些附加数据往往还得自己写脚本。

所以我的选择很简单:

哪个最省事、最不容易丢数据,就选哪个。

导出数据

LeanCloud 控制台里可以导出两种东西:

  • JSON 格式:按 Class 导出,适合转换后导入 SQL 系数据库
  • BSON 格式:数据库备份,适合直接导入 MongoDB

我一开始导了 JSONL,后来发现还能导 BSON,果断又导了一份 BSON。官方说BSON导出更快,但实际上官网下载有问题,等半天还显示导出中,邮件发来的倒是可以直接下载了。

最后拿到手的文件是:

Comment.bson.gz
Users.bson.gz
Counter.bson.gz

评论 139 条,访问量 351 条,用户 7 个。不多,但也不少。

导入过程:并没有想象中顺利

我本来以为 BSON 导出来之后,直接 mongorestore 就完事了。

结果发现一个坑:

Comment 的 _id 不全是 ObjectId。

有一部分是 "187"、"186" 这种普通字符串,另一部分才是 MongoDB 的 ObjectId。而评论之间的 pid、rid 又互相引用这些旧 ID。

如果直接导入,Waline 按 objectId 查评论时,字符串 ID 不是合法 ObjectId,回复层级就会坏掉。

所以最后写了个小脚本:

  1. 给旧的字符串 ID 生成新的 ObjectId;
  2. 建立旧 ID → 新 ID 的映射;
  3. 同步改写每条评论的 pid、rid;
  4. 再导入 MongoDB。

导入完成后还检查了一遍:

  • 所有 pid / rid 都能找到对应评论;
  • 所有 user_id 都能找到对应用户;
  • 评论、用户、访问量数量都一致。

Vercel 的连环坑

第一个坑:ERR_REQUIRE_ESM

部署到 Vercel 后,一访问就 500:

Error [ERR_REQUIRE_ESM]: require() of ES Module
/var/task/node_modules/@exodus/bytes/encoding-lite.js

原因是 Waline 依赖的 jsdom@29 底层用了 ESM-only 的 @exodus/bytes,而 Vercel 的 Node 运行时不支持直接 require() 它。

一开始我以为升级 Node.js 版本到 24.x 就行,结果没用。

只覆盖 html-encoding-sniffer 也不行,因为 whatwg-url 也 require 了同一个 ESM 包。

最后最有效的办法是:把 jsdom 降到 26.1.0,整个依赖链就回到 CommonJS 了。

{
  "overrides": {
    "jsdom": "26.1.0",
    "html-encoding-sniffer": "4.0.0"
  }
}

第二个坑:MathJax 字体文件丢失

ESM 问题解决后,又变成:

Cannot find module
'/var/task/node_modules/@mathjax/mathjax-newcm-font/mjs/chtml/default.js'

原因是 Vercel 打包 Serverless 函数时,自动文件追踪漏掉了 MathJax 的字体文件。

解决方法是在 vercel.json 里显式把字体文件包含进去:

"config": {
  "includeFiles": [
    "node_modules/@mathjax/mathjax-newcm-font/**/*",
    "node_modules/mhchemparser/**/*",
    "node_modules/ip2region/data/**"
  ]
}

第三个坑:登录管理后台显示账号密码错误

服务终于能打开了,但登录管理后台时一直提示“账号密码错误”。

我一度以为迁移把密码弄坏了,甚至准备直接重置密码。

后来我用接口测了一下:

POST /token

返回值并不是密码错误,而是:

{"errno":500,"errmsg":"ResourceRequest timed out"}

这是 MongoDB 连接超时。

原因很简单:MongoDB Atlas 的白名单里只加了我本机的 IP,但 Waline 跑在 Vercel 的 Serverless 环境里,Vercel 的服务器 IP 是动态的,根本连不上数据库。

解决办法也简单:在 Atlas Network Access 里加上:

0.0.0.0/0

允许所有 IP 访问。数据库本身有用户名密码保护,个人博客这样用问题不大。

最终结果

折腾了一晚上,终于跑通了。

现在的架构是:

Hugo 博客
   ↓
Waline(Vercel Serverless)
   ↓
MongoDB Atlas

历史评论、回复层级、访问量、用户数据都还在。

Waline 环境变量大概是这样的:

MONGO_DB=waline
MONGO_USER=xxx
MONGO_PASSWORD=xxx
MONGO_HOST=["cluster0-shard-00-00.xxx.mongodb.net","cluster0-shard-00-01.xxx.mongodb.net","cluster0-shard-00-02.xxx.mongodb.net"]
MONGO_PORT=[27017,27017,27017]
MONGO_REPLICASET=atlas-xxx-shard-0
MONGO_AUTHSOURCE=admin
MONGO_OPT_SSL=true

一些感受

这次迁移本身不难,难的是那些“看起来毫不相关”的连锁坑:

  • Node 运行时版本;
  • 依赖包从 CJS 变 ESM;
  • Vercel 打包漏文件;
  • 数据库白名单没放行 Serverless。

总结:

  1. 先备份,再折腾;
  2. 能用 BSON 直导就不要手动转 CSV;
  3. Serverless 连数据库,第一时间检查 IP 白名单;
  4. 遇到登录类报错,先看接口真实返回,别被前端提示误导。

记录一下,下次再遇到类似迁移,至少知道该先看哪里。

本文作者: 孤筝

本文标题: LeanCloud要停了,我把Waline评论搬到了MongoDB Atlas

本文发布时间: 2026-08-18

本文更新时间: 2026-08-18

本文版权: CC BY-NC-SA 4.0

何时一樽酒,重与细论文。

搜索