前言

目前我使用的评论是 Waline,总体来说 体验还算可以。但是在使用过程中也遇到过一些问题,比如加上邮件推送后, 评论的速度会变的很慢。

研究过后发现 Waline 貌似是在评论时 直接进行发送邮件的,同步进行发信便会导致评论耗时较长,很影响用户体验,有时评论需要耗时几秒甚至超时。

Waline 应该是使用的 ThinkJs,理论上可以通过异步函数来进行异步发信,不清楚为什么要同步发信。由于我对于 ThinkJs 并没有太深入了解,直接修改源码也可能造成部署麻烦,于是我尝试寻找另外的方式来实现异步发信,在研究过后, 发现 Waline 可以设置一个 Webhook 地址,在评论时会自动向该地址发送一个 POST 请求。

其 POST 内容大致为:

评论文章时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"type": "new_comment",
"data": {
"comment": {
"link": "https://blog.example.com",
"mail": "email@example.com",
"nick": "xcsoft",
"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.115 Safari/537.36",
"url": "/",
"comment": "test",
"ip": "127.0.0.1",
"insertedAt": "2022-07-09T13:46:52.085Z",
"status": "approved",
"objectId": 1,
"rawComment": "test"
}
}
}

回复别人评论时:

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
{
"type": "new_comment",
"data": {
"comment": {
"link": "https://blog.example.com",
"mail": "email@example.com",
"nick": "xcsoft",
"pid": 1,
"rid": 1,
"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.115 Safari/537.36",
"url": "/",
"comment": "[@xcsoft](#1): reply",
"ip": "127.0.0.1",
"insertedAt": "2022-07-09T13:47:53.526Z",
"user_id": 1,
"status": "approved",
"objectId": 2,
"rawComment": "reply"
},
"reply": {
"user_id": null,
"comment": "test",
"insertedAt": "2022-07-09 21:46:52",
"ip": "127.0.0.1",
"link": "https://blog.example.com",
"mail": "email@example.com",
"nick": "xcsoft",
"rid": null,
"pid": null,
"sticky": null,
"status": "approved",
"like": null,
"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.115 Safari/537.36",
"url": "/",
"createdAt": "datetime('now', 'localtime')",
"updatedAt": "datetime('now', 'localtime')",
"objectId": 1
}
}
}

实现

了解了 Webhook 内容后, 便打算使用 golang-gin 开发服务端, 得益于 Golang 的协程 异步发信还是很方便的。

我这里直接通过 gin 提供的 ShouldBindJSON来解析 Waline 发送的 json, 其结构体类似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type CommentStruct struct {
Type string `json:"type"`
Data struct {
Comment struct {
Nick string `json:"nick"`
Mail string `json:"mail"`
Url string `json:"url"`
Comment string `json:"comment"`
RawComment string `json:"rawComment"`
Ip string `json:"ip"`
InsertedAt string `json:"insertedAt"`
Status string `json:"status"`
} `json:"comment"`
Reply struct {
Nick string `json:"nick"`
Mail string `json:"mail"`
Comment string `json:"comment"`
InsertedAt string `json:"insertedAt"`
Status string `json:"status"`
} `json:"reply"`
} `json:"data"`
}

其中 Type 始终为 new_comment, Data.Comment 为评论信息, Data.Reply 为被回复信息(可能为空)

邮件采用的是 gomail 库, 由于大多数服务商默认是有发送频率限制的, 于是我通过 Redis 实现了一个消息队列, 用来控制发信频率。

发信效果

reply

模版及图片 来源于网络

开源

源码以 Apache 2.0 协议开源于 soxft/waline-async-mail

基本部署方式可以在 wiki 找到