Jekyll 中使用 Firebase/Wilddog 部署评论系统

2017-04-01 17:03:45   技术   javascript firebase wilddog jquery

更新

4/4 前端:修改首页最新评论的锚记名字;添加加载后滑动到指定锚记;添加用cookies记录已经发送评论的昵称邮箱网址。

4/5 添加Comments.post.commentCount.check(callback); 添加js端排序;修改所有comments返回的格式。以上修改是为了动态导入野狗数据准备

4/5 从多说迁移数据成功

碎碎念

这件事情的起因是这样的

多说网 webmaster@duoshuo.com 3月22日 (10天前)

发送至 我 你好!

因公司业务调整,非常遗憾的向大家宣布多说项目即将关闭。

我们将于2017年6月1日正式关停服务,在此之前您可以通过后台的数据导出功能导出自己站点的评论数据。

对此给您造成的不便,我们深表歉意,感谢您的一路相伴。

其实我第一个反应就是直接迁移野狗。

迁移评论这种事儿我也不是第一次做了,上次新浪云迁移过来的时候已经做了一次从django/mysql迁移多说的工作(上次用的脚本去哪儿了,不知道能不能找到利用一下)。

其实我还是希望玩一玩最近的新技术,也搜索了一圈,结果没什么好的发现。

那继续野狗吧。

顺手撸的代码在: firebase-comments

一开始比较拙劣,弄到一半的时候我就去逛逛知乎了。不出我的所料,已经有那么点儿讨论了,比如 知乎:如何评价“多说”即将关闭?有什么替代方案? 。看了一圈,各种推荐网易跟帖,换Disqus的,还有个韩国弄的什么的。里面有几个回答有点吸引我,有个是把评论推到github上的。然后就发现了一个小伙伴 Frank Lin 在干和我一样的事儿,有点儿小兴奋呢。

关于代码

具体怎么用我在 Demo Page 里面已经写了2个,当然比较粗糙了

firebase/wilddog 设置

安全域名

这就没什么好说的了,防止偷跑流量(去死啦,就那么点评论有几个流量)

数据操作规则

在这个问题上面我这次好好研(在Frank的页面上各种捣蛋)究了一下,最后写了这堆复杂的东西

{
  "rules": {
    ".read": true,
    "comments": {
      ".indexOn": ["post", "timestamp"],
      "$commentid": {
        ".write": "!data.exists() && newData.hasChild('name') && newData.hasChild('email') && newData.hasChild('post') && newData.hasChild('comment')",
        ".validate": "newData.child('name').val().length > 0 && newData.child('name').val().length < 20 && newData.child('name').isString() && newData.child('email').val().matches(/^[\\.a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$/) && newData.child('post').val().length > 0 && newData.child('comment').val().length > 0 && newData.child('comment').val().length < 2048 && newData.child('comment').isString() && newData.child('timestamp').isNumber() && (!newData.hasChild('url') || newData.child('url').val().matches(/^http(s?):\\/\\/[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*((0-9)*)*(\\/?)([a-zA-Z0-9\\-\\.\\?\\,\\'\\/\\\\+&=%\\$#_]*)?$/))"
      }
    },
    "posts": {
      "blog":{
        "post":{
          "$number":{
            ".write": "newData.hasChild('count')",
            ".validate": "newData.child('count').isNumber() && newData.child('count').val() >= 0"
          }
        }
      }
    }
  }
}

这里有一点 posts的嵌套规则是和各人的路径有关的,我因为改过jekyll的配置,所以和默认的不一样,默认的应该是这样的

"posts": {
      // the path format depends on your own jekyll permalink setting
      "$year":{
        "$month":{
          "$day":{
            "$title":{
              ".write": "newData.hasChild('count')",
              ".validate": "newData.child('count').isNumber() && newData.child('count').val() >= 0"
            }
          }
        }
      }
    }

所谓前端的validate都是耍流氓,就是这样

翻译成人话是这样的:

  • 所有用户可读
  • 用post和index对comments索引
  • comments只允许新写数据
  • comments新写数据必须包含name, email, post, comment
  • name长度在1-19,name类型是string
  • email符合regex
  • post长度大于0
  • comment长度在1-2047,comment类型必须是string
  • timestamp必须是数字
  • url如果存在符合regex
  • posts的必须包含count且count>=0

不要问我上面的1-19和1-2047怎么来的,拍脑袋决定的。

遇到如下的的坑:

  • firebase/wilddog里面正则和javascript里面略有不同,转义有的地方需要双斜杠
  • write和validate是不同的,没弄清楚会造成set({})等于remove()的恶劣效果,这个其实在firebase文档里面写了,我没仔细看

关于comments.js

因为很大一部分代码都是复用了抓鬼的代码,所以风格和抓鬼的一致,比如:

  • callback各种嵌套
  • 原生js

methods

Comments.handleError(error)

这里可以用来重载错误处理,比如我这里就bootstrapt调用一个模态框

Comments.errors

整理的错误列表,如果其他人用可以翻译一下或者改成其他文字什么的

errors: {
    NO_POST: "没有指定文章",
    NO_NAME: "昵称为空",
    NAME_TOO_LONG: "昵称太长",
    NO_EMAIL: "邮件地址为空",
    EMAIL_INVALD: "邮件地址无效",
    URL_INVALD: "网址无效",
    NO_COMMENT: "评论为空",
    COMMENT_TOO_LONG: "评论太长",
    NO_COUNT: "没有指定数量"
}

Comments.comment.list(post, callback)

列出某个post的所有comment,因为是异步,所以就带了callback。下面所有带callback的都是因为异步

这里有个TODO问题,firebase/wilddog在查询上都有限制说最多500条,所以有个分页问题。但是我这里评论啥时候能超过500条呢,我再做吧(去死啦)

Comments.comment.listCallback(post, callback)

上面那个方法的监听版本

Comments.comment.add(name, email, post, comment, url, reply, callback)

添加评论,在方法里面校验了所有需要的东西,校验规则和上面firebase的规则设置一样,这里是前端先保护下

Comments.comment.recent.get(count, callback)

获取最近count条评论,这个就是用来显示在主页上的东西

Comments.comment.recent.updateCallback(count, callback)

上面那个方法的监听版本

Comments.comment.recent.removeCallback()

保留的删除最近条数监听的的方法,虽然我没有用到(咦我为什么要提出来这个,难道是因为抓鬼用到了类似的吗?)

Comments.post.commentCount.get(post, callback)

获取某个post的评论条数。Frank因为只在当前post上需要,所以他直接用了ChildrenCount。考虑到我列表页满屏的数字,我还是用数据冗余另提一个出来

在这里有坑

  • 因为异步操作,我一开始的版本是直接返回了count,然后再循环的时候就各种一团乱了。我的解决方案是直接让这个方法返回{“post”: “XXX”, “count”: X}
  • 因为数据冗余,造成新建评论的时候需要维护post的count,为了节省流量我就忍了
  • 同样因为数据冗余,后面我要写的多说转firebase格式有个大坑。还好我是直接空的迁移,如果更新迁移就晕菜了

Comments.post.commentCount.updateCallback(post, callback)

上面那个方法的监听版本,会返回的监听Ref,可以remove来关闭监听

Comments.post.commentCount.set(post, count, callback)

设置某个post的评论数量。我在考虑要不要把这个方法给_了,因为毕竟是内部方法。不过也可以考虑以后写个校验评论数量的方法之类的。

Comments.post.commentCount.check(callback)

校验所有post的评论数量并更新。这个也可以用来防止数据冗余造成不配对的情况,可以手动校验一下。这里有坑:

  • 同样还是分页问题
  • 在使用之前必须要给posts加上.write是true,不然它没法工作

具体用法

先是引用firebase/wilddog的库,然后引用comments(.min).js

<script src="https://www.gstatic.com/firebasejs/3.7.3/firebase.js"></script>
<script src="/js/comments.min.js"></script>

或者

<script src="https://cdn.wilddog.com/sdk/js/2.5.2/wilddog.js"></script>
<script src="/js/comments.min.js"></script>

然后初始化

<script type="text/javascript">
  //firebase
  var config = {
    apiKey: "*******",
    authDomain: "*******.firebaseapp.com",
    databaseURL: "https://*******.firebaseio.com",
    storageBucket: "*******.appspot.com",
    messagingSenderId: "*******"
  };
  Comments.init("firebase",config);
</script>

或者

<script type="text/javascript">
  //Wilddog
  var config = {
    authDomain: "*******.wilddog.com",
    syncURL: "https://*******.wilddogio.com"
  };
  Comments.init("wilddog",config);
</script>

在然后就是看jekyll怎么写的了,这个各人博客都不太一样。

之后就是各种jquery和js+ES6的大坑,就是前端的问题了。

我自己的博客直接提了3个独立的js出来为了防止代码太乱(去死啦)

还碰到奇怪的坑,到手机端全都看不到了。怀疑是MD5方法坏了,后来才发现是comment.js里面的格式带了ES6特性,手机的(以及IE)都不认识就挂了。

迁移自多说

为了迁移我做了一些有点古怪的事儿,首先是下面2个框,我放这里只是为了自己不想把双引号转成单引号

迁移有2种方法

一种是如果firebase或者野狗还没有新评论的时候,直接用上面我这个框转换一下,然后手工导入进去,接着开一下posts的write权限,然后Comments.post.commentCount.check(),最后关掉posts的write权限

另一种情况是这样的,firebase或者野狗已经有新评论稍微有一点儿费劲,我写了个方法Comments.comment.transferFromDuoShuo(sourceJson, callback),先打开comments和posts的Write权限(且关掉所有其他的乱七八糟限制),sourceJson里面填写上面框的结果里面的comments的值部分(不包括”comments”:),然后调用,然后再把comments和posts权限给关了

我在写这段文字的时候还没迁移,我先去试一试成不成功

更新:已经迁移成功了,同时发现一个小问题,因为我不记得多说有没有引用了,我的脚本里面没有写引用,如果有小伙伴的数据里面有关于引用的东西,可以借我来实验一下,不过这种转换也是一次性的,挺不高兴维护……

还没干的事儿

  • callback说:我想变成Promise_(:зゝ∠)_
  • Reply说:我的UI为什么那么丑_(:зゝ∠)_

(╯‵□′)╯︵┻━┻ 事儿是干不完的

评论共