From 4cbc8b2226ffae6c724630acf8d9765c8ffed752 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 7 Jun 2017 11:08:39 -0600 Subject: [PATCH] Updates to setup process --- bin/cli-serve | 13 +- bin/cli-setup | 309 ++++++++++++++++++++++-------------------- services/migration.js | 22 +-- 3 files changed, 183 insertions(+), 161 deletions(-) diff --git a/bin/cli-serve b/bin/cli-serve index 7978c6c85..63eda3af2 100755 --- a/bin/cli-serve +++ b/bin/cli-serve @@ -5,6 +5,7 @@ const app = require('../app'); const {createServer} = require('http'); const scraper = require('../services/scraper'); const mailer = require('../services/mailer'); +const MigrationService = require('../services/migration'); const kue = require('../services/kue'); const mongoose = require('../services/mongoose'); const util = require('./util'); @@ -87,7 +88,17 @@ function onListening() { /** * Start the app. */ -function startApp(program) { +async function startApp(program) { + + try { + + // Verify that the minimum migration version is met. + await MigrationService.verify(); + + } catch(e) { + console.error(e); + process.exit(1); + } /** * Listen on provided port, on all network interfaces. diff --git a/bin/cli-setup b/bin/cli-setup index e9f0959ef..add134209 100755 --- a/bin/cli-setup +++ b/bin/cli-setup @@ -12,6 +12,7 @@ const MODERATION_OPTIONS = require('../models/enum/moderation_options'); const SettingsService = require('../services/settings'); const SetupService = require('../services/setup'); const UsersService = require('../services/users'); +const MigrationService = require('../services/migration'); const util = require('./util'); const errors = require('../errors'); @@ -33,166 +34,186 @@ program // Setup the application //============================================================================== -const performSetup = () => { +const performSetup = async () => { if (program.defaults) { - return SettingsService - .init() - .then(() => { - console.log('Settings created.'); - console.log('\nTalk is now installed!'); - util.shutdown(); - }) - .catch((err) => { - console.error(err); - util.shutdown(1); - }); + await SettingsService.init(); + + console.log('Settings created.'); + console.log('\nTalk is now installed!'); + + return; } // Get the current settings, we are expecing an error here. - return SettingsService - .retrieve() - .then(() => { + try { - // We should NOT have gotten a settings object, this means that the - // application is already setup. Error out here. - throw errors.ErrSettingsInit; + // Try to get the settings. + await SettingsService.retrieve(); - }) - .catch((err) => { + // We should NOT have gotten a settings object, this means that the + // application is already setup. Error out here. + throw errors.ErrSettingsInit; - // If the error is `not init`, then we're good, otherwise, it's something - // else. - if (err !== errors.ErrSettingsNotInit) { - throw err; + } catch (e) { + + // If the error is `not init`, then we're good, otherwise, it's something + // else. + if (e !== errors.ErrSettingsNotInit) { + throw e; + } + } + + // Get the migrations to run. + let migrations = await MigrationService.listPending(); + if (migrations.length > 0) { + + console.log('Now going to run the following migrations:\n'); + + for (let {filename} of migrations) { + console.log(`\tmigrations/${filename}`); + } + + console.log(''); + + let {confirm} = await inquirer.prompt([ + { + type: 'confirm', + name: 'confirm', + message: 'Proceed with migrations', + default: false } + ]); - }) - .then(() => { + if (!confirm) { + throw new Error('migrations are needed to complete setup'); + } - // Create the base settings model. - let settings = new SettingModel(); + // Perform all migrations. + await MigrationService.run(migrations); + } else { + console.log('No migrations have to be run.'); + } - console.log('We\'ll ask you some questions in order to setup your installation of Talk.\n'); + // Create the base settings model. + let settings = new SettingModel(); - return inquirer.prompt([ - { - type: 'input', - name: 'organizationName', - message: 'Organization Name', - default: settings.organizationName, - validate: (input) => { - if (input && input.length > 0) { - return true; - } + console.log('\nWe\'ll ask you some questions in order to setup your installation of Talk.\n'); - return 'Organization Name is required.'; - } - }, - { - type: 'list', - choices: MODERATION_OPTIONS, - name: 'moderation', - default: settings.moderation, - message: 'Select a moderation mode' - }, - { - type: 'confirm', - name: 'requireEmailConfirmation', - default: settings.requireEmailConfirmation, - message: 'Should emails always be confirmed' - } - ]) - .then((answers) => { - - // Update the settings that were changed. - Object.keys(answers).forEach((key) => { - if (answers[key] !== undefined) { - settings[key] = answers[key]; - } - }); - - console.log('\nWe\'ll ask you some questions about your first admin user.\n'); - - return inquirer.prompt([ - { - type: 'input', - name: 'username', - message: 'Username', - filter: (username) => { - return UsersService - .isValidUsername(username, false) - .catch((err) => { - throw err.message; - }); - } - }, - { - name: 'email', - message: 'Email', - format: 'email', - validate: (value) => { - if (value && value.length >= 3) { - return true; - } - - return 'Email is required'; - } - }, - { - name: 'password', - message: 'Password', - type: 'password', - filter: (password) => { - return UsersService - .isValidPassword(password) - .catch((err) => { - throw err.message; - }); - } - }, - { - name: 'confirmPassword', - message: 'Confirm Password', - type: 'password', - filter: (confirmPassword) => { - return UsersService - .isValidPassword(confirmPassword) - .catch((err) => { - throw err.message; - }); - } - }, - ]); - }) - .then((user) => { - - if (user.password !== user.confirmPassword) { - return Promise.reject(new Error('Passwords do not match')); + let answers = await inquirer.prompt([ + { + type: 'input', + name: 'organizationName', + message: 'Organization Name', + default: settings.organizationName, + validate: (input) => { + if (input && input.length > 0) { + return true; } - return SetupService.setup({ - settings: settings.toObject(), - user: { - email: user.email, - username: user.username, - password: user.password - } - }); - }); - }) - .then(({user}) => { - console.log('Settings created.'); - console.log(`User ${user.id} created.`); - console.log('\nTalk is now installed!'); - console.log('\nWe recommend adding TALK_INSTALL_LOCK=TRUE to your environment to turn off the dynamic setup.'); - util.shutdown(); - }) - .catch((err) => { - console.error(err); - util.shutdown(1); - }); + return 'Organization Name is required.'; + } + }, + { + type: 'list', + choices: MODERATION_OPTIONS, + name: 'moderation', + default: settings.moderation, + message: 'Select a moderation mode' + }, + { + type: 'confirm', + name: 'requireEmailConfirmation', + default: settings.requireEmailConfirmation, + message: 'Should emails always be confirmed' + } + ]); + + // Update the settings that were changed. + Object.keys(answers).forEach((key) => { + if (answers[key] !== undefined) { + settings[key] = answers[key]; + } + }); + + console.log('\nWe\'ll ask you some questions about your first admin user.\n'); + + let user = await inquirer.prompt([ + { + type: 'input', + name: 'username', + message: 'Username', + filter: (username) => { + return UsersService + .isValidUsername(username, false) + .catch((err) => { + throw err.message; + }); + } + }, + { + name: 'email', + message: 'Email', + format: 'email', + validate: (value) => { + if (value && value.length >= 3) { + return true; + } + + return 'Email is required'; + } + }, + { + name: 'password', + message: 'Password', + type: 'password', + filter: (password) => { + return UsersService + .isValidPassword(password) + .catch((err) => { + throw err.message; + }); + } + }, + { + name: 'confirmPassword', + message: 'Confirm Password', + type: 'password', + filter: (confirmPassword) => { + return UsersService + .isValidPassword(confirmPassword) + .catch((err) => { + throw err.message; + }); + } + }, + ]); + + if (user.password !== user.confirmPassword) { + return Promise.reject(new Error('Passwords do not match')); + } + + let {user: newUser} = await SetupService.setup({ + settings: settings.toObject(), + user: { + email: user.email, + username: user.username, + password: user.password + } + }); + + console.log('Settings created.'); + console.log(`User ${newUser.id} created.`); + console.log('\nTalk is now installed!'); + console.log('\nWe recommend adding TALK_INSTALL_LOCK=TRUE to your environment to turn off the dynamic setup.'); }; // Start tthe setup process. -performSetup(); +performSetup() + .then(() => { + util.shutdown(); + }) + .catch((e) => { + console.error(e); + util.shutdown(1); + }); diff --git a/services/migration.js b/services/migration.js index 11eec3247..8676b91be 100644 --- a/services/migration.js +++ b/services/migration.js @@ -157,34 +157,24 @@ class MigrationService { return latestMigration[0].version; } - static async verify(requiredVersion) { + static async verify() { // If the requiredVersion isn't specified or is 0, then don't complain. - if (typeof requiredVersion !== 'number' || requiredVersion === 0) { + if (typeof minVersion !== 'number' || minVersion === 0) { return; } // If the latest migration does not match the required version, then error // out. let latestVersion = await MigrationService.latestVersion(); - if (!latestVersion || latestVersion < requiredVersion) { - throw new Error(`A database migration is required, version required ${requiredVersion}, found ${latestVersion}. Please run \`./bin/cli migration run\``); + if (!latestVersion || latestVersion < minVersion) { + throw new Error(`A database migration is required, version required ${minVersion}, found ${latestVersion}. Please run \`./bin/cli migration run\``); } + debug(`minimum migration version ${minVersion} was met with version ${latestVersion}`); + return latestVersion; } } -// Verify that the minimum migration version is met. -MigrationService - .verify(minVersion) - .then((latestVersion) => { - debug(`minimum migration version ${minVersion} was met with version ${latestVersion}`); - }) - .catch((e) => { - - // Throw the error in the catch block. - throw e; - }); - module.exports = MigrationService;