偶然看到通過語雀 webhook 發佈文章到 Hexo 靜態博客,很方便,實現過程也很有意思。同樣的原理可以運用到 Gatsby.js 博客上。
因爲使用了 netlify,自動部署的事情就不用自己擔心了,本文講述的有一下兩點:
- 熟悉語雀 webhook
- 使用 GitHub api 更新 GitHub 倉庫(更新倉庫後 netlify 自動部署)
除了以上兩個重點,整個流程是:
在語雀發佈文章 -> 觸發語雀 webhook -> express(node.js)接收到文章推送 -> 請求信息中抽取文章內容和必要信息 -> 調用 GitHub api 更新倉庫 -> netlify 自動部署 -> 文章在博客發佈
語雀 webhook
語雀webhook文檔 自帶完整指引,以下講講關鍵步驟。
在知識庫頁面配置訂閱地址
本地測試
官方推薦使用 ngrok,ngrok 能讓你的本地服務暴露到外網,方便測試。
我的配置:
express 接收 webhook 推送
app.post('/yuque/webhook', function(req, res) {
console.log(req.body.data)
})
此時在語雀發佈文章,接口就會收到推送的文章信息。
GitHub api 更新倉庫
原理
使用 api 更新 GitHub 倉庫的方法可以參考:使用 Github API 更新倉庫
主要代碼
var updateGitHubRes = function(blob, path) {
var commitSha
var commitTreeSha
return getRef()
.then(({ data }) => {
commitSha = data.object.sha
return getCommit(commitSha)
})
.then(({ data }) => {
commitTreeSha = data.tree.sha
return createBlob(blob)
})
.then(({ data }) => {
var blobSha = data.sha
return createTree(commitTreeSha, path, blobSha)
})
.then(({ data }) => {
var treeSha = data.sha
return createCommit(commitSha, treeSha)
})
.then(({ data }) => {
var newCommitSha = data.sha
return updataRef(newCommitSha)
})
.catch(err => {
console.log(err)
})
}
var getRef = function() {
return axios.get(`/${owner}/${repo}/git/refs/heads/master`)
}
var getCommit = function(commitSha) {
return axios.get(`/${owner}/${repo}/git/commits/${commitSha}`)
}
var createBlob = function(content) {
return axios.post(`/${owner}/${repo}/git/blobs`, {
content,
encoding: 'utf-8'
})
}
var createTree = function(base_tree, path, sha) {
return axios.post(`/${owner}/${repo}/git/trees`, {
base_tree, // commit tree 的 sha
tree: [
{
path, // 文件路徑
mode: '100644', // 類型,詳情看文檔
type: 'blob',
sha // 剛纔生成的 blob 的 sha
}
]
})
}
var createCommit = function(
parentCommitSha,
tree,
message = ':memo: update post'
) {
return axios.post(`/${owner}/${repo}/git/commits`, {
message,
parents: [parentCommitSha],
tree
})
}
var updataRef = function(newCommitSha) {
return axios.post(`/${owner}/${repo}/git/refs/heads/master`, {
sha: newCommitSha,
force: true
})
}
組合
把接受 webhook 請求的功能和 GitHub 更新流程組合起來,有如下代碼:
app.post('/yuque/webhook', function(req, res) {
console.log('web hook')
var postData = req.body.data
if (!postData) {
console.log('nothing append')
return res.json({
msg: 'nothing append'
})
}
var title = postData.title
var date = postData.created_at
var content = postData.body
var tagsReg = new RegExp(/(?<=<tags>).*(?=<\/tags>)/)
var removeTagsReg = new RegExp(/<tags>.*<\/tags>/)
var pathReg = new RegExp(/(?<=<path>).*(?=<\/path>)/)
var removePathReg = new RegExp(/<path>.*<\/path>/)
var replaceBrReg = new RegExp(/<br \/>/g)
var tags = content.match(tagsReg)
content = content.replace(removeTagsReg, '')
var postPath = content.match(pathReg)
content = content.replace(removePathReg, '')
content = content.replace(replaceBrReg, '\n')
tags = tags && tags[0]
postPath = postPath && postPath[0]
var tagsString = JSON.stringify(tags.split(','))
var contentHeader = `---
path: "${postPath}"
date: "${date}"
title: "${title}"
tags: ${tagsString}
---
`
updateGitHubRes(
contentHeader + content,
`src/pages/${date.substring(0, 10)}-${postPath.substring(1)}/index.md`
).then(({ data }) => {
console.log('finish')
return res.json({
msg: 'finish'
})
})
})
因爲語雀沒有 tag 之類的選項,只能自己用特定標記寫到文章裏再在後端提取,並且添加信息頭部。內容組合好調用更新 api 即可完成整個流程。
功能部署
如果你自己有服務器,正常部署即可,若沒有,可以使用 Heroku。Heroku 可以爲你提供免費的程序部署服務。你可以先把上面寫好的功能上傳到 GitHub,然後從選擇從 GitHub 拉取倉庫。拉取倉庫後 Heroku 會自動運行 npm start
。
npm start
映射到 node index.js
就可以了。
值得注意的是,heroku 的端口是系統分配的,所以需要使用環境變量提供的端口:
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Our app is running on port ${ PORT }`);
});
大功告成
在語雀發佈文章即可在博客同時發佈,這確實比手寫 md 再 push 發佈只方便了一點,但是更讓人期待的是語雀移動端的上線!那麼之後就能直接在手機更新靜態博客了!不過有點地方還是想吐槽,語雀的 md 編輯器有時候會語法失效,而且不能直接看到 md 代碼,總覺得對格式有種不能完全控制源碼的束縛感。