0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-13 22:41:32 -05:00
ghost/core/server/data/migration/index.js
Hannah Wolfe af6137248d New URL helper - URL consistency fixes
fixes #1765
fixes #1811
issue #1833

New UrlFor functions

- moved body of url helper to config.path.urlFor, which can generate a URL for various scenarios
- urlFor can take a string (name) or object (relativeUrl: '/') as the first
  argument - this is the first step towards issue #1833
- also added config.path.urlForPost which is async and handles getting
  permalink setting
- frontend controller, ghost_head helper, cache invalidation all now use
  urlFor or urlForPost all urls should be correct and consistent

URL Consistency Improvements

- refactored invalidateCache into cacheInvalidationHeader which returns a
  promise so that url can be generated properly by urlForPost
- moved isPost from models to schema, and refactored schema to have a tables object
- deleted posts now return the whole object, not just id and slug,
  ensuring cache invalidation header can be set on delete
- frontend controller rss and archive page redirects work properly with subdirectory
- removes {{url}} helper from admin and client, and replaced with adminUrl
  helper which also uses urlFor
- in res.locals ghostRoot becomes relativeUrl, and path is removed
2014-01-06 15:15:48 +00:00

272 lines
No EOL
9.4 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

var _ = require('underscore'),
when = require('when'),
errors = require('../../errorHandling'),
client = require('../../models/base').client,
knex = require('../../models/base').knex,
sequence = require('when/sequence'),
defaultSettings = require('../default-settings'),
Settings = require('../../models/settings').Settings,
fixtures = require('../fixtures'),
schema = require('../schema').tables,
initialVersion = '000',
schemaTables = _.keys(schema),
defaultDatabaseVersion,
init,
reset,
migrateUp,
migrateUpFreshDb,
getTables;
// Default Database Version
// The migration version number according to the hardcoded default settings
// This is the version the database should be at or migrated to
function getDefaultDatabaseVersion() {
if (!defaultDatabaseVersion) {
// This be the current version according to the software
defaultDatabaseVersion = _.find(defaultSettings.core, function (setting) {
return setting.key === 'databaseVersion';
}).defaultValue;
}
return defaultDatabaseVersion;
}
// Database Current Version
// The migration version number according to the database
// This is what the database is currently at and may need to be updated
function getDatabaseVersion() {
return knex.schema.hasTable('settings').then(function (exists) {
// Check for the current version from the settings table
if (exists) {
// Temporary code to deal with old databases with currentVersion settings
return knex('settings')
.where('key', 'databaseVersion')
.orWhere('key', 'currentVersion')
.select('value')
.then(function (versions) {
var databaseVersion = _.reduce(versions, function (memo, version) {
if (isNaN(version.value)) {
errors.throwError('Database version is not recognised');
}
return parseInt(version.value, 10) > parseInt(memo, 10) ? version.value : memo;
}, initialVersion);
if (!databaseVersion || databaseVersion.length === 0) {
// we didn't get a response we understood, assume initialVersion
databaseVersion = initialVersion;
}
return databaseVersion;
});
}
throw new Error('Settings table does not exist');
});
}
function setDatabaseVersion() {
return knex('settings')
.where('key', 'databaseVersion')
.update({ 'value': defaultDatabaseVersion });
}
function createTable(table) {
return knex.schema.createTable(table, function (t) {
var column,
columnKeys = _.keys(schema[table]);
_.each(columnKeys, function (key) {
if (schema[table][key].hasOwnProperty('maxlength')) {
column = t[schema[table][key].type](key, schema[table][key].maxlength);
} else {
column = t[schema[table][key].type](key);
}
if (schema[table][key].hasOwnProperty('nullable') && schema[table][key].nullable === true) {
column.nullable();
} else {
column.notNullable();
}
if (schema[table][key].hasOwnProperty('primary') && schema[table][key].primary === true) {
column.primary();
}
if (schema[table][key].hasOwnProperty('unique') && schema[table][key].unique) {
column.unique();
}
if (schema[table][key].hasOwnProperty('unsigned') && schema[table][key].unsigned) {
column.unsigned();
}
if (schema[table][key].hasOwnProperty('references') && schema[table][key].hasOwnProperty('inTable')) {
//check if table exists?
column.references(schema[table][key].references);
column.inTable(schema[table][key].inTable);
}
if (schema[table][key].hasOwnProperty('defaultTo')) {
column.defaultTo(schema[table][key].defaultTo);
}
});
});
}
function deleteTable(table) {
return knex.schema.dropTableIfExists(table);
}
function getDeleteCommands(oldTables, newTables) {
var deleteTables = _.difference(oldTables, newTables);
if (!_.isEmpty(deleteTables)) {
return _.map(deleteTables, function (table) {
return function () {
return deleteTable(table);
};
});
}
}
function getAddCommands(oldTables, newTables) {
var addTables = _.difference(newTables, oldTables);
if (!_.isEmpty(addTables)) {
return _.map(addTables, function (table) {
return function () {
return createTable(table);
};
});
}
}
function getTablesFromSqlite3() {
return knex.raw("select * from sqlite_master where type = 'table'").then(function (response) {
return _.reject(_.pluck(response[0], 'tbl_name'), function (name) {
return name === 'sqlite_sequence';
});
});
}
function getTablesFromPgSQL() {
return knex.raw("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'").then(function (response) {
return _.flatten(_.map(response[0], function (entry) {
return _.values(entry);
}));
});
}
function getTablesFromMySQL() {
return knex.raw("show tables").then(function (response) {
return _.flatten(_.map(response[0], function (entry) {
return _.values(entry);
}));
});
}
// Check for whether data is needed to be bootstrapped or not
init = function () {
var self = this;
// There are 4 possibilities:
// 1. The database exists and is up-to-date
// 2. The database exists but is out of date
// 3. The database exists but the currentVersion setting does not or cannot be understood
// 4. The database has not yet been created
return getDatabaseVersion().then(function (databaseVersion) {
var defaultVersion = getDefaultDatabaseVersion();
if (databaseVersion === defaultVersion) {
// 1. The database exists and is up-to-date
return when.resolve();
}
if (databaseVersion < defaultVersion) {
// 2. The database exists but is out of date
// Migrate to latest version
return self.migrateUp().then(function () {
// Finally update the databases current version
return setDatabaseVersion();
});
}
if (databaseVersion > defaultVersion) {
// 3. The database exists but the currentVersion setting does not or cannot be understood
// In this case we don't understand the version because it is too high
errors.logErrorAndExit(
'Your database is not compatible with this version of Ghost',
'You will need to create a new database'
);
}
}, function (err) {
if (err.message || err === 'Settings table does not exist') {
// 4. The database has not yet been created
// Bring everything up from initial version.
return self.migrateUpFreshDb();
}
// 3. The database exists but the currentVersion setting does not or cannot be understood
// In this case the setting was missing or there was some other problem
errors.logErrorAndExit('There is a problem with the database', err.message || err);
});
};
// ### Reset
// Delete all tables from the database in reverse order
reset = function () {
var tables = [];
tables = _.map(schemaTables, function (table) {
return function () {
return deleteTable(table);
};
}).reverse();
return sequence(tables);
};
// Only do this if we have no database at all
migrateUpFreshDb = function () {
var tables = [];
tables = _.map(schemaTables, function (table) {
return function () {
return createTable(table);
};
});
return sequence(tables).then(function () {
// Load the fixtures
return fixtures.populateFixtures().then(function () {
// Initialise the default settings
return Settings.populateDefaults();
});
});
};
// Migrate from a specific version to the latest
migrateUp = function () {
return getTables().then(function (oldTables) {
var deleteCommands = getDeleteCommands(oldTables, schemaTables),
addCommands = getAddCommands(oldTables, schemaTables),
commands = [];
if (!_.isEmpty(deleteCommands)) {
commands = commands.concat(deleteCommands);
}
if (!_.isEmpty(addCommands)) {
commands = commands.concat(addCommands);
}
if (!_.isEmpty(commands)) {
return sequence(commands);
}
return;
});
};
getTables = function () {
if (client === 'sqlite3') {
return getTablesFromSqlite3();
}
if (client === 'mysql') {
return getTablesFromMySQL();
}
if (client === 'pg') {
return getTablesFromPgSQL();
}
return when.reject("No support for database client " + client);
};
module.exports = {
getDatabaseVersion: getDatabaseVersion,
init: init,
reset: reset,
migrateUp: migrateUp,
migrateUpFreshDb: migrateUpFreshDb
};