逃离 Leancloud:博客评论系统彻底迁移至 Waline + Vercel + Supabase 填坑指南

随着 Leancloud 越来越收紧外部服务以及随时可能降临的停服预警,如果博客依旧死守基于 Leancloud 的 Valine 等老派评论系统,无异于将最珍贵的读者评论和交互数据交托于悬崖之上。

为了实现永久数据平权以及一套现代化的交互系统,我决定把本站的评论阵地进行一次“大手术”:彻底迁往 Waline。并且,它的后端计算我交给了免费劳模 Vercel,而最核心的数据仓储,则放置到了当前极具前景、被我完全掌控密码的 PostgreSQL 无服务器云端——Supabase

如果你也正有此意,这篇避坑指南会替你省下起码三天的摸黑排雷时间。

一、方案全景设计

  • 前端:依然享受轻量化的 Waline(由于我的 Hugo FixIt 主题内置支持极好,基本算开箱即用)。
  • 后端计算引擎:借助 @waline/vercel 官方服务端框架,托管到 Vercel。
  • 数据老巢:放弃 Leancloud,采用目前生态最好的 PostgreSQL 阵营提供商 Supabase。不仅支持连接池,且可以轻松导出和备份物理关系型数据库图表。

下面正式开始最折磨人的干货部署避重就轻环节。


二、部署 Vercel 的终极环境变量(绝杀秘籍)

我们在部署 Vercel 环境的时候,最经常在连接 Supabase 上翻车。请永远不要尝试图省事用一条长长的 DATABASE_URL 直接拼接密码与特殊符号。那极其容易导致各种 URL 编解码解析报错,最终导致服务直接扔出 No valid storage found 的死亡五百错误。

老老实实把参数拆解成最纯净的 “五老星配置法”!并在 Vercel 的 Environment Variables 中逐个填好:

  • PG_HOST: 即你 Supabase 的主机连接池地址(例如 aws-0-xx.pooler.supabase.com
  • PG_PORT: 6543 (Supabase 推荐的稳定 Transaction pooler 端口)
  • PG_DB: 默认写 postgres
  • PG_USER: 你被分配的超长专属账号用户名。
  • PG_PASSWORD: 这是大头!最好在配置初将其专门修改为“无任何诸如逗号、感叹号、井号等特殊字符”的极长混合字母纯数字强密码。纯天然,防解析错误。

三、史上最搞笑的乌龙:关于 403 Forbidden 的自我阻断

为了防那些蹭域名的水军,Waline 提供了一个神兵利器叫:SECURE_DOMAINS。 在这里写上你的本站域名,就没人能在其他来源滥用你的评论框。你会很开心地写下:

SECURE_DOMAINS = blog.mulinux.com,localhost,127.0.0.1

结果坑来了: 当你部署完毕,洋洋得意地想在浏览器里直接打开后台大门 https://你的vercel后台域名.vercel.app/ui 去注册时…… 当场被拒之门外,页面冰冷地显示:{"errno":403,"errmsg":"Forbidden"}

原理: 因为你在白名单里只允许别人从博客页发请求,压根忘记把“这个后方控制中心自身的域名”给加上去!你在浏览器地址栏直达(来源为空),当场就被自己家的无情卫兵乱棍打出。

解法: 请记得必定要把那个属于 Vercel 的后端域名,也加到这串名单里!如: SECURE_DOMAINS = blog.你的博客域名.com, localhost, 你的后端控制台域名.vercel.app


四、至暗时刻:当数据库告诉你 relation “wl_comment” does not exist

如果你挺过了上面所有劫难,终于能在前台看到评论框了。但是只要你打一排字按一下“提交”,或者你费尽心机想加载后台时,左上角红字疯狂报警:

500: relation "wl_comment" does not exist

不要慌!它不是因为密码不对,它的意思是“连接已通!但我环顾四周一片荒凉,连一张名字叫‘评论’的老旧纸皮桌子都没找到”。

这就是 PostgreSQL 连接的特有难题:Vercel 的初始状态在连到全新的 Supabase 荒地时,极大可能无法自动触发初始建表引擎(自动开荒战车失灵了)

终极解决绝招:当一回土木工程师,手动砸坑! 我们需要把官方隐藏在深处的底座设计原稿扒出来,自己去一键建造。不要写那些乱七八糟没有 Sequence (序列) 的网传瞎掰代码。

操作如下:

  1. 登入 Supabase 你的项目数据库后台控制中心。
  2. 左边栏找到**「SQL Editor」**(那个写字敲代码的入口) -> New query
  3. 把底下这段真正的原味核心 SQL 完全复制并粘贴进黑板里,然后点击绿色的 Run 轰炸下去:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
CREATE SEQUENCE wl_comment_seq;

CREATE TABLE wl_comment (
  id int check (id > 0) NOT NULL DEFAULT NEXTVAL ('wl_comment_seq'),
  user_id int DEFAULT NULL,
  comment text,
  insertedAt timestamp(0) without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
  ip varchar(100) DEFAULT '',
  link varchar(255) DEFAULT NULL,
  mail varchar(255) DEFAULT NULL,
  nick varchar(255) DEFAULT NULL,
  pid int DEFAULT NULL,
  rid int DEFAULT NULL,
  sticky numeric DEFAULT NULL,
  status varchar(50) NOT NULL DEFAULT '',
  "like" int DEFAULT NULL,
  ua text,
  url varchar(255) DEFAULT NULL,
  createdAt timestamp(0) without time zone NULL DEFAULT CURRENT_TIMESTAMP,
  updatedAt timestamp(0) without time zone NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
) ;

CREATE SEQUENCE wl_counter_seq;

CREATE TABLE wl_counter (
  id int check (id > 0) NOT NULL DEFAULT NEXTVAL ('wl_counter_seq'),
  time int DEFAULT NULL,
  reaction0 int DEFAULT NULL,
  reaction1 int DEFAULT NULL,
  reaction2 int DEFAULT NULL,
  reaction3 int DEFAULT NULL,
  reaction4 int DEFAULT NULL,
  reaction5 int DEFAULT NULL,
  reaction6 int DEFAULT NULL,
  reaction7 int DEFAULT NULL,
  reaction8 int DEFAULT NULL,
  url varchar(255) NOT NULL DEFAULT '',
  createdAt timestamp(0) without time zone NULL DEFAULT CURRENT_TIMESTAMP,
  updatedAt timestamp(0) without time zone NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
) ;

CREATE SEQUENCE wl_users_seq;

CREATE TABLE wl_users (
  id int check (id > 0) NOT NULL DEFAULT NEXTVAL ('wl_users_seq'),
  display_name varchar(255) NOT NULL DEFAULT '',
  email varchar(255) NOT NULL DEFAULT '',
  password varchar(255) NOT NULL DEFAULT '',
  type varchar(50) NOT NULL DEFAULT '',
  label varchar(255) DEFAULT NULL,
  url varchar(255) DEFAULT NULL,
  avatar varchar(255) DEFAULT NULL,
  github varchar(255) DEFAULT NULL,
  twitter varchar(255) DEFAULT NULL,
  facebook varchar(255) DEFAULT NULL,
  google varchar(255) DEFAULT NULL,
  weibo varchar(255) DEFAULT NULL,
  qq varchar(255) DEFAULT NULL,
  oidc varchar(255) DEFAULT NULL,
  huawei varchar(255) DEFAULT NULL,
  "2fa" varchar(32) DEFAULT NULL,
  createdAt timestamp(0) without time zone NULL DEFAULT CURRENT_TIMESTAMP,
  updatedAt timestamp(0) without time zone NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
) ;

当你看到屏幕下方出现伟大的 Success. No rows returned(没报错数据且零输出)时,你的 wl_commentwl_userswl_counter 三大支柱就已经深深地打好了地基。不要理会旁边的红字 UNRESTRICTED 提示(无 Row Level Security),因为系统全靠 Vercel 防火墙去读入数据库,不需要强上公网拦截安全机制。

然后再回 /ui 管理员面板,你的账号就是这片处女地上的一号领主。

五、彩蛋:如何验证自己的“最高统治权”?

Waline 的安全机制非常明确:在这个系统中第一个成功注册账号的人,会被强行钦定为拥有所有生杀大权的“超级管理员”。后续无论任何人再找到注册链接,都只能被注册成普通游民。

如果你想在这个被你掌控的数据库中亲自确认一遍自己的王座,可以如此操作: 在 Supabase 的 SQL Editor 中运行这句最为简单的查询探测魔法:

1
2
3
4
5
6
SELECT 
    id, 
    display_name AS "昵称", 
    email AS "邮箱账户", 
    type AS "权柄身份"
FROM wl_users;

不出意外,黑板下方会哗的一下展出一张兵团花名册。你的大名和邮箱紧随其后,而在最关键那一列 权柄身份 里,一定会写着高高在上的那一串至尊代号:administrator。这就是纯技术路线带给站长的独属的硬核浪漫。

六、画龙点睛:接入 PushPlus,评论即时推送到微信

数据库搭好了、评论跑通了,但还缺最后一块——当有人在你博客留言时,你怎么第一时间知道?

答案是接入 PushPlus(推送加)。它免费、无需服务器,只需在 Waline 的 Vercel 项目里设置三个环境变量,读者一留言,你的微信就能收到推送通知。

第一步:获取 PushPlus Token

  1. 前往 pushplus.plus 用微信扫码登录
  2. 在首页复制你的专属 Token(一串 32 位字符)
  3. 用手机微信扫码关注**「推送加」**公众号(⚠️ 这步不能省,否则永远收不到消息)

第二步:在 Vercel 配置三个环境变量

进入 Vercel 控制台 → waline-backend-nine 项目 → Settings → Environment Variables,依次添加:

变量名 示例值 说明
PUSH_PLUS_KEY 你的Token PushPlus 首页复制的 Token
SITE_NAME mulinux 博客名称,显示在通知标题里
SITE_URL https://blog.mulinux.com 博客地址,点击通知可跳转

三个都保存后,点右上角 Redeploy 让配置生效。

第三步:验证

随便打开一篇博文,在 Waline 评论框里发一条测试评论,几秒之内微信就会收到来自「推送加」公众号的通知,内容包含评论者昵称、评论内容以及文章链接。

费用说明: PushPlus 免费用户每日可发送 200 次推送,个人博客日常评论量完全够用,无需付费。


结语

从配置完以上所有的步骤后返回文章下随意评论一条,响应极其丝滑,且同时附带极其完美的 PV 统计功能。终于将生命体征握在自己的手术刀下,祝愿大家早日在这场没有退路的自我保卫战里拿到满分通过。