Sukka's Blog

童话只美在真实却从不续写

  1. 1. 缘起
  2. 2. 思路
  3. 3. 反代
  4. 4. 一些坑

DisqusJS 是一个使用 Disqus API 的超轻量级的「评论基础模式」实现,搭配 Disqus API 的反代可以实现在网络审查地区加载 Disqus 评论列表;支持自动检测访客的 Disqus 可用性自动选择加载原生 DISQUS (评论完整模式)和 DisqusJS 提供的评论基础模式。

npm version Author npm license Size Travis Codacy Badge Dependency Status jsDelivr Hits

GitHub | Demo

缘起

在写「Suka Theme」主题的时候,给主题适配了 8 种评论系统;真到给自己的博客选一个评论系统的时候却开始犹豫了——我应该选哪一个?
搜狐的畅言的后台给人一种已经停止维护的感觉;gitalk 和 gitment 让人感觉很 Geek,但一样存在评论管理不方便的问题,还有 GitHub Issue Label 的限制影响 post identifier 的长度;Valine 需要运行在 LeanCloud 上,而我对 LeanCloud 的印象并不很好。WildFire 不支持 SNS 登陆。。。想来想去,还是老牌的 Disqus 好用,支持 SNS 登陆、后台界面友好;虽然默认会引入一大堆 Tracker 和 AFF Link、但是至少还是可以关掉的。

但是早在 2015 年,Disqus 就已经被墙了。。。https://imququ.com 的博主屈屈根据 Disqus API 开发出了评论基础模式,从刚开始可以看评论列表到现在已经可以匿名发表新评论,当时很多人都很羡慕,然而屈屈并没有开源。
过去了几年,终于有人也搞出了类似的 Disqus 的「评论基础模式」,比如 fooleap 用 最好的语言写的 disqus-php-api,后来 ciqulover 用 NodeJS 实现了一个 Disqus-Proxy;这两个工具都可以实现在网络审查地区展示评论列表、发布新评论的功能。

但是这两个项目本质都是 Disqus 的一个第三方 SDK——需要一个专门的后端服务转发请求。所以我想,能不能仅依赖前端和 AJAX 实现「评论基础模式」,于是就有了 DisqusJS

思路

首先,Disqus「评论基础模式」毕竟是一个「基础模式」,所以自然应该优先加载完整的 Disqus。所以 DisqusJS 需要实现一个在前端检查访客能不能连通 Disqus 的设计。因为加载 Disqus 需要同时加载这几个域名下面的文件:shortname.disqus.com 域名下面的 embed.jsdisqus.com 的 API,*.disquscdn.com 加载媒体文件等,只有这三个域名都可以访问了才可以加载完整的 Disqus。

参考了一下 disqus-php-api 和 Disqus-Proxy 的检测方法,前者直接检查 disqus.com/next/config.json 是否可用(这个 URI 有 CORS 响应头,可以直接发起 AJAX);Disqus-Proxy 除了检查 config.json,还使用 embed.js 文件是否可用、用 Promise 实现了一个超时检查;
我在写 DisqusJS 时,在 shortname.disqus.com 下找了半天,找不到带 CORS 的小文件。后来在屈屈的博客的评论中看到屈屈说他其实是检查的 Disqus 两个域名下的 favicon,而图片文件加载并不需要担心跨域。所以 DisqusJS 也一样通过加载 disqus.com/favicon.icoshortname.disqus.com/favicon.ico 判断访客的 Disqus 连通性。

对于评论列表的渲染,我去研究了一下其它评论系统的技术栈:WildFire 直接引入了完整的 Vue,Gitalk 引入了「轻量」的 Preact。不过考虑到 DisqusJS 的目标是「超轻量级」,所以我直接使用了 ES6 模板字符串渲染评论条目(反正构建生产环境版本时会用 babel 转译)。

反代

之前提到的两个 Disqus SDK 都需要运行一个后端程序,但是 DisqusJS 只依赖 Disqus API 原始 API,搭建反代就相对容易多了。如果有自己的 VPS 自然就很简单了,使用 Caddy 或者 Nginx 进行反代的教程 Google 上一搜一大把,所以这里就不需要再提及了。
需要注意的是,Disqus 被墙的方式是 DNS 污染,本身 IP 并没有被墙。所以在反代时如果设置了目标 IP,使用国内的机器也可以反代。不过 Disqus 的主站还是在用 Slowly Fastly,国内主机连接速度还是比较慢。

但是如果没有自己的 VPS,或者懒得配置 nginx.conf 这种,可以用一些 CDN 服务实现反代。注意,只适用于支持设置使用 IP 回源和回源 Host 的 CDN。在 CDN 上配置如下:

  • 回源 IP:151.101.64.134 151.101.0.134 151.101.128.134 151.101.192.134
  • 回源端口:443
  • 回源 Host:disqus.com
  • 缓存:api/* 完全不缓存

其中,又拍云 CDN 不仅上述项目都支持设置,而且回源节点在香港,所以很适合做为 Disqus API 的反代(但是又拍云的节点质量不敢恭维就是了,而且又拍云的海外节点非常匮乏)

但是连 CDN 都没有的也不用担心,我搭建了一个 Disqus API 的反代 https://disqus.skk.moe/disqus/ 供大家使用(但是不保证 SLA)。

一些坑

DisqusJS 不支持新创建评论或者初始化 Thread,不是因为 DIsqusJS 的「超轻量级实现」,而是因为 Disqus API 虽然诸多 create 方法并不需要 OAuth,但是却需要使用 private 方法调用(不带 origin 不带 Referrer),所以必须依赖后端、或者反代回源时请求头不跟随,总之都是需要后端程序;所以 DisqusJS 本着不需要专门的后端程序就直接不做 create 方法了。

如果根据 identifer 找不到对应的 Thread,Disqus 会直接忽略这个 Identifier 然后返回这个 Forum 里面所有 Thread,所以需要检查 reponse.length 是否为 1,如果大于 1 则需要提示「尚未初始化」。

disq.us 是 Disqus 的短链接,和主站一样用的 Fastly,虽然没有被墙却胜似被墙,所以需要实现一个解析 disq.us 获得原链接的方法,主要就是去掉链接开头的 disq.us 和末尾的 Key、然后 decodeURIComponent。相关代码剥离出来以后我开源在 Gist 上了。

Disqus API 返回的评论时间并不是时间戳而是 UTC 时间,所以还需要转换为 UTC+8;直接给小时数加 8 显然是不行的,不然会有 25 点这种东西跑出来。所以必须先将 UTC 时间解析成时间戳、加上 8 小时(也就是 28800000ms)、然后再格式化成 yyyy-MM-dd hh:mm:ss 的格式。

Disqus API 返回的评论条目是扁平的,如果是子评论就会带一个 parent 字段标明归属,DisqusJS 写了一个遍历所有评论的方法把归属都理清了。但是这还引出了一个新的问题:Disqus API 存在翻页、一页仅仅展示 25 条;如果子评论在上一页、而其父评论在下一页,那么这条子评论就找不到归属了。所以 DisqusJS 用了 一个简单粗暴的解决方案 ——每次加载一页 API 后就把所有的数据合并然后用所有的数据重新渲染一次整个评论列表。

本文作者: Sukka
本文采用 CC BY-NC-SA 4.0 许可协议。转载和引用时请注意遵守协议!
本文链接 : https://blog.skk.moe/post/disqusjs/

本文最后更新于 天前,文中所描述的信息可能已发生改变