mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-21 09:52:06 +03:00
0b79abf5b2
Background: - Ghosts existing boot process is split across multiple files, has affordances for outdated ways of running Ghost and is generally non-linear making it nigh-impossible to follow - The web of dependencies that are loaded on boot are also impossible to unpick, which makes it really hard to decouple Ghost - With 4.0 we want to introduce a new, linear, simpler, clearer way to boot up Ghost to unlock decoupling Ghost into much smaller pieces This commit: - adds a new ghost.js file which switches between boot mode with `node index` or `node index old` so that if we find bugs we can work around them this week - Note: the old boot process will go away very soon, but ghost.js will remain as the interface between the command to start Ghost and the application code - reworks the database migration process into a standalone utility, so that the DB is handled as one simple step of the boot process, decoupled from everything else - is missing tests for this new db utility - leaves a lot of work to do around loading core code, services, express apps in a sensible order, as work to fix this would start to break the old boot process - doesn't use the new maintenance app because we aren't restarting the server here, instead we have the concept of a "core app" that starts in maintenance mode - need to think about how apps will be decoupled in the near future
107 lines
2.9 KiB
JavaScript
107 lines
2.9 KiB
JavaScript
const KnexMigrator = require('knex-migrator');
|
|
const errors = require('@tryghost/errors');
|
|
|
|
const states = {
|
|
READY: 0,
|
|
NEEDS_INITIALISATION: 1,
|
|
NEEDS_MIGRATION: 2,
|
|
ERROR: 3
|
|
};
|
|
|
|
const printState = ({state, logging}) => {
|
|
if (state === states.READY) {
|
|
logging.info('Database is in a ready state.');
|
|
}
|
|
|
|
if (state === states.NEEDS_INITIALISATION) {
|
|
logging.warn('Database state requires initialisation.');
|
|
}
|
|
|
|
if (state === states.NEEDS_MIGRATION) {
|
|
logging.warn('Database state requires migration.');
|
|
}
|
|
|
|
if (state === states.ERROR) {
|
|
logging.error('Database is in an error state.');
|
|
}
|
|
};
|
|
|
|
class DatabaseStateManager {
|
|
constructor({knexMigratorFilePath}) {
|
|
this.knexMigrator = new KnexMigrator({
|
|
knexMigratorFilePath
|
|
});
|
|
}
|
|
|
|
async getState() {
|
|
let state = states.READY;
|
|
try {
|
|
await this.knexMigrator.isDatabaseOK();
|
|
return state;
|
|
} catch (error) {
|
|
// CASE: database has not yet been initialised
|
|
if (error.code === 'DB_NOT_INITIALISED') {
|
|
state = states.NEEDS_INITIALISATION;
|
|
return state;
|
|
}
|
|
|
|
// CASE: there's no migration table so we can't understand
|
|
if (error.code === 'MIGRATION_TABLE_IS_MISSING') {
|
|
state = states.NEEDS_INITIALISATION;
|
|
return state;
|
|
}
|
|
|
|
// CASE: database needs migrations
|
|
if (error.code === 'DB_NEEDS_MIGRATION') {
|
|
state = states.NEEDS_MIGRATION;
|
|
return state;
|
|
}
|
|
|
|
// CASE: database connection errors, unknown cases
|
|
let errorToThrow = error;
|
|
if (!errors.utils.isIgnitionError(errorToThrow)) {
|
|
errorToThrow = new errors.GhostError({message: errorToThrow.message, err: errorToThrow});
|
|
}
|
|
|
|
throw errorToThrow;
|
|
}
|
|
}
|
|
|
|
async makeReady({logging}) {
|
|
try {
|
|
let state = await this.getState();
|
|
|
|
if (logging) {
|
|
printState({state, logging});
|
|
}
|
|
|
|
if (state === states.READY) {
|
|
return;
|
|
}
|
|
|
|
if (state === states.NEEDS_INITIALISATION) {
|
|
await this.knexMigrator.init();
|
|
}
|
|
|
|
if (state === states.NEEDS_MIGRATION) {
|
|
await this.knexMigrator.migrate();
|
|
}
|
|
|
|
state = await this.getState();
|
|
|
|
if (logging) {
|
|
printState({state, logging});
|
|
}
|
|
} catch (error) {
|
|
let errorToThrow = error;
|
|
if (!errors.utils.isIgnitionError(error)) {
|
|
errorToThrow = new errors.GhostError({message: errorToThrow.message, err: errorToThrow});
|
|
}
|
|
|
|
throw errorToThrow;
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = DatabaseStateManager;
|