在我看来,Artalk 真正吸引我的点在于拥有独立的『通知中心』,评论内容不再是孤单的只与具体的页面存在联系。无论是访客还是管理员,拥有了一个统一的页面去管理所有的评论,同时它也能提醒用户未读消息的数目,在邮件通知之外又多了一层消息提醒能力,属于网站本身的通知。

本文为杂谈 💭

一、前言

以自托管评论系统来说,Artalk 是我使用的第三个评论系统,前两者是 Valine,Twikoo。Valine 后期闭源再加上存在严重的安全问题,被放弃了;Twikoo 倒是还好,不过相比较来说 Artalk 可以自行部署更吸引到我,而且该说不说腾讯云云开发的数据库用着不太顺手,文档型数据库终归没 MySql 熟悉。

以上为前提,Artalk 的后端支持两种部署方式:Docker 运行和可执行程序运行。由于应用程序为单文件且没什么环境需求,为了方(tou)便(lan)我这边使用了可执行程序的方式部署,利用 Supervisor 作持久化运行,数据库连接本地的 MariaDB(还可以搭上宝塔面板的自动备份快车)。

接下来是数据迁移,事实上我最初的迁移过程中面临了不少麻烦,But 人家 Artalk 的作者更新快,这期间迭代了不少版本的说。不过数据都存储到数据库中了,手撸 SQL 也不是说不行。比如因 Jsdelivr 的 ICP 备案被取消造成的历史表情访问失败的这类问题,就可以直接替换修改啦:

简单粗暴的替换成文字

Artalk 前后端分离,后端 Go 语言不会,没学过不作研究,但是!我可以自行调用后端提供的 Api,例如博客首页的热门文章/热评文章/最新评论这个侧边栏卡片的实现;前端这类 TypeScript 的到可以上下其手。所以事实上本博引用的前端样式是稍作修改的,以去线留白为核心,按照个人喜好修改了一小部分样式呈现。

二、推荐

So,Artalk 我也用了有那么一阵子了,这里推荐下它与别的评论系统最大不同的点吧。

2.1 通知中心

我将其视为自托管评论系统中的史诗级更新,一般来说,这类评论系统大都为匿名评论,至多需要访客提供昵称/邮箱而已,所以评论的提醒功能也自然而然的落在了邮件通知上,但这种提醒可以说是和网站本身隔离的,而且评论也只与页面相关联,如果遗漏/忘记了通知用的邮件,访客能否得知评论的回复与否完全落在了是否再次访问相关页面。而以腾讯邮箱为例,开启了会话模式后,同一邮件地址的邮件信息合并在一个页面里,历史信息折叠显示,曾经有那么几次由于折叠缘故,遗漏过邮件的查阅(它被设置为已读了)。

而 Artalk 的评论中心提供了全站评论管理能力,无论是访客还是管理员均可查看,同时它还拥有未读消息数目的小徽标。可以说这个评论中心是与网站正相关的,各个页面的评论由它被组织起来,联系到一起。此外,未来 Artalk 在提供获取未读消息的 API 后,我们甚至可以做到在进入的网站的第一刻,自行查询实现用户欢迎,提示查阅未读消息等。

2.2 后端控制前端

这里指的是,可以在后端覆写前端的配置。众所周知,Hexo 的渲染部署速度一直蛮受诟病,如果因需要修改评论的相关配置而去走一遍部署实在是略显麻烦了。以本博为例,从提交到最终部署,Github Action 大约需要 4-10 分钟左右。而倘若只是修改后端配置文件,就只需要重启一次程序即可。

2.3 前端资源文本

Artalk 后端同时提供了前端所需的 JS/CSS 文件,且这个文件百分百前后端版本匹配,所以博客引用前端文件时,直接使用后端提供的地址,如此 Artalk 的升级便只需要关心后端的升级了。

2.4 图片上传管理

目前来说,Artalk 存在两类上传方式,前端上传和后端上传。后端上传进服务器目录(也可通过 Upgit 再从后端上传至它处)。当然这里我想表达的是,你可以将评论产生的图片管理在你的服务器中,起码能够避免图源失效的可能性。

2.5 多站点/多管理员

字面意思,支持多个网站,同时每个网站的管理员互相独立,另外还有一个全局管理员。

三、自定义

本站所用评论在样式上稍作修改,如有需求,将相关引用资源替换为:

- css: https://o.static.szyink.com/storage/artalk/Artalk.css
- js: https://o.static.szyink.com/storage/artalk/Artalk.js

与原版的差异项如下:

自带一个表情放大功能

表情弹窗显示

控制中心弹窗显示

部分样式修订

评论

:doodle { @grid: 1x5 / 100vmin; } @place-cell: center; width: @rand(45vmin, 75vmin); height: @rand(45vmin, 75vmin); transform: translate(@rand(-120%, 120%), @rand(-80%, 80%)) scale(@rand(.8, 2.8)) skew(@rand(45deg)); clip-path: polygon( @r(0, 30%) @r(0, 50%), @r(30%, 60%) @r(0%, 30%), @r(60%, 100%) @r(0%, 50%), @r(60%, 100%) @r(50%, 100%), @r(30%, 60%) @r(60%, 100%), @r(0, 30%) @r(60%, 100%) ); background: @pick(#f44336, #9c27b0, #673ab7, #3f51b5, #60569e, #e6437d, #ebbf4d, #00bcd4, #03a9f4, #2196f3, #009688, #5ee463, #f8e645, #ffc107, #ff5722, #43f8bf, #e136eb, #32ed39); opacity: @rand(.5, .9); position: relative; top: @rand(-80%, 80%); left: @rand(0%, 80%); animation: colorChange @rand(6.1s, 26.1s) infinite @rand(-.5s, -2.5s) linear alternate; @keyframes colorChange { 100% { left: 0; top: 0; filter: hue-rotate(360deg); } }