解决 Gitalk 无法获取 Github Token 问题

前言

记一次 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-RouterKoa-BodyParserKoa-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-typejson 的,那么就简单了,直接 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 项目上复制过来的

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

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

然后我们登录到 vercelDashboard - 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
[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
/**
* @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 文件。

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

OPTIONSPOST ,其他方法分别处理即可。

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

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

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

选择 cors-server 仓库:

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

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

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

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

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

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

接口访问效果如下:

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

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

感谢 @Jorbenzhu 提供的 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 了。