mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-17 05:21:36 +03:00
d81bc91bd2
refs #7116, refs #2001 - Changes the way Ghost errors are implemented to benefit from proper inheritance - Moves all error definitions into a single file - Changes the error constructor to take an options object, rather than needing the arguments to be passed in the correct order. - Provides a wrapper so that any errors that haven't already been converted to GhostErrors get converted before they are displayed. Summary of changes: * 🐛 set NODE_ENV in config handler * ✨ add GhostError implementation (core/server/errors.js) - register all errors in one file - inheritance from GhostError - option pattern * 🔥 remove all error files * ✨ wrap all errors into GhostError in case of HTTP * 🎨 adaptions - option pattern for errors - use GhostError when needed * 🎨 revert debug deletion and add TODO for error id's
151 lines
5.0 KiB
JavaScript
151 lines
5.0 KiB
JavaScript
// # Update Database
|
|
// Handles migrating a database between two different database versions
|
|
var Promise = require('bluebird'),
|
|
_ = require('lodash'),
|
|
backup = require('./backup'),
|
|
fixtures = require('./fixtures'),
|
|
errors = require('../../errors'),
|
|
logging = require('../../logging'),
|
|
i18n = require('../../i18n'),
|
|
db = require('../../data/db'),
|
|
versioning = require('../schema').versioning,
|
|
sequence = function sequence(tasks, modelOptions, logger) {
|
|
// utils/sequence.js does not offer an option to pass cloned arguments
|
|
return Promise.reduce(tasks, function (results, task) {
|
|
return task(_.cloneDeep(modelOptions), logger)
|
|
.then(function (result) {
|
|
results.push(result);
|
|
return results;
|
|
});
|
|
}, []);
|
|
},
|
|
updateDatabaseSchema,
|
|
migrateToDatabaseVersion,
|
|
execute, logger, isDatabaseOutOfDate;
|
|
|
|
logger = {
|
|
info: function info(message) {
|
|
logging.info('Migrations:' + message);
|
|
},
|
|
warn: function warn(message) {
|
|
logging.warn('Skipping Migrations:' + message);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* update database schema for one single version
|
|
*/
|
|
updateDatabaseSchema = function (tasks, logger, modelOptions) {
|
|
if (!tasks.length) {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
return sequence(tasks, modelOptions, logger);
|
|
};
|
|
|
|
/**
|
|
* update each database version as one transaction
|
|
* if a version fails, rollback
|
|
* if a version fails, stop updating more versions
|
|
*/
|
|
migrateToDatabaseVersion = function migrateToDatabaseVersion(version, logger, modelOptions) {
|
|
return new Promise(function (resolve, reject) {
|
|
db.knex.transaction(function (transaction) {
|
|
var migrationTasks = versioning.getUpdateDatabaseTasks(version, logger),
|
|
fixturesTasks = versioning.getUpdateFixturesTasks(version, logger);
|
|
|
|
logger.info('Updating database to ' + version);
|
|
|
|
modelOptions.transacting = transaction;
|
|
|
|
updateDatabaseSchema(migrationTasks, logger, modelOptions)
|
|
.then(function () {
|
|
return fixtures.update(fixturesTasks, logger, modelOptions);
|
|
})
|
|
.then(function () {
|
|
return versioning.setDatabaseVersion(transaction, version);
|
|
})
|
|
.then(function () {
|
|
transaction.commit();
|
|
resolve();
|
|
})
|
|
.catch(function (err) {
|
|
logger.warn('rolling back because of: ' + err.stack);
|
|
|
|
transaction.rollback();
|
|
});
|
|
}).catch(function () {
|
|
reject();
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* ## Update
|
|
* Does a backup, then updates the database and fixtures
|
|
*/
|
|
execute = function execute(options) {
|
|
options = options || {};
|
|
|
|
var fromVersion = options.fromVersion,
|
|
toVersion = options.toVersion,
|
|
forceMigration = options.forceMigration,
|
|
versionsToUpdate,
|
|
modelOptions = {
|
|
context: {
|
|
internal: true
|
|
}
|
|
};
|
|
|
|
fromVersion = forceMigration ? versioning.canMigrateFromVersion : fromVersion;
|
|
|
|
// Figure out which versions we're updating through.
|
|
// This shouldn't include the from/current version (which we're already on)
|
|
versionsToUpdate = versioning.getMigrationVersions(fromVersion, toVersion).slice(1);
|
|
|
|
return backup(logger)
|
|
.then(function () {
|
|
logger.info('Migration required from ' + fromVersion + ' to ' + toVersion);
|
|
return Promise.mapSeries(versionsToUpdate, function (versionToUpdate) {
|
|
return migrateToDatabaseVersion(versionToUpdate, logger, modelOptions);
|
|
});
|
|
})
|
|
.then(function () {
|
|
logger.info('Finished!');
|
|
});
|
|
};
|
|
|
|
isDatabaseOutOfDate = function isDatabaseOutOfDate(options) {
|
|
options = options || {};
|
|
|
|
var fromVersion = options.fromVersion,
|
|
toVersion = options.toVersion,
|
|
forceMigration = options.forceMigration;
|
|
|
|
// CASE: current database version is lower then we support
|
|
if (fromVersion < versioning.canMigrateFromVersion) {
|
|
return {
|
|
error: new errors.DatabaseVersionError({
|
|
message: i18n.t('errors.data.versioning.index.cannotMigrate.error'),
|
|
context: i18n.t('errors.data.versioning.index.cannotMigrate.context'),
|
|
help: i18n.t('common.seeLinkForInstructions', {link: 'http://support.ghost.org/how-to-upgrade/'})
|
|
})
|
|
};
|
|
}
|
|
// CASE: the database exists but is out of date
|
|
else if (fromVersion < toVersion || forceMigration) {
|
|
return {migrate: true};
|
|
}
|
|
// CASE: database is up-to-date
|
|
else if (fromVersion === toVersion) {
|
|
return {migrate: false};
|
|
}
|
|
// CASE: we don't understand the version
|
|
else {
|
|
return {error: new errors.DatabaseVersionError({message: i18n.t('errors.data.versioning.index.dbVersionNotRecognized')})};
|
|
}
|
|
};
|
|
|
|
exports.execute = execute;
|
|
exports.isDatabaseOutOfDate = isDatabaseOutOfDate;
|