测试一下评论

写了个简单的评论系统,封面的恐龙就是 Logo,如果用 Github 登录的话可以看得到。

测试一下评论

虽然没有人看,但还是想给 ook.dev 加个评论系统。

最初方案肯定是 Disqus,但使用 Disqus 的话要引入一个 JS 文件,而它会 document.write 一堆东西,再插个 iframe 进来。需要引入的东西太多,不能保证可控,而且与 ook.dev 的安全策略抵触。

Disqus 有两点很有意思:

  • 评论本身是在一个 iframe 里加载的
  • Disqus 希望能传一个 id 给它(而不是它自行获取页面地址)

再加上 Disqus 分配的二级域名。Hostname 和内容 ID 可以唯一的确定评论内容的归属。如果用一个独立的页面承载评论,那在 URL 里加上这两个参数,就可以只用一个 iframe 完成外挂评论的功能。完全独立,不需要同域,也不引入需要额外的脚本。总之这个想法就这么冒了出来。

其它第三方评论

在那之前,先找来现有第三方评论系统看了一下。实现上有服务端的大同小异,无服务端的倒是各有千秋。 // 这里居然用了两个成语

无服务端一类:Staticman 最硬核 … 但是喜欢不起来。Gitalk 挺好的,Feddy 的博客就在用,但是有一大副作用是在 Github 产生大量内容,尤其 Issue 比站点权重高,搜索的时候十分尴尬。Valine 很精致,利用 LeanCloud 直接在前端提交,而且文档表示有计划增加 Firebase。有过给 Feddy 换成 Valine 的想法,不过没什么必要。

有服务端的都差不多。在这里面喜欢 Isso,非常非常喜欢,可以自行部署,数据库只是一个 SqliteDB,文档提到“评论不属于大量内容”,如果它是用 Golang 写的那马上整一个。前面的想法依旧在萌动 … 仿个 Isso 出来罢。

最简单的匿名评论

于是用 Golang 做了类似的事情,实现上同前面的内容相似,一个页面用于展示评论内容。阻止了搜索引擎收录。页面需要额外的 hostname 和 target 参数在 URL 中,用以判断评论归属。Hostname 本是可以省略的,自用的话。但是将来需要再改就太麻烦了,也放进来好了。

另外两个接口获取 / 提交评论内容。分页暂时不需要,增加最大数量限制就好。与内容一同提交的还有可选的邮箱,昵称和站点地址,真的是在仿 Isso。

通过 postMessage 通知更新 iframe 高度

慢着,内容增多之后,iframe 不会变高对吧?同时跨域也导致无法直接获取 iframe 内容高度。好在 postMessage 可以解决这个问题,评论变多的时候往 parent 吼一下。浏览器兼容性什么的?last five versions 就好。

加个登录?

嗯,可以,虽然有些细节没处理,头像也没加。跑到别人采用了 Disqus 的博客去感受一下,完全不支持匿名评论,不清楚是不是特意关掉了。嗯,也对,加个第三方登录更讲究,至少来个 Github。

好在 Github,Google 之类的申请权限很简单。这里顺便画了个 Logo 给 GitHub 这边,选颜色调边角用了蛮久。

选个库来做第三方登录吧,goth 看起来不错,支持那么多,examples 看着也挺好,把获取的 key 填进去感觉挺不错。不过问题在于,gothic 这个包和 Gollria 太耦合了,而直接用 goth 体验又不好。// Gothic 一个希望解耦的 issue 2017 年的,到现在还没解决。

还是用 golang/oauth2 吧。圣斗士不会被同一个招式打败两次 … 程序员也不想把一段代码写好几遍 … 这里是两点,为 Google 和 GitHub 写 OAuth 逻辑时,肯定有很多地方是重复的,能抽出来就抽出来。另一个是 Golang 有好多 if err != nil,看起来很糟糕。打开 Google 试图寻找一些 meme 233。看了一些文章和讨论,最后看到 Golang Blog,errors are values。总之对 error 加深了一些认识吧。

算了,去掉匿名评论

好,实现的差不多了,还有一些细节要处理,再就是头像从服务商下载过来比较好一些?

不,比起那些,登录成功之后怎么通知到页面?如果 window.open 开一个窗口,是可以不停的查询结果并关闭窗口的,但是“不停的”听起来就不怎么优雅,即便登录成功之前时间不会很久。

那,postMessage?不,登录页面会跳走到服务商,再回来的时候事件已经没有了,收不到通知。

不然做成本页面跳转到登录地址,事后再跳回来?可以,但是 URL 里的 target 和 hostname 会丢,需要存在哪里。直接放 url 的话回来就没了,用本地存储又很奇怪。最合理的办法看起来是放 url 里,服务端拿到之后存在什么地方,登录成功之后再带上重定向回来。可以,但是依然很奇怪,而且怎么解决对应关系呢?

不过,OAuth 有个 state(拍脑袋),为了安全建议 state 为动态生成有效期和整个验证过程一致,而且服务商收到之后重定向回来的时候会带上。正好用这个,存数据库,state 做 key,初始 URL 做值,再加个时间。auth/name 的时候就生成 state 并且存储原始 url。到 /auth/name/callback 的时候通过 state 找到对应的 url,同时判断时间间隔,不合理直接丢弃。这里还需要个“已用”的值确保 state 不被记录下来。

其它

哦对,这个所谓的评论页面是服务端渲染的,因为功能太简单所以看起来没必要加第三方库,更不用说 React 之类的东西。大概,目前是这么设计的。

暂时,暂时是写到这里,2019.4.7.Sun 23.55,实际需要解决的问题还有很多,而且写着写着会有新的思路冒出来,改不改?不知道还有多久能搞好丢上线。

补充一下

考虑砍掉 Ajax 而直接采用原生 Form 来创建评论,因为已经在使用 URL 来传递服务端的返回信息了,而且懒得写一堆 JS 来弹消息。不过这个还没有做,先这样吧,反正没人访问,而且挂掉也没关系。

其他修改

  • 认证状态维护改了几次
  • 登录成功从 Github / Google 把头像下载过来