利用github构建静态博客数据库

利用github构建静态博客数据库

引言

因为使用如hexo的框架搭建博客,是静态的,但是难免会有一些使用动态数据的需求产生,面对这些需求时,选择机械地编写硬编码,再上传生成显然不是一个优雅的做法。

而目前大多框架的主题都带有评论系统,其中支持gitalk的便使用了OAuth以及github中的issue,通过OAuth授权验证,issue作为存储。那么我们可以考虑利用这种模式,将动态数据更新到特定repo的某个issue中,再通过github提供的API访问获取数据。除此之外,关于这个存储repo,虽然不能私有,但是其中的issue可以选择关闭,只有管理员可以浏览编辑。这就只剩下一个问题,数据泄露,如果有比较机密的数据,当然不适合用这种方法了。(不会真有人往博客里写秘密吧,谁把秘密写在博客里,能写在博客的那能叫秘密吗?)

认识gitalk

看明白gitalk的工作原理,就成功大半了。

我,前端菜鸡,看到前端代码就头疼,能瞎写绝不优化。差不多就行了。

github token

此章参考Gitalk 运作原理GitalkGithub文档token分为三类,gitalk使用OAuth App,属于github的三种token`之一。

  1. GitHub Apps
  2. OAuth Apps
  3. Personal access tokens

OAuth Apps

实现了OAuth2.0认证协议,主要针对第三方平台想要通过GitHub API获取用户私密信息时,用户可不必向第三方平台提供GitHub账号密码即可完成认证。通过OAuth Apps授权的token代表的是该授权用户,而不是App,即App本身不具有任何权限。在权限限制上也与GitHub App不同,OAuth App只能读。值得一提的是GitHub使用的是Authorization code grant模式,也是最推荐的模式。以gitalk为例在该模式下的认证流程为

gitalk1

OAth2.0 协议

OAuth2 有4个角色:

  1. 资源拥有者 (user)
  2. 第三方客户端 (client)
  3. 资源服务器 (resource server)
  4. 认证服务器 (authentication server)

Authentication code grant 模式流程如下:

OAuth2

  1. user在第三方平台A登录,登录方式是使用另一个平台B的身份信息。
  2. A携带clientID跳转到B的授权页,请user授权。(这个clientID就是A在B登记的OAuth身份标识,这也表明使用OAuth协议前必须先在平台注册登记)
  3. user授权A可以从B获取身份信息,grant_code作为凭据。
  4. A的【后台】向B发起access_token申请,携带参数clientID+clientSecret+grant_code。
  5. B验证无误后返回代表该用户的access_token。
  6. A可以使用access_token通过B的API调取user信息了。

可以看出认证授权有两个阶段,这也是OAuth协议被认为安全的关键。第一阶段只能拿到一个临时许可令牌grant_code,可能十几分钟的过期。第二阶段拿access_token。

  1. 第一阶段
    clientID可以保存在前端,比如web页面,跳转认证时直接从前端取,不需要请求后台。grant_code只作为一个用户许可,并不能确保是否是可信任平台发起的请求,所以要验证平台是否可信,通过clientSecret。
  2. 第二阶段
    clientSecret只能保存在后台,而且获取access_token的请求只能由后台服务器发起和接收。这个过程跟前端无关,所以被认为是最安全的。

access_token一般会被保存起来,过期时间会久一些,可能几个月。

既然如此,是否可以说凡是使用OAuth2.0的认证就一定是最安全的呢?并不是,这要看是否使用的规范。gitalk就是一个不规范使用的例子,它把clientSecret也暴露在前端。因为我们没有后台。无后台server。缺点是暴露了clientSecret,不符合OAuth2.0规范。但是在GitHub上这样做是安全的,因为access_token只有读权限,不能对user的repo造成任何损害。

gitalk源码

gitalk没有后台server,clientSecret也是保存在前端,它的代理服务器地址。
gitalk的代码使用的代理

1
proxy: 'https://cors-anywhere.herokuapp.com/https://github.com/login/oauth/access_token',

一个公开的免费代理,声称是不会保存任何转发信息。

  1. access_token

    1
    2
    3
    4
    5
    6
    7
    get accessToken () {
    return this._accessToke || window.localStorage.getItem(GT_ACCESS_TOKEN)
    }
    set accessToken (token) {
    window.localStorage.setItem(GT_ACCESS_TOKEN, token)
    this._accessToken = token
    }

    access_token获取之后被保存起来,不关闭浏览器是不需要重复授权的。

  2. comments

    1
    2
    3
    4
    5
    6
    createIssue () {
    const { owner, repo, title, body, id, labels, url } = this.options
    return axiosGithub.post(`/repos/${owner}/${repo}/issues`, {
    title,
    labels: labels.concat(id),
    body: body || `${url} \n\n ${

    注意labels后面连接的id,这个id就是文章的唯一标识,一般设置为URL。

    GitHub的label长度限制在63字符之内,超过就不能创建Issue。为此可以对URL进行md5哈希,md5后生成128-bit摘要,用16进制表示,长度恒等于32.

    Issues获取也是根据labels,不同文章的id不同,获取的列表自然也就不同,把Issues作为评论展示出来,这样一个简单的评论系统就完成了。

读取issue

这个简单,通过github api就完事了,有一定的次数限制,不过可以添加token授权来解除上限,带上token后续也可以做一些其他的操作,上锁、删除等等。

官方文档:issue API

主要用到的就是获取具体内容,其中的评论等。

1
2
3
4
get https://api.github.com/repos/USER_NAME/repo_NAME/issues/number
// 获取第几个issue的内容
get https://api.github.com/repos/USER_NAME/repo_NAME/issues/number/comments
// 获取第几个issue的评论内容,读取其中的body再解析就行了。

写个XMLHttpRequest请求。

写入issue

写入比较麻烦,因为只读的请求是可以匿名的,写入修改是需要授权的,需要编写授权跳转页面,api地址https://github.com/login/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=http://localhost:8000/home.html

授权之后构造符合post请求的信息发送就可以了,但是要带上token,其中如果使用access token,不能直接明文记录,会被删除,可以分段记录在不同的变量拼接,当然了,会有一定的泄露风险,不过无所谓,只要授权不是最高级别的也没多大问题。

使用octokit

但是以上直接通过github API造轮子的办法比较繁琐,文档也没有太多例子,构造、测试非常的麻烦。所以可以直接用轮子,文档:octokit/rest.js,github页面:octokit/rest.js官方描述如下GitHub REST API client for JavaScript,直接查文档就行了。构造octokit对象,把参数填完就可以了。非常方便。

octokit使用注意事项

因为使用octokit需要引入模块,官方给出了两种方式。

浏览器

Load @octokit/rest directly from cdn.skypack.dev

1
2
3
<script type="module">
import { Octokit } from "https://cdn.skypack.dev/@octokit/rest";
</script>

Node

Install with npm install @octokit/rest

1
2
const { Octokit } = require("@octokit/rest");
// or: import { Octokit } from "@octokit/rest";

如果使用浏览器的话,直接引用,在script标签里编写函数就可以了。

如果在js文件中使用第二种方式,大概率会报错:Uncaught ReferenceError:require is not defined,因为浏览器环境没有RequireJS,需要另外引入,比较麻烦。可以通过加载定义Require的库,或者使用browerify,webpack等工具将js文件重新打包成带依赖的文件,但是会变大很多。建议还是使用第一种方式。

实例

读取issue

读取issue >folded
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var onload = function () {
var url = "https://api.github.com/repos/User_name/repo_name/issues/issue_id;";
var request = new XMLHttpRequest();
var clientID = 'YOUR_clientID';
var clientSecret = 'YOUR_clientSecret';
var req = new XMLHttpRequest();
req.open('GET', url, true);
req.send(null);
req.onload = function(){
if(req.status === 200){
var json = JSON.parse(req.responseText);
}
}
}

按需解析json即可,匿名读取有限制,每分钟60次,一般没问题,如果需要解除限制,就带上clientID和Secret区请求。

使用octokit

使用octokit的实例 >folded
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<body>
<p id="pre">hello</p>
<p id="next">bye</p>

<script type="module">
import { Octokit } from "https://cdn.skypack.dev/@octokit/rest";
var pre = document.getElementById("pre");
pre.onclick = function(){
console.log("hello")
}
next.onclick = function(){
const octokit = new Octokit({
auth:"your_access_token",
userAgent: 'myApp v1.2.3'

})
octokit.rest.issues.createComment({
owner:"name",
repo:"repo_name",
issue_number:issue_id,
body:"The content you want to send.",
});
}
</script>

</body>
</html>>

其他的octokit编写也差不多,看文档配参数就行,文档十分完善。

一些bug

构建js代码时,会在hexo博客上遇到一些坑,一般是因为其他插件造成的,比如Aplayer的代码注入或是重复载入 Aplayer.js 资源脚本问题,会在js文件头部注入<link>标签,导致报错, 查阅官方文档给出的方式是在配置文件中加入

1
2
aplayer:
asset_inject: false

但也很可能没有用!最好的方式就是,卸载APlayer,然后重装一个hexo-tag-aplayer(反正我也不在页面内插入音乐盒子,文章里用hexo-tag-aplayer就够了),然后再添加上述配置。


参考

gitalkhttps://github.com/gitalk/gitalk

利用 github pages github api 搭建博客:https://github.com/eyasliu/blog/issues/2

gitalk的运作原理:https://carl-zk.github.io/blog/2020/03/03/gitalk-%E8%BF%90%E4%BD%9C%E5%8E%9F%E7%90%86/

github Issue 作为博客微型数据库的应用:https://removeif.github.io/theme/github-Issue-%E4%BD%9C%E4%B8%BA%E5%8D%9A%E5%AE%A2%E5%BE%AE%E5%9E%8B%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9A%84%E5%BA%94%E7%94%A8.html

透过Github Action生成数据:https://blog.uiharu.top/archives/generate-links-json-via-github-action.html

利用github api获取issue的想法:https://github.com/isaaxite/blog/issues/44

github文档的issues操作:https://docs.github.com/en/rest/reference/issues

github API的调用教程:https://segmentfault.com/a/1190000015144126

利用github issue实现评论插件:https://segmentfault.com/a/1190000011100934

octokit文档:https://octokit.github.io/rest.js/v18

解决require问题:https://stackoverflow.com/questions/19059580/client-on-node-js-uncaught-referenceerror-require-is-not-defined

使用hexo-tag-aplayer以及配置书写:https://easyhexo.com/3-Plugins-use-and-config/3-1-hexo-tag-aplayer/#%E4%BB%8B%E7%BB%8D

解决Aplayer注入问题:https://blog.csdn.net/qq_27439819/article/details/105011943

作者

ivy

发布于

2020-07-01

更新于

2023-03-25

许可协议

CC BY-NC-SA 4.0

评论