動機
昨天菊霜,多說評論系統(tǒng)正式關閉了。
早在一個月前還在做畢設的時候谆刨,聽聞如此噩耗塘娶,就以在糾結到底遷移到哪個評論系統(tǒng)比較好。
但可惜痊夭,除了多說之外刁岸,國內似乎找不到靠譜點的社會化評論系統(tǒng)。
暢言是搜狐出的評論系統(tǒng)她我,試用了一段時間后就棄坑了虹曙。主要缺點是不支持facebook,twitter番舆,github等任何國外的平臺酝碳,其次評論管理很不方便,因為后臺賬號不能用來評論合蔽,所以在自己網(wǎng)站上評論還得重新注冊击敌,好傻呀是不是。官網(wǎng)的用來管理評論的chrome插件還過期不維護了拴事。
韓國人做的來必力沃斤,試過一下,支持的SNS很全刃宵,但也有問題衡瓶,比如不支持評論導入。而且肉眼望過去牲证,bug比較多哮针。
網(wǎng)易云跟帖適合灌水,對于博客網(wǎng)站并不是很適合坦袍。
友言就不吐槽了十厢。
所以捂齐,轉了一圈蛮放,發(fā)現(xiàn)還是老牌的diqus最靠譜,也不用擔心突然哪一天它也跪了的問題奠宜。
由于眾所周知的原因包颁,disqus在國內不能訪問。但由于disqus提供了API(待會再說明這玩意怎么坑)压真,于是可以通過API來訪問和創(chuàng)建評論娩嚼。
于是半個多月前,就做了一些準備工作滴肿。一是確認了通過API訪問使用disqus的可行性岳悟,二是大概查了一下有沒有現(xiàn)成的輪子。于是找到了fooleap用php為jekyll做的一個代理泼差。
考慮到對php不是很熟悉竿音,如果要改源代碼并在hexo里集成對于我而言有些難度和屎,所以最終打算自己用熟悉的Node.js重新造個輪子,用來為hexo博客做disqus的代理春瞬。
并且,我可以自定義評論的樣式套啤,來深度整合進自己寫的主題aqua宽气,感覺會很贊。
項目地址:Disqus-Proxy
配置說明:Disqus-Proxy-Config
思路
整體流程是這樣的潜沦,在前端頁面上測試disqus加載是否成功萄涯,如果成功則顯示disqus的評論框,反之加載獨立的評論框唆鸡,并將請求發(fā)送給自己在國外的vps涝影,利用vps做反向代理,接收來自客戶端的請求到disqus服務器并再轉發(fā)給客戶端争占。
流程圖大概長這樣:
于是問題就來了:如何檢測disqus是否能成功加載燃逻?
我們先來看看disqus的通用植入代碼
<div id="disqus_thread"></div>
<script>
var disqus_config = function () {
this.page.url = PAGE_URL; // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
(function() {
var d = document, s = d.createElement('script');
s.src = 'https://yourname.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
可以看到disqus是通過創(chuàng)建script
標簽,通過設置src
屬性動態(tài)引入腳本的臂痕。因此伯襟,我們可以通過判斷這個腳本在指定時間內加載是否完成來判斷是否載入自制評論框。
這里感謝fooleap在這篇文章里提供的思路握童,下面是我在React.js
實現(xiàn)判斷的代碼:
componentWillMount() {
const s = document.createElement('script')
const username = window.disqusProxy.username
s.src = `https://${username}.disqus.com/embed.js`
s.async = true
s.setAttribute('data-timestamp', String(+new Date()))
s.onload = () => {
this.setState({disqusLoaded: true})
}
s.onerror = () => {
this.setState({disqusLoaded: false})
console.log('Failed to load disqus. Load disqus-proxy instead.')
}
document.body.appendChild(s)
setTimeout(async () => {
if (!this.state.disqusLoaded) {
const DisqusProxy = await import('./DisqusProxy')
this.setState({DisqusProxy: DisqusProxy.default})
}
}, 3000)
}
如果在3秒后姆怪,腳本仍然未loaded
,那么引入自制的評論框澡绩。
這里利用ES6中動態(tài)import
的命令稽揭,通過webpack
來分割代碼,使得根據(jù)disqus加載狀況肥卡,決定要不要加載剩余的模塊溪掀,以此減少不必要的腳本載入。
評論框使用了React.js
召调,后端用了Node.js
的Koa
框架膨桥,負責請求轉發(fā)。
配置
- 一臺國外的VPS服務器
前端配置
在 Hexo 主題 Aqua 中使用 diqus-proxy
Hexo 主題 aqua 已經集成了 disqus-proxy唠叛,可以直接在主題的config.yml里配置只嚣。
# 如果disqus賬號名沒設置 那么disqus_proxy也不會生效
disqus_username: ciqu
disqus_proxy:
# 下面兩項你需要更改為自己服務器的域名和端口
server: disqus-proxy.ycwalker.com
port: 443 #端口號需要與后端設置一致
# 頭像路徑設置
default_avatar: /avatars/default-avatar.png
admin_avatar: /avatars/admin-avatar.jpg
# 腳本和css路徑通常不需要更改
script_path: /static/js/main.0d0338ae.js
css_path: /static/css/main.0603c539.css
所以在這里,通常只需要配置三項就可以了艺沼,分別是
- disqus 的用戶名稱
- 你用于代理disqus的VPS服務器地址
- 你用于代理disqus的端口
對于 aqua 册舞,這些就是disqus-proxy全部前端配置。
在其他 Hexo 主題中使用 diqus-proxy
如果你用其他主題障般,那么前端配置會麻煩一些调鲸。
Hexo主題一般用的渲染引擎有pug
(原jade
)盛杰,ejs
和swig
等。
如何看hexo的渲染主題是哪一個藐石? 進入主題目錄下的layout目錄即供,看文件的格式就知道了。
下面我會一一說明配置方法于微。
模板引擎pug(jade)
進入hexo的主題目錄逗嫡,找到layout
文件夾。
通常來說株依,評論會單獨寫成一個文件驱证,比如comment.pug
,在layout
文件夾下面或者其子目錄下面。
在這個comment.pug
中恋腕,hexo
在生成(hexo g
)時抹锄,會對每一篇生成的文章調用這個文件進行渲染。
在渲染的過程中荠藤,hexo
會提供page
這個全局變量伙单,在pug文件中,你可以在生成的script中以#{page}
調用商源。
將comment.pug
全部替換成如下內容(注意縮進):
div#disqus_thread
div#disqus_proxy_thread
script.
window.disqusProxy = {
username: 'ciqu',
server: 'disqus-proxy.ycwalker.com',
port: 5509,
defaultAvatar: '/avatars/default-avatar.png',
adminAvatar: '/avatars/admin-avatar.jpg',
identifier: "#{page.path}"
};
window.disqus_config = function () {
this.page.url = "#{page.permalink}"
this.page.identifier = "#{page.path}"
};
var s = document.createElement('script');
s.src = '/static/js/main.56688539.js';
s.async = true;
document.body.appendChild(s);
link(rel="stylesheet" href="/static/css/main.0603c539.css")
這個文件將會渲染出這樣的結果
<div id="disqus_thread"></div>
<div id="disqus_proxy_thread"></div>
<script>
window.disqusProxy = {
username: 'ciqu',
server: 'disqus-proxy.ycwalker.com',
port: 5509,
defaultAvatar: '/avatars/default-avatar.png',
adminAvatar: '/avatars/admin-avatar.jpg',
identifier: "2017/05/25/have-a-nice-weekend/"
};
window.disqus_config = function() {
this.page.url = "http://ycwalker.com/2017/05/25/have-a-nice-weekend/"
this.page.identifier = "2017/06/01/diqus-proxy-config/"
}
;
var s = document.createElement('script');
s.src = '/static/js/main.56688539.js';
s.async = true;
document.body.appendChild(s);
</script>
<link rel="stylesheet" href="/static/css/main.0603c539.css">
其中window.disqusProxy
對象是自制的評論框參數(shù)车份,參數(shù)說明如下:
-
userName
是disqus賬戶名-
server
是你啟用disqus代理的VPS的域名 -
port
是VPS服務器啟用disqus代理的端口,需要和后端設置的端口一致 -
defaultAvatar
和adminAvatar
分別是默認頭像和管理員頭像 -
identifier
是告訴disqus該文章對應評論的標識符牡彻,一般以文章的路徑做標識符扫沼。
-
其中window.disqus_config
是disqus成功加載后,disqus用到的參數(shù)庄吼,分別是文章的地址和標識符缎除。
模板引擎swig
對于渲染引擎為swig
的hexo
主題,可以將類似comment.swig
直接改成這樣
{% if true %}
<div id="disqus_proxy_thread"></div>
<div id="disqus_thread">
<script type="text/javascript">
window.disqusProxy = {
username: 'ciqu',
server: 'disqus-proxy.ycwalker.com',
port: 5509,
defaultAvatar: '/avatars/default-avatar.png',
adminAvatar: '/avatars/admin-avatar.jpg',
identifier: '{{ page.path }}'
};
window.disqus_config = function () {
this.page.url = '{{ page.permalink }}';
this.page.identifier = '{{ page.path }}';
};
var s = document.createElement('script');
s.src = '/static/js/main.0d0338ae.js';
s.async = true;
document.body.appendChild(s);
</script>
<link rel="stylesheet" href="/static/css/main.0603c539.css">
{% endif %}
比如著名的hexo
主題next就用了swig
做模板引擎总寻,你只要將其主題下的next/layout/_partial
目錄下的comments.swig
內容全部替換成上面的代碼就OK了器罐。
注意,window.disqusProxy
和window.disqus_config
的配置項請參閱前文pug
部分的說明渐行。
模板引擎ejs
對于渲染引擎為ejs
的hexo
主題轰坊,可以將類似comment.ejs
直接改成這樣:
<div id="disqus_proxy_thread"></div>
<div id="disqus_thread"></div>
<script>
window.disqusProxy = {
username: 'ciqu',
server: 'disqus-proxy.ycwalker.com',
port: 5509,
defaultAvatar: '/avatars/default-avatar.png',
adminAvatar: '/avatars/admin-avatar.jpg',
identifier: '<%= page.path %>'
};
window.disqus_config = function () {
this.page.url = '<%= page.permalink %>';
this.page.identifier = '<%= page.path %>';
};
var s = document.createElement('script');
s.src = '/static/js/main.0d0338ae.js';
s.async = true;
document.body.appendChild(s);
</script>
<link rel="stylesheet" href="/static/css/main.0603c539.css">
比如使用ejs
為模板引擎的fexo主題,可以直接將fexo/layout/_partial/component/
目錄下的comments.ejs
全部改為上述文件就行了祟印。注意肴沫,window.disqusProxy
和window.disqus_config
的配置項請參閱前文pug
部分的說明。
劃重點:
下載或者克隆項目代碼 disqus-proxy
復制disqus-porxy中已經build完畢的disqus-proxy/build
目錄下的static
文件夾和avatars
文件夾到主題目錄的source文件夾下蕴忆。
將static/js/
下的以main開頭js文件名替換上面代碼中的s.src = '/static/js/main.56688539.js';
文件名颤芬。
將static/css/
目錄下的css文件名替換link(rel="stylesheet" href="/static/css/main.0603c539.css")
中的文件名。
PS:你會發(fā)現(xiàn)static/js/
目錄下有兩個js文件,其中main開頭的腳本用于檢測網(wǎng)絡狀況以選擇性加載disqus站蝠,另一個js文件為自制的評論框汰具,在disqus不能加載時會被之前的js文件動態(tài)請求加載,所以不用管它菱魔,你只要確認它的路徑在/static/js/
下就可以了留荔。
至此,前端部分配置完成澜倦。
后端配置
后端用了Node.js
存谎,由于采用了Koa
框架和async/await
語法,所以需要Node.js
版本7.6
以上肥隆,話說端午節(jié)后Node.js
最新版本都到8了耶。
在服務器上clone代碼:
git clone https://github.com/ciqulover/disqus-proxy
安裝依賴
只需要安裝后端的依賴
npm i --production
// 或者
yarn install --production
配置server
目錄下的config.js
module.exports = {
// 服務端端口稚失,需要與disqus-proxy前端設置一致
port: 5509,
// 你的diqus secret key
api_secret: 'your secret key',
// 你的disqus名稱
username:'ciqu',
// 服務端socks5代理轉發(fā)栋艳,便于在本地測試,生產環(huán)境通常為null
socks5Proxy: null,
// 日志輸出位置,輸出到文件或控制臺 'file' | 'console'
log: 'console'
}
獲取api-secret
api-secret
需要你在disqus的官方網(wǎng)站上開啟API權限句各,申請成功后會得到這個秘鑰吸占。
并且需要在后臺的Settings => Community里開啟訪客評論
啟動
cd server
node index.js
推薦用pm2
在生產環(huán)境啟動,否則你斷開ssh凿宾,node進程就終止了
npm i pm2 -g
pm2 start index.js
如果你在配置文件中選擇log
類型為file
, 那么輸出的日志文件將在默認為server目錄下的disqus-proxy.log
Done !
關于disqus的API
雖然disqus提供了豐富的API矾屯,和界面友好的文檔,但應用起來是有很多問題的初厚。
首先件蚕,在服務端發(fā)通過API發(fā)送匿名評論是一個非官方支持的API,下面是之前咨詢這個問題時官方的答復郵件:
所以产禾,通過這個API創(chuàng)建匿名評論時排作,并不能傳遞IP地址的參數(shù),因為disqus在拿到評論時會自動以請求的IP地址作為評論的IP地址亚情。換句話說妄痪,通過后端API創(chuàng)建評論的IP是固定的。就是VPS的地址楞件。
為了解決這個問題衫生,可以拿到disqus用戶的token之后調用API評論。但這很不現(xiàn)實土浸。如果通過OAuth 2罪针,需要跳轉登錄,但一是用戶可能還沒注冊disqus請求匿名評論栅迄,二是既然能跳轉disqus登錄站故,還需要代理個毛線啊。
并且,由于disqus并沒有提供admin賬號的登錄API西篓,所以無法在服務端拿到moderator用戶的cookie愈腾,所以不能直接審核通過匿名評論,而需要在匿名評論發(fā)布后岂津,admin登錄到控制臺審核通過評論虱黄。
由于不能指定評論的IP,并且評論的IP地址只能是VPS主機的IP吮成,所以導致點贊等功能是無法通過后端API來搞的橱乱。
對于https網(wǎng)站
由于我的網(wǎng)站是位于github上的靜態(tài)的https網(wǎng)站,所以在向我的VPS服務器發(fā)送http的XHR請求時就出了問題粱甫。通常來說泳叠,VPS主機是沒有證書的,所以在不購買或者不用自簽名正式的情況下是不能啟用https的茶宵。然而危纫,在https網(wǎng)站發(fā)送請求http會被瀏覽器無情的block掉。
這里我的解決方案是又拍云乌庶。
又拍云提供了源站回源的功能种蝶,目的是緩存源站的靜態(tài)文件。于是瞒大,我就想到了使用又拍云對前端頁面https請求的目標地址進行http回源螃征,這樣相當于在前端頁面與VPS再加了一層代理,前端頁面的https請求通過又拍云以http協(xié)議請求到VPS服務器透敌,得到結果后返回前端頁面盯滚。這就相當于在前端頁面與disqus服務器間架設了兩層代理。
所以這樣就相當于以https訪問又拍云拙泽,再以又拍云以http協(xié)議訪問VPS淌山。只要在這里不設置緩存就行了。
這會不會導致速度減慢呢顾瞻?其實泼疑,國內直連國外的VPS主機到并不一定比連接又拍云后再由又拍云連接VPS速度快。畢竟又拍云的各個主機通常位于主干網(wǎng)絡上荷荤,兩邊的訪問都會很快退渗,相當于一個網(wǎng)游加速器了。