From Tumblr to Ghost

Post by Saul Shanabrook

Hey world, I changed my blog!

I finally got sick and tired of that little tiny windows Tumblr gives you to compose posts. Plus, lets be honest, themes for Tumblr also pretty much stink.

So why not do the hip new thing and move to Ghost?

The editor is just darn beautiful! (oooh meta) screen shot of this editor

So after a couple of long days I finally succeeded in getting Ghost hosted on Heroku. If you want to do the same, check out saulshanabrook/ghost-heroku. That is the source for this blog actually.

A few other people seem to be hosting Ghost on Heroku, btu I wanted to do a few things differently. You can see them all at work in saulshanabrook/ghost-heroku.

Database URL from env

I wanted to use the DATABASE_URL directly from the environment, without having to split it up into different parts, like every other tutorial reccomends.

So not this:

database: {  
    client: 'postgres',
    connection: {
          host: process.env.POSTGRES_HOST,
          user: process.env.POSTGRES_USER,
          password: process.env.POSTGRES_PASSWORD,
          database: process.env.POSTGRES_DATABASE,
          port: '5432'
    }

I figured out that you can actually do just that:

database: {  
    client: 'pg',
    connection: process.env.DATABASE_URL,
    debug: false
},

The Ghost documentation doesn't mention this feature, but I dug down a few libraries to figure out what was actually parsing the database config. It turns out that
node-postgres is actually used to do the actual parsing. I tried it and luckily it worked! So please stop splitting up DATABSAE_URL for ghost!

Ghost as npm module

The Ghost documentation reccomends downloading a zip of the repo and simply modifying stuff where you need it. Luckily, all the modification can really take place in one file config.js. I wanted to install ghost with npm install ghost --production --save for a couple reasons. First, it keeps my work seperate from the Ghost work. I can easily see which files are specific to my project and which ones are specific to ghost, because it is relegated to /node_modules/ghost/. Second, it makes upgrading ghost super painless, I just have to change the version in my package.json.

I spent quite a long time trying different ways of running ghost as a installed modules (it doesn't really like it). For a while I was simply copying the config.js in the directory root to /node_modules/ghost/config.js, cd-ing into /node_modules/ghost and running npm start. However, that felt a little gross. So instead I looked at where the config was being imported. I figured out that the function at ghost/core actually takes an optional config object, so I created my own index.js in my module that calls that function with my core.

var ghost = require('ghost/core'),  
    config = require('./config.js');

ghost(config);  

Also I had to add an environmental variable GHOST_CONFIG that pointed to the absolute path of my new config.

I just put this in my config.js

process.env.GHOST_CONFIG = path.join(__dirname, 'config.js');  

Import Tumblr Posts and URLs

José Padilla wrote an awesome web app that will create a Ghost import file for your tumblr app. The only issue I had was that it create two tags with same name, but different cases, that broke the Ghost importer. Deleting one of those tags solved the problem.

However, one thing it didn't import was the old post URLs. I couldn't find anyone writing Ghost URL redirects that didn't use an .htaccess file which was out the question for Heroku.

So after much internet searching, I figured maybe I could somehow write one. I didn't want to create my own fork of Ghost and I didn't think it would be a good addition to the core, so I wanted to somehow hook it in without messing with the internal Ghost library. I knew that in Python WSGI allows you to add middleware before a request even touches your web framework, like Django. I wondered if NodeJS had a similar mechanism.

I figured out that Ghost uses express.js as it's web framework, but I this seemed to a dead end. Ghost wraps express, you can't use it (currently) as an express middleware.

I kept digging around the Ghost source (which is rather well writter, by the way), I saw that /ghost/core/index.js calls /ghost/core/server/index.js with an argument options.app.

...
        var ghost = require('./server');
        return ghost(options.app).then(deferred.resolve).otherwise(function (e) {
...

Then I saw that /ghost/core/server/index.js will only create a new express app/server if one is not passed in. Then I figured out that if I set the app attribute on the config to an existing express app, I could add some middleware to that app first and Ghost might pick up on that middleware and run with it.

So I set out to build some express middleware that will take a mapping of old and new paths and redirect if it hits an old path. Tada custom-redirects was born!

Somehow, it actually all worked together. I put some redirects in a redirects.js file and imported them into my config and added a customRedirects middleware to config.app with them.

var path = require('path'),  
    express = require('ghost/node_modules/express'),
    customRedirects = require('custom-redirects'),
    redirects = require('./redirects.js'),
    config;


config = {  
   ...
};

config.app = express();  
config.app.use(customRedirects(redirects));


module.exports = config;  

Somehow it all worked! My old posts redirect, blah blah blah, Yeah Ghost!