From c634e03cb553e21158fddc6d4221a54aa799de79 Mon Sep 17 00:00:00 2001 From: marzavec Date: Mon, 18 Mar 2019 23:36:21 -0700 Subject: refactoring 1 of 2 --- server/src/scripts/configLib/SetupWizard.js | 107 ++++++++++++++++++++++++++++ server/src/scripts/configure.js | 14 ++-- server/src/scripts/setupSchema/Banner.js | 28 ++++++++ server/src/scripts/setupSchema/Footer.js | 7 ++ server/src/scripts/setupSchema/Questions.js | 55 ++++++++++++++ 5 files changed, 203 insertions(+), 8 deletions(-) create mode 100644 server/src/scripts/configLib/SetupWizard.js create mode 100644 server/src/scripts/setupSchema/Banner.js create mode 100644 server/src/scripts/setupSchema/Footer.js create mode 100644 server/src/scripts/setupSchema/Questions.js (limited to 'server/src/scripts') diff --git a/server/src/scripts/configLib/SetupWizard.js b/server/src/scripts/configLib/SetupWizard.js new file mode 100644 index 0000000..339878d --- /dev/null +++ b/server/src/scripts/configLib/SetupWizard.js @@ -0,0 +1,107 @@ +/** + * Server setup wizard, quick server setup and all that jazz. . . + * + * Version: v2.0.0 + * Developer: Marzavec ( https://github.com/marzavec ) + * License: WTFPL ( http://www.wtfpl.net/txt/copying/ ) + * + */ + +const fse = require('fs-extra'); +const prompt = require('prompt'); +const path = require('path'); + +class SetupWizard { + /** + * Create a `SetupWizard` instance for initializing the server's config.json + * + * @param {Object} serverConfig reference to the server config class + */ + constructor (serverConfig) { + this.serverConfig = serverConfig; + } + + /** + * Roll a d20 and begin the wizarding process + * + */ + async start () { + // load the current config to use as defaults, if available + let currentConfig = await this.serverConfig.load() || {}; + + // auto generate the salt if not currrently created + currentConfig.tripSalt = currentConfig.tripSalt || + [...Array(Math.floor(Math.random()*1024)+1024)].map(i=>(~~(Math.random()*36)).toString(36)).join(''); + + // load the setup questions & set their defaults + let questions = require('../setupSchema/Questions.js'); + questions.properties = this.setQuestionDefaults(questions.properties, currentConfig); + + // force password re-entry + questions.properties.adminTrip.default = ''; + questions.properties.adminTrip.required = true; + + // output the packages setup banner + require('../setupSchema/Banner.js'); + + // let's start playing 20 questions + prompt.start(); + prompt.get(questions, (err, result) => this.finalize(err, result)); + } + + /** + * Compares the currently loaded config with the stock questions, adds a default + * and required option to the question + * + * @param {Object} questions the set of questions from /setupSchema + * @param {Object} currentConfig the current server options + */ + setQuestionDefaults (questions, currentConfig) { + Object.keys(questions).forEach(qName => { + if (typeof currentConfig[qName] !== 'undefined') { + questions[qName].default = currentConfig[qName]; + questions[qName].required = false; + } else { + questions[qName].required = true; + } + }); + + return questions; + } + + /** + * Looks like all the questions have been answered, check for errors or save + * the new config file + * + * @param {Object} err any errors generated by Prompt + * @param {Object} result the answers / new config setup + */ + async finalize (err, result) { + // output errors and die if needed + if (err) { + console.error(err); + process.exit(0); + } + + // initialize default mods config + if (typeof result.mods === 'undefined') { + result.mods = []; + } + + // finally create the actual JSON file + try { + this.serverConfig.config = result; + await this.serverConfig.save(); + } catch (e) { + console.error(`Couldn't write config to ${this.serverConfig.configPath} + ${e.stack}`); + } + + // output the packages final notice before quitting + require('../setupSchema/Footer.js'); + + process.exit(0); + } +} + +module.exports = SetupWizard; diff --git a/server/src/scripts/configure.js b/server/src/scripts/configure.js index 0ecd858..d7f2bf2 100644 --- a/server/src/scripts/configure.js +++ b/server/src/scripts/configure.js @@ -1,5 +1,5 @@ /** - * Server configuration script, used reconfiguring server options + * Server configuration script, to (re)configure server options * * Version: v2.0.0 * Developer: Marzavec ( https://github.com/marzavec ) @@ -11,13 +11,11 @@ // import required classes const path = require('path'); -const ImportsManager = require('../managers/imports-manager'); -const ConfigManager = require('../managers/config'); +const ConfigManager = require('../serverLib/ConfigManager'); +const SetupWizard = require('./configLib/SetupWizard'); // import and initialize configManager & dependencies -const importManager = new ImportsManager(null, path.join(__dirname, '../..')); -importManager.init(); -const configManager = new ConfigManager(null, path.join(__dirname, '../..'), importManager); +const serverConfig = new ConfigManager(path.join(__dirname, '../..')); +const setup = new SetupWizard(serverConfig); -// execute config load with `reconfiguring` flag set to true -configManager.load(true); +setup.start(); diff --git a/server/src/scripts/setupSchema/Banner.js b/server/src/scripts/setupSchema/Banner.js new file mode 100644 index 0000000..823f0fe --- /dev/null +++ b/server/src/scripts/setupSchema/Banner.js @@ -0,0 +1,28 @@ +/** + * This script will be run before the package starts asking for the config data, + * used to output a simple guide for the coming questions, or to spam some sexy + * ascii art at the user. + * + */ + +const stripIndents = require('common-tags').stripIndents; +const chalk = require('chalk'); + +// gotta have that sexy console +console.log(stripIndents` + ${chalk.magenta('°º¤ø,¸¸,ø¤º°`°º¤ø,¸,ø¤°º¤ø,¸¸,ø¤º°`°º¤ø,¸°º¤ø,¸¸,ø¤º°`°º¤ø')} + ${chalk.gray('--------------(') + chalk.white(' HackChat Setup Wizard v2.0 ') + chalk.gray(')--------------')} + ${chalk.magenta('°º¤ø,¸¸,ø¤º°`°º¤ø,¸,ø¤°º¤ø,¸¸,ø¤º°`°º¤ø,¸°º¤ø,¸¸,ø¤º°`°º¤ø')} + + For advanced setup, see the documentation at: + ${chalk.green('https://github.com/hack-chat/main/tree/master/documentation')} + + ${chalk.white('Note:')} ${chalk.green('npm/yarn run config')} will re-run this utility. + + You will now be asked for the following: + - ${chalk.magenta(' Salt')}, the salt for username trip + - ${chalk.magenta('Admin Name')}, the initial admin username + - ${chalk.magenta('Admin Pass')}, the initial admin password + - ${chalk.magenta(' Port')}, the port for the websocket + \u200b +`); diff --git a/server/src/scripts/setupSchema/Footer.js b/server/src/scripts/setupSchema/Footer.js new file mode 100644 index 0000000..d2ee14f --- /dev/null +++ b/server/src/scripts/setupSchema/Footer.js @@ -0,0 +1,7 @@ +/** + * This script will be run once all questions have finished and no errors have + * occured. You can congratulate the user on their fine choice in software usage + * + */ + +console.log('Config generated! You may now start the server normally.'); diff --git a/server/src/scripts/setupSchema/Questions.js b/server/src/scripts/setupSchema/Questions.js new file mode 100644 index 0000000..f84d32f --- /dev/null +++ b/server/src/scripts/setupSchema/Questions.js @@ -0,0 +1,55 @@ +/** + * This object contains Prompt ( https://www.npmjs.com/package/prompt ) style + * questions that the SetupWizard will require an answer to. Questions are asked + * in the order they are specified here. + * + * The resulting config.json file will be used by the server, accessed by the + * name specified. IE, a valid use is; config.adminName + * + */ + +const Questions = { + properties: { + tripSalt: { + description: 'Salt (leave as default)', + type: 'string', + hidden: true, + replace: '*', + before: value => { + salt = value; + return value; + } + }, + + adminName: { + description: 'Admin Nickname', + pattern: /^"?[a-zA-Z0-9_]+"?$/, + type: 'string', + message: 'Nicks can only contain letters, numbers and underscores', + before: value => value.replace(/"/g, '') + }, + + adminTrip: { + type: 'string', + hidden: true, + replace: '*', + description: 'Admin Password', + message: 'You must enter or re-enter a password', + before: value => { + const crypto = require('crypto'); + let sha = crypto.createHash('sha256'); + sha.update(value + salt); + return sha.digest('base64').substr(0, 6); + } + }, + + websocketPort: { + type: 'integer', + message: 'The port may only be a number!', + description: 'Websocket Port', + default: '6060' + } + } +} + +module.exports = Questions; -- cgit v1.2.1