简单修改 Ghost 0.5.8,使其支持使用反代方式的 CDN 加速

2015-01-24 18:43:21 +08:00
 soulteary
Ghost最近势头不错,连续几个月,基本每个月都是release 2个版本,不过也正是因为如此,ghost每次发布之后,API都会有些许变动。

故,不建议在官方merge任何i18N功能之前,使用中文版本,毕竟后台没几个英文,前台主题可以依赖自己的主题模板去实现i18N,没有使用影响。

下面分享一个简单修改的细节,可以使用七牛一类的CDN服务商无痛加速网站资源。

七牛官方分享的加速方案是网友修改storage接口,将内容不存自己的服务器,直接使用CDN,个人觉得不妥,不利于迁移维护,以及临时灾备,况且把AK,SK都存下来,对于后续升级也不利。而且CDN基本都提供反代功能,可能大家不叫这个词,业界唤作“一键加速”。

0.5.8版本的Ghost默认暂时不支持CDN,不过简单修改模板helpers可以完成我们的需求。
首先,我们需要把通用配置添加到config.js中。这里我们使用一个对象来储存cdn信息,以方便扩展和维护。

```
varpath=require('path'),
config;

config={
// ### Production
// When running Ghost in the wild, use the production environment
// Configure your URL and mail settings here
production:{
url:'http://my-ghost-blog.com',
mail:{},
database:{
client:'sqlite3',
connection:{
filename:path.join(__dirname,'/content/data/ghost.db')
},
debug:false
},

server:{
// Host to be passed to node's `net.Server#listen()`
host:'127.0.0.1',
// Port to be passed to node's `net.Server#listen()`, for iisnode set this to `process.env.PORT`
port:'2368'
},
cdn:{assets:"//your-cdn-path"}
},
...
```

如果你想关闭CDN,请设置cdn:false,或者删除这个对象。

然后我们需要修改模板的assets helper,这个helper主要是针对模板使用assets方法引入的资源进行地址修正。

关键代码如下:

```
...
// Get rid of any leading slash on the context
context=context.replace(/^\//, '');
output+=context;


// cdn assets option
// 如果设置了CDN,那么使用CDN配置项目中的路径进行资源替换
if(!isAdmin&&config['cdn']&&config['cdn']['assets']){
output=utils.assetTemplate({
source:config['cdn']['assets']+output,
version:config.assetHash
});
}else{
if(!context.match(/^favicon\.ico$/)){
output=utils.assetTemplate({
source:output,
version:config.assetHash
});
}
}
...
```

image helper会修改markdown中的image标签的路径,如果你直接有使用低版本,会插入完整的路径,需要自己修改,或者在此基础上,进一步修改,没几篇内容的话,自己手动改下吧。

关键代码如下:

```
image=function(options){
varabsolute=options&&options.hash.absolute,
imgPath=this.image;
if(this.image){
// cdn assets
// 如果设定了cdn,那么对内容添加cdn
if(config['cdn']&&config['cdn']['assets']){
imgPath=config['cdn']['assets']+this.image;
}
returnconfig.urlFor('image',{image:imgPath},absolute);
}
};
```

最后修改url helper对遗漏的内容进行补刀,在这个helper里,你可以拿到最后的render结果。

关键代码如下:

```
if(schema.isUser(this)){
returnconfig.urlFor('author',{author:this},absolute);
}
// compressing page content can improve the response speed
// 较稳妥的压缩页面输出内容,提高响应速度,markdown转换之后,效果明显。
this.body=this.body.replace(/>(\n*|\r*|\s*)</gm,'><').replace(/>(\s*|\n*|\r*)/gm,'>');
// enable cdn for post content
if(config['cdn']&&config['cdn']['assets']){
this.body=this.body.replace(/<img.*?src=\"(.*?)\".*?\/?>/g,function(src,uri){
if(uri.indexOf(config['cdn']['assets'])!==0){
if(uri.indexOf(config['url'])===0){
returnsrc.replace(config['url'],config['url'].split('://')[0]+':'+config['cdn']['assets']);
}else{
if(uri.indexOf('://')===-1){
returnsrc.replace(uri,config['cdn']['assets']+uri);
}
}
}
});
}
returnconfig.urlFor(this,absolute);
```

另外,英文摘要输出对于中文摘要来说不太友好,简单的优化如下:

```
// Strip inline and bottom footnotes
excerpt=excerpt.replace(/<a href="#fn.*?rel="footnote">.*?<\/a>/gi,'');
excerpt=excerpt.replace(/<div class="footnotes"><ol>.*?<\/ol><\/div>/,'');

// truncate for GBK when use character mode
// 中文内容截断,根据字符的方式
if(truncateOptions.characters){
// remove heading tags with content and other html tags
excerpt=excerpt.replace(/<h\d.*?\/h\d>/gi,'');
excerpt=excerpt.replace(/<\/?[^>]+>/gi,'');
vararr=excerpt.split("\n"),tmp="";
for(vari=0,j=arr.length;i<j;i++){
// remove \n && \r
arr[i]=arr[i].replace(/(\r\n|\n|\r)+/gm,'');
if(arr[i]){
if(tmp.length<truncateOptions.characters){
varlen=tmp.length;
if(len+arr[i].length>truncateOptions.characters){
tmp+=arr[i];
tmp=tmp.substr(0,truncateOptions.characters-3);
tmp=tmp.substr(0,len)+tmp.substr(len).replace(/(,|\,|\:|\-|\"|、|“|”)/gm,'')+'...';
break;
}else{
// less than 10 chars is unnecessary
if(truncateOptions.characters-len<10&&arr[i].length<10){
break;
}else{
tmp+=arr[i];
}
}
}
}
}
excerpt=tmp.replace(/(\r\n|\n|\r)+/gm,' ');
}else{
// Strip other html
excerpt=excerpt.replace(/<\/?[^>]+>/gi,'');
excerpt=excerpt.replace(/(\r\n|\n|\r)+/gm,' ');
}

/*jslint regexp:false */

if(!truncateOptions.words&&!truncateOptions.characters){
truncateOptions.words=50;
}
```

如果你觉得修改麻烦,也可以直接到相关PR页面下载文件: https://github.com/TryGhost/Ghost/pull/4839/files

–EOF–

原文地址: http://www.soulteary.com/2015/01/24/speedly-ghost-static-files.html
3517 次点击
所在节点    分享创造
18 条回复
soulteary
2015-01-24 18:43:44 +08:00
...心说不用gist了...然后代码...
Cee
2015-01-24 18:45:30 +08:00
@soulteary 赞一个w
soulteary
2015-01-24 18:51:57 +08:00
@Cee 么么哒
sanddudu
2015-01-24 19:04:56 +08:00
之前解释过了,不存在本地其实是为了不占用本地空间,不过其实就是一行代码的事情。
目前 Ghost 升级都不需要修改 config.js (内容够用,而且涉及网站关键内容),所以存下来不至于出现不好升级的情况。
另外对于添加 CDN 功能,我不好评价好坏,功能的添加还是需要官方的定夺。另外我猜测 merge 的可能性不大(目前仍然不完善)。
另外,这样引用配置内容必须保证没有以模块的方式启动(如果以模块式启动,第一次初始化用的是默认的 config.js,第二次才会加载参数指定的配置文件,会导致运行失败),我的做法是在 config\index.js 里加入判断,当读取文件中出现配置字段再插入 config 对象导入。
sanddudu
2015-01-24 19:06:16 +08:00
我指的不完善是系统功能的不完善,CDN 功能可能不会官方做兼容(通过 API 进行实现)
hjc4869
2015-01-24 19:17:54 +08:00
我在想为何这么长时间没有人在CDN方面给官方发pull request,而是每个版本都自己修改
理论上说这个并不难啊。。
soulteary
2015-01-24 19:19:53 +08:00
@sanddudu

个人木有看到这个解释,加之不喜使用push的方式去同步内容,于是写了这个,如有冒犯,见谅。

-- api && merge
是的,对外的api不完善,以及每版都会有细节修改,以及这个并非主流需求,merge概率不大,但是如果有人需要的话,关键词搜索可以得到一个相对简单高效的思路。

-- CDN方式
关于本地是否留原始copy,我想我使用CDN的方式已经表明了态度,即使SLA再高,本地备份还是需要的。而且push浪费资源,成本相对较高,反向代理基本无痛。

-- config.js储存数据
诚如官方示例,config可以写sql pass,写ak,sk也无所谓,只是这里有一个差异。
sql pass一般人不会开启远程连接,防火墙开启的端口也有限(RDC另外谈)。而cdn的ak,sk泄露后...你懂的。但是存一个反代地址,没有什么风险。
soulteary
2015-01-24 19:20:10 +08:00
@hjc4869 见上,非主流需求。
easychen
2015-01-24 19:22:22 +08:00
我还是比较喜欢用中文版本。至少分享到微博和微信不用自己折腾模板了 https://github.com/diancloud/Ghost
soulteary
2015-01-24 19:26:32 +08:00
@easychen easy大叔,这个点的话,你用多说dev版本,自动就带分享了,或者引用一个分享组件,没啥成本的。官方如果开i18N后,我觉得可以稳妥使用“汉化版”。
soulteary
2015-01-24 19:28:44 +08:00
@easychen 另外,中文版本太多了,前两天舍友还star另外一个,而这类中文版本的修改点建立在不稳定API上,个人不喜,https://github.com/bigertech/Ghost
sanddudu
2015-01-24 19:56:59 +08:00
@soulteary 官方的 i18n 估计今年会开始
中文版本推荐 GhostChina 和点云的,这两个版本的制作者我都接触过,比较靠谱。
soulteary
2015-01-24 20:05:25 +08:00
@sanddudu 点开你资料后get到为啥你的默认方式不是存本地了.... :D

不过,还是保留观点,ghost后台哪怕是现在的0.5.8都是几个十分简单的单词,除了mail外,中文都可以通过模板完成。实在需求中文化的话,使用也无可厚非,不过还是建议等官方出来i18N后使用。
sanddudu
2015-01-24 20:06:53 +08:00
@soulteary 另外提醒一下,点云的版本的 gravatar 获取用的是他们自己的反代,在国外用就不需要,有可能拖慢速度,可以自己把地址修改回来

core/server/models/user.js
L881
把 "//diancloud.sinaapp.com/avatar/" 改回 "//www.gravatar.com/avatar/"
soulteary
2015-01-24 20:11:59 +08:00
@sanddudu = =...

我个人习惯用默认or自己搭的cache/其实多说最近换成0.gravatar.com速度蛮快的。
http://www.soulteary.com/2014/12/14/speedly-your-gravatar.html
thonatos
2015-01-25 03:16:31 +08:00
虽然不用,但也默默点个赞,╮(╯▽╰)╭
niuer
2015-01-26 14:03:49 +08:00
是否进行迁移维护或者临时灾备,仅是用户自己在对七牛的使用上的差异性:
1. 用户可以存储数据在自己的服务器上,或者依然使用原来的storage接口上传到自己的服务器上,然后从自己的服务器上直接上传到七牛,对外提供七牛的链接即可;
2. 用户可以修改上传接口,直接上传到七牛的空间中,然后回调业务服务器,业务服务器根据回调的信息可以拿到上传到七牛的资源url,然后备份到本地即可。

ak、sk每个账号只有两对,升级维护设置一个全局变量后基本不太需要修改,cdn确实提供了"一键加速的功能",但是不是所有的用户的使用场景都非常适合纯cdn的一键加速的,毕竟七牛还有图片处理和音视频转码的服务。
soulteary
2015-01-26 14:35:10 +08:00
@niuer 目测是相关工作人员,:D

确实因人而异了,方法并非针对所有人。

不过对于一般用户而言,如非内容托管商,需要节约空间减少带宽消耗以节约成本以及提供更好访问性外,显然你提到的1 & 2不便于使用。

ak/sk如果不调用接口上传,根本没必要储存本地,潜在的隐患是被三方拿到滥用。

如果使用者依赖转码或者图片处理,那么使用接口无可厚非,且根本不会使用我的方法,你多虑了。

最后,个人认为,服务提供商不应该构建使用成本,而应当缩小使用成本,用户选择一个平台以及其对应的功能,会出自心底的信任,而非产品的使用门槛。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/165082

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX