UNPKG

sundry

Version:

A dynamically configurable, load balancing, reverse proxy.

356 lines (330 loc) 8.74 kB
var config = require('../lib/Configuration/configLoader') var url = require('url'); var redis = require('redis'); var inquirer = require('inquirer'); var Promise = require('bluebird'); var chalk = require('chalk'); var _ = require('lodash'); Promise.promisifyAll(inquirer); var red; config.on('ready', function(){ var env = config.loadedConfig var redisHost = url.parse(env.sundry_redis_url); var client = Promise.promisifyAll(redis.createClient(redisHost.port, redisHost.hostname)); client.on('ready', function(){ red = require('./redis')(client); red.listHosts().then(function(keys){ mainPrompt() }) }); }) var utilities = { getHostList: getHostList, Prompt: Prompt, Finish: finishPrompt, Continue: continuePrompt, Confirm: confirmPrompt, RunAgain: runAgain, Main: mainPrompt, Error: errorPrompt, handleError: handleError, Meta: { balance: balancePrompt, ttl: ttlPrompt } } var output = { backendList: backendlist, hostList: hostList, metaDataList: hostMetadata, metaDataEdited: metaDataEdited, deleteBackend: deleteBackend, Separator: separate } function getHostList(message){ return red.listHosts() .bind({}) .then(function(hosts) { return Prompt({ type: 'list', name: 'route', message: message, choices: output.hostList(hosts) }) }) } function mainPrompt() { process.stdout.write('\033c'); return Prompt({ type: 'list', name: 'action', message: 'Select an action.', choices: [ {name: chalk.green('List Host Info'), value: 'list'}, {name: chalk.green('Add Host'), value: 'add'}, {name: chalk.green('Add Host backend'), value: 'addBackend'}, {name: chalk.green('Edit Host metadata'), value: 'addMeta'}, {name: chalk.yellow('Remove Host'), value: 'removeHost'}, {name: chalk.yellow('Remove Host backend'), value: 'removeBackend'}, {name: chalk.red('Exit'), value: 'exit'} ] }) .then(function(answer) { switch (answer) { case 'list': return require('./prompts/listHosts')(red, utilities, output)(); break; case 'add': return require('./prompts/addHost')(red, utilities, output)(); break; case 'addBackend': return require('./prompts/addBackend')(red, utilities, output)(); break; case 'addMeta': return require('./prompts/editMetadata')(red, utilities, output)(); break; case 'removeHost': return require('./prompts/removeHost')(red, utilities, output)(); break; case 'removeBackend': return require('./prompts/removeBackend')(red, utilities, output)(); break; case 'exit': process.stdout.write('\033c'); process.exit(); } }).catch(function(err){ console.log(chalk.red('There was an error in the last command.')); continuePrompt(); }); }; /* * Reusable prompts */ function runAgain(stop, run){ return Prompt({ type: 'list', name: 'runAgain', message: stop + ' or ' + run, choices: [ {name: stop, value: false}, {name: run, value: true} ] }) } function continuePrompt(){ return Prompt({ type: 'list', name: 'continue', message: 'Continue or Exit?', choices: [ {name: 'Continue', value: true}, {name: chalk.red('Exit'), value: false} ] }).then(function(answers){ if(answers){ return mainPrompt(); } process.stdout.write('\033c'); return process.exit(0); }) }; function confirmPrompt(value){ return Prompt({ type: 'confirm', name: 'valid', message: value + ' <- is this correct?' }) } function finishPrompt(fn) { return Prompt({ type: 'list', name: 'error', message: 'Go back, Main Menu or exit?', choices: [ {name: chalk.green('Back'), value: 'back'}, {name: chalk.yellow('Main'), value: 'main'}, {name: chalk.red('Exit'), value: 'exit'} ] }).then(function(action){ switch(action){ case 'back': process.stdout.write('\033c'); return fn(); break; case 'main': process.stdout.write('\033c'); return mainPrompt(); break; case 'exit': process.stdout.write('\033c'); return process.exit(0); } }) } function errorPrompt() { return Prompt({ type: 'list', name: 'error', message: 'try again, go back or exit?', choices: [ {name: chalk.green('Try Again?'), value: 'again'}, {name: chalk.yellow('Go Back'), value: 'back'}, {name: chalk.red('Exit'), value: 'exit'} ] }) } function handleError(err, fn){ switch (err) { case 'again': return fn() break; case 'back': return mainPrompt() break; default: process.stdout.write('\033c'); return process.exit(0); } } function balancePrompt() { return Prompt({ type: 'list', name: 'balance', default: 0, message: 'Assign load balance type to this host? Default:', choices: [ {name: 'Round robin', value: 'round'}, {name: 'Sticky Session', value: 'sticky'} ] }) } function ttlPrompt(){ return Prompt({ type: 'input', name: 'ttl', default: 60, message: 'Assign a host specific TTL, in seconds to this host? Default:' }) } /* * Outputters */ function backendlist(title, host, arrayData) { var metaOnly = []; var routesOnly = _.filter(arrayData, function(i){ var id = i.split(':') if(id[0] === '***data'){ metaOnly.push(id[1]); return false } return true }) /* * Output */ console.log(separate('yellow', title, true)); console.log(chalk.green.bold('- ' + host)) console.log(chalk.green(' - Routes')) _.each(routesOnly, function(d){ console.log(chalk.white(' - ' + d)) }) console.log(chalk.green(' - Metadata')) _.each(metaOnly, function(m){ var m = m.split(','); console.log(chalk.white(' - ' + m[0] + ' -- ' + m[1])); }) console.log(separate('yellow', true)); } function deleteBackend(arrData){ var data = _.chain(arrData) .filter(function(item){ return item.split(':')[0] !== '***data' }) .map(function(item){ return {name: 'Ip/hostname:port ' + item, value: item} }) .value(); data.unshift(separate('red','Backends')); data.unshift({name: chalk.underline.green('Back'), value: 'back'}) data.push(separate('red')); return data } function hostMetadata(host, arrdata){ host = host.split(':')[1]; var data = _.chain(arrdata) .filter(function(item) { return item.split(':')[0] === '***data' }) .map(function(item) { var s = item.split(':')[1].split(','); return {name: 'meta: ' + s[0] + ' = ' + s[1] , value: item} }) .value() data.unshift(separate('yellow', 'Edit Metadata: ' + host)); data.unshift({name: chalk.underline.green('Back'), value: 'back'}) data.push(separate('yellow')); return data } function hostList(list){ list = _.map(list, function(r) { return {name: r.split(':')[1], value: r} }); if(!list.length){ list.unshift(separate('red', 'No Hosts found!')); } if(list.length > 0) { list.unshift(separate('yellow','Hosts')); list.push(separate('yellow')); } list.unshift({name: chalk.underline.green('Back'), value: 'back'}); return list } function metaDataEdited(host, edited){ console.log(output.Separator('yellow', 'Edited metadata', true)) console.log(chalk.green('- ' + host)) _.each(edited, function(e){ console.log(chalk.green(' - meta: ' + e.prop + ' = ' + e.val)) }) console.log(output.Separator('yellow', true)) } /** * * @param color * @param message * @param {boolean} plain If false returns a inquirer separator for use in lists. * @returns {*} */ function separate(color, message, plain){ if(_.isBoolean(message)){ plain = message message = false } var format = message ? ' ' + message + ' ' : [] var count = (format.length >= 80) ? 0 : 80 - format.length /** * Add invisible chars here after length has been established */ format = chalk.bold(format); for(; count--;){ if(count % 2){ format = '-' + format } else{ format = format + '-' } } if(plain){ return chalk[color](format) } return new inquirer.Separator(chalk[color](format)) } /* * For shame for shame, inquirer breaks node conventions, and doesn't return error first callbacks. */ function Prompt(prompt){ return new Promise(function(resolve, reject){ inquirer.prompt(prompt, function(answer){ var unwrapped = answer[_.keys(answer)[0]] resolve(unwrapped); }) }) }