前言
Gitalk 是一个基于 GitHub Issues 和 Preact 开发的评论插件,与之类似的项目还有 Gitment
本文主要以本博客为例讲述 Gitalk 的配置以及如何实现自动初始化 issues
配置Gitalk
因为插件是基于 GitHub Issues 的,所以首先必须要有一个 GitHub 账号
没有的话点这里去注册一下
然后我们需要创建一个用于储存评论的仓库,我这里直接用的是博客代码的仓库,你可以和我一样,也可以新建一个仓库


建好仓库后到 GitHub 的 OAuth 页面新建一个新的 OAuth 应用程序。

点击 Generate a new client secret

记住你的 Client ID 和 Client secrets (刷新页面后 Client secrets 就消失了)
完成上面的步骤后,来到 _config.icarus.yml 配置文件,我用的 Icarus 主题集成了 Gitalk 所以直接在配置文件里配置就可以,如果你用的主题没有集成或者你想在其他地方使用 Gitalk 的话,参考官方文档进行使用
1 2 3 4 5 6 7 8 9 10 11 12 13
| comment: type: gitalk client_id: '' client_secret: '' repo: '' owner: 'lvboda' admin: ['lvboda'] language: zh-CN per_page: 20 pager_direction: last distraction_free_mode: false create_issue_manually: true proxy: ''
|
配置Proxy
proxy 这里单独讲一下
获取 access_token 这一步是禁止跨域的POST请求
POST https://github.com/login/oauth/access_token
出于一些安全问题, OAuth 验证无法在纯粹的浏览器内完成,所以需要做一下代理转发
GitHub 上的相关问题:https://github.com/isaacs/github/issues/330
proxy 默认值是 https://cors-anywhere.azm.workers.dev/https://github.com/login/oauth/access_token, 但是这个国内网络是不支持的,所以我推荐自己配置一个转发的服务
1 2 3 4
| # proxy location /proxy/ { proxy_pass $arg_addr; }
|
在 nginx.conf 配置文件里的 server 配置中加入上面的配置
1
| proxy: 'https://<你的服务器ip或域名>/proxy/?addr=https://github.com/login/oauth/access_token'
|
然后就可以了,就这么简单。如果没有服务器的话可以使用一些第三方的服务,这里就不例举了
自动初始化Issues
Gitalk 最新版本有一项配置 createIssueManually 默认为 false 是可以自动初始化 Issues 的,如果该项配置为 true 则需要手动点击初始化 Issues
我这里还提供一个自动初始化 Issues 的脚本贴在下面
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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
|
const fs = require("fs"); const path = require("path"); const url = require("url"); const request = require("request"); const xmlParser = require("xml-parser"); const YAML = require("yamljs"); const cheerio = require("cheerio"); const crypto = require('crypto');
const config = { username: "", token: "", repo: "blog", sitemapUrl: path.resolve(__dirname, "./public/sitemap.xml"), kind: "Gitalk", };
const urlList = sitemapXmlReader(path.resolve(__dirname, "../public/sitemap.xml")); const websiteConfig = YAML.parse(fs.readFileSync(path.resolve(__dirname, "../_config.yml"), "utf8")); const issuesUrl = `https://api.github.com/repos/${config.username}/${config.repo}/issues`;
const requestGetOpt = { url: `${issuesUrl}?page=1&per_page=1000`, json: true, headers: { "User-Agent": "github-user", "Authorization": `token ${config.token}`, } };
const requestPostOpt = { ...requestGetOpt, url: issuesUrl, method: "POST", body: {}, };
console.log("开始初始化评论...");
(async function() { console.log("开始检索链接,请稍等..."); try { console.log("开始获取已经初始化的issues:"); const issueList = await send(requestGetOpt); console.log(`已经存在${issueList.length}个issues`);
const notInitIssueUrlList = urlFilter(urlList, issueList);
if (notInitIssueUrlList.length > 0) { console.log(`本次有${notInitIssueUrlList.length}个链接需要初始化issue:`); console.log(notInitIssueUrlList.map((item) => decodeURIComponent(item))); console.log("开始提交初始化请求, 大约需要40秒...");
setTimeout(async ()=>{ for (const notInitIssueUrl of notInitIssueUrlList) { const html = await send({ ...requestGetOpt, url: notInitIssueUrl }); const title = cheerio.load(html)("title").text(); const desc = decodeURIComponent(notInitIssueUrl) + "\n\n" + cheerio.load(html)("meta[name='description']").attr("content"); let pathLabel = url.parse(notInitIssueUrl).path.replace(websiteConfig.root || "", ""); const label = crypto.createHash('md5').update(decodeURIComponent(pathLabel)).digest('hex'); await send({ ...requestPostOpt, body: { body: desc, labels: [config.kind, label], title } }); } console.log(`初始化issues成功,完成${notInitIssueUrlList.length}个!`); }, 40000); } else { console.log("本次发布无新增页面,无需初始化issue!!"); } } catch (e) { console.log(`初始化issue出错,错误如下:`); console.log(e); } })();
function sitemapXmlReader(file) { let data = fs.readFileSync(file, "utf8"); let sitemap = xmlParser(data); return sitemap.root.children.map(function (url) { let loc = url.children.filter(function (item) { return item.name === "loc"; })[0]; return loc.content; }); }
function urlFilter(urlList, issueList) { if (!urlList || !Array.isArray(urlList) || !issueList || !Array.isArray(issueList)) throw Error("");
return urlList.filter((url) => { const path = decodeURIComponent(new URL(url).pathname).replace(websiteConfig.root || "", "");
const isPathFormat = path && path !== "/" && !/categories|tags|friends|about/g.test(path); const hasIssues = issueList.findIndex((issue) => issue.body.includes(path)) !== -1;
return isPathFormat && !hasIssues; }); }
function send(options) { return new Promise(function (resolve, reject) { request(options, function (error, response, body) { if (!error) { resolve(body); } else { reject(error); } }); }); }
|
配置完直接用 node 跑就可以,也可以把它放在部署脚步中执行
1 2 3 4 5 6 7 8 9
| #!/bin/bash
hexo clean hexo generate echo "--静态文件已生成--" rsync -av -e ssh public/ roo<你的服务器ip>:/usr/local/nginx/html/blog/ echo "--自动化部署完成--" node script/gitalk-auto-init-issues.js echo "--自动初始化issues完成--"
|
最后
还有一个类似的开源项目是 Gitment 但是个人觉得还是 Gitalk 的 ui 做的比较好看,所以就用了这个,这个插件也有一个缺点就是必须要有 GitHub 账号才可以评论,如果是用来写计算机相关的技术博客那受众人群应该都会有 GitHub 账号,不是技术博客就不推荐这类的插件了,看个人选择吧
参考资料