V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
soulteary
V2EX  ›  分享创造

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

  •  
  •   soulteary ·
    soulteary · 2015-01-24 18:43:21 +08:00 · 3497 次点击
    这是一个创建于 3599 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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
    18 条回复    2015-01-26 14:35:10 +08:00
    soulteary
        1
    soulteary  
    OP
       2015-01-24 18:43:44 +08:00
    ...心说不用gist了...然后代码...
    Cee
        2
    Cee  
       2015-01-24 18:45:30 +08:00
    @soulteary 赞一个w
    soulteary
        3
    soulteary  
    OP
       2015-01-24 18:51:57 +08:00
    @Cee 么么哒
    sanddudu
        4
    sanddudu  
       2015-01-24 19:04:56 +08:00
    之前解释过了,不存在本地其实是为了不占用本地空间,不过其实就是一行代码的事情。
    目前 Ghost 升级都不需要修改 config.js (内容够用,而且涉及网站关键内容),所以存下来不至于出现不好升级的情况。
    另外对于添加 CDN 功能,我不好评价好坏,功能的添加还是需要官方的定夺。另外我猜测 merge 的可能性不大(目前仍然不完善)。
    另外,这样引用配置内容必须保证没有以模块的方式启动(如果以模块式启动,第一次初始化用的是默认的 config.js,第二次才会加载参数指定的配置文件,会导致运行失败),我的做法是在 config\index.js 里加入判断,当读取文件中出现配置字段再插入 config 对象导入。
    sanddudu
        5
    sanddudu  
       2015-01-24 19:06:16 +08:00
    我指的不完善是系统功能的不完善,CDN 功能可能不会官方做兼容(通过 API 进行实现)
    hjc4869
        6
    hjc4869  
       2015-01-24 19:17:54 +08:00
    我在想为何这么长时间没有人在CDN方面给官方发pull request,而是每个版本都自己修改
    理论上说这个并不难啊。。
    soulteary
        7
    soulteary  
    OP
       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
        8
    soulteary  
    OP
       2015-01-24 19:20:10 +08:00
    @hjc4869 见上,非主流需求。
    easychen
        9
    easychen  
       2015-01-24 19:22:22 +08:00
    我还是比较喜欢用中文版本。至少分享到微博和微信不用自己折腾模板了 https://github.com/diancloud/Ghost
    soulteary
        10
    soulteary  
    OP
       2015-01-24 19:26:32 +08:00
    @easychen easy大叔,这个点的话,你用多说dev版本,自动就带分享了,或者引用一个分享组件,没啥成本的。官方如果开i18N后,我觉得可以稳妥使用“汉化版”。
    soulteary
        11
    soulteary  
    OP
       2015-01-24 19:28:44 +08:00
    @easychen 另外,中文版本太多了,前两天舍友还star另外一个,而这类中文版本的修改点建立在不稳定API上,个人不喜,https://github.com/bigertech/Ghost
    sanddudu
        12
    sanddudu  
       2015-01-24 19:56:59 +08:00
    @soulteary 官方的 i18n 估计今年会开始
    中文版本推荐 GhostChina 和点云的,这两个版本的制作者我都接触过,比较靠谱。
    soulteary
        13
    soulteary  
    OP
       2015-01-24 20:05:25 +08:00
    @sanddudu 点开你资料后get到为啥你的默认方式不是存本地了.... :D

    不过,还是保留观点,ghost后台哪怕是现在的0.5.8都是几个十分简单的单词,除了mail外,中文都可以通过模板完成。实在需求中文化的话,使用也无可厚非,不过还是建议等官方出来i18N后使用。
    sanddudu
        14
    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
        15
    soulteary  
    OP
       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
        16
    thonatos  
       2015-01-25 03:16:31 +08:00 via Android
    虽然不用,但也默默点个赞,╮(╯▽╰)╭
    niuer
        17
    niuer  
       2015-01-26 14:03:49 +08:00
    是否进行迁移维护或者临时灾备,仅是用户自己在对七牛的使用上的差异性:
    1. 用户可以存储数据在自己的服务器上,或者依然使用原来的storage接口上传到自己的服务器上,然后从自己的服务器上直接上传到七牛,对外提供七牛的链接即可;
    2. 用户可以修改上传接口,直接上传到七牛的空间中,然后回调业务服务器,业务服务器根据回调的信息可以拿到上传到七牛的资源url,然后备份到本地即可。

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

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

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

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

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

    最后,个人认为,服务提供商不应该构建使用成本,而应当缩小使用成本,用户选择一个平台以及其对应的功能,会出自心底的信任,而非产品的使用门槛。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2708 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 11:26 · PVG 19:26 · LAX 03:26 · JFK 06:26
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.