Hexo Next 主题使用 Waline 评论系统

前言

本教程的内容虽然会持续更新,但一切内容以 Waline 官方文档 为准。

版本说明

软件版本描述
linuxCentOS 7.9
docker20.10.5
mysql5.7.26
node14.17.3
hexo5.4.0
next8.12.1
waline-admin0.18.0
waline-client2.5.1
waline-server1.18.5

Hexo 评论系统选择

Hexo 各类评论系统的对比可看这里,之前博客一直使用的评论系统是基于 Github Issue 的 Utterances。由于 Utterances 默认没有 CDN 加速,经常造成页面加载完成后无法正常显示 Utterances 的评论区,而且需用用户登录 Github 账号才能评论,因此打算更换博客的评论系统。国外的 Disqus、Hypercomments 暂时不考虑,国内访问被墙的概率很大。Gitment、Gitalk、Gitter 和 Utterances 一样,访问速度不太稳定,且登录 Github 才能评论,暂时也不考虑。这一波排除下来,剩下的方案只有 Valine、Isso、Waline 或者 自建评论系统。考虑到 Valine 依赖 Leancloud 第三方服务,且需要在 Leancloud 额外部署 Valine Admin 才能实现邮件通知与评论管理等功能,这样一来感觉也不靠谱,万一 Leancloud 以后退出商业市场竞争呢?综合考虑下来,最终选择了 Waline 评论系统,一款从 Valine 衍生的带后端评论系统,支持多种部署方式和数据存储方式,这样就可以省去 自建评论系统 的开发成本,同时也可以尽量少依赖第三方服务,增加日后扩展和维护的自由度。

Hexo 评论系统介绍

Valine 评论系统

Valine 是一款基于 Leancloud 的快速、简洁且高效的无后端评论系统,用户无需登录即可评论,目前已有 Hexo、Jekyll、Typecho、Hugo、Ghost 等博客程序在使用。由于 Valine 自身不支持邮件通知支持,因此诞生了 Valine Admin 开源项目;一个对 Valine 评论系统的拓展应用,可增强 Valine 的邮件通知功能;基于 Leancloud 的云引擎与云函数,主要实现评论邮件通知、评论管理、自定义邮件通知模板等功能,而且还可以提供邮件 通知博主@ 通知 的功能。

Waline 评论系统

Waline 一款从 Valine 衍生的带后端评论系统,可以将 Waline 等价成 With backend Valine,采用 Client/Server 架构并基于 NodeJS 开发。Valine 支持 MarkDown 语法、邮件通知、评论管理、多种部署方式多种数据存储方式

Waline
客户端脚本服务端部署数据存储
@waline/clientVercelLeanCloud
MiniValineCloudBaseCloudBase
DockerMongoDB
独立部署 MySQL
SQLite
PostgreSQL
Github

Waline 支持的功能:

  • 邮件通知
  • 微信通知
  • QQ 通知
  • Telegram 通知
  • Akismet 反垃圾评论
  • 文章统计
  • 多语言
  • 自定义语言支持
  • 登录支持
  • 评论管理
  • 评论删除
  • 其它数据库服务支持(已支持 LeanCloud, MySQL, MongoDB, SQLite, PostgreSQL)
  • 基于 IP 的评论发布频率限制
  • 基于关键词的评论过滤限制
  • IP 黑名单
  • 重复内容检测
  • CloudBase 腾讯云开发部署支持
  • 社交登录
  • AWS, GCP, Azure 部署支持
  • 置顶评论
  • 评论赞踩

Docker 部署 MySQL Server

使用 Docker 部署 MySQL,容器管理工具使用 Docker-Compose。

Doker 部署 MySQL

在下述的 Docker-Compose 配置里,指定了 MySQL 容器的静态 IP 地址与系统时区,yourPassword 为数据库密码,/usr/local/docker-volumes/mysql/* 是 MySQL 容器各个数据卷目录的路径

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
version: "3.5"

services:
mysql:
image: mysql:5.7.26
container_name: waline-mysql
restart: always
privileged: false
environment:
TZ: 'Asia/Shanghai'
MYSQL_ROOT_PASSWORD: yourPassword
ports:
- 3306:3306
networks:
waline-network:
ipv4_address: 172.23.0.3
volumes:
- '/usr/local/docker-volumes/mysql/conf:/etc/mysql/conf.d'
- '/usr/local/docker-volumes/mysql/data:/var/lib/mysql'
- '/usr/local/docker-volumes/mysql/log:/var/log/mysql'
command: --default-authentication-plugin=mysql_native_password

networks:
waline-network:
name: waline-network
driver: bridge
ipam:
config:
- subnet: 172.23.0.0/24

MySQL 数据库表初始化

创建并启动 MySQL 的 Docker 容器后,导入最新的 waline.sql 脚本来创建好 Waline Server 所需的数据库表,其中相关表的结构如下:

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
CREATE DATABASE waline DEFAULT CHARACTER SET utf8mb4;

CREATE TABLE `wl_Comment` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`comment` text,
`insertedAt` timestamp 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(11) DEFAULT NULL,
`rid` int(11) DEFAULT NULL,
`sticky` boolean DEFAULT NULL,
`status` varchar(50) NOT NULL DEFAULT '',
`like` int(11) DEFAULT NULL,
`ua` text,
`url` varchar(255) DEFAULT NULL,
`createdAt` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updatedAt` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `wl_Counter` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`time` int(11) DEFAULT NULL,
`url` varchar(255) NOT NULL DEFAULT '',
`createdAt` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updatedAt` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `wl_Users` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`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,
`2fa` varchar(32) DEFAULT NULL,
`createdAt` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updatedAt` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Docker 部署 Waline Server

使用 Docker 部署 Waline Server,数据存储方式选择 MySQL,容器管理工具使用 Docker-Compose。

构建 Waline Server 镜像

若不希望自己构建 Waline Server 的 Docker 镜像,可以直接使用 Waline 官方的 Docker 镜像,操作步骤如下:

1
2
3
4
5
6
7
8
# 拉取最新的代码
# git clone https://github.com/lizheming/waline.git

# 进入代码目录
# cd waline/packages/server/

# 构建镜像
# docker build -t lizheming/waline -f Dockerfile .

值得一提的是,官方提供的 Dockerfile 默认会使用最新的 Waline Server 代码来构建 Docker 镜像,若希望使用本地的 Waline Server 代码(经过更改的)来构建 Docker 镜像,需要自行更改 Dockerfile 的内容(如下所示);为了统一使用东八区时区,建议更改系统默认的时区。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# https://github.com/nodejs/LTS
FROM node:lts AS build
WORKDIR /app
ENV NODE_ENV production
RUN set -eux; \
# npm config set registry https://registry.npmmirror.com; \
npm install --production --silent @waline/vercel
# use local source code
RUN rm -rf /app/node_modules/@waline/vercel/src/*
COPY ./src/ /app/node_modules/@waline/vercel/src

FROM node:lts-buster-slim
WORKDIR /app
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
ENV TZ Asia/Shanghai
ENV NODE_ENV production
COPY --from=build /app .
EXPOSE 8360
CMD ["node", "node_modules/@waline/vercel/vanilla.js"]

提示

若构建 Waline Server 的 Docker 镜像时,一直卡在 NPM 安装模块的过程里,可以更改对应的 Dockerfile(如下所示),使用淘宝的 NPM 源来加速 NPM 模块下载

1
2
3
4
5
6
$ vim waline/packages/server/Dockerfile

...(省略)
npm config set registry https://registry.npmmirror.com; \
npm install --production --silent @waline/vercel
...(省略)

Docker 部署 Waline Server

在上面 MySQL 的 Docker-Compose 配置基础上,指定 Waline Server 容器的静态 IP 地址 与 Waline Server 启动时所需的环境变量

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
version: "3.5"

services:
mysql:
image: mysql:5.7.26
container_name: waline-mysql
restart: always
privileged: false
environment:
TZ: 'Asia/Shanghai'
MYSQL_ROOT_PASSWORD: yourPassword
ports:
- 3306:3306
networks:
waline-network:
ipv4_address: 172.23.0.3
volumes:
- '/usr/local/docker-volumes/mysql/conf:/etc/mysql/conf.d'
- '/usr/local/docker-volumes/mysql/data:/var/lib/mysql'
- '/usr/local/docker-volumes/mysql/log:/var/log/mysql'
command: --default-authentication-plugin=mysql_native_password

waline:
container_name: waline
image: lizheming/waline:latest
restart: always
privileged: false
depends_on:
- mysql
ports:
- 8360:8360
networks:
waline-network:
ipv4_address: 172.23.0.4
environment:
TZ: "Asia/Shanghai"
AKISMET_KEY: "false"
DISABLE_USERAGENT: "true"
SITE_NAME: "Your site name"
SECURE_DOMAINS: "example.cn"
AUTHOR_EMAIL: "example@qq.com"
SITE_URL: "https://www.example.cn"
MYSQL_HOST: 172.23.0.3
MYSQL_PORT: 3306
MYSQL_DB: waline
MYSQL_PREFIX: wl_
MYSQL_USER: root
MYSQL_PASSWORD: yourPassword
volumes:
- /usr/local/waline/data:/app/data

networks:
waline-network:
name: waline-network
driver: bridge
ipam:
config:
- subnet: 172.89.0.0/24

Waline Server 的环境变量

Waline Server 的 自身环境变量如下:

环境变量名称必填默认值备注
TZ 时区
SITE_URL 站点 URL
SITE_NAME 站点名称
AUTHOR_EMAIL 博主邮箱
SECURE_DOMAINS 安全域名配置,支持逗号分隔配置多个域名,配置后非该域名来源的请求会返回 403 状态码,不配置表示允许所有域名来源
IPQPS60 基于 IP 的评论发布频率限制,单位为秒。默认为 60 秒,设置为 0 不限制
DISABLE_USERAGENTfalse 是否隐藏评论者的 UA,默认为否
DISABLE_REGIONfalse 是否隐藏评论者的归属地,默认为否
DISABLE_AUTHOR_NOTIFYfalse 是否禁止新评论通知,默认为否
COMMENT_AUDIT 评论发布审核开关,默认为否,配置后建议在 Placehoder 上提供文案提示
AKISMET_KEYAkismet 反垃圾评论服务的 Key(默认开启,不用请设置为 false,关闭后可以加快评论提交的速度)
AVATAR_PROXYhttps://avatar.75cdn.workers.dev/ 头像的代理地址,设置 false 可以关闭代理
LOGIN 当设置为 LOGIN: 'force' 时,服务端会要求客户端必须登录才能评论;同时 Waline 客户端需要增加 login: force 的配置用于隐藏博客页面上的评论匿名输入框
GRAVATAR_STRhttps://seccdn.libravatar.org/avatar/{{mail|md5}}Gravatar 头像的地址,基于 Nunjucks 语法
OAUTH_URLhttps://user.75.teamOAuth 第三方登录服务地址,也可以使用 auth 自建
WEBHOOK 评论成功后会向 WEBHOOK 配置的地址发送一条 POST 请求
  • COMMENT_AUDIT:阅读源代码发现,环境变量中不配置 COMMENT_AUDIT,则默认关闭评论发布的审核功能,只要在环境变量中添加了该属性,无论属性值是什么,都会开启评论发布的审核功能

Waline Server 的 MySQL 环境变量如下:

环境变量名称必填默认值备注
MYSQL_HOST127.0.0.1MySQL 服务的地址
MYSQL_PORT3306MySQL 服务的端口
MYSQL_DBMySQL 数据库库名
MYSQL_USERMySQL 数据库的用户名
MYSQL_PASSWORDMySQL 数据库的密码
MYSQL_PREFIXwl_MySQL 数据表的表前缀
MYSQL_CHARSETutf8mb4MySQL 数据表的字符集

Waline Server 运行测试

分别创建并启动 MySQL 与 Waline Server 容器,然后浏览器访问 http://ip:port/ui/register,打开 Waline Server 的 Web 管理界面进行注册,第一个注册的用户会被 Waline Server 识别为系统管理员(博主),成功登录后的界面如下:

hexo-waline-ui

若 Waline Server 的 Web 管理界面无法正常访问,可以使用以下命令查看 Docker 容器的日志来定位问题

1
# docker logs -f --tail 20 waline

Waline Server 配置反向代理

Nginx 反向代理配置

若使用 Nginx 作为 Waline Server 的反向代理,可参考以下配置内容。

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
server {
listen 80;
listen 443 ssl;
server_name www.example.com

if ($server_port !~ 443){
rewrite ^(/.*)$ https://$host$1 permanent;
}

# SSL 证书
ssl_certificate /usr/local/nginx/cert/waline.example.com.crt;
ssl_certificate_key /usr/local/nginx/cert/waline.example.com.key;

# SSL 性能调优
ssl_session_timeout 10m;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4:HIGH:!MD5:!aNULL:!eNULL:!NULL:!DH:!EDH:!AESGCM;
add_header Strict-Transport-Security 'max-age=31536000';

location / {
# 反向代理
proxy_pass http://$server_name;
proxy_set_header Host $host:$server_port;
proxy_set_header X-NginX-Proxy true;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header REMOTE-HOST $remote_addr;

# 缓存
add_header X-Cache $upstream_cache_status;
add_header Cache-Control no-cache;
expires 12h;
}
}

Nginx 配置跨域访问

默认情况下,当在 Waline 服务端的环境变量里添加了 SITE_URL 属性,那么 Nginx 的反向代理不再需要配置跨域,因为 Waline Server 会自动将 SITE_URL 添加到允许跨域的名单里。若 Waline Server 自带的跨域配置不能满足要求,可以参考以下内容自行配置 Nginx 的跨域。

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
location /comment {
# 清除Waline自带的跨域Header
proxy_hide_header Access-Control-Allow-Origin;
proxy_hide_header Access-Control-Allow-Methods;
proxy_hide_header Access-Control-Allow-Headers;
proxy_hide_header Access-Control-Allow-Credentials;

# 添加自定义的跨域Header
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Origin' always;

# 反向代理
proxy_pass http://$server_name;
proxy_set_header Host $host:$server_port;
proxy_set_header X-NginX-Proxy true;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header REMOTE-HOST $remote_addr;

# 缓存
add_header X-Cache $upstream_cache_status;
add_header Cache-Control no-cache;
expires 12h;
}

Next 主题安装 Waline 官方插件

安装 Waline 官方插件

Waline 官方插件的版本必须与 Next 主题的版本匹配,否则 Waline 官方插件无法正常使用,两者的兼容性说明如下:

Next 主题的版本 Waline 官方插件的版本
<= 8.3.0<= 1.0.8
>= 8.4.0>= 2.0.0
1
2
3
4
5
# 进入博客的根目录
$ cd /blog-root

# 安装Waline插件(默认是最新版本)
$ npm install @waline/hexo-next --save

配置 Waline 官方插件

更改 Next 主题的配置文件 themes/next/_config.yml,添加以下内容,其中 serverURL 是 Waline Server 的访问 URL,需要自行更改该属性的值

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
# Waline Config File
# For more information:
# - https://waline.js.org
# - https://waline.js.org/reference/component.html
waline:
# New! Whether enable this plugin
enable: true

# Waline server address url, you should set this to your own link
serverURL: https://waline.vercel.app

# Waline library CDN url, you can set this to your preferred CDN
# libUrl: https://unpkg.com/@waline/client@v2/dist/waline.js

# Waline CSS styles CDN url, you can set this to your preferred CDN
cssUrl: https://unpkg.com/@waline/client@v2/dist/waline.css

# Custom locales
# locale:
# placeholder: Welcome to comment # Comment box placeholder

# If false, comment count will only be displayed in post page, not in home page
commentCount: true

# Pageviews count, Note: You should not enable both `waline.pageview` and `leancloud_visitors`.
pageview: false

# Custom emoji
# emoji:
# - https://unpkg.com/@waline/emojis@1.0.1/weibo
# - https://unpkg.com/@waline/emojis@1.0.1/alus
# - https://unpkg.com/@waline/emojis@1.0.1/bilibili
# - https://unpkg.com/@waline/emojis@1.0.1/qq
# - https://unpkg.com/@waline/emojis@1.0.1/tieba
# - https://unpkg.com/@waline/emojis@1.0.1/tw-emoji

# Comment infomation, valid meta are nick, mail and link
# meta:
# - nick
# - mail
# - link

# Set required meta field, e.g.: [nick] | [nick, mail]
# requiredMeta:
# - nick

# Language, available values: en-US, zh-CN, zh-TW, pt-BR, ru-RU, jp-JP
# lang: zh-CN

# Word limit, no limit when setting to 0
# wordLimit: 0

# Whether enable login, can choose from 'enable', 'disable' and 'force'
# login: enable

# comment per page
# pageSize: 10

更改 Next 主题的样式

由于 Next 主题默认对所有图片都添加了 display: block; CSS 样式,这会导致 Waline 的表情包图片独立一行显示,需要往 Next 主题里添加以下自定义样式来解决,例如更改样式文件 themes/next/source/css/_common/scaffolding/base.styl

1
2
3
.wl-content .vemoji, .wl-content .wl-emoji {
display: inline !important;
}

Hexo 构建失败的解决方法

若 Next 主题安装 Waline 官方插件后,执行 hexo g 命令抛出以下异常,这是由于 Hexo 或者 Next 的版本过低导致,此时需要升级 Hexo 或者 Next 的版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ERROR Render HTML failed: index.html
TypeError: Cannot read property 'parent' of null
at Function.exports.update (/usr/local/hexo/node_modules/cheerio/lib/parse.js:55:26)
at module.exports (/usr/local/hexo/node_modules/cheerio/lib/parse.js:17:11)
at Function.exports.load (/usr/local/hexo/node_modules/cheerio/lib/static.js:22:14)
at Hexo.hexoMetaGeneratorInject (/usr/local/hexo/node_modules/hexo/lib/plugins/filter/meta_generator.js:8:21)
at Hexo.tryCatcher (/usr/local/hexo/node_modules/bluebird/js/release/util.js:16:23)
at Hexo.<anonymous> (/usr/local/hexo/node_modules/bluebird/js/release/method.js:15:34)
at Promise.each.filter (/usr/local/hexo/node_modules/hexo/lib/extend/filter.js:60:50)
at tryCatcher (/usr/local/hexo/node_modules/bluebird/js/release/util.js:16:23)
at Object.gotValue (/usr/local/hexo/node_modules/bluebird/js/release/reduce.js:166:18)
at Object.gotAccum (/usr/local/hexo/node_modules/bluebird/js/release/reduce.js:155:25)
at Object.tryCatcher (/usr/local/hexo/node_modules/bluebird/js/release/util.js:16:23)
at Promise._settlePromiseFromHandler (/usr/local/hexo/node_modules/bluebird/js/release/promise.js:547:31)
at Promise._settlePromise (/usr/local/hexo/node_modules/bluebird/js/release/promise.js:604:18)
at Promise._settlePromiseCtx (/usr/local/hexo/node_modules/bluebird/js/release/promise.js:641:10)
at _drainQueueStep (/usr/local/hexo/node_modules/bluebird/js/release/async.js:97:12)
at _drainQueue (/usr/local/hexo/node_modules/bluebird/js/release/async.js:86:9)
at Async._drainQueues (/usr/local/hexo/node_modules/bluebird/js/release/async.js:102:5)
at Immediate.Async.drainQueues [as _onImmediate] (/usr/local/hexo/node_modules/bluebird/js/release/async.js:15:14)
at runCallback (timers.js:705:18)
at tryOnImmediate (timers.js:676:5)
at processImmediate (timers.js:658:5)

Waline 第三方插件列表

hexo-waline-next

  • hexo-waline-next,一款更强大且适用于 Next 主题的 Waline 插件,支持上传评论图片到七牛图床

hexo-next-darkmode

  • hexo-next-darkmode,一款适用于 Waline 与 Next 主题的暗黑模式切换插件,详细的使用说明请看这里

Waline 进阶配置

客户端使用 CDN

Waline 客户端若想使用 CDN 加速,只需在 Next 主题的 _config.yml 配置文件中,更改 Waline 官方插件的配置(如下所示)即可。由于 Waline 采用 Client/Server 架构,客户端与服务端的版本一般需要匹配才能正常运行;因此不建议每次自动都获取最新版的客户端文件,而是推荐日后统一更新客户端与服务端的版本。

1
2
3
4
5
6
7
8
9
# 获取最新版本的Waline
waline:
libUrl: https://unpkg.com/@waline/client@v2/dist/waline.js
cssUrl: https://unpkg.com/@waline/client@v2/dist/waline.css

# 获取指定版本的Waline
waline:
libUrl: https://unpkg.com/@waline/client@2.0.7/dist/waline.js
cssUrl: https://unpkg.com/@waline/client@2.0.7/dist/waline.css

验证用户注册邮箱

用户注册和评论的邮件通知都会用到邮件服务,配置邮件服务相关变量后,用户注册流程会增加邮箱验证码确认相关的操作,用来防止恶意的注册。

环境变量名称备注
SMTP_SERVICESMTP 邮件发送服务提供商
SMTP_HOSTSMTP 服务器地址,一般可以在邮箱的设置中找到。
SMTP_PORTSMTP 服务器端口,一般可以在邮箱的设置中找到。
SMTP_USERSMTP 邮件发送服务的用户名,一般为登录邮箱。
SMTP_PASSSMTP 邮件发送服务的密码,一般为邮箱登录密码,部分邮箱 (例如 163) 是单独的 SMTP 密码。
SENDER_NAME自定义发送邮件的发件人
SENDER_EMAIL自定义发送邮件的发件地址

提示:可以在这里查看支持的邮箱服务商,SMTP_SERVICE 和 (SMTP_HOSTSMTP_PORT)任选其一进行配置即可。如果在邮箱服务商列表中没有对应的 SMTP_SERVICE ,则需要同时配置 SMTP_HOSTSMTP_PORT

客户端配置用户头像

Waline 目前使用 Libravatar 来获取评论列表头像。Libravatar 是自由、开放的头像服务,支持联邦托管并与 Gravatar 完全兼容。首先博主或者用户自行使用邮箱登录或注册 Libravatar,然后更改自己的 Libravatar 头像。当在博客评论的时候,留下在 Libravatar 注册时所使用的邮箱即可,或者使用 Waline 客户端提供的登录功能;最后 Waline 会自动根据邮箱地址去 Libravatar 获取用户的头像,当未能从 Libravatar 查询到头像时,将会自动转为从 Gravatar 查询。目前 Waline 非自定义头像有以下 7 种默认值可选:

waline-avatar

在 Waline Server 中,通过配置环境变量 GRAVATAR_STR 来指定 Libravatar 头像服务的地址(基于 Nunjucks 语法),这样就可以自定义客户端所使用的用户头像类型,如下所示:

1
GRAVATAR_STR: 'https://seccdn.libravatar.org/avatar/{{mail|md5}}?d=robohash'

若 Libravatar 或者 Gravatar 在国内被墙,此时还可以使用 Cravatar 替代。在 Waline Server 中配置以下环境变量,就可以快速切换到 Cravatar,并自定义客户端所使用的用户头像类型:

1
2
AVATAR_PROXY: 'false'
GRAVATAR_STR: 'https://cravatar.cn/avatar/{{mail|md5}}?d=robohash'

特别注意

尽管诸如谷歌、QQ 等邮件提供商对电子邮件不区分大小写,但是你仍需要保证 Libravatar 或者 Gravatar 注册的邮箱和填入的邮箱地址对应。虽然全球大部分大型邮件提供商均不对电子邮件用户名区分大小写,但是根据 RFC 5231 的规定,电子邮件是区分大小写的。这意味着邮件提供商可以将 abc@xxx.comABC@xxx.com 视为不同的账号,而且也的确有邮件提供商这样处理。所以为防止使用此类邮件提供商的用户无法收到邮件或显示错误的头像,Waline 并不会对邮箱进行大小写转换。

服务端配置评论通知

当博客有用户发布评论或者用户回复评论时,Waline 支持对博主和回复评论作者进行通知。博主评论通知支持邮件、微信、QQ 与 Telegram,回复评论作者仅支持邮件通知。由于篇幅有限,这里仅介绍邮箱通知的配置,其他的通知方式可参考官方文档。邮件通知需要在环境变量中配置以下属性:

  • AUTHOR_EMAIL:博主邮箱,用来区分发布的评论是否是博主本身发布的。如果是博主发布的则不进行提醒通知。
  • SMTP_SERVICE:SMTP 邮件发送服务提供商,可以在 这里 查看所有支持的运营商。如果没在列表中的可以自行配置 SMTP_HOSTSMTP_PORT
  • SMTP_HOST:SMTP 服务器地址,一般可以在邮箱的设置中找到。如果未配置 SMTP_SERVICE 的话该项必填。
  • SMTP_PORT:SMTP 服务器端口,一般可以在邮箱的设置中找到。如果未配置 SMTP_SERVICE 的话该项必填。
  • SMTP_USER:SMTP 邮件发送服务的用户名,一般为登录邮箱。
  • SMTP_PASS:SMTP 邮件发送服务的密码,一般为邮箱登录密码,部分邮箱(例如 163)是单独的 SMTP 密码。
  • SMTP_SECURE: SMTP 邮件发送加密,默认为 true,设置为 false 则不会加密请求
  • SITE_NAME:网站名称,用于在消息中显示。
  • SITE_URL:网站地址,用于在消息中显示。
  • SENDER_NAME:自定义发送邮件的发件人,选填。
  • SENDER_EMAIL:自定义发送邮件的发件地址,选填。
  • MAIL_SUBJECT:评论回复邮件标题自定义
  • MAIL_TEMPLATE:评论回复邮件内容自定义
  • MAIL_SUBJECT_ADMIN:新评论通知邮件标题自定义
  • MAIL_TEMPLATE_ADMIN:新评论通知邮件内容自定义

提示

  1. 用户注册和评论的邮件通知都会用到邮件服务
  2. 由于国内腾讯云、阿里云默认禁用了 25 端口,若 Waline 的服务端是部署在云服务器上,则需要使用 465 端口,并启用 SSL 邮件加密,最后系统防火墙别忘了开放 465 端口

Waline Client 的其他特性

自定义样式

Waline 客户端默认提供了一些 CSS 变量,可以很轻松的通过这些变量自定义 Waline 客户端的 CSS 样式:

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
:root {
/* 字体大小 */
--waline-font-size: 16px;

/* 常规颜色 */
--waline-white: #fff;
--waline-light-grey: #999;
--waline-dark-grey: #666;

/* 主题色 */
--waline-theme-color: #27ae60;
--waline-active-color: #2ecc71;

/* 布局颜色 */
--waline-color: #444;
--waline-bgcolor: #fff;
--waline-bgcolor-light: #f8f8f8;
--waline-bgcolor-hover: #f0f0f0;
--waline-border-color: #ddd;
--waline-disable-bgcolor: #f8f8f8;
--waline-disable-color: #bbb;
--waline-code-bgcolor: #282c34;

/* 特殊颜色 */
--waline-bq-color: #f0f0f0;

/* 头像 */
--waline-avatar-size: 3.25rem;
--waline-m-avatar-size: calc(var(--waline-avatar-size) * 9 / 13);

/* 徽章 */
--waline-badge-color: #3498db;
--waline-badge-font-size: 0.775em;

/* 信息 */
--waline-info-bgcolor: #f8f8f8;
--waline-info-color: #999;
--waline-info-font-size: 0.625em;

/* 渲染选择 */
--waline-border: 1px solid var(--waline-border-color);
--waline-avatar-radius: 50%;
--waline-box-shadow: none;
}

如果使用了一个大量运用阴影 (box-shadow) 的主题,可以通过修改 --waline-border--waline-box-shadow 来更改 Waline 客户端的阴影样式,如:

1
2
3
4
5
6
7
8
9
10
:root {
--waline-border: none;
--waline-box-shadow: 0 12px 40px rgb(134 151 168 / 25%);
}

@media (prefers-color-scheme: dark) {
body {
--waline-box-shadow: 0 12px 40px #0f0e0d;
}
}

如果上面的 CSS 变量无法满足你对 Waline 样式的定制要求,你可以停止导入 Waline 官方提供的样式,并自己制作 CSS。

自定义表情包

启用暗黑模式

Waline 客户端默认支持暗黑模式,只需在 Waline 客户端初始化的时候,指定 dark 参数即可

  • 设置 dark: auto 会根据设备颜色模式自动切换
  • 填入 CSS 选择器,则会在对应选择器生效时启用暗黑模式

针对不同的 Hexo 主题,Waline 客户端的配置示例如下:

  • vuepress-theme-hope:它会在 <body> 上添加 theme-dark class 来开启暗黑模式,那么需要将 dark 选项设置为 body.theme-dark
  • Docusaurus:它会在 <html> 上通过设置 data-theme="dark" 开启暗黑模式,那么需要将 dark 选项设置为 'html[data-theme="dark"]'
  • hexo-theme-fluid:它会在 <html> 上通过设置 data-user-color-scheme="dark" 开启暗黑模式,那么需要将 dark 选项设置为 'html[data-user-color-scheme="dark"]'

若 Hexo 使用的是 Next 主题,且是通过插件 hexo-next-darkmode 来自动添加可切换的暗黑模式,那么可以在 Next 主题的 _config.yml 配置文件里添加以下内容来启用 Waline 客户端的暗黑模式

1
2
3
4
waline:
enable: true
dark: 'body.darkmode--activated'
...

在暗黑模式下,Waline 客户端默认会使用以下样式,若希望自定义暗黑模式的 CSS 样式,直接覆盖以下 CSS 样式即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* 根据用户设置 ↓ */
darkmode-selector {
/* 常规颜色 */
--waline-white: #000;
--waline-light-grey: #666;
--waline-dark-grey: #999;

/* 布局颜色 */
--waline-color: #888;
--waline-bgcolor: #1e1e1e;
--waline-bgcolor-light: #272727;
--waline-border-color: #333;
--waline-disable-bgcolor: #444;
--waline-disable-color: #272727;

/* 特殊颜色 */
--waline-bq-color: #272727;

/* 其他颜色 */
--waline-info-bgcolor: #272727;
--waline-info-color: #666;
}

提示

Next 主题使用插件 hexo-next-darkmode 来自动添加可切换的暗黑模式,详细的步骤可以参考这篇博客

Github 社交登录

最新版 Waline 增加了登录评论功能,除了普通的账号登录之外,还支持使用第三方社交账号进行直接登录。目前官方支持 Github 社交账号登录,当然默认没有开启 Github 社交账号登录功能,需要做一些配置才能支持。若要增加 Github 账号登录功能,需要配置 Github OAuth 密钥。点击 《Register a new OAuth application》 进入 Github OAuth 应用申请页面,这里需要填入以下几个配置:

  • Application name:应用名称,可以随意,会在用户授权时显示,推荐使用博客名称。
  • Homepage URL:应用主页地址,可以随意,会在用户授权时显示,推荐使用博客地址。
  • Appcation description:应用描述,可以随意,会在用户授权时显示,非必填项。
  • Authorization callback URL:应用的回调地址,登录时需要使用。填入 <serverURL>/oauth/github 其中 <serverURL> 是你的 Waline 服务端地址。

填完后点击 Register application 按钮就成功创建应用了,可以在页面中看到 Client ID。点击 Client secrets 栏右边的 Generate a new client secret 按钮,可以获取到该应用的 Client secrets


最后按照如下环境变量配置,将上面获取到的密钥(Client secrets)配置进 Waline 服务端的环境变量中,然后重新部署 Waline 服务端后即可使用 Github 登录。

环境变量名称备注
GITHUB_ID对应 Github OAuth Application 中的 Client ID
GITHUB_SECRET对应 Github OAuth Application 中的 Client secrets

由于 Github 的 API 调用在国内不太稳定,建议直接使用普通的账号登录

上传图片至七牛图床

Waline 客户端内置了图像上传的支持,默认会将图片转换为 Base64 字符串,然后通过 Waline Server 进行存储,例如将图片存储到 MySQL。在 Hexo 的 Next 主题下,若希望使用七牛图床,则可以安装 Waline 的第三方插件 hexo-waline-next 来实现。

1
2
3
4
5
# 卸载Waline官方插件
$ npm uninstall @waline/hexo-next --save

# 安装Waline第三方插件(默认是最新版本)
$ npm install hexo-waline-next --save

第三方插件 hexo-waline-next 使用了七牛官方的 Qiniu-JavaScript-SDK ,为了安全考虑,默认没有包含 Upload Token 的生成实现,因此 Upload Token 需要通过网络从服务端(自建)获取,服务端代码可以参考七牛服务端 SDK 的文档,插件的配置示例如下:

1
2
3
4
5
6
7
waline:
enable: true
qiniuDebug: false # print the error message of the picture uploaded by qiniu
qiniuDomain: https://qiniu.example.cn # The custom domain for qiniu, e.g https://qiniu.example.cn
qiniuTokenUrl: https://api.example.cn/qiniu/sdk/token/upload # The api to get qiniu token, e.g https://api.example.cn/qiniu/sdk/token/upload
qiniuLibUrl: https://cdn.jsdelivr.net/npm/qiniu-js@3.3.1/dist/qiniu.min.js # The custom library url for qiniu javascript sdk
...
  • qiniuDomain:七牛的外链域名
  • qiniuDebug:前端是否输出七牛上传图片的错误信息
  • qiniuTokenUrl:获取七牛 Upload Token 的接口地址
  • qiniuLibUrl:七牛 JavaScript SDK 的 CDN 资源链接

若希望禁用图片上传的功能,可以使用以下配置内容,适用于 Waline 客户端默认的图片上传和七牛图床上传:

1
2
3
4
waline:
enable: true
allowUploadImage: false # Allow upload picture
...
  • allowUploadImage:是否允许上传图片,默认值为:true

第三方插件 hexo-waline-next 对七牛 Upload Token 接口返回数据(JSON)的定义如下:

1
2
3
4
{
"data": "tdvdhnpSs2JFt8U9-c9hL74ddWtEj",
"msg": "success"
}
参数名称类型实例值说明
status codeNumber200HTTP 响应状态码,成功返回 200,非法请求来源返回 403,接口调用太频繁返回 429,系统内部出错返回 500
dataStringtdvdhnpSs2JFt8U9-c9hL74ddWtEjUpload Token 的值
msgStringsuccess 消息提示内容

提高七牛 Upload Token 接口的安全性:

  • 全站启用 HTTPS 协议
  • 通过 HTTP Header 的 refererX-Real-IPX-Forwarded-For 等来限制请求来源的域名、IP
  • 获取 Upload Token 的接口应该内置限流功能,避免外部恶意频繁调用接口,例如限制每分钟只能调用两次接口
  • 服务端生成 Upload Token 时,应该指定上传策略,例如设置 Token 的有效时间(expiresdeadline),具体可参考七牛官方文档一七牛官方文档二,Java 版服务端的示例代码如下:
1
2
3
4
5
6
7
8
9
10
11
String bucket = "bucket name";
String accessKey = "access key";
String secretKey = "secret key";

//指定UploadToken的有效时间为10秒
long expireSeconds = 10;

StringMap putPolicy = new StringMap();
Auth auth = Auth.create(accessKey, secretKey);
String upToken = auth.uploadToken(bucket, null, expireSeconds, putPolicy);
System.out.println(upToken);

Waline 开发指南

准备工作

  1. 使用 Git 克隆项目
1
$ git clone https://github.com/lizheming/waline.git
  1. 保证 NPM 的版本是 7

Node 14 及以下默认使用 npm@v6,你需要确保自己使用 npm@v7 版本,否则运行或者编译构建 Waline 组件时会出错

1
2
3
4
5
# 安装最新版的NPM
$ npm i -g npm@latest

# 查看NPM的版本
$ npm -v
  1. 安装依赖
1
2
3
$ cd waline

$ npm i

本地开发

本地使用以下命令启动 @waline/client,由于 Waline 采用 Client/Server 架构,在调试 client 时,必须配置本地环境变量 SERVERURLwaline/packages/client/.env,其中在 waline/packages/client/.env.example 文件里有可参考的配置示例。

1
$ npm run client:dev

@waline/client 正常启动时,输出的日志信息如下,此时浏览器直接访问 http://127.0.0.1:9000 就可以开始测试本地的 @waline/client

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
> client:dev
> npm run dev --workspace=@waline/client

> @waline/client@1.3.3 dev
> webpack serve --mode=development --config ./build/webpack.config.js

<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://127.0.0.1:9000
<i> [webpack-dev-server] On Your Network (IPv4): http://192.168.1.128:9000/
<i> [webpack-dev-server] Content not from webpack is served from '/usr/local/waline/packages/client/public' directory
asset Waline.min.js 1010 KiB [emitted] (name: main) 1 related asset
asset index.html 967 bytes [emitted]
runtime modules 27.1 KiB 13 modules
cacheable modules 832 KiB
modules by path ./src/ 95.3 KiB 48 modules
modules by path ../../node_modules/ 737 KiB
modules by path ../../node_modules/webpack-dev-server/client/ 48.9 KiB 12 modules
modules by path ../../node_modules/style-loader/dist/runtime/*.js 5.02 KiB 6 modules
modules by path ../../node_modules/webpack/hot/*.js 4.3 KiB 4 modules
modules by path ../../node_modules/html-entities/lib/*.js 81.3 KiB 4 modules
modules by path ../../node_modules/@vue/ 437 KiB 4 modules
modules by path ../../node_modules/url/ 37.4 KiB 3 modules
modules by path ../../node_modules/querystring/*.js 4.51 KiB
../../node_modules/querystring/index.js 127 bytes [built] [code generated]
../../node_modules/querystring/decode.js 2.34 KiB [built] [code generated]
../../node_modules/querystring/encode.js 2.04 KiB [built] [code generated]
webpack 5.50.0 compiled successfully in 3160 ms

如果希望在 Hexo Next 主题里测试本地的 @waline/client,那么可以在 Next 的 _config.yml 配置文件中指定 Waline 插件的 libUrl 参数,配置示例如下:

1
2
3
4
waline:
enable: true
libUrl: http://127.0.0.1:9000/Waline.min.js # Set custom waline cdn url
....

本地使用以下命令启动 @waline/server,为了使 @waline/server 能在本地正常运行,需要配置必要的本地环境变量至 waline/example/.env,其中在 waline/example/.env.example 文件里有可参考的配置示例。@waline/server 正常启动后,默认的服务器地址是 http://127.0.0.1:9000

1
$ npm run server:dev

编译构建

1
2
3
4
5
6
7
8
# 构建@waline/admin
$ npm run admin:build

# 构建@waline/client
$ npm run client:build

# 或者同时构建@waline/admin与@waline/client
$ npm run build

Waline 组件编译构建完成后,默认会在 waline/node_modules/@waline 目录下生成对应的文件,目录结构如下:

1
2
3
4
5
waline/node_modules/@waline
├── admin -> ../../packages/admin
├── client -> ../../packages/client
├── cloudbase -> ../../packages/cloudbase
└── vercel -> ../../packages/server

值得一提的是,以 @waline/client 组件为例,编译构建完成后,实际的输出路径是:waline/packages/client/dist

Waline 常见问题

防止恶意刷评论

  • Waline 服务端启用评论审核功能
  • Waline 服务端启用基于 IP 的评论发布频率限制功能
  • Waline 服务端启用客户端登录后才允许评论的功能,确保服务端的版本大于等于 0.26.0,同时 Waline 的客户端需要增加 login=force 的配置用于隐藏博客页面上的评论匿名输入框
  • Waline 的服务端如果启用客户端登录后才允许评论的功能,那么还需要在服务端的环境变量里配置邮件服务相关的参数;这是为了防止恶意注册,当配置了邮件服务相关的环境变量后,用户注册流程会增加邮箱验证码确认相关的操作

Waline 发布评论很慢

发布评论的时候因为一些特殊原因,例如垃圾邮件检测、评论通知都是串联操作。其中垃圾邮件检测使用的是 Akismet 提供的服务,这块由于调用国外服务可能会造成访问过慢,可以通过 AKISMET_KEY=false 后端环境变量关闭垃圾评论检测功能来定位问题。除了垃圾评论检测服务之外,评论通知中的邮件通知也有可能造成超时,这块建议可以先关闭评论通知再测一下是否是因为该功能导致的过慢。

Github 登录无法管理后台

Github 登录后无法管理后台,解决方法可参考 Github Issue:通过 Github 登录无法管理后台

Waline 版本更新流程

  • 更新 MySQL 数据库表结构
  • 更新 Hexo-Waline-Next 插件
  • 更新 Waline Server 的代码,重新构建 Docker 镜像
  • 若在 Next 主题的 _config.yml 文件中,Waline 官方插件的 CDN 配置里有指定 Waline Client 的版本(如下配置),那么还需要更改 Client 的版本号,否则默认会使用最新版的 Waline Client
1
2
3
4
waline:
enable: true
libUrl: https://unpkg.com/@waline/client@2.0.7/dist/waline.js
cssUrl: https://unpkg.com/@waline/client@2.0.7/dist/waline.css

提示

  1. 由于 Waline 采用 Client/Server 架构,客户端与服务端的版本一般需要匹配才能正常运行。
  2. waline/packages/server/src/controller/index.js 源文件涉及到 Waline Client 版本的定义(最新版本)
  3. waline/packages/server/src/middleware/dashboard.js 源文件涉及到 Waline Admin 版本的定义(最新版本)

项目源码

参考博客