Ghost でリダイレクト処理を追加する

  • 1
    いいね
  • 0
    コメント

※ 2017/03/11 追記

v0.11.4 にてリダイレクト機能が実装されている模様です. 詳細は以下.
Redirects

この記事に書いているカスタマイズを行う必要はもうありません. ただ自分の備忘録として記事内容は残しておきます.

また、2017年3月現在での安定版は v0.11.* となっています ( ただし, 2017年3月20日にはサポートが切れます ) . 公式では既に v1.0 の α が公開されています。Github 上でソースを見る限り仕様・機能も大幅に変更・追加されているようです. 公式のブログでもそのように言及されています. 特に注目したいのが PostgreSQL のサポートがなくなることです. 今後, Ghostの使用するデータベースは MySQL 標準となるようです. ちなみに SQLite3 は開発用に残しておくそうです. v0.11 から v1.0 へのアップデート方法は CLI で行うか, 1クリックで行う方法を提供するようです.

以上の点から下記内容のカスタマイズを行うと正常にアップデートできない恐れがあるので, あくまで参考程度に留めておいてください.

目次

概要

Ghost は node.js 製のブログエンジンです. Ghost ではポストのパスはタイトルで決定します. 日本語のタイトルをつけるとパスが結構ひどい感じになるのが Ghost の不満点です.

  1. 「液晶ペンタブレット DTH-W1300 での外部出力に失敗!」

  2. /ye-jing-pentaburetuto-dth-w1300-denowai-bu-chu-li-nishi-bai/

例えば 1 のタイトルはデフォルトのままだと 2 のようなパスが設定されてしまいます(汗).

使い始めはあまり気にしませんでしたが, 投稿数が多くなるとやはり気になってしまいます. とはいえ一旦世の中に公開してしまえばそのパスは資産です.

パスが気に入らなければ修正可能です. ポストの編集画面にある歯車ボタンから変更できます. しかし, ある程度アクセスがある記事だとうかつにパスを修正するのはためらいますよね.

そこで大抵行き着く回答が古いパスを新しいパスにリダイレクトする解決手段です.
※ 余談ではありますが, Ghost では Dated Permalinks 設定により日付によるパーマリンクは可能.

リダイレクト処理

単にリダイレクト処理をするだけなら Ghost を使わずとも色々な手段が考えられます. Nginx をフロントサーバーにして Ghost を使っているのであればリダイレクト処理を Nginx に任せるのも手です.

ただ今回のお話しは, Ghost そのものにリダイレクト処理を組み込むお話です.

実装 - 設定ファイル編 -

とりあえず以下の様な仕様で実装します.

  • あるパスを新規に設定されたポストのパスに Ghost でリダイレクトする
  • リダイレクトの設定は redirects.json に記述する

redirects.json は以下のような構造を考えました.

{ 
    "host": "http://xxx.xxxxx.xxx",

    "redirects":{

        "/old-path1/":{
            "host": "",
            "path": "/new-path1/"
        },

        "/old-path2/":{
            "host": "http://ooo.ooooo.ooo",
            "path": "/new-path2/"
        },
        .
        .
        .
}
  • hostにはリダイレクト先のホストを指定します.
  • redirectsには旧パスに対するリダイレクト設定を指定します.
  • redirects内の各設定項目としては以下の通りです.
    • キーはリダイレクト前の旧パスにする. ※ポイントは//で囲むことです.
    • host にはそのパスに対するリダイレクト先のホストを指定. 何も指定しない場合はルートに定義されたhostの設定が優先される
    • pathにはリダイレクト先のパスを指定

つまり上記のredirects.jsonの設定だと以下の様なリダイレクト設定になる予定です.

*http://xxx.xxxxx.xxx/old-path1/http://xxx.xxxxx.xxx/new-path1/
*http://xxx.xxxxx.xxx/pld-path2/http://ooo.ooooo.ooo/new-path2/

実装 -コア編 -

参考にしたサイトは以下の通りです.

前提としてどこかのディレクトリに Ghsot がインストールされているものとします. ここでは話を分かりやすくするために /var/www/ghost/ 直下にインストールされているものとします.

機能を追加するためにコアをいじることになりますが, 可能ならばバックアップを取るか Git 等のバージョン管理ソフトで /var/www/ghsot/ 直下をバージョン管理してしまいましょう.

編集するファイル

Ghost を特にカスタマイズしていないのであればディレクトリ直下のファイル一覧は以下の様になっているものと思われます.

Gruntfile.js
LICENSE
PRIVACY.md
README.md
config.example.js
config.js
content
core
index.js
node_modules
npm-shrinkwrap.json
package.json

sudo touch /var/www/ghost/redirects.json でリダイレクト設定を記述するファイルを生成して実装 - 設定ファイル編 - の設定をコーディングしましょう.

続いて, 編集するファイルは /var/www/ghost/core/routes/frontend.js です.

ソース改変

まず, オリジナルのソースは以下の通りです.

node.js
var frontend        = require('../controllers/frontend'),
    channels        = require('../controllers/frontend/channels'),
    config          = require('../config'),
    express         = require('express'),
    utils           = require('../utils'),
    privateBlogging = require('../apps/private-blogging'),

    frontendRoutes;

frontendRoutes = function frontendRoutes() {
    var router = express.Router(),
        subdir = config.paths.subdir,
        routeKeywords = config.routeKeywords;

    // ここにコードを追加する

    // ### Admin routes
    router.get(/^\/(logout|signout)\/$/, function redirectToSignout(req, res) {
        utils.redirect301(res, subdir + '/ghost/signout/');
    });
    router.get(/^\/signup\/$/, function redirectToSignup(req, res) {
        utils.redirect301(res, subdir + '/ghost/signup/');
    });

    // redirect to /ghost and let that do the authentication to prevent redirects to /ghost//admin etc.
    router.get(/^\/((ghost-admin|admin|wp-admin|dashboard|signin|login)\/?)$/, function redirectToAdmin(req, res) {
        utils.redirect301(res, subdir + '/ghost/');
    });

    // Post Live Preview
    router.get('/' + routeKeywords.preview + '/:uuid', frontend.preview);

    // Channels
    router.use(channels.router());

    // Default
    router.get('*', frontend.single);

    // @TODO: this can be removed once the proper app route hooks have been set up.
    privateBlogging.setupRoutes(router);

    return router;
};

module.exports = frontendRoutes;

コードを追加する箇所は14行目です.

追加するコードは以下の通りです.

node.js
// リダイレクトの設定を記述した JSON を読み込む
var redirects = require('../../../redirects.json');
var host = redirects.host;

// リダイレクト前のパスに一致する正規表現の文字列を生成する
var rpathRegularExcpression = '\/(';
for(redirect in redirects.redirects){
    rpathRegularExcpression += redirect.replace(/\//g, '') + '|';
}
rpathRegularExcpression = rpathRegularExcpression.substr(0, (rpathRegularExcpression.length -1));
rpathRegularExcpression += ')\/';

// ### redirects routes
router.get(new RegExp(rpathRegularExcpression), function redirectOldURLRouter(req, res){

        var rhost = redirects.redirects[req.url].host;
        var redirectpath = '';

        // リダイレクト先の URL を生成する
        if(rhost != '') redirectpath = rhost + redirects.redirects[req.rul].path;
        else redirectpath = host + redirects.redirects[req.url].path;

        console.log("REDIRECT", req.url + " -> " + redirectpath);
        // リダイレクト
        res.redirect(301, redirectpath);

});

上記のコードのポイントを解説します.

追加コード解説

2 行目

設定ファイルの JSON は node.js であれば require でオブジェクトとして読み込むことができます. Ghost でも使用しているようなので気にせず同じ方法で読み込んでしまいましょう.

6 ~ 14 行目

続いて, 読み込んだ設定データにあるリダイレクト前のパスを検出する正規表現を作成します. node.js つまり javascript では正規表現は new RegExp() で生成可能です. 引数には正規表現を表す文字列を指定します.

14 ~ 26 行目

ghost では, 各ポストのパスは /パス名/ で生成されます. router.get メソッドは第1引数に指定された文字列または正規表現にマッチするパスがリクエストされた時, 第2引数のコールバックに処理が移ります. コールバック関数の引数にはリクエストオブジェクトとレスポンスオブジェクトが渡されます.

16 行目

引数の req オブジェクトからキャッチしたリクエストパスを取得できます. 読み込んだ JSON オブジェクトの要素にそのままキーとして渡せば対応するリダイレクト設定を取得できるはずです.

20 行目

host キーに値が設定されているかどうかでリダイレクト先のホストを決定します. ホストと新パスを組み合わせてリダイレクト先の URL が生成されます.

25 行目

引数の res オブジェクトを使用してリダイレクトさせます.

最終結果

以上の内容から最終的なコードは以下の通りです.

node.js
var frontend        = require('../controllers/frontend'),
    channels        = require('../controllers/frontend/channels'),
    config          = require('../config'),
    express         = require('express'),
    utils           = require('../utils'),
    privateBlogging = require('../apps/private-blogging'),

    frontendRoutes;

frontendRoutes = function frontendRoutes() {
    var router = express.Router(),
        subdir = config.paths.subdir,
        routeKeywords = config.routeKeywords;

    var redirects = require('../../../redirects.json');
    var host = redirects.host;

    var rpathRegularExcpression = '\/(';
    for(redirect in redirects.redirects){
        rpathRegularExcpression += redirect.replace(/\//g, '') + '|';
    }
    rpathRegularExcpression = rpathRegularExcpression.substr(0, (rpathRegularExcpression.length -1));
    rpathRegularExcpression += ')\/';

    // ### redirects routes
    router.get(new RegExp(rpathRegularExcpression), function redirectOldURLRouter(req, res){

        var rhost = redirects.redirects[req.url].host;
        var redirectpath = '';

        if(rhost != '') redirectpath = rhost + redirects.redirects[req.rul].path;
        else redirectpath = host + redirects.redirects[req.url].path;

        console.log("REDIRECT", req.url + " -> " + redirectpath);
        res.redirect(301, redirectpath);

    });

    // ### Admin routes
    router.get(/^\/(logout|signout)\/$/, function redirectToSignout(req, res) {
        utils.redirect301(res, subdir + '/ghost/signout/');
    });
    router.get(/^\/signup\/$/, function redirectToSignup(req, res) {
        utils.redirect301(res, subdir + '/ghost/signup/');
    });

    // redirect to /ghost and let that do the authentication to prevent redirects to /ghost//admin etc.
    router.get(/^\/((ghost-admin|admin|wp-admin|dashboard|signin|login)\/?)$/, function redirectToAdmin(req, res) {
        utils.redirect301(res, subdir + '/ghost/');
    });

    // Post Live Preview
    router.get('/' + routeKeywords.preview + '/:uuid', frontend.preview);

    // Channels
    router.use(channels.router());

    // Default
    router.get('*', frontend.single);

    // @TODO: this can be removed once the proper app route hooks have been set up.
    privateBlogging.setupRoutes(router);

    return router;
};

module.exports = frontendRoutes;

上記コードを適用したら Ghost を再起動します. 問題がなければ正常に再起動できます.
設定ファイルに記述した旧パスでアクセスして新パスにリダイレクトされたら成功です.

まとめ

  • プラグイン機能が欲しいンゴ