0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-10 23:36:14 -05:00
ghost/core/server/data/migration/update.js
Katharina Irrgang 1882278b5b 🎨 configurable logging with bunyan (#7431)
- 🛠  add bunyan and prettyjson, remove morgan

-   add logging module
  - GhostLogger class that handles setup of bunyan
  - PrettyStream for stdout

-   config for logging
  - @TODO: testing level fatal?

-   log each request via GhostLogger (express middleware)
  - @TODO: add errors to output

- 🔥  remove errors.updateActiveTheme
  - we can read the value from config

- 🔥  remove 15 helper functions in core/server/errors/index.js
  - all these functions get replaced by modules:
    1. logging
    2. error middleware handling for html/json
    3. error creation (which will be part of PR #7477)

-   add express error handler for html/json
  - one true error handler for express responses
  - contains still some TODO's, but they are not high priority for first implementation/integration
  - this middleware only takes responsibility of either rendering html responses or return json error responses

- 🎨  use new express error handler in middleware/index
  - 404 and 500 handling

- 🎨  return error instead of error message in permissions/index.js
  - the rule for error handling should be: if you call a unit, this unit should return a custom Ghost error

- 🎨  wrap serve static module
  - rule: if you call a module/unit, you should always wrap this error
  - it's always the same rule
  - so the caller never has to worry about what comes back
  - it's always a clear error instance
  - in this case: we return our notfounderror if serve static does not find the resource
  - this avoid having checks everywhere

- 🎨  replace usages of errors/index.js functions and adapt tests
  - use logging.error, logging.warn
  - make tests green
  - remove some usages of logging and throwing api errors -> because when a request is involved, logging happens automatically

- 🐛  return errorDetails to Ghost-Admin
  - errorDetails is used for Theme error handling

- 🎨  use 500er error for theme is missing error in theme-handler

- 🎨  extend file rotation to 1w
2016-10-04 16:33:43 +01:00

148 lines
5 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.DatabaseVersion(
i18n.t('errors.data.versioning.index.cannotMigrate.error'),
i18n.t('errors.data.versioning.index.cannotMigrate.context'),
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.DatabaseVersion(i18n.t('errors.data.versioning.index.dbVersionNotRecognized'))};
}
};
exports.execute = execute;
exports.isDatabaseOutOfDate = isDatabaseOutOfDate;