在hexo-next配置gitalk

🕒 阅读时间:9 min read 📝 字数:12597 👀 阅读量: Loading...

如何在hexo-next配置gitalk评论

你是否也想在你的博客里面添加这样的评论区? gitalk 那就快来看以下教程吧:)

打开github,点击右上角的图片,选择Settings,然后在左边栏最下方找到Developer settings并点击,然后在左边栏找到OAuth Apps选中点击,主页面右上角有new OAuth App 点击创建新OAuth Apps,输入必要参数,记录ID,创建密码,记得复制,然后转到blog\themes\next下的_config.yml文件修改其中的配置,转到cloudflare配置worker,然后就大功告成了,不过光听我讲没用,看图更容易理解,所以请一步一步按图片示意做:

在github上创建OAuth Apps

scene1 scene2 scene3 scene4 scene5 scene6 scene7 scene8 可以先用文本编辑器创建txt文件记录Client id和secrets

切记不要把Client id和secrets泄露给他人!这里仅作演示,此测试OAuh Apps已删除


转到blog\themes\next下的_config.yml文件修改其中的配置

## ---------------------------------------------------------------
## Comments Settings
## See: https://theme-next.org/docs/third-party-services/comments
## ---------------------------------------------------------------
## Multiple Comment System Support
comments:
#use: Valine
text: true
# Available values: tabs | buttons
style: tabs
count: true
# Choose a comment system to be displayed by default.
# Available values: changyan | disqus | disqusjs | gitalk | livere | valine
active: gitalk
# Setting `true` means remembering the comment system selected by the visitor.
storage: true
# Lazyload all comment systems.
lazyload: false
# Modify texts or order for any navs, here are some examples.
nav:
gitalk:
order: -1
#disqus:
# text: Load Disqus
# order: -1
#gitalk:
# order: -2

下面的也是配置,记得把用户名改一下

## Gitalk
## For more information: https://gitalk.github.io, https://github.com/gitalk/gitalk
gitalk:
enable: true
github_id: xingwangzhe
repo: xingwangzhe.github.io
client_id: sdaf # OAuth app的
client_secret: asdfghjgds #同上
admin_user: xingwangzhe
distraction_free_mode: true # Facebook-like distraction free mode
# Gitalk's display language depends on user's browser or system environment
# If you want everyone visiting your site to see a uniform language, you can set a force language value
# Available values: en | es-ES | fr | ru | zh-CN | zh-TW
language: zh-CN
proxy: "https://cors-anywhere.xingwangzhe.workers.dev/https://github.com/login/oauth/access_token"
#worker,请记住这段链接,接下来要用到

利用cloudflare创建worker代理

1scene 2scene 3scene 4scene 5scene 别忘了注册的用户名是你的博客用户名 在左侧worker代码栏添加如下代码,但是请记得替换用户名。 代码栏有替换工具,这里就不演示了 源码地址

const exclude = []; // Regexp for blacklisted urls
const include = [/^https?:\/\/.*xingwangzhe\.github\.io$/, /^https?:\/\/localhost/]; // Regexp for whitelisted origins e.g.
const apiKeys = {
EZWTLwVEqFnaycMzdhBz: {
name: "Test App",
expired: false,
exclude: [], // Regexp for blacklisted urls
include: ["^http.?://xingwangzhe.github.io$", "xingwangzhe.github.io$", "^https?://localhost/"], // Regexp for whitelisted origins
},
};
// Config is all above this line.
// It should not be necessary to change anything below.
function verifyCredentials(request) {
// Throws exception on verification failure.
const requestApiKey = request.headers.get("x-cors-proxy-api-key");
if (!Object.keys(apiKeys).includes(requestApiKey)) {
throw new UnauthorizedException("Invalid authorization key.");
}
if (apiKeys[requestApiKey].expired) {
throw new UnauthorizedException("Expired authorization key.");
}
if (apiKeys[requestApiKey].expiresAt && apiKeys[requestApiKey].expiresAt.getTime() < Date.now()) {
throw new UnauthorizedException(
`Expired authorization key.\nKey was valid until: ${apiKeys[requestApiKey].expiresAt}`,
);
}
return apiKeys[requestApiKey];
}
function checkRequiredHeadersPresent(request) {
// Throws exception on verification failure.
if (!request.headers.get("Origin") && !request.headers.get("x-requested-with")) {
throw new BadRequestException(
"Missing required request header. Must specify one of: origin,x-requested-with",
);
}
}
function UnauthorizedException(reason) {
this.status = 401;
this.statusText = "Unauthorized";
this.reason = reason;
}
function BadRequestException(reason) {
this.status = 400;
this.statusText = "Bad Request";
this.reason = reason;
}
function isListed(uri, listing) {
let returnValue = false;
console.log(uri);
if (typeof uri === "string") {
for (const m of listing) {
if (uri.match(m) !== null) {
returnValue = true;
}
}
} else {
// Decide what to do when Origin is null
returnValue = true; // True accepts null origins false rejects them.
}
return returnValue;
}
function fix(myHeaders, request, isOPTIONS) {
myHeaders.set("Access-Control-Allow-Origin", request.headers.get("Origin"));
if (isOPTIONS) {
myHeaders.set(
"Access-Control-Allow-Methods",
request.headers.get("access-control-request-method"),
);
const acrh = request.headers.get("access-control-request-headers");
if (acrh) {
myHeaders.set("Access-Control-Allow-Headers", acrh);
}
myHeaders.delete("X-Content-Type-Options");
}
return myHeaders;
}
function parseURL(requestUrl) {
const match = requestUrl.match(
/^(?:(https?:)?\/\/)?(([^/?]+?)(?::(\d{0,5})(?=[/?]|$))?)([/?][\S\s]*|$)/i,
);
// ^^^^^^^ ^^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^
// 1:protocol 3:hostname 4:port 5:path + query string
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// 2:host
if (!match) {
console.log("no match");
throw new BadRequestException("Invalid URL for proxy request.");
}
console.log("parseURL:match:", match);
if (!match[1]) {
console.log("nothing in match group 1");
if (/^https?:/i.test(requestUrl)) {
console.log(
'The pattern at top could mistakenly parse "http:///" as host="http:" and path=///.',
);
throw new BadRequestException("Invalid URL for proxy request.");
}
// Scheme is omitted.
if (requestUrl.lastIndexOf("//", 0) === -1) {
console.log('"//" is omitted');
requestUrl = "//" + requestUrl;
}
requestUrl = (match[4] === "443" ? "https:" : "http:") + requestUrl;
}
const parsed = new URL(requestUrl);
if (!parsed.hostname) {
console.log('"http://:1/" and "http:/notenoughslashes" could end up here.');
throw new BadRequestException("Invalid URL for proxy request.");
}
return parsed;
}
async function proxyRequest(request, activeApiKey) {
const isOPTIONS = request.method === "OPTIONS";
const originUrl = new URL(request.url);
const origin = request.headers.get("Origin");
// ParseURL throws when the url is invalid
const fetchUrl = parseURL(request.url.replace(originUrl.origin, "").slice(1));
// Throws if it fails the check
checkRequiredHeadersPresent(request);
// Excluding urls which are not allowed as destination urls
// Exclude origins which are not int he included ones
if (
isListed(fetchUrl.toString(), [...exclude, ...(activeApiKey?.exclude || [])]) ||
!isListed(origin, [...include, ...(activeApiKey?.include || [])])
) {
throw new BadRequestException("Origin or Destination URL is not allowed.");
}
let corsHeaders = request.headers.get("x-cors-headers");
if (corsHeaders !== null) {
try {
corsHeaders = JSON.parse(corsHeaders);
} catch {}
}
if (!originUrl.pathname.startsWith("/")) {
throw new BadRequestException('Pathname does not start with "/"');
}
const recvHpaireaders = {};
for (const pair of request.headers.entries()) {
if (
pair[0].match("^origin") === null &&
pair[0].match("eferer") === null &&
pair[0].match("^cf-") === null &&
pair[0].match("^x-forw") === null &&
pair[0].match("^x-cors-headers") === null
) {
recvHpaireaders[pair[0]] = pair[1];
}
}
if (corsHeaders !== null) {
for (const c of Object.entries(corsHeaders)) {
recvHpaireaders[c[0]] = c[1];
}
}
const newRequest = new Request(request, {
headers: recvHpaireaders,
});
const response = await fetch(fetchUrl, newRequest);
let myHeaders = new Headers(response.headers);
const newCorsHeaders = [];
const allh = {};
for (const pair of response.headers.entries()) {
newCorsHeaders.push(pair[0]);
allh[pair[0]] = pair[1];
}
newCorsHeaders.push("cors-received-headers");
myHeaders = fix(myHeaders, request, isOPTIONS);
myHeaders.set("Access-Control-Expose-Headers", newCorsHeaders.join(","));
myHeaders.set("cors-received-headers", JSON.stringify(allh));
const body = isOPTIONS ? null : await response.arrayBuffer();
return new Response(body, {
headers: myHeaders,
status: isOPTIONS ? 200 : response.status,
statusText: isOPTIONS ? "OK" : response.statusText,
});
}
function homeRequest(request) {
const isOPTIONS = request.method === "OPTIONS";
const originUrl = new URL(request.url);
const origin = request.headers.get("Origin");
const remIp = request.headers.get("CF-Connecting-IP");
const corsHeaders = request.headers.get("x-cors-headers");
let myHeaders = new Headers();
myHeaders = fix(myHeaders, request, isOPTIONS);
let country = false;
let colo = false;
if (typeof request.cf !== "undefined") {
country = typeof request.cf.country === "undefined" ? false : request.cf.country;
colo = typeof request.cf.colo === "undefined" ? false : request.cf.colo;
}
return new Response(
"CLOUDFLARE-CORS-ANYWHERE\n\n" +
"Source:\nhttps://github.com/chrisspiegl/cloudflare-cors-anywhere\n\n" +
"Usage:\n" +
originUrl.origin +
"/{uri}\n" +
"Header x-cors-proxy-api-key must be set with valid api key\n" +
"Header origin or x-requested-with must be set\n\n" +
// + 'Limits: 100,000 requests/day\n'
// + ' 1,000 requests/10 minutes\n\n'
(origin === null ? "" : "Origin: " + origin + "\n") +
"Ip: " +
remIp +
"\n" +
(country ? "Country: " + country + "\n" : "") +
(colo ? "Datacenter: " + colo + "\n" : "") +
"\n" +
(corsHeaders === null ? "" : "\nx-cors-headers: " + JSON.stringify(corsHeaders)),
{ status: 200, headers: myHeaders },
);
}
async function handleRequest(request) {
const { protocol, pathname } = new URL(request.url);
// In the case of a "Basic" authentication, the exchange MUST happen over an HTTPS (TLS) connection to be secure.
if (protocol !== "https:" || request.headers.get("x-forwarded-proto") !== "https") {
throw new BadRequestException("Must use a HTTPS connection.");
}
switch (pathname) {
case "/favicon.ico":
case "/robots.txt":
return new Response(null, { status: 204 });
case "/":
return homeRequest(request);
default: {
// Not 100% sure if this is a good idea…
// Right now all OPTIONS requests are just simply replied to because otherwise they fail.
// This is necessary because apparently, OPTIONS requests do not carry the `x-cors-proxy-api-key` header so this can not be authorized.
if (request.method === "OPTIONS") {
return new Response(null, {
headers: fix(new Headers(), request, true),
status: 200,
statusText: "OK",
});
}
// The "x-cors-proxy-api-key" header is sent when authenticated.
//if (request.headers.has('x-cors-proxy-api-key')) {
// Throws exception when authorization fails.
//const activeApiKey = verifyCredentials(request);
// Only returns this response when no exception is thrown.
return proxyRequest(request);
//}
// Not authenticated.
//throw new UnauthorizedException('Valid x-cors-proxy-api-key header has to be provided.');
}
}
}
addEventListener("fetch", async (event) => {
event.respondWith(
handleRequest(event.request).catch((error) => {
const message = error.reason || error.stack || "Unknown Error";
return new Response(message, {
status: error.status || 500,
statusText: error.statusText || null,
headers: {
"Content-Type": "text/plain;charset=UTF-8",
// Disables caching by default.
"Cache-Control": "no-store",
// Returns the "Content-Length" header for HTTP HEAD requests.
"Content-Length": message.length,
},
});
}),
);
});

然后就大功告成了:)

无脑解决 2024/9/16

将 proxy 替换为了

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

再次 hexo clean && hexo g -d, 即可使用 gitalk, 评论区会作为设置中的 repo 的 issue 存在.

后话

如果你为你的博客购买了其他的域名,记得要在OAuh Apps设置的返回链接改成你的域名 当然,连带着worker应该也要改,但是在这里我还是建议你直接使用无脑的那个proxy

我为此折腾了半天:(

在hexo-next配置gitalk

作者:xingwangzhe

本文链接: https://xingwangzhe.fun/posts/8403dd78/

本文采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。

留言评论