Hexo | 解决Gitalk无法获取Github Token问题

搬运自Dedicatus545

前言

记一次Hexo Next主题下Gitalk无法获取Github Token问题

目前博客采用的Gitalk来作为帖子的评论系统

其原理是通过帖子名来生成一个唯一id,用这个在Github仓库下开一个issue,这个issue就成为帖子的评论仓库了

由于要操作到Github仓库,所以是需要借助Github的开放API来完成的

其中有一步需要获取一个access_token,操蛋的是,这个API是不支持跨域访问的

https://github.com/login/oauth/access_token

所幸 Gitalk 使用了亚马逊的云服务代理里这个接口

https://cors-anywhere.azm.workers.dev/https://github.com/login/oauth/access_token

看起来没问题了,更操蛋的又来了,这个地址被墙了,意味着现在没法代理接口了,要么自己买服务器代理接口,要么科学上网

科学上网不现实,你不能指望大家开着飞机来看你的帖子

所以只能在自己写代理服务器上做文章了

作为一个抠门抠到家的码农,要我花钱,你这是要我的命🤯

果断百度 “免费VPS”

一看搜索结果…

额,还是算了,免费的就是最贵的…

正在我思来想去如何解决的时候,我想起了之前fork的一个网易云API项目,这个项目部署在了一个公共的服务上,好像还是免费的

我就直接冲进我的仓库列表中进行一个地毯式地查找

没错,就是它,Vercel

正文

本文会通过两个方面来讲述整个过程

  • 代理https://github.com/login/oauth/access_token这个接口
  • 部署到 Vercel 上

代理Github接口

作为一个切图仔,不对,前端工程师,首选 JS 来作为编写语言,毫无疑问,使用 Node 来作为代理服务器

这里使用的技术栈为 Koa ,以及它的一些中间件,比如 Koa-Router, Koa-BodyParser,Koa-Cors,以及一个请求库,当然是我们的老朋友 axios

然后,我们就可以写出一个整体的框架

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
const Koa = require('koa');
const KoaCors = require('@koa/cors');
const KoaRouter = require('@koa/router');
const KoaBodyParser = require('koa-bodyparser');
const axios = require('axios');

const app = new Koa();
const router = new KoaRouter();

router.post('/github_access_token', async (ctx, next) => {
// TODO
await next();
});

router.get('/', async (ctx, next) => {
ctx.body = 'a cors proxy server!';
await next();
})

app.use(KoaCors());
app.use(KoaBodyParser());
app.use(router.routes()).use(router.allowedMethods());

app.listen(9999, () => {
console.log('cors-server success!');
});

这里面最重要的就是post那个请求了

我们可以查看亚马逊代理的请求头

发现它的content-type是json的,那么就简单了,直接axios发送然后把请求体带上即可

1
2
3
4
5
6
router.post('/github_access_token', async (ctx, next) => {
const reqBody = ctx.request.body;
const res = await axios.post('https://github.com/login/oauth/access_token', reqBody);
ctx.body = res.data;
await next();
});

当然,上面的代码是有问题的,官方的接口返回的是一串类似 URL 参数的东西,如下

1
access_token=****************&scope=public_repo&token_type=bearer

而亚马逊的代理会把它转成 json 格式返回,所以这里我们也需要转成 json 格式

这个转化我们使用 URLSearchParams 来完成,非常简单

1
2
3
4
5
6
7
8
9
10
router.post('/github_access_token', async (ctx, next) => {
const reqBody = ctx.request.body;
const res = await axios.post('https://github.com/login/oauth/access_token', reqBody);
const params = new URLSearchParams(res.data);
ctx.body = Array.from(params.entries()).reduce((obj, [key, value]) => {
obj[key] = value;
return obj;
}, {});
await next();
});

这样子我们就完成了全部代码的编写,是不是很简单?

部署到 Vercel 上

这里我们需要在项目根目录下新建一个 vercel.json 配置文件,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"version": 2,
"builds": [
{
"src": "./index.js",
"use": "@vercel/node"
}
],
"routes": [
{
"src": "/(.*)",
"dest": "/"
}
]
}

当然,其实我没看过文档啥的,我是从那个网易云 API 项目上复制过来的

不过粗略猜测一下,build 的 src 应该是指定了入口函数,而 routes 制定了路由映射的规则

虽然我们项目启动的是 9999 端口,但是 vercel 部署统一都是 443 端口的(应该) ,内部再做转发

然后我们登录到 vercel ,Dashboard - Vercel ,使用 github 账号登录即可

然后我们选择 new Project

import 这个 cors-server 项目

不用任何设置,直接点 deploy 部署即可

稍等一会之后,我们就可以看到项目部署成功了

我们访问 https://cors-server-ecru.vercel.app 就可以看到服务了

然后我们修改 Next 主题下的配置文件,把 _config.yml 里的配置改成这个接口即可

提交到 github 上,然后即可成功使用 github 登录 gitalk

可以看到 Options 预检请求成功,证明跨域没问题,Post 请求也成功返回

如果不想折腾,直接使用我这个下面这个地址替换亚马逊的地址即可

https://cors-server-ecru.vercel.app/github_access_token

当然,如果觉得不放心,完全可以 fork 我的项目,检查源代码啥的,然后自己部署到 vercel,几分钟的事

项目地址:cors-server

喜欢可以点个 star 哦~

2022-8-26 vercel 使用自定义域名

经评论区提醒,vercel 的服务无法访问。

我去百度了一下,发现之前也有过这种情况,应该不是墙,只是 DNS 污染的间歇性抽风,目前已经找到解决办法。

只需把地址换成 https://vercel.prohibitorum.top/github_access_token 即可,服务还是 vercel 的。

如果你有自己的域名,那么可以按照如下来配置,首先打开的域名控制台,这里我是阿里云的域名,添加一条如下的规则。

然后我们进入 vercel 的控制台,按照如下图,添加对应的域名,这里要和我们在域名控制台设置的一样。

我们在域名控制台添加了 vercel.prohibitorum.top 指向了 76.223.126.88 ,这里我们就填 vercel.prohibitorum.top 即可。

新建之后我们点击右侧的 edit 按钮,会出现如下:

点击 View DNS Records & More for XXX → 这个链接,跳转到如下界面,然后添加一条 CNAME 规则,如下:

这样就完成了,然后访问你设置的网址,如果出现了如下页面就是成功了。

然后我们修改 next 主题下的配置即可,如下图所示,这里我用我的服务做示范:

部署,然后就可以正常获取 token 了。

话说 github 也是间歇性抽风,写个帖子是真的不容易,各种推送失败…

2022-10-22 使用 netlify 部署服务

评论区有朋友说 vercel 有点慢,想使用 netlify 来部署。

我也没用过 netlify ,不过既然都差不多,也就花了点时间搞了下,还行,可以部署。

和 vercel 不同的是,这里 netlify 好像不支持路由映射?即使通过函数启动了服务器,好像也没用,这个没搞懂。

不过这里用了另一种函数,Edge Function 边缘函数,它允许我们导出函数来拦截对应的请求。

所以,你可以看到我们在 netlify.toml 文件内放了写了如下的配置。

1
2
3
4
5
6
7
[build]
edge_functions = "edge_functions"

[[edge_functions]]
path = "/github_access_token"
function = "github_access_token"

edge_functions = “edge_functions” 指定了放边缘函数文件夹的路径。

下面的部分即当访问 /github_access_token 这个路径时,用 edge_functions 下的 github_access_token 文件来处理它。

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
* @param {Request} request
*/
export default async function (request) {
if (request.method === "OPTIONS") {
// 预检请求
const resp = new Response(null, {
status: 204,
});
resp.headers.set("Access-Control-Allow-Origin", "*");
resp.headers.set("Access-Control-Allow-Methods", "POST, OPTIONS");
resp.headers.set("Access-Control-Allow-Headers", "Content-Type");
resp.headers.set("Access-Control-Max-Age", `${86400 * 30}`);
return resp;
}
if (request.method === "POST") {
try {
const reqBody = await request.text();
console.log("request body: ", reqBody);
const res = await fetch("https://github.com/login/oauth/access_token", {
method: "POST",
body: reqBody,
headers: {
"Content-type": "application/json",
},
});
const text = await res.text();
console.log("github api res: ", text);
const params = new URLSearchParams(text);
const resp = new Response(
JSON.stringify(
Array.from(params.entries()).reduce((obj, [key, value]) => {
obj[key] = value;
return obj;
}, {})
),
{
status: 200,
}
);
resp.headers.set("Access-Control-Allow-Origin", "*");
resp.headers.set("Access-Control-Allow-Methods", "POST, OPTIONS");
resp.headers.set("Access-Control-Allow-Headers", "Content-Type");
resp.headers.set("Access-Control-Max-Age", `${86400 * 30}`);
return resp;
} catch (e) {
console.error(e);
throw e;
}
}

return new Response("a cors proxy by netlify!");
}

目前代码已经更新,根目录下新增了 edge_functions 目录下的 github_access_token.js 文件。

当然,这里我们就没必要用三方库了,直接手撕。

对 OPTIONS ,POST ,其他方法分别处理即可。

部署的话,点击这里进入 netlify 。

注册的话我们直接用 github 登录就行了,方便导入相应的仓库。

选择 github ,第一次进要跳转到 github 那边授权,照做就行了。

选择 cors-server 仓库:

配置默认即可,不需要填,直接点击 deploy site 。

如果成功了,那么就有如下画面:

然后我们进入 https://xxxx.netlify.app/github_access_token ,如果出现如下的画面,那么就完成了(这里 xxxx 是 netlify 随机的一个前缀)。

至于域名的话,我觉得没必要,因为目前来看,没有污染问题。

大家如果嫌麻烦,可以使用我的地址:https://strong-caramel-969805.netlify.app/github_access_token 即可。

不过我还是建议大家自行注册,因为每个账号是有免费的额度的。

接口访问效果如下:

2023-08-13 使用 Docker 部署服务

已支持 Docker 容器方式部署,不过这种方式适合你自己有服务器的情况,并且服务器要能正确代理原始的地址。

感谢 (@Jorbenzhu)[https://github.com/jorben] 提供的 Dockerfile 文件。

镜像已经提交到 DockerHub ,可以使用以下命令来拉取镜像。

1
docker pull dedicatus545/github-cors-server:1.0.0

然后使用以下命令启动镜像

1
docker run -d --name cors-server -p8080:9999 dedicatus545/github-cors-server:1.0.0

这里容器内部是 9999 端口,绑定主机的 8080 端口,这里可以根据你的服务器端口占用情况进行动态修改。

后记

什么?你问我如果 Github 要是被墙了怎么办?

那就凉拌,转 Gitee 是不可能转 Gitee 的,这辈子都不可能转 Gitee 。

要是转了,我怕我的 JavaScript 标签有一天就变成 J**aScript 了。