博客评论系统换了一个又一个,有的功能性太差,有的太臃肿,有的广告满天飞。由于各种原因还是全都删掉了。后来那段时间里甚至觉得没有评论系统也不是什么大不了的事情,但事实是,我做不到那样佛系,读者的反馈对于我来说还是很重要的,哪怕是一句无意义的 “test”,都能让我很开心 —— 原来真的有人在看我的文章。
思来想去还是决定加上评论系统,但是这次,我要自己来!在网上浏览了一些方案之后,我最终锁定了方案:实现一个基于 GitHub Issues 的评论系统。Issues 本来是用来提交软件问题的一个板块,由于做得太 🐮🍺,已经有不少开源项目拿它做评论系统甚至社交软件了。下面我们就来看看我具体怎么实现吧!
前置需求
- 一台自己的服务器
- 基础的前端开发知识
- HTML / CSS / JavaScript
- 基础的后端开发知识
- 任意后端语言(本篇使用 NodeJS)
- 如何发送与接收 http 请求
创建 OAuth App
这里使用了 GitHub 自己的 OAuth API 来作为通信桥梁,第一步自然就是创建一个 OAuth APP。点击这里可以直接跳转到创建页面:
创建过程中只需要填写这三个参数:
- 你的 App 名字
- 你的博客主页 URL
- 用于接收 GitHub 数据的接口 URL
创建完成之后你会获得:
- Client ID
- Client secrets
请注意一定要保存好自己的 Client secrets,且不要告诉其他人!
在自己的博客中配置 GitHub 授权
具体步骤可以看官方给出的文档,但是这里有几点需要注意的地方:
第一个接口:
GET https://github.com/login/oauth/authorize
- 这在博客里其实只是一个跳转链接,不要当接口来用(样式上可以是一个超链接,也可以是一个按钮)。
- 参数
redirect_uri
的域名必须和创建过程中的Authorization callback URL
一样 - 参数
redirect_uri
可以是https://<redirect_uri>?r=<当前博客地址>
,这样自己的服务器就可以知道在 OAuth 验证成功之后往哪里跳转了。 - 参数
scope
默认为空,但是如果你想让所有用户都可以正常评论,scope
应该等于public_repo
假设:
redirect_uri = https://api.jw1.dev/gho
当前博客地址 = https://jw1.dev/2022/10/12/a01.html
那么跳转链接则为:
https://github.com/login/oauth/authorize?
client_id=<client_id>&
scope=public_repo&
redirect_uri=https://api.jw1.dev/gho?r=https://jw1.dev/2022/10/12/a01.html
# 换行是为了可读性,请不要在真实代码中使用换行符号
接收来自 Github 的请求并获取 access token
当用户授权之后,GitHub 会向你的接口发送一个 get 请求:
GET https://api.jw1.dev/gho?
r=https://jw1.dev/2022/10/12/a01.html&
code=<code>
该请求会带上一个 query 参数code
,如果你在 OAuth 验证链接中填了redirect_uri
,那么 query 中也会一并带上,这里我们可以暂存一下r
参数。带上这个 code 参数,我们来到第二个接口:
POST https://github.com/login/oauth/access_token
必需参数有三个:
client_id
client_secret
code
如果请求参数全部正确,GitHub 会返回一个 URL encoded 字符串:
access_token=gho_16C7e42F292c6912E7710c838347Ae178B4a&scope=public_repo&token_type=bearer
你可以直接使用这个字符串,但是为了易用性,我们可以设置请求头,让 GitHub 返回 json 格式的数据:
Accept: application/json
{
"access_token": "gho_16C7e42F292c6912E7710c838347Ae178B4a",
"scope": "public_repo",
"token_type": "bearer"
}
到这里,我们才完成 App 的验证,拿到了进入 GitHub 大门的钥匙:access_token
。
重定向到 OAuth 验证前的博客页面
拿到access_token
之后,我们需要返回到用户离开时的页面,这时候之前存的r
参数就能派上用场了:
r = 'https://jw1.dev/2022/10/12/a01.html'
redir_url = `${r}?token=${access_token}`
redirect(302, redir_url)
这里我们就成功回到了之前的页面,并且获得了access_token
。
// express代码
const express = require('express')
const router = express.Router()
const needle = require('needle')
const client_id = '你的client id'
const secret = '你的client secret'
let gho = router.get('/', function (req, res) {
let code = req.query.code
let redir = req.query.r
needle.post(
'https://github.com/login/oauth/access_token',
{
client_id: client_id,
client_secret: secret,
code: code
},
{},
function (err, resp) {
let params = resp.body.toString()
res.redirect(302, `${redir}?${params}`)
}
)
})
module.exports = {
gho
}
客户端获取数据
最难的部分算是解决了,接下来就是使用 token 获取数据了,这里其实反而没有什么难度,我就把用到的接口列出来好了:
-
获取评论数据
https://api.github.com/repos/OWNER/REPO/issues/ISSUE_NUMBER/comments
-
创建评论
https://api.github.com/repos/OWNER/REPO/issues/ISSUE_NUMBER/comments
-
删除评论
https://api.github.com/repos/OWNER/REPO/issues/comments/COMMENT_ID
这里有一个小细节要注意一下,以上接口中提到的ISSUE_NUMBER
即 GitHub Issues 的编号,关于这个编号怎么获取我走了一点点弯路,原本的思路是这样的:
- 获取博客 repo 下的所有 issues
- 查看是否已经为当前博客创建过 issue,如果有,则使用该 issue 的编号,如果没有,则创建一条新的
这个思路有几个很严重的问题:
- 每次加载文章都需要重新获取所有 issues,加载速度慢
- 把创建 issues 的权限交给了陌生用户,如果该用户删除 issue,那么你整个文章的评论都没了
- 有几率重复创建 issue
针对以上问题,我重新调整了思路:
- 文章还在草稿阶段时,手动创建 issue
- 将 issue number 写死在文章的变量中
之前的问题将不复存在,而且节省了一个 请求数量 (GitHub 有着 5000 QPH 的限制,所以用的越少越好)
客户端交互
写好布局,写好操作逻辑,这样一个评论系统就诞生了!样式和逻辑都在前端,你们想看都看得到,这个博客网站也是开源的(想抄就抄吧😎)。
好了,今天就到这里,拜拜!👋