rittenhop-ghost/versions/5.94.2/core/cli/timetravel.js

88 lines
3.2 KiB
JavaScript

const Command = require('./command');
const chalk = require('chalk');
// we use logins as the basis for our offset
const datum = {
table: 'members_login_events',
column: 'created_at'
};
const helpText = `Updates the Ghost db and shifts all dates up to present day.
By default the offset is based on the date of the last member login.\n
${chalk.red('warning')} This is a destructive operation for testing purposes only. DO NOT run it against a database you care about.`;
module.exports = class TimeTravel extends Command {
setup() {
this.help(helpText);
this.argument('--offset', {type: 'number', desc: 'Specify a date offset (in days)'});
this.argument('--force', {type: 'boolean', desc: 'Continue without confirmation'});
}
async handle(argv = {}) {
const _ = require('lodash');
// knex has to be loaded _after_ the call to setup()
// the db connection requires nconf which passes argv to yargs,
// which intercepts --help and stops execution
const knex = require('../server/data/db/connection');
const schema = require('../server/data/schema');
const {DateTime} = require('luxon');
if (!argv.offset) {
const datumPoint = await knex(datum.table)
.max(datum.column, {as: datum.column})
.first();
if (!datumPoint[datum.column]) {
this.error('No data to use as baseline. Use --offset instead');
knex.destroy();
return;
}
argv.offset = Math.floor(
DateTime.utc()
.diff(DateTime.fromJSDate(datumPoint[datum.column]), 'days')
.toObject()
.days
);
}
const dateOffset = argv.offset;
this.info(`Timetravel will use an offset of +${dateOffset} days`);
this.warn('This is a destructive command that will modify your database.');
const confirm = argv.force || await this.confirm('Are you sure you want to continue?');
if (!confirm) {
this.warn('Aborting');
process.exit(1);
}
// map schema to {table: [dateTimeColumn,...]}
const dateTimeFields = _.pickBy(
_.mapValues(schema.tables, (table) => {
return _.keys(_.pickBy(table, (spec) => {
return spec.type === 'dateTime';
}));
}),
fields => fields.length > 0
);
const totalFields = _.reduce(dateTimeFields, (result, value) => {
return result + value.length;
}, 0);
const progressBar = this.progressBar(totalFields);
const db = await knex.transaction();
for (const table in dateTimeFields) {
for (const column of dateTimeFields[table]) {
progressBar.update({status: `Updating ${table}.${column}`});
await db(table)
.update(column, db.raw(`DATE_ADD(${column}, interval ${dateOffset} day)`));
progressBar.increment();
}
}
this.info(`Updated ${totalFields} fields`);
await db.commit();
knex.destroy();
}
};