mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 11:05:54 +01:00 
			
		
		
		
	CLI refactor with commander (#6058)
* CLI refactor with commander - Modularized the functionality - All functionality done directly from `./nodebb` now (still available from `app` for backwards compatibility) - Moved all CLI code from `./nodebb` to `src/cli` - Fixed `nodebb.bat` to work from any location, like `./nodebb`, and also hides command output - Overwrite some commander methods to add CLI color support - Added `./nodebb info` for quick info including git hash, NodeBB version, node version, and some database info - Refactored `./nodebb reset` to allow multiple resets at once - Changed `./nodebb restart` to essentially stop and start, as Windows doesn't support signals - Added `-l, --log` option which works on `./nodebb start` and `./nodebb restart` to show logging, like `./nodebb slog` - Expanded `-d, --dev` option which works on them as well, like `./nodebb dev` - Improvements to self-help. `./nodebb build -h` will output all possible targets - `./nodebb reset` explains usage better * Fix some style inconsistencies * Fix prestart being required before modules installed * Fix travis failures * Fix `help` command to output help for subcommands * Pick steps of the upgrade process to run * Fix formatting for upgrade help * Fix web installer
This commit is contained in:
		
				
					committed by
					
						 Barış Soner Uşaklı
						Barış Soner Uşaklı
					
				
			
			
				
	
			
			
			
						parent
						
							c731661a39
						
					
				
				
					commit
					ae24bca16e
				
			
							
								
								
									
										266
									
								
								app.js
									
									
									
									
									
								
							
							
						
						
									
										266
									
								
								app.js
									
									
									
									
									
								
							| @@ -30,47 +30,16 @@ nconf.argv().env({ | ||||
| 	separator: '__', | ||||
| }); | ||||
|  | ||||
| var url = require('url'); | ||||
| var async = require('async'); | ||||
| var winston = require('winston'); | ||||
| var path = require('path'); | ||||
| var pkg = require('./package.json'); | ||||
|  | ||||
| var file = require('./src/file'); | ||||
| var debug = require('./src/meta/debugFork').debugging; | ||||
|  | ||||
| global.env = process.env.NODE_ENV || 'production'; | ||||
|  | ||||
| winston.remove(winston.transports.Console); | ||||
| winston.add(winston.transports.Console, { | ||||
| 	colorize: true, | ||||
| 	timestamp: function () { | ||||
| 		var date = new Date(); | ||||
| 		return nconf.get('json-logging') ? date.toJSON() :	date.getDate() + '/' + (date.getMonth() + 1) + ' ' + date.toTimeString().substr(0, 8) + ' [' + global.process.pid + ']'; | ||||
| 	}, | ||||
| 	level: nconf.get('log-level') || (global.env === 'production' ? 'info' : 'verbose'), | ||||
| 	json: (!!nconf.get('json-logging')), | ||||
| 	stringify: (!!nconf.get('json-logging')), | ||||
| }); | ||||
|  | ||||
| if (debug) { | ||||
| 	var winstonCommon = require('winston/lib/winston/common'); | ||||
| 	// Override to use real console.log etc for VSCode debugger | ||||
| 	winston.transports.Console.prototype.log = function (level, message, meta, callback) { | ||||
| 		const output = winstonCommon.log(Object.assign({}, this, { | ||||
| 			level, | ||||
| 			message, | ||||
| 			meta, | ||||
| 		})); | ||||
|  | ||||
| 		console[level in console ? level : 'log'](output); | ||||
|  | ||||
| 		setImmediate(callback, null, true); | ||||
| 	}; | ||||
| } | ||||
|  | ||||
|  | ||||
| // Alternate configuration file support | ||||
| var	configFile = path.join(__dirname, '/config.json'); | ||||
| var	configFile = path.join(__dirname, 'config.json'); | ||||
|  | ||||
| if (nconf.get('config')) { | ||||
| 	configFile = path.resolve(__dirname, nconf.get('config')); | ||||
| @@ -78,8 +47,9 @@ if (nconf.get('config')) { | ||||
|  | ||||
| var configExists = file.existsSync(configFile) || (nconf.get('url') && nconf.get('secret') && nconf.get('database')); | ||||
|  | ||||
| loadConfig(); | ||||
| versionCheck(); | ||||
| var prestart = require('./src/prestart'); | ||||
| prestart.loadConfig(configFile); | ||||
| prestart.versionCheck(); | ||||
|  | ||||
| if (!process.send) { | ||||
| 	// If run using `node app`, log GNU copyright info along with server info | ||||
| @@ -89,224 +59,40 @@ if (!process.send) { | ||||
| 	winston.info(''); | ||||
| } | ||||
|  | ||||
|  | ||||
| if (nconf.get('setup') || nconf.get('install')) { | ||||
| 	setup(); | ||||
| 	require('./src/cli/setup').setup(); | ||||
| } else if (!configExists) { | ||||
| 	require('./install/web').install(nconf.get('port')); | ||||
| } else if (nconf.get('upgrade')) { | ||||
| 	upgrade(); | ||||
| 	require('./src/cli/upgrade').upgrade(true); | ||||
| } else if (nconf.get('reset')) { | ||||
| 	async.waterfall([ | ||||
| 		async.apply(require('./src/reset').reset), | ||||
| 		async.apply(require('./src/meta/build').buildAll), | ||||
| 	var options = { | ||||
| 		theme: nconf.get('t'), | ||||
| 		plugin: nconf.get('p'), | ||||
| 		widgets: nconf.get('w'), | ||||
| 		settings: nconf.get('s'), | ||||
| 		all: nconf.get('a'), | ||||
| 	}; | ||||
|  | ||||
| 	async.series([ | ||||
| 		async.apply(require('./src/cli/reset').reset, options), | ||||
| 		require('./src/meta/build').buildAll, | ||||
| 	], function (err) { | ||||
| 		process.exit(err ? 1 : 0); | ||||
| 		if (err) { | ||||
| 			throw err; | ||||
| 		} | ||||
|  | ||||
| 		process.exit(0); | ||||
| 	}); | ||||
| } else if (nconf.get('activate')) { | ||||
| 	activate(); | ||||
| 	require('./src/cli/manage').activate(nconf.get('activate')); | ||||
| } else if (nconf.get('plugins')) { | ||||
| 	listPlugins(); | ||||
| 	require('./src/cli/manage').listPlugins(); | ||||
| } else if (nconf.get('build')) { | ||||
| 	require('./src/meta/build').build(nconf.get('build')); | ||||
| } else if (nconf.get('events')) { | ||||
| 	async.series([ | ||||
| 		async.apply(require('./src/database').init), | ||||
| 		async.apply(require('./src/events').output), | ||||
| 	]); | ||||
| 	require('./src/cli/manage').listEvents(); | ||||
| } else { | ||||
| 	require('./src/start').start(); | ||||
| } | ||||
|  | ||||
| function loadConfig(callback) { | ||||
| 	winston.verbose('* using configuration stored in: %s', configFile); | ||||
|  | ||||
| 	nconf.file({ | ||||
| 		file: configFile, | ||||
| 	}); | ||||
|  | ||||
| 	nconf.defaults({ | ||||
| 		base_dir: __dirname, | ||||
| 		themes_path: path.join(__dirname, 'node_modules'), | ||||
| 		upload_path: 'public/uploads', | ||||
| 		views_dir: path.join(__dirname, 'build/public/templates'), | ||||
| 		version: pkg.version, | ||||
| 	}); | ||||
|  | ||||
| 	if (!nconf.get('isCluster')) { | ||||
| 		nconf.set('isPrimary', 'true'); | ||||
| 		nconf.set('isCluster', 'false'); | ||||
| 	} | ||||
|  | ||||
| 	// Ensure themes_path is a full filepath | ||||
| 	nconf.set('themes_path', path.resolve(__dirname, nconf.get('themes_path'))); | ||||
| 	nconf.set('core_templates_path', path.join(__dirname, 'src/views')); | ||||
| 	nconf.set('base_templates_path', path.join(nconf.get('themes_path'), 'nodebb-theme-persona/templates')); | ||||
|  | ||||
| 	nconf.set('upload_path', path.resolve(nconf.get('base_dir'), nconf.get('upload_path'))); | ||||
|  | ||||
| 	if (nconf.get('url')) { | ||||
| 		nconf.set('url_parsed', url.parse(nconf.get('url'))); | ||||
| 	} | ||||
|  | ||||
| 	// Explicitly cast 'jobsDisabled' as Bool | ||||
| 	var castAsBool = ['jobsDisabled']; | ||||
| 	nconf.stores.env.readOnly = false; | ||||
| 	castAsBool.forEach(function (prop) { | ||||
| 		var value = nconf.get(prop); | ||||
| 		if (value) { | ||||
| 			nconf.set(prop, typeof value === 'boolean' ? value : String(value).toLowerCase() === 'true'); | ||||
| 		} | ||||
| 	}); | ||||
| 	nconf.stores.env.readOnly = true; | ||||
|  | ||||
| 	if (typeof callback === 'function') { | ||||
| 		callback(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| function setup() { | ||||
| 	winston.info('NodeBB Setup Triggered via Command Line'); | ||||
|  | ||||
| 	var install = require('./src/install'); | ||||
| 	var build = require('./src/meta/build'); | ||||
|  | ||||
| 	process.stdout.write('\nWelcome to NodeBB!\n'); | ||||
| 	process.stdout.write('\nThis looks like a new installation, so you\'ll have to answer a few questions about your environment before we can proceed.\n'); | ||||
| 	process.stdout.write('Press enter to accept the default setting (shown in brackets).\n'); | ||||
|  | ||||
| 	async.series([ | ||||
| 		async.apply(install.setup), | ||||
| 		async.apply(loadConfig), | ||||
| 		async.apply(build.buildAll), | ||||
| 	], function (err, data) { | ||||
| 		// Disregard build step data | ||||
| 		data = data[0]; | ||||
|  | ||||
| 		var separator = '     '; | ||||
| 		if (process.stdout.columns > 10) { | ||||
| 			for (var x = 0, cols = process.stdout.columns - 10; x < cols; x += 1) { | ||||
| 				separator += '='; | ||||
| 			} | ||||
| 		} | ||||
| 		process.stdout.write('\n' + separator + '\n\n'); | ||||
|  | ||||
| 		if (err) { | ||||
| 			winston.error('There was a problem completing NodeBB setup', err); | ||||
| 			throw err; | ||||
| 		} else { | ||||
| 			if (data.hasOwnProperty('password')) { | ||||
| 				process.stdout.write('An administrative user was automatically created for you:\n'); | ||||
| 				process.stdout.write('    Username: ' + data.username + '\n'); | ||||
| 				process.stdout.write('    Password: ' + data.password + '\n'); | ||||
| 				process.stdout.write('\n'); | ||||
| 			} | ||||
| 			process.stdout.write('NodeBB Setup Completed. Run \'./nodebb start\' to manually start your NodeBB server.\n'); | ||||
|  | ||||
| 			// If I am a child process, notify the parent of the returned data before exiting (useful for notifying | ||||
| 			// hosts of auto-generated username/password during headless setups) | ||||
| 			if (process.send) { | ||||
| 				process.send(data); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		process.exit(); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function upgrade() { | ||||
| 	var db = require('./src/database'); | ||||
| 	var meta = require('./src/meta'); | ||||
| 	var upgrade = require('./src/upgrade'); | ||||
| 	var build = require('./src/meta/build'); | ||||
| 	var tasks = [db.init, meta.configs.init]; | ||||
|  | ||||
| 	if (nconf.get('upgrade') !== true) { | ||||
| 		// Likely an upgrade script name passed in | ||||
| 		tasks.push(async.apply(upgrade.runParticular, nconf.get('upgrade').split(','))); | ||||
| 	} else { | ||||
| 		tasks.push(upgrade.run, build.buildAll); | ||||
| 	} | ||||
| 	// disable mongo timeouts during upgrade | ||||
| 	nconf.set('mongo:options:socketTimeoutMS', 0); | ||||
| 	async.series(tasks, function (err) { | ||||
| 		if (err) { | ||||
| 			winston.error(err.stack); | ||||
| 			process.exit(1); | ||||
| 		} else { | ||||
| 			process.exit(0); | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function activate() { | ||||
| 	var db = require('./src/database'); | ||||
| 	var plugins = require('./src/plugins'); | ||||
| 	var events = require('./src/events'); | ||||
| 	var plugin = nconf.get('activate'); | ||||
| 	async.waterfall([ | ||||
| 		function (next) { | ||||
| 			db.init(next); | ||||
| 		}, | ||||
| 		function (next) { | ||||
| 			if (plugin.indexOf('nodebb-') !== 0) { | ||||
| 				// Allow omission of `nodebb-plugin-` | ||||
| 				plugin = 'nodebb-plugin-' + plugin; | ||||
| 			} | ||||
| 			plugins.isInstalled(plugin, next); | ||||
| 		}, | ||||
| 		function (isInstalled, next) { | ||||
| 			if (!isInstalled) { | ||||
| 				return next(new Error('plugin not installed')); | ||||
| 			} | ||||
|  | ||||
| 			winston.info('Activating plugin `%s`', plugin); | ||||
| 			db.sortedSetAdd('plugins:active', 0, plugin, next); | ||||
| 		}, | ||||
| 		function (next) { | ||||
| 			events.log({ | ||||
| 				type: 'plugin-activate', | ||||
| 				text: plugin, | ||||
| 			}, next); | ||||
| 		}, | ||||
| 	], function (err) { | ||||
| 		if (err) { | ||||
| 			winston.error('An error occurred during plugin activation', err); | ||||
| 			throw err; | ||||
| 		} | ||||
| 		process.exit(0); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function listPlugins() { | ||||
| 	require('./src/database').init(function (err) { | ||||
| 		if (err) { | ||||
| 			winston.error(err.stack); | ||||
| 			process.exit(1); | ||||
| 		} | ||||
|  | ||||
| 		var db = require('./src/database'); | ||||
|  | ||||
| 		db.getSortedSetRange('plugins:active', 0, -1, function (err, plugins) { | ||||
| 			if (err) { | ||||
| 				winston.error(err.stack); | ||||
| 				process.exit(1); | ||||
| 			} | ||||
|  | ||||
| 			winston.info('Active plugins: \n\t - ' + plugins.join('\n\t - ')); | ||||
| 			process.exit(); | ||||
| 		}); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function versionCheck() { | ||||
| 	var version = process.version.slice(1); | ||||
| 	var range = pkg.engines.node; | ||||
| 	var semver = require('semver'); | ||||
| 	var compatible = semver.satisfies(version, range); | ||||
|  | ||||
| 	if (!compatible) { | ||||
| 		winston.warn('Your version of Node.js is too outdated for NodeBB. Please update your version of Node.js.'); | ||||
| 		winston.warn('Recommended ' + range.green + ', '.reset + version.yellow + ' provided\n'.reset); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -27,6 +27,7 @@ | ||||
|       "chart.js": "^2.7.0", | ||||
|       "colors": "^1.1.2", | ||||
|       "compression": "^1.7.1", | ||||
|       "commander": "^2.11.0", | ||||
|       "connect-ensure-login": "^0.1.1", | ||||
|       "connect-flash": "^0.1.1", | ||||
|       "connect-mongo": "2.0.0", | ||||
| @@ -52,7 +53,6 @@ | ||||
|       "logrotate-stream": "^0.2.5", | ||||
|       "lru-cache": "4.1.1", | ||||
|       "mime": "^2.0.3", | ||||
|       "minimist": "^1.2.0", | ||||
|       "mkdirp": "^0.5.1", | ||||
|       "mongodb": "2.2.33", | ||||
|       "morgan": "^1.9.0", | ||||
|   | ||||
							
								
								
									
										547
									
								
								nodebb
									
									
									
									
									
								
							
							
						
						
									
										547
									
								
								nodebb
									
									
									
									
									
								
							| @@ -2,549 +2,4 @@ | ||||
|  | ||||
| 'use strict'; | ||||
|  | ||||
| var fs = require('fs'); | ||||
| var path = require('path'); | ||||
| var cproc = require('child_process'); | ||||
|  | ||||
| var packageInstall = require('./src/meta/package-install'); | ||||
|  | ||||
| // check to make sure dependencies are installed | ||||
| try { | ||||
| 	fs.readFileSync(path.join(__dirname, './package.json')); | ||||
| 	fs.readFileSync(path.join(__dirname, 'node_modules/async/package.json')); | ||||
| } catch (e) { | ||||
| 	if (e.code === 'ENOENT') { | ||||
| 		process.stdout.write('Dependencies not yet installed.\n'); | ||||
| 		process.stdout.write('Installing them now...\n\n'); | ||||
|  | ||||
| 		packageInstall.updatePackageFile(); | ||||
| 		packageInstall.preserveExtraneousPlugins(); | ||||
| 		packageInstall.npmInstallProduction(); | ||||
| 	} else { | ||||
| 		throw e; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| var minimist; | ||||
| var request; | ||||
| var semver; | ||||
| var prompt; | ||||
| var async; | ||||
|  | ||||
| try { | ||||
| 	require('colors'); | ||||
| 	minimist = require('minimist'); | ||||
| 	request = require('request'); | ||||
| 	semver = require('semver'); | ||||
| 	prompt = require('prompt'); | ||||
| 	async = require('async'); | ||||
| } catch (e) { | ||||
| 	process.stdout.write( | ||||
| 		'\x1b[31mNodeBB could not be initialised because there was an error while loading dependencies.\n' + | ||||
| 		'Please run "\x1b[33mnpm install --production\x1b[31m" and try again.\x1b[0m\n\n' + | ||||
| 		'For more information, please see: https://docs.nodebb.org/installing/os/\n\n' | ||||
| 	); | ||||
|  | ||||
| 	throw e; | ||||
| } | ||||
|  | ||||
| var args = minimist(process.argv.slice(2)); | ||||
|  | ||||
| var loaderPath = path.join(__dirname, 'loader.js'); | ||||
| var appPath = path.join(__dirname, 'app.js'); | ||||
|  | ||||
| if (args.dev) { | ||||
| 	process.env.NODE_ENV = 'development'; | ||||
| } | ||||
|  | ||||
| function getRunningPid(callback) { | ||||
| 	fs.readFile(path.join(__dirname, 'pidfile'), { | ||||
| 		encoding: 'utf-8', | ||||
| 	}, function (err, pid) { | ||||
| 		if (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
|  | ||||
| 		try { | ||||
| 			process.kill(parseInt(pid, 10), 0); | ||||
| 			callback(null, parseInt(pid, 10)); | ||||
| 		} catch (e) { | ||||
| 			callback(e); | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
| function getCurrentVersion(callback) { | ||||
| 	fs.readFile(path.join(__dirname, 'package.json'), { encoding: 'utf-8' }, function (err, pkg) { | ||||
| 		if (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
|  | ||||
| 		try { | ||||
| 			pkg = JSON.parse(pkg); | ||||
| 			return callback(null, pkg.version); | ||||
| 		} catch (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
| function fork(args) { | ||||
| 	return cproc.fork(appPath, args, { | ||||
| 		cwd: __dirname, | ||||
| 		silent: false, | ||||
| 	}); | ||||
| } | ||||
| function getInstalledPlugins(callback) { | ||||
| 	async.parallel({ | ||||
| 		files: async.apply(fs.readdir, path.join(__dirname, 'node_modules')), | ||||
| 		deps: async.apply(fs.readFile, path.join(__dirname, 'package.json'), { encoding: 'utf-8' }), | ||||
| 	}, function (err, payload) { | ||||
| 		if (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
|  | ||||
| 		var isNbbModule = /^nodebb-(?:plugin|theme|widget|rewards)-[\w-]+$/; | ||||
| 		var moduleName; | ||||
| 		var isGitRepo; | ||||
|  | ||||
| 		payload.files = payload.files.filter(function (file) { | ||||
| 			return isNbbModule.test(file); | ||||
| 		}); | ||||
|  | ||||
| 		try { | ||||
| 			payload.deps = JSON.parse(payload.deps).dependencies; | ||||
| 			payload.bundled = []; | ||||
| 			payload.installed = []; | ||||
| 		} catch (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
|  | ||||
| 		for (moduleName in payload.deps) { | ||||
| 			if (isNbbModule.test(moduleName)) { | ||||
| 				payload.bundled.push(moduleName); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// Whittle down deps to send back only extraneously installed plugins/themes/etc | ||||
| 		payload.files.forEach(function (moduleName) { | ||||
| 			try { | ||||
| 				fs.accessSync(path.join(__dirname, 'node_modules/' + moduleName, '.git')); | ||||
| 				isGitRepo = true; | ||||
| 			} catch (e) { | ||||
| 				isGitRepo = false; | ||||
| 			} | ||||
|  | ||||
| 			if ( | ||||
| 				payload.files.indexOf(moduleName) !== -1 &&	// found in `node_modules/` | ||||
| 				payload.bundled.indexOf(moduleName) === -1 &&	// not found in `package.json` | ||||
| 				!fs.lstatSync(path.join(__dirname, 'node_modules/' + moduleName)).isSymbolicLink() &&	// is not a symlink | ||||
| 				!isGitRepo	// .git/ does not exist, so it is not a git repository | ||||
| 			) { | ||||
| 				payload.installed.push(moduleName); | ||||
| 			} | ||||
| 		}); | ||||
|  | ||||
| 		getModuleVersions(payload.installed, callback); | ||||
| 	}); | ||||
| } | ||||
| function getModuleVersions(modules, callback) { | ||||
| 	var versionHash = {}; | ||||
|  | ||||
| 	async.eachLimit(modules, 50, function (module, next) { | ||||
| 		fs.readFile(path.join(__dirname, 'node_modules/' + module + '/package.json'), { encoding: 'utf-8' }, function (err, pkg) { | ||||
| 			if (err) { | ||||
| 				return next(err); | ||||
| 			} | ||||
|  | ||||
| 			try { | ||||
| 				pkg = JSON.parse(pkg); | ||||
| 				versionHash[module] = pkg.version; | ||||
| 				next(); | ||||
| 			} catch (err) { | ||||
| 				next(err); | ||||
| 			} | ||||
| 		}); | ||||
| 	}, function (err) { | ||||
| 		callback(err, versionHash); | ||||
| 	}); | ||||
| } | ||||
| function checkPlugins(standalone, callback) { | ||||
| 	if (standalone) { | ||||
| 		process.stdout.write('Checking installed plugins and themes for updates... '); | ||||
| 	} | ||||
|  | ||||
| 	async.waterfall([ | ||||
| 		async.apply(async.parallel, { | ||||
| 			plugins: async.apply(getInstalledPlugins), | ||||
| 			version: async.apply(getCurrentVersion), | ||||
| 		}), | ||||
| 		function (payload, next) { | ||||
| 			var toCheck = Object.keys(payload.plugins); | ||||
|  | ||||
| 			if (!toCheck.length) { | ||||
| 				process.stdout.write('OK'.green + '\n'.reset); | ||||
| 				return next(null, []);	// no extraneous plugins installed | ||||
| 			} | ||||
|  | ||||
| 			request({ | ||||
| 				method: 'GET', | ||||
| 				url: 'https://packages.nodebb.org/api/v1/suggest?version=' + payload.version + '&package[]=' + toCheck.join('&package[]='), | ||||
| 				json: true, | ||||
| 			}, function (err, res, body) { | ||||
| 				if (err) { | ||||
| 					process.stdout.write('error'.red + '\n'.reset); | ||||
| 					return next(err); | ||||
| 				} | ||||
| 				process.stdout.write('OK'.green + '\n'.reset); | ||||
|  | ||||
| 				if (!Array.isArray(body) && toCheck.length === 1) { | ||||
| 					body = [body]; | ||||
| 				} | ||||
|  | ||||
| 				var current; | ||||
| 				var suggested; | ||||
| 				var upgradable = body.map(function (suggestObj) { | ||||
| 					current = payload.plugins[suggestObj.package]; | ||||
| 					suggested = suggestObj.version; | ||||
|  | ||||
| 					if (suggestObj.code === 'match-found' && semver.gt(suggested, current)) { | ||||
| 						return { | ||||
| 							name: suggestObj.package, | ||||
| 							current: current, | ||||
| 							suggested: suggested, | ||||
| 						}; | ||||
| 					} | ||||
| 					return null; | ||||
| 				}).filter(Boolean); | ||||
|  | ||||
| 				next(null, upgradable); | ||||
| 			}); | ||||
| 		}, | ||||
| 	], callback); | ||||
| } | ||||
| function upgradePlugins(callback) { | ||||
| 	var standalone = false; | ||||
| 	if (typeof callback !== 'function') { | ||||
| 		callback = function () {}; | ||||
| 		standalone = true; | ||||
| 	} | ||||
|  | ||||
| 	checkPlugins(standalone, function (err, found) { | ||||
| 		if (err) { | ||||
| 			process.stdout.write('Warning'.yellow + ': An unexpected error occured when attempting to verify plugin upgradability\n'.reset); | ||||
| 			return callback(err); | ||||
| 		} | ||||
|  | ||||
| 		if (found && found.length) { | ||||
| 			process.stdout.write('\nA total of ' + String(found.length).bold + ' package(s) can be upgraded:\n'); | ||||
| 			found.forEach(function (suggestObj) { | ||||
| 				process.stdout.write('  * '.yellow + suggestObj.name.reset + ' (' + suggestObj.current.yellow + ' -> '.reset + suggestObj.suggested.green + ')\n'.reset); | ||||
| 			}); | ||||
| 			process.stdout.write('\n'); | ||||
| 		} else { | ||||
| 			if (standalone) { | ||||
| 				process.stdout.write('\nAll packages up-to-date!'.green + '\n'.reset); | ||||
| 			} | ||||
| 			return callback(); | ||||
| 		} | ||||
|  | ||||
| 		prompt.message = ''; | ||||
| 		prompt.delimiter = ''; | ||||
|  | ||||
| 		prompt.start(); | ||||
| 		prompt.get({ | ||||
| 			name: 'upgrade', | ||||
| 			description: 'Proceed with upgrade (y|n)?'.reset, | ||||
| 			type: 'string', | ||||
| 		}, function (err, result) { | ||||
| 			if (err) { | ||||
| 				return callback(err); | ||||
| 			} | ||||
|  | ||||
| 			if (['y', 'Y', 'yes', 'YES'].indexOf(result.upgrade) !== -1) { | ||||
| 				process.stdout.write('\nUpgrading packages...'); | ||||
| 				var args = ['i']; | ||||
| 				found.forEach(function (suggestObj) { | ||||
| 					args.push(suggestObj.name + '@' + suggestObj.suggested); | ||||
| 				}); | ||||
|  | ||||
| 				cproc.execFile((process.platform === 'win32') ? 'npm.cmd' : 'npm', args, { stdio: 'ignore' }, function (err) { | ||||
| 					if (!err) { | ||||
| 						process.stdout.write(' OK\n'.green); | ||||
| 					} | ||||
|  | ||||
| 					callback(err); | ||||
| 				}); | ||||
| 			} else { | ||||
| 				process.stdout.write('\nPackage upgrades skipped'.yellow + '. Check for upgrades at any time by running "'.reset + './nodebb upgrade-plugins'.green + '".\n'.reset); | ||||
| 				callback(); | ||||
| 			} | ||||
| 		}); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| var commands = { | ||||
| 	status: { | ||||
| 		description: 'View the status of the NodeBB server', | ||||
| 		usage: 'Usage: ' + './nodebb status'.yellow, | ||||
| 		handler: function () { | ||||
| 			getRunningPid(function (err, pid) { | ||||
| 				if (!err) { | ||||
| 					process.stdout.write('\nNodeBB Running '.bold + '(pid '.cyan + pid.toString().cyan + ')\n'.cyan); | ||||
| 					process.stdout.write('\t"' + './nodebb stop'.yellow + '" to stop the NodeBB server\n'); | ||||
| 					process.stdout.write('\t"' + './nodebb log'.yellow + '" to view server output\n'); | ||||
| 					process.stdout.write('\t"' + './nodebb restart'.yellow + '" to restart NodeBB\n\n'); | ||||
| 				} else { | ||||
| 					process.stdout.write('\nNodeBB is not running\n'.bold); | ||||
| 					process.stdout.write('\t"' + './nodebb start'.yellow + '" to launch the NodeBB server\n\n'.reset); | ||||
| 				} | ||||
| 			}); | ||||
| 		}, | ||||
| 	}, | ||||
| 	start: { | ||||
| 		description: 'Start the NodeBB server', | ||||
| 		usage: 'Usage: ' + './nodebb start'.yellow, | ||||
| 		handler: function () { | ||||
| 			process.stdout.write('\nStarting NodeBB\n'.bold); | ||||
| 			process.stdout.write('  "' + './nodebb stop'.yellow + '" to stop the NodeBB server\n'); | ||||
| 			process.stdout.write('  "' + './nodebb log'.yellow + '" to view server output\n'); | ||||
| 			process.stdout.write('  "' + './nodebb restart'.yellow + '" to restart NodeBB\n\n'.reset); | ||||
|  | ||||
| 			// Spawn a new NodeBB process | ||||
| 			cproc.fork(loaderPath, process.argv.slice(3), { | ||||
| 				env: process.env, | ||||
| 			}); | ||||
| 		}, | ||||
| 	}, | ||||
| 	stop: { | ||||
| 		description: 'Stop the NodeBB server', | ||||
| 		usage: 'Usage: ' + './nodebb stop'.yellow, | ||||
| 		handler: function () { | ||||
| 			getRunningPid(function (err, pid) { | ||||
| 				if (!err) { | ||||
| 					process.kill(pid, 'SIGTERM'); | ||||
| 					process.stdout.write('Stopping NodeBB. Goodbye!\n'); | ||||
| 				} else { | ||||
| 					process.stdout.write('NodeBB is already stopped.\n'); | ||||
| 				} | ||||
| 			}); | ||||
| 		}, | ||||
| 	}, | ||||
| 	restart: { | ||||
| 		description: 'Restart the NodeBB server', | ||||
| 		usage: 'Usage: ' + './nodebb restart'.yellow, | ||||
| 		handler: function () { | ||||
| 			getRunningPid(function (err, pid) { | ||||
| 				if (!err) { | ||||
| 					process.kill(pid, 'SIGHUP'); | ||||
| 					process.stdout.write('\nRestarting NodeBB\n'.bold); | ||||
| 				} else { | ||||
| 					process.stdout.write('NodeBB could not be restarted, as a running instance could not be found.\n'); | ||||
| 				} | ||||
| 			}); | ||||
| 		}, | ||||
| 	}, | ||||
| 	log: { | ||||
| 		description: 'Open the output log (useful for debugging)', | ||||
| 		usage: 'Usage: ' + './nodebb log'.yellow, | ||||
| 		handler: function () { | ||||
| 			process.stdout.write('\nHit '.red + 'Ctrl-C '.bold + 'to exit'.red); | ||||
| 			process.stdout.write('\n\n'.reset); | ||||
| 			cproc.spawn('tail', ['-F', './logs/output.log'], { | ||||
| 				cwd: __dirname, | ||||
| 				stdio: 'inherit', | ||||
| 			}); | ||||
| 		}, | ||||
| 	}, | ||||
| 	slog: { | ||||
| 		description: 'Start the NodeBB server and view the live output log', | ||||
| 		usage: 'Usage: ' + './nodebb slog'.yellow, | ||||
| 		handler: function () { | ||||
| 			process.stdout.write('\nStarting NodeBB with logging output\n'.bold); | ||||
| 			process.stdout.write('\nHit '.red + 'Ctrl-C '.bold + 'to exit'.red); | ||||
| 			process.stdout.write('\n\n'.reset); | ||||
|  | ||||
| 			// Spawn a new NodeBB process | ||||
| 			cproc.fork(loaderPath, { | ||||
| 				env: process.env, | ||||
| 			}); | ||||
| 			cproc.spawn('tail', ['-F', './logs/output.log'], { | ||||
| 				cwd: __dirname, | ||||
| 				stdio: 'inherit', | ||||
| 			}); | ||||
| 		}, | ||||
| 	}, | ||||
| 	dev: { | ||||
| 		description: 'Start NodeBB in verbose development mode', | ||||
| 		usage: 'Usage: ' + './nodebb dev'.yellow, | ||||
| 		handler: function () { | ||||
| 			process.env.NODE_ENV = 'development'; | ||||
| 			cproc.fork(loaderPath, ['--no-daemon', '--no-silent'], { | ||||
| 				env: process.env, | ||||
| 			}); | ||||
| 		}, | ||||
| 	}, | ||||
| 	build: { | ||||
| 		description: 'Compile static assets (CSS, Javascript, etc)', | ||||
| 		usage: 'Usage: ' + './nodebb build'.yellow + ' [js,clientCSS,acpCSS,tpl,lang]'.red + '\n' + | ||||
| 			'    e.g. ' + './nodebb build js,tpl'.yellow + '\tbuilds JS and templates\n' + | ||||
| 			'         ' + './nodebb build'.yellow + '\t\tbuilds all targets\n', | ||||
| 		handler: function () { | ||||
| 			var arr = ['--build'].concat(process.argv.slice(3)); | ||||
| 			fork(arr); | ||||
| 		}, | ||||
| 	}, | ||||
| 	setup: { | ||||
| 		description: 'Run the NodeBB setup script', | ||||
| 		usage: 'Usage: ' + './nodebb setup'.yellow, | ||||
| 		handler: function () { | ||||
| 			var arr = ['--setup'].concat(process.argv.slice(3)); | ||||
| 			fork(arr); | ||||
| 		}, | ||||
| 	}, | ||||
| 	reset: { | ||||
| 		description: 'Disable plugins and restore the default theme', | ||||
| 		usage: 'Usage: ' + './nodebb reset '.yellow + '{-t|-p|-w|-s|-a}'.red + '\n' + | ||||
| 			'    -t <theme>\tuse specified theme\n' + | ||||
| 			'    -p <plugin>\tdisable specified plugin\n' + | ||||
| 			'\n' + | ||||
| 			'    -t\t\tuse default theme\n' + | ||||
| 			'    -p\t\tdisable all but core plugins\n' + | ||||
| 			'    -w\t\twidgets\n' + | ||||
| 			'    -s\t\tsettings\n' + | ||||
| 			'    -a\t\tall of the above\n', | ||||
| 		handler: function () { | ||||
| 			var arr = ['--reset'].concat(process.argv.slice(3)); | ||||
| 			fork(arr); | ||||
| 		}, | ||||
| 	}, | ||||
| 	activate: { | ||||
| 		description: 'Activate a plugin for the next startup of NodeBB', | ||||
| 		usage: 'Usage: ' + './nodebb activate <plugin>'.yellow, | ||||
| 		handler: function () { | ||||
| 			var name = args._[1]; | ||||
| 			if (!name) { | ||||
| 				process.stdout.write(commands.activate.usage + '\n'); | ||||
| 				process.exit(); | ||||
| 			} | ||||
| 			if (name.startsWith('nodebb-theme')) { | ||||
| 				fork(['--reset', '-t', name]); | ||||
| 				return; | ||||
| 			} | ||||
| 			var arr = ['--activate=' + name].concat(process.argv.slice(4)); | ||||
| 			fork(arr); | ||||
| 		}, | ||||
| 	}, | ||||
| 	plugins: { | ||||
| 		description: 'List all installed plugins', | ||||
| 		usage: 'Usage: ' + './nodebb plugins'.yellow, | ||||
| 		handler: function () { | ||||
| 			var arr = ['--plugins'].concat(process.argv.slice(3)); | ||||
| 			fork(arr); | ||||
| 		}, | ||||
| 	}, | ||||
| 	upgrade: { | ||||
| 		description: 'Run NodeBB upgrade scripts, ensure packages are up-to-date', | ||||
| 		usage: 'Usage: ' + './nodebb upgrade'.yellow, | ||||
| 		handler: function () { | ||||
| 			if (process.argv[3]) { | ||||
| 				process.stdout.write('\nUpdating NodeBB data store schema...\n'.yellow); | ||||
| 				var arr = ['--upgrade'].concat(process.argv.slice(3)); | ||||
| 				var upgradeProc = fork(arr); | ||||
|  | ||||
| 				return upgradeProc.on('close', function (err) { | ||||
| 					if (err) { | ||||
| 						process.stdout.write('Error occurred during upgrade'); | ||||
| 						throw err; | ||||
| 					} | ||||
| 				}); | ||||
| 			} | ||||
|  | ||||
| 			async.series([ | ||||
| 				function (next) { | ||||
| 					packageInstall.updatePackageFile(); | ||||
| 					packageInstall.preserveExtraneousPlugins(); | ||||
| 					next(); | ||||
| 				}, | ||||
| 				function (next) { | ||||
| 					process.stdout.write('1. '.bold + 'Bringing base dependencies up to date... \n'.yellow); | ||||
| 					packageInstall.npmInstallProduction(); | ||||
| 					next(); | ||||
| 				}, | ||||
| 				function (next) { | ||||
| 					process.stdout.write('OK\n'.green); | ||||
| 					process.stdout.write('2. '.bold + 'Checking installed plugins for updates... '.yellow); | ||||
| 					upgradePlugins(next); | ||||
| 				}, | ||||
| 				function (next) { | ||||
| 					process.stdout.write('3. '.bold + 'Updating NodeBB data store schema...\n'.yellow); | ||||
| 					var arr = ['--upgrade'].concat(process.argv.slice(3)); | ||||
| 					var upgradeProc = fork(arr); | ||||
|  | ||||
| 					upgradeProc.on('close', next); | ||||
| 					upgradeProc.on('error', next); | ||||
| 				}, | ||||
| 			], function (err) { | ||||
| 				if (err) { | ||||
| 					process.stdout.write('Error occurred during upgrade'); | ||||
| 					throw err; | ||||
| 				} | ||||
|  | ||||
| 				var message = 'NodeBB Upgrade Complete!'; | ||||
| 				// some consoles will return undefined/zero columns, so just use 2 spaces in upgrade script if we can't get our column count | ||||
| 				var columns = process.stdout.columns; | ||||
| 				var spaces = columns ? new Array(Math.floor(columns / 2) - (message.length / 2) + 1).join(' ') : '  '; | ||||
|  | ||||
| 				process.stdout.write('OK\n'.green); | ||||
| 				process.stdout.write('\n' + spaces + message.green.bold + '\n\n'.reset); | ||||
| 			}); | ||||
| 		}, | ||||
| 	}, | ||||
| 	upgradePlugins: { | ||||
| 		hidden: true, | ||||
| 		description: '', | ||||
| 		handler: function () { | ||||
| 			upgradePlugins(); | ||||
| 		}, | ||||
| 	}, | ||||
| 	events: { | ||||
| 		description: 'Outputs the last ten (10) administrative events recorded by NodeBB', | ||||
| 		usage: 'Usage: ' + './nodebb events'.yellow, | ||||
| 		handler: function () { | ||||
| 			fork(['--events']); | ||||
| 		}, | ||||
| 	}, | ||||
| 	help: { | ||||
| 		description: 'Display the help message for a given command', | ||||
| 		usage: 'Usage: ' + './nodebb help <command>'.yellow, | ||||
| 		handler: function () { | ||||
| 			var command = commands[args._[1]]; | ||||
| 			if (command) { | ||||
| 				process.stdout.write(command.description + '\n'.reset); | ||||
| 				process.stdout.write(command.usage + '\n'.reset); | ||||
|  | ||||
| 				return; | ||||
| 			} | ||||
| 			var keys = Object.keys(commands).filter(function (key) { | ||||
| 				return !commands[key].hidden; | ||||
| 			}); | ||||
|  | ||||
| 			process.stdout.write('\nWelcome to NodeBB\n\n'.bold); | ||||
| 			process.stdout.write('Usage: ./nodebb {' + keys.join('|') + '}\n\n'); | ||||
|  | ||||
| 			var usage = keys.map(function (key) { | ||||
| 				var line = '\t' + key.yellow + (key.length < 8 ? '\t\t' : '\t'); | ||||
| 				return line + commands[key].description; | ||||
| 			}).join('\n'); | ||||
|  | ||||
| 			process.stdout.write(usage + '\n'.reset); | ||||
| 		}, | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| commands['upgrade-plugins'] = commands.upgradePlugins; | ||||
|  | ||||
| if (!commands[args._[0]]) { | ||||
| 	commands.help.handler(); | ||||
| } else { | ||||
| 	commands[args._[0]].handler(); | ||||
| } | ||||
| require('./src/cli'); | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| node ./nodebb %* | ||||
| @echo off && cd %~dp0 && node ./src/cli %* | ||||
|   | ||||
							
								
								
									
										127
									
								
								src/cli/colors.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								src/cli/colors.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | ||||
| 'use strict'; | ||||
|  | ||||
|  | ||||
| // override commander functions | ||||
| // to include color styling in the output | ||||
| // so the CLI looks nice | ||||
|  | ||||
| var Command = require('commander').Command; | ||||
|  | ||||
| var commandColor = 'yellow'; | ||||
| var optionColor = 'cyan'; | ||||
| var argColor = 'magenta'; | ||||
| var subCommandColor = 'green'; | ||||
| var subOptionColor = 'blue'; | ||||
| var subArgColor = 'red'; | ||||
|  | ||||
| Command.prototype.helpInformation = function () { | ||||
| 	var desc = []; | ||||
| 	if (this._description) { | ||||
| 		desc = [ | ||||
| 			'  ' + this._description, | ||||
| 			'', | ||||
| 		]; | ||||
| 	} | ||||
|  | ||||
| 	var cmdName = this._name; | ||||
| 	if (this._alias) { | ||||
| 		cmdName = cmdName + ' | ' + this._alias; | ||||
| 	} | ||||
| 	var usage = [ | ||||
| 		'', | ||||
| 		'  Usage: ' + cmdName[commandColor] + ' '.reset + this.usage(), | ||||
| 		'', | ||||
| 	]; | ||||
|  | ||||
| 	var cmds = []; | ||||
| 	var commandHelp = this.commandHelp(); | ||||
| 	if (commandHelp) { | ||||
| 		cmds = [commandHelp]; | ||||
| 	} | ||||
|  | ||||
| 	var options = [ | ||||
| 		'', | ||||
| 		'  Options:', | ||||
| 		'', | ||||
| 		'' + this.optionHelp().replace(/^/gm, '    '), | ||||
| 		'', | ||||
| 	]; | ||||
|  | ||||
| 	return usage | ||||
| 		.concat(desc) | ||||
| 		.concat(options) | ||||
| 		.concat(cmds) | ||||
| 		.join('\n'.reset); | ||||
| }; | ||||
|  | ||||
| function humanReadableArgName(arg) { | ||||
| 	var nameOutput = arg.name + (arg.variadic === true ? '...' : ''); | ||||
|  | ||||
| 	return arg.required ? '<' + nameOutput + '>' : '[' + nameOutput + ']'; | ||||
| } | ||||
|  | ||||
| Command.prototype.usage = function () { | ||||
| 	var args = this._args.map(function (arg) { | ||||
| 		return humanReadableArgName(arg); | ||||
| 	}); | ||||
|  | ||||
| 	var usage = '[options]'[optionColor] + | ||||
| 		(this.commands.length ? ' [command]' : '')[subCommandColor] + | ||||
| 		(this._args.length ? ' ' + args.join(' ') : '')[argColor]; | ||||
|  | ||||
| 	return usage; | ||||
| }; | ||||
|  | ||||
| function pad(str, width) { | ||||
| 	var len = Math.max(0, width - str.length); | ||||
| 	return str + Array(len + 1).join(' '); | ||||
| } | ||||
|  | ||||
| Command.prototype.commandHelp = function () { | ||||
| 	if (!this.commands.length) { | ||||
| 		return ''; | ||||
| 	} | ||||
|  | ||||
| 	var commands = this.commands.filter(function (cmd) { | ||||
| 		return !cmd._noHelp; | ||||
| 	}).map(function (cmd) { | ||||
| 		var args = cmd._args.map(function (arg) { | ||||
| 			return humanReadableArgName(arg); | ||||
| 		}).join(' '); | ||||
|  | ||||
| 		return [ | ||||
| 			cmd._name[subCommandColor] + | ||||
| 				(cmd._alias ? ' | ' + cmd._alias : '')[subCommandColor] + | ||||
| 				(cmd.options.length ? ' [options]' : '')[subOptionColor] + | ||||
| 				' ' + args[subArgColor], | ||||
| 			cmd._description, | ||||
| 		]; | ||||
| 	}); | ||||
|  | ||||
| 	var width = commands.reduce(function (max, command) { | ||||
| 		return Math.max(max, command[0].length); | ||||
| 	}, 0); | ||||
|  | ||||
| 	return [ | ||||
| 		'', | ||||
| 		'  Commands:', | ||||
| 		'', | ||||
| 		commands.map(function (cmd) { | ||||
| 			var desc = cmd[1] ? '  ' + cmd[1] : ''; | ||||
| 			return pad(cmd[0], width) + desc; | ||||
| 		}).join('\n').replace(/^/gm, '    '), | ||||
| 		'', | ||||
| 	].join('\n'); | ||||
| }; | ||||
|  | ||||
| Command.prototype.optionHelp = function () { | ||||
| 	var width = this.largestOptionLength(); | ||||
|  | ||||
| 	// Append the help information | ||||
| 	return this.options | ||||
| 		.map(function (option) { | ||||
| 			return pad(option.flags, width)[optionColor] + '  ' + option.description; | ||||
| 		}) | ||||
| 		.concat([pad('-h, --help', width)[optionColor] + '  output usage information']) | ||||
| 		.join('\n'); | ||||
| }; | ||||
							
								
								
									
										265
									
								
								src/cli/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										265
									
								
								src/cli/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,265 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var fs = require('fs'); | ||||
| var path = require('path'); | ||||
|  | ||||
| var packageInstall = require('../meta/package-install'); | ||||
| var dirname = require('./paths').baseDir; | ||||
|  | ||||
| // check to make sure dependencies are installed | ||||
| try { | ||||
| 	fs.readFileSync(path.join(dirname, 'package.json')); | ||||
| 	fs.readFileSync(path.join(dirname, 'node_modules/async/package.json')); | ||||
| } catch (e) { | ||||
| 	if (e.code === 'ENOENT') { | ||||
| 		process.stdout.write('Dependencies not yet installed.\n'); | ||||
| 		process.stdout.write('Installing them now...\n\n'); | ||||
|  | ||||
| 		packageInstall.updatePackageFile(); | ||||
| 		packageInstall.preserveExtraneousPlugins(); | ||||
| 		packageInstall.npmInstallProduction(); | ||||
|  | ||||
| 		require('colors'); | ||||
| 		process.stdout.write('OK'.green + '\n'.reset); | ||||
| 	} else { | ||||
| 		throw e; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| require('colors'); | ||||
| var nconf = require('nconf'); | ||||
| var program = require('commander'); | ||||
|  | ||||
| var pkg = require('../../package.json'); | ||||
| var file = require('../file'); | ||||
| var prestart = require('../prestart'); | ||||
|  | ||||
| program | ||||
| 	.name('./nodebb') | ||||
| 	.description('Welcome to NodeBB') | ||||
| 	.version(pkg.version) | ||||
| 	.option('--json-logging', 'Output to logs in JSON format', false) | ||||
| 	.option('--log-level <level>', 'Default logging level to use', 'info') | ||||
| 	.option('-d, --dev', 'Development mode, including verbose logging', false) | ||||
| 	.option('-l, --log', 'Log subprocess output to console', false) | ||||
| 	.option('-c, --config <value>', 'Specify a config file', 'config.json') | ||||
| 	.parse(process.argv); | ||||
|  | ||||
| nconf.argv().env({ | ||||
| 	separator: '__', | ||||
| }); | ||||
|  | ||||
| var env = program.dev ? 'development' : (process.env.NODE_ENV || 'production'); | ||||
| process.env.NODE_ENV = env; | ||||
| global.env = env; | ||||
|  | ||||
| prestart.setupWinston(); | ||||
|  | ||||
| // Alternate configuration file support | ||||
| var	configFile = path.resolve(dirname, program.config); | ||||
| var configExists = file.existsSync(configFile) || (nconf.get('url') && nconf.get('secret') && nconf.get('database')); | ||||
|  | ||||
| prestart.loadConfig(configFile); | ||||
| prestart.versionCheck(); | ||||
|  | ||||
| if (!configExists && process.argv[2] !== 'setup') { | ||||
| 	require('./setup').webInstall(); | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| // running commands | ||||
| program | ||||
| 	.command('start') | ||||
| 	.description('Start the NodeBB server') | ||||
| 	.action(function () { | ||||
| 		require('./running').start(program); | ||||
| 	}); | ||||
| program | ||||
| 	.command('slog', null, { | ||||
| 		noHelp: true, | ||||
| 	}) | ||||
| 	.description('Start the NodeBB server and view the live output log') | ||||
| 	.action(function () { | ||||
| 		program.log = true; | ||||
| 		require('./running').start(program); | ||||
| 	}); | ||||
| program | ||||
| 	.command('dev', null, { | ||||
| 		noHelp: true, | ||||
| 	}) | ||||
| 	.description('Start NodeBB in verbose development mode') | ||||
| 	.action(function () { | ||||
| 		program.dev = true; | ||||
| 		process.env.NODE_ENV = 'development'; | ||||
| 		global.env = 'development'; | ||||
| 		require('./running').start(program); | ||||
| 	}); | ||||
| program | ||||
| 	.command('stop') | ||||
| 	.description('Stop the NodeBB server') | ||||
| 	.action(function () { | ||||
| 		require('./running').stop(program); | ||||
| 	}); | ||||
| program | ||||
| 	.command('restart') | ||||
| 	.description('Restart the NodeBB server') | ||||
| 	.action(function () { | ||||
| 		require('./running').restart(program); | ||||
| 	}); | ||||
| program | ||||
| 	.command('status') | ||||
| 	.description('Check the running status of the NodeBB server') | ||||
| 	.action(function () { | ||||
| 		require('./running').status(program); | ||||
| 	}); | ||||
| program | ||||
| 	.command('log') | ||||
| 	.description('Open the output log (useful for debugging)') | ||||
| 	.action(function () { | ||||
| 		require('./running').log(program); | ||||
| 	}); | ||||
|  | ||||
| // management commands | ||||
| program | ||||
| 	.command('setup') | ||||
| 	.description('Run the NodeBB setup script') | ||||
| 	.action(function () { | ||||
| 		require('./setup').setup(); | ||||
| 	}); | ||||
|  | ||||
| program | ||||
| 	.command('install') | ||||
| 	.description('Launch the NodeBB web installer for configuration setup') | ||||
| 	.action(function () { | ||||
| 		require('./setup').webInstall(); | ||||
| 	}); | ||||
| program | ||||
| 	.command('build [targets...]') | ||||
| 	.description('Compile static assets ' + '(JS, CSS, templates, languages, sounds)'.red) | ||||
| 	.action(function (targets) { | ||||
| 		require('./manage').build(targets.length ? targets : true); | ||||
| 	}) | ||||
| 	.on('--help', function () { | ||||
| 		require('./manage').buildTargets(); | ||||
| 	}); | ||||
| program | ||||
| 	.command('activate [plugin]') | ||||
| 	.description('Activate a plugin for the next startup of NodeBB (nodebb-plugin- prefix is optional)') | ||||
| 	.action(function (plugin) { | ||||
| 		require('./manage').activate(plugin); | ||||
| 	}); | ||||
| program | ||||
| 	.command('plugins') | ||||
| 	.action(function () { | ||||
| 		require('./manage').listPlugins(); | ||||
| 	}) | ||||
| 	.description('List all installed plugins'); | ||||
| program | ||||
| 	.command('events') | ||||
| 	.description('Outputs the last ten (10) administrative events recorded by NodeBB') | ||||
| 	.action(function () { | ||||
| 		require('./manage').listEvents(); | ||||
| 	}); | ||||
| program | ||||
| 	.command('info') | ||||
| 	.description('Outputs various system info') | ||||
| 	.action(function () { | ||||
| 		require('./manage').info(); | ||||
| 	}); | ||||
|  | ||||
| // reset | ||||
| var resetCommand = program.command('reset'); | ||||
|  | ||||
| resetCommand | ||||
| 	.description('Reset plugins, themes, settings, etc') | ||||
| 	.option('-t, --theme [theme]', 'Reset to [theme] or to the default theme') | ||||
| 	.option('-p, --plugin [plugin]', 'Disable [plugin] or all plugins') | ||||
| 	.option('-w, --widgets', 'Disable all widgets') | ||||
| 	.option('-s, --settings', 'Reset settings to their default values') | ||||
| 	.option('-a, --all', 'All of the above') | ||||
| 	.action(function (options) { | ||||
| 		var valid = ['theme', 'plugin', 'widgets', 'settings', 'all'].some(function (x) { | ||||
| 			return options[x]; | ||||
| 		}); | ||||
| 		if (!valid) { | ||||
| 			process.stdout.write('\n  No valid options passed in, so nothing was reset.\n'.red); | ||||
| 			resetCommand.help(); | ||||
| 		} | ||||
|  | ||||
| 		require('./reset').reset(options, function (err) { | ||||
| 			if (err) { throw err; } | ||||
| 			require('../meta/build').buildAll(function (err) { | ||||
| 				if (err) { throw err; } | ||||
|  | ||||
| 				process.exit(); | ||||
| 			}); | ||||
| 		}); | ||||
| 	}); | ||||
|  | ||||
| // upgrades | ||||
| program | ||||
| 	.command('upgrade [scripts...]') | ||||
| 	.description('Run NodeBB upgrade scripts and ensure packages are up-to-date, or run a particular upgrade script') | ||||
| 	.option('-m, --package', 'Update package.json from defaults', false) | ||||
| 	.option('-i, --install', 'Bringing base dependencies up to date', false) | ||||
| 	.option('-p, --plugins', 'Check installed plugins for updates', false) | ||||
| 	.option('-s, --schema', 'Update NodeBB data store schema', false) | ||||
| 	.option('-b, --build', 'Rebuild assets', false) | ||||
| 	.on('--help', function () { | ||||
| 		process.stdout.write( | ||||
| 			'\n' + | ||||
| 			'When running particular upgrade scripts, options are ignored.\n' + | ||||
| 			'By default all options are enabled. Passing any options disables that default.\n' + | ||||
| 			'Only package and dependency updates: ' + './nodebb upgrade -mi\n'.yellow + | ||||
| 			'Only database update: ' + './nodebb upgrade -d\n\n'.yellow | ||||
| 		); | ||||
| 	}) | ||||
| 	.action(function (scripts, options) { | ||||
| 		require('./upgrade').upgrade(scripts.length ? scripts : true, options); | ||||
| 	}); | ||||
|  | ||||
| program | ||||
| 	.command('upgrade-plugins', null, { | ||||
| 		noHelp: true, | ||||
| 	}) | ||||
| 	.alias('upgradePlugins') | ||||
| 	.description('Upgrade plugins') | ||||
| 	.action(function () { | ||||
| 		require('./upgrade-plugins').upgradePlugins(function (err) { | ||||
| 			if (err) { | ||||
| 				throw err; | ||||
| 			} | ||||
| 			process.stdout.write('OK\n'.green); | ||||
| 			process.exit(); | ||||
| 		}); | ||||
| 	}); | ||||
|  | ||||
| program | ||||
| 	.command('help [command]') | ||||
| 	.description('Display help for [command]') | ||||
| 	.action(function (name) { | ||||
| 		if (!name) { | ||||
| 			return program.help(); | ||||
| 		} | ||||
|  | ||||
| 		var command = program.commands.find(function (command) { return command._name === name; }); | ||||
| 		if (command) { | ||||
| 			command.help(); | ||||
| 		} else { | ||||
| 			program.help(); | ||||
| 		} | ||||
| 	}); | ||||
|  | ||||
| program | ||||
| 	.command('*', {}, { | ||||
| 		noHelp: true, | ||||
| 	}) | ||||
| 	.action(function () { | ||||
| 		program.help(); | ||||
| 	}); | ||||
|  | ||||
| require('./colors'); | ||||
|  | ||||
| program.executables = false; | ||||
|  | ||||
| program.parse(process.argv); | ||||
							
								
								
									
										143
									
								
								src/cli/manage.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								src/cli/manage.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var async = require('async'); | ||||
| var winston = require('winston'); | ||||
| var childProcess = require('child_process'); | ||||
| var _ = require('lodash'); | ||||
|  | ||||
| var build = require('../meta/build'); | ||||
| var db = require('../database'); | ||||
| var plugins = require('../plugins'); | ||||
| var events = require('../events'); | ||||
| var reset = require('./reset'); | ||||
|  | ||||
| function buildTargets() { | ||||
| 	var aliases = build.aliases; | ||||
| 	var length = 0; | ||||
| 	var output = Object.keys(aliases).map(function (name) { | ||||
| 		var arr = aliases[name]; | ||||
| 		if (name.length > length) { | ||||
| 			length = name.length; | ||||
| 		} | ||||
|  | ||||
| 		return [name, arr.join(', ')]; | ||||
| 	}).map(function (tuple) { | ||||
| 		return '     ' + _.padEnd('"' + tuple[0] + '"', length + 2).magenta + '  |  ' + tuple[1]; | ||||
| 	}).join('\n'); | ||||
| 	process.stdout.write( | ||||
| 		'\n\n  Build targets:\n' + | ||||
| 		('\n     ' + _.padEnd('Target', length + 2) + '  |  Aliases').green + | ||||
| 		'\n     ------------------------------------------------------\n'.blue + | ||||
| 		output + '\n\n' | ||||
| 	); | ||||
| } | ||||
|  | ||||
| function activate(plugin) { | ||||
| 	if (plugin.startsWith('nodebb-theme-')) { | ||||
| 		reset.reset({ | ||||
| 			theme: plugin, | ||||
| 		}, function (err) { | ||||
| 			if (err) { throw err; } | ||||
| 			process.exit(); | ||||
| 		}); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	async.waterfall([ | ||||
| 		function (next) { | ||||
| 			db.init(next); | ||||
| 		}, | ||||
| 		function (next) { | ||||
| 			if (!plugin.startsWith('nodebb-')) { | ||||
| 				// Allow omission of `nodebb-plugin-` | ||||
| 				plugin = 'nodebb-plugin-' + plugin; | ||||
| 			} | ||||
| 			plugins.isInstalled(plugin, next); | ||||
| 		}, | ||||
| 		function (isInstalled, next) { | ||||
| 			if (!isInstalled) { | ||||
| 				return next(new Error('plugin not installed')); | ||||
| 			} | ||||
|  | ||||
| 			winston.info('Activating plugin `%s`', plugin); | ||||
| 			db.sortedSetAdd('plugins:active', 0, plugin, next); | ||||
| 		}, | ||||
| 		function (next) { | ||||
| 			events.log({ | ||||
| 				type: 'plugin-activate', | ||||
| 				text: plugin, | ||||
| 			}, next); | ||||
| 		}, | ||||
| 	], function (err) { | ||||
| 		if (err) { | ||||
| 			winston.error('An error occurred during plugin activation', err); | ||||
| 			throw err; | ||||
| 		} | ||||
| 		process.exit(0); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function listPlugins() { | ||||
| 	async.waterfall([ | ||||
| 		db.init, | ||||
| 		function (next) { | ||||
| 			db.getSortedSetRange('plugins:active', 0, -1, next); | ||||
| 		}, | ||||
| 		function (plugins) { | ||||
| 			winston.info('Active plugins: \n\t - ' + plugins.join('\n\t - ')); | ||||
| 			process.exit(); | ||||
| 		}, | ||||
| 	], function (err) { | ||||
| 		throw err; | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function listEvents() { | ||||
| 	async.series([ | ||||
| 		db.init, | ||||
| 		events.output, | ||||
| 	]); | ||||
| } | ||||
|  | ||||
| function info() { | ||||
| 	async.waterfall([ | ||||
| 		function (next) { | ||||
| 			var version = require('../../package.json').version; | ||||
| 			process.stdout.write('\n  version:  ' + version); | ||||
|  | ||||
| 			process.stdout.write('\n  Node ver: ' + process.version); | ||||
| 			next(); | ||||
| 		}, | ||||
| 		function (next) { | ||||
| 			process.stdout.write('\n  git hash: '); | ||||
| 			childProcess.execSync('git rev-parse HEAD', { | ||||
| 				stdio: 'inherit', | ||||
| 			}); | ||||
| 			next(); | ||||
| 		}, | ||||
| 		function (next) { | ||||
| 			var config = require('../../config.json'); | ||||
| 			process.stdout.write('\n  database: ' + config.database); | ||||
| 			next(); | ||||
| 		}, | ||||
| 		db.init, | ||||
| 		function (next) { | ||||
| 			db.info(db.client, next); | ||||
| 		}, | ||||
| 		function (info, next) { | ||||
| 			process.stdout.write('\n        version: ' + info.version); | ||||
| 			process.stdout.write('\n        engine:  ' + info.storageEngine); | ||||
| 			next(); | ||||
| 		}, | ||||
| 	], function (err) { | ||||
| 		if (err) { throw err; } | ||||
| 		process.exit(); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| exports.build = build.build; | ||||
| exports.buildTargets = buildTargets; | ||||
| exports.activate = activate; | ||||
| exports.listPlugins = listPlugins; | ||||
| exports.listEvents = listEvents; | ||||
| exports.info = info; | ||||
							
								
								
									
										15
									
								
								src/cli/paths.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/cli/paths.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var path = require('path'); | ||||
|  | ||||
| var baseDir = path.join(__dirname, '../../'); | ||||
| var loader = path.join(baseDir, 'loader.js'); | ||||
| var app = path.join(baseDir, 'app.js'); | ||||
| var pidfile = path.join(baseDir, 'pidfile'); | ||||
|  | ||||
| module.exports = { | ||||
| 	baseDir: baseDir, | ||||
| 	loader: loader, | ||||
| 	app: app, | ||||
| 	pidfile: pidfile, | ||||
| }; | ||||
| @@ -3,79 +3,85 @@ | ||||
| require('colors'); | ||||
| var path = require('path'); | ||||
| var winston = require('winston'); | ||||
| var nconf = require('nconf'); | ||||
| var async = require('async'); | ||||
| var db = require('./database'); | ||||
| var events = require('./events'); | ||||
| var fs = require('fs'); | ||||
| 
 | ||||
| var Reset = {}; | ||||
| var db = require('../database'); | ||||
| var events = require('../events'); | ||||
| var meta = require('../meta'); | ||||
| var plugins = require('../plugins'); | ||||
| var widgets = require('../widgets'); | ||||
| 
 | ||||
| Reset.reset = function (callback) { | ||||
| 	db.init(function (err) { | ||||
| 		if (err) { | ||||
| 			winston.error(err); | ||||
| 			throw err; | ||||
| 		} | ||||
| var dirname = require('./paths').baseDir; | ||||
| 
 | ||||
| 		if (nconf.get('t')) { | ||||
| 			var themeId = nconf.get('t'); | ||||
| exports.reset = function (options, callback) { | ||||
| 	var map = { | ||||
| 		theme: function (next) { | ||||
| 			var themeId = options.theme; | ||||
| 			if (themeId === true) { | ||||
| 				resetThemes(callback); | ||||
| 				resetThemes(next); | ||||
| 			} else { | ||||
| 				if (themeId.indexOf('nodebb-') !== 0) { | ||||
| 				if (!themeId.startsWith('nodebb-theme-')) { | ||||
| 					// Allow omission of `nodebb-theme-`
 | ||||
| 					themeId = 'nodebb-theme-' + themeId; | ||||
| 				} | ||||
| 
 | ||||
| 				resetTheme(themeId, callback); | ||||
| 				resetTheme(themeId, next); | ||||
| 			} | ||||
| 		} else if (nconf.get('p')) { | ||||
| 			var pluginId = nconf.get('p'); | ||||
| 		}, | ||||
| 		plugin: function (next) { | ||||
| 			var pluginId = options.plugin; | ||||
| 			if (pluginId === true) { | ||||
| 				resetPlugins(callback); | ||||
| 				resetPlugins(next); | ||||
| 			} else { | ||||
| 				if (pluginId.indexOf('nodebb-') !== 0) { | ||||
| 				if (!pluginId.startsWith('nodebb-plugin-')) { | ||||
| 					// Allow omission of `nodebb-plugin-`
 | ||||
| 					pluginId = 'nodebb-plugin-' + pluginId; | ||||
| 				} | ||||
| 
 | ||||
| 				resetPlugin(pluginId, callback); | ||||
| 				resetPlugin(pluginId, next); | ||||
| 			} | ||||
| 		} else if (nconf.get('w')) { | ||||
| 			resetWidgets(callback); | ||||
| 		} else if (nconf.get('s')) { | ||||
| 			resetSettings(callback); | ||||
| 		} else if (nconf.get('a')) { | ||||
| 			require('async').series([resetWidgets, resetThemes, resetPlugins, resetSettings], function (err) { | ||||
| 				if (!err) { | ||||
| 					winston.info('[reset] Reset complete.'); | ||||
| 				} else { | ||||
| 					winston.error('[reset] Errors were encountered while resetting your forum settings: %s', err); | ||||
| 				} | ||||
| 		}, | ||||
| 		widgets: resetWidgets, | ||||
| 		settings: resetSettings, | ||||
| 		all: function (next) { | ||||
| 			async.series([resetWidgets, resetThemes, resetPlugins, resetSettings], next); | ||||
| 		}, | ||||
| 	}; | ||||
| 
 | ||||
| 				callback(); | ||||
| 			}); | ||||
| 		} else { | ||||
| 			process.stdout.write('\nNodeBB Reset\n'.bold); | ||||
| 			process.stdout.write('No arguments passed in, so nothing was reset.\n\n'.yellow); | ||||
| 			process.stdout.write('Use ./nodebb reset ' + '{-t|-p|-w|-s|-a}\n'.red); | ||||
| 			process.stdout.write('    -t\tthemes\n'); | ||||
| 			process.stdout.write('    -p\tplugins\n'); | ||||
| 			process.stdout.write('    -w\twidgets\n'); | ||||
| 			process.stdout.write('    -s\tsettings\n'); | ||||
| 			process.stdout.write('    -a\tall of the above\n'); | ||||
| 	var tasks = Object.keys(map) | ||||
| 		.filter(function (x) { return options[x]; }) | ||||
| 		.map(function (x) { return map[x]; }); | ||||
| 
 | ||||
| 			process.stdout.write('\nPlugin and theme reset flags (-p & -t) can take a single argument\n'); | ||||
| 			process.stdout.write('    e.g. ./nodebb reset -p nodebb-plugin-mentions, ./nodebb reset -t nodebb-theme-persona\n'); | ||||
| 			process.stdout.write('         Prefix is optional, e.g. ./nodebb reset -p markdown, ./nodebb reset -t persona\n'); | ||||
| 	if (!tasks.length) { | ||||
| 		process.stdout.write('\nNodeBB Reset\n'.bold); | ||||
| 		process.stdout.write('No arguments passed in, so nothing was reset.\n\n'.yellow); | ||||
| 		process.stdout.write('Use ./nodebb reset ' + '{-t|-p|-w|-s|-a}\n'.red); | ||||
| 		process.stdout.write('    -t\tthemes\n'); | ||||
| 		process.stdout.write('    -p\tplugins\n'); | ||||
| 		process.stdout.write('    -w\twidgets\n'); | ||||
| 		process.stdout.write('    -s\tsettings\n'); | ||||
| 		process.stdout.write('    -a\tall of the above\n'); | ||||
| 
 | ||||
| 			process.exit(0); | ||||
| 		process.stdout.write('\nPlugin and theme reset flags (-p & -t) can take a single argument\n'); | ||||
| 		process.stdout.write('    e.g. ./nodebb reset -p nodebb-plugin-mentions, ./nodebb reset -t nodebb-theme-persona\n'); | ||||
| 		process.stdout.write('         Prefix is optional, e.g. ./nodebb reset -p markdown, ./nodebb reset -t persona\n'); | ||||
| 
 | ||||
| 		process.exit(0); | ||||
| 	} | ||||
| 
 | ||||
| 	async.series([db.init].concat(tasks), function (err) { | ||||
| 		if (err) { | ||||
| 			winston.error('[reset] Errors were encountered during reset', err); | ||||
| 			throw err; | ||||
| 		} | ||||
| 
 | ||||
| 		winston.info('[reset] Reset complete'); | ||||
| 		callback(); | ||||
| 	}); | ||||
| }; | ||||
| 
 | ||||
| function resetSettings(callback) { | ||||
| 	var meta = require('./meta'); | ||||
| 	meta.configs.set('allowLocalLogin', 1, function (err) { | ||||
| 		winston.info('[reset] Settings reset to default'); | ||||
| 		callback(err); | ||||
| @@ -83,10 +89,7 @@ function resetSettings(callback) { | ||||
| } | ||||
| 
 | ||||
| function resetTheme(themeId, callback) { | ||||
| 	var meta = require('./meta'); | ||||
| 	var fs = require('fs'); | ||||
| 
 | ||||
| 	fs.access(path.join(__dirname, '../node_modules', themeId, 'package.json'), function (err) { | ||||
| 	fs.access(path.join(dirname, 'node_modules', themeId, 'package.json'), function (err) { | ||||
| 		if (err) { | ||||
| 			winston.warn('[reset] Theme `%s` is not installed on this forum', themeId); | ||||
| 			callback(new Error('theme-not-found')); | ||||
| @@ -108,8 +111,6 @@ function resetTheme(themeId, callback) { | ||||
| } | ||||
| 
 | ||||
| function resetThemes(callback) { | ||||
| 	var meta = require('./meta'); | ||||
| 
 | ||||
| 	meta.themes.set({ | ||||
| 		type: 'local', | ||||
| 		id: 'nodebb-theme-persona', | ||||
| @@ -163,13 +164,11 @@ function resetPlugins(callback) { | ||||
| 
 | ||||
| function resetWidgets(callback) { | ||||
| 	async.waterfall([ | ||||
| 		require('./plugins').reload, | ||||
| 		require('./widgets').reset, | ||||
| 		plugins.reload, | ||||
| 		widgets.reset, | ||||
| 		function (next) { | ||||
| 			winston.info('[reset] All Widgets moved to Draft Zone'); | ||||
| 			next(); | ||||
| 		}, | ||||
| 	], callback); | ||||
| } | ||||
| 
 | ||||
| module.exports = Reset; | ||||
							
								
								
									
										119
									
								
								src/cli/running.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								src/cli/running.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var fs = require('fs'); | ||||
| var childProcess = require('child_process'); | ||||
|  | ||||
| var fork = require('../meta/debugFork'); | ||||
| var paths = require('./paths'); | ||||
|  | ||||
| var dirname = paths.baseDir; | ||||
|  | ||||
| function getRunningPid(callback) { | ||||
| 	fs.readFile(paths.pidfile, { | ||||
| 		encoding: 'utf-8', | ||||
| 	}, function (err, pid) { | ||||
| 		if (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
|  | ||||
| 		pid = parseInt(pid, 10); | ||||
|  | ||||
| 		try { | ||||
| 			process.kill(pid, 0); | ||||
| 			callback(null, pid); | ||||
| 		} catch (e) { | ||||
| 			callback(e); | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function start(options) { | ||||
| 	if (options.dev) { | ||||
| 		process.env.NODE_ENV = 'development'; | ||||
| 		fork(paths.loader, ['--no-daemon', '--no-silent'], { | ||||
| 			env: process.env, | ||||
| 			cwd: dirname, | ||||
| 			stdio: 'inherit', | ||||
| 		}); | ||||
| 		return; | ||||
| 	} | ||||
| 	if (options.log) { | ||||
| 		process.stdout.write('\nStarting NodeBB with logging output\n'.bold); | ||||
| 		process.stdout.write('\nHit '.red + 'Ctrl-C '.bold + 'to exit'.red); | ||||
|  | ||||
| 		process.stdout.write('\nThe NodeBB process will continue to run in the background'); | ||||
| 		process.stdout.write('\nUse "' + './nodebb stop'.yellow + '" to stop the NodeBB server\n'); | ||||
| 		process.stdout.write('\n\n'.reset); | ||||
| 	} else if (!options.silent) { | ||||
| 		process.stdout.write('\nStarting NodeBB\n'.bold); | ||||
| 		process.stdout.write('  "' + './nodebb stop'.yellow + '" to stop the NodeBB server\n'); | ||||
| 		process.stdout.write('  "' + './nodebb log'.yellow + '" to view server output\n'); | ||||
| 		process.stdout.write('  "' + './nodebb restart'.yellow + '" to restart NodeBB\n\n'.reset); | ||||
| 	} | ||||
|  | ||||
| 	// Spawn a new NodeBB process | ||||
| 	fork(paths.loader, process.argv.slice(3), { | ||||
| 		env: process.env, | ||||
| 		cwd: dirname, | ||||
| 	}); | ||||
| 	if (options.log) { | ||||
| 		childProcess.spawn('tail', ['-F', './logs/output.log'], { | ||||
| 			cwd: dirname, | ||||
| 			stdio: 'inherit', | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| function stop() { | ||||
| 	getRunningPid(function (err, pid) { | ||||
| 		if (!err) { | ||||
| 			process.kill(pid, 'SIGTERM'); | ||||
| 			process.stdout.write('Stopping NodeBB. Goodbye!\n'); | ||||
| 		} else { | ||||
| 			process.stdout.write('NodeBB is already stopped.\n'); | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function restart(options) { | ||||
| 	getRunningPid(function (err, pid) { | ||||
| 		if (!err) { | ||||
| 			process.stdout.write('\nRestarting NodeBB\n'.bold); | ||||
| 			process.kill(pid, 'SIGTERM'); | ||||
|  | ||||
| 			options.silent = true; | ||||
| 			start(options); | ||||
| 		} else { | ||||
| 			process.stdout.write('NodeBB could not be restarted, as a running instance could not be found.\n'); | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function status() { | ||||
| 	getRunningPid(function (err, pid) { | ||||
| 		if (!err) { | ||||
| 			process.stdout.write('\nNodeBB Running '.bold + '(pid '.cyan + pid.toString().cyan + ')\n'.cyan); | ||||
| 			process.stdout.write('\t"' + './nodebb stop'.yellow + '" to stop the NodeBB server\n'); | ||||
| 			process.stdout.write('\t"' + './nodebb log'.yellow + '" to view server output\n'); | ||||
| 			process.stdout.write('\t"' + './nodebb restart'.yellow + '" to restart NodeBB\n\n'); | ||||
| 		} else { | ||||
| 			process.stdout.write('\nNodeBB is not running\n'.bold); | ||||
| 			process.stdout.write('\t"' + './nodebb start'.yellow + '" to launch the NodeBB server\n\n'.reset); | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function log() { | ||||
| 	process.stdout.write('\nHit '.red + 'Ctrl-C '.bold + 'to exit'.red); | ||||
| 	process.stdout.write('\n\n'.reset); | ||||
| 	childProcess.spawn('tail', ['-F', './logs/output.log'], { | ||||
| 		cwd: dirname, | ||||
| 		stdio: 'inherit', | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| exports.start = start; | ||||
| exports.stop = stop; | ||||
| exports.restart = restart; | ||||
| exports.status = status; | ||||
| exports.log = log; | ||||
							
								
								
									
										59
									
								
								src/cli/setup.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/cli/setup.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var winston = require('winston'); | ||||
| var async = require('async'); | ||||
|  | ||||
| var install = require('../../install/web').install; | ||||
|  | ||||
| function setup() { | ||||
| 	var install = require('../install'); | ||||
| 	var build = require('../meta/build'); | ||||
| 	var prestart = require('../prestart'); | ||||
|  | ||||
| 	winston.info('NodeBB Setup Triggered via Command Line'); | ||||
|  | ||||
| 	process.stdout.write('\nWelcome to NodeBB!\n'); | ||||
| 	process.stdout.write('\nThis looks like a new installation, so you\'ll have to answer a few questions about your environment before we can proceed.\n'); | ||||
| 	process.stdout.write('Press enter to accept the default setting (shown in brackets).\n'); | ||||
|  | ||||
| 	async.series([ | ||||
| 		install.setup, | ||||
| 		prestart.loadConfig, | ||||
| 		build.buildAll, | ||||
| 	], function (err, data) { | ||||
| 		// Disregard build step data | ||||
| 		data = data[0]; | ||||
|  | ||||
| 		var separator = '     '; | ||||
| 		if (process.stdout.columns > 10) { | ||||
| 			for (var x = 0, cols = process.stdout.columns - 10; x < cols; x += 1) { | ||||
| 				separator += '='; | ||||
| 			} | ||||
| 		} | ||||
| 		process.stdout.write('\n' + separator + '\n\n'); | ||||
|  | ||||
| 		if (err) { | ||||
| 			winston.error('There was a problem completing NodeBB setup', err); | ||||
| 			throw err; | ||||
| 		} else { | ||||
| 			if (data.hasOwnProperty('password')) { | ||||
| 				process.stdout.write('An administrative user was automatically created for you:\n'); | ||||
| 				process.stdout.write('    Username: ' + data.username + '\n'); | ||||
| 				process.stdout.write('    Password: ' + data.password + '\n'); | ||||
| 				process.stdout.write('\n'); | ||||
| 			} | ||||
| 			process.stdout.write('NodeBB Setup Completed. Run \'./nodebb start\' to manually start your NodeBB server.\n'); | ||||
|  | ||||
| 			// If I am a child process, notify the parent of the returned data before exiting (useful for notifying | ||||
| 			// hosts of auto-generated username/password during headless setups) | ||||
| 			if (process.send) { | ||||
| 				process.send(data); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		process.exit(); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| exports.setup = setup; | ||||
| exports.webInstall = install; | ||||
							
								
								
									
										216
									
								
								src/cli/upgrade-plugins.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								src/cli/upgrade-plugins.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,216 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var async = require('async'); | ||||
| var prompt = require('prompt'); | ||||
| var request = require('request'); | ||||
| var cproc = require('child_process'); | ||||
| var semver = require('semver'); | ||||
| var fs = require('fs'); | ||||
| var path = require('path'); | ||||
|  | ||||
| var paths = require('./paths'); | ||||
|  | ||||
| var dirname = paths.baseDir; | ||||
|  | ||||
| function getModuleVersions(modules, callback) { | ||||
| 	var versionHash = {}; | ||||
|  | ||||
| 	async.eachLimit(modules, 50, function (module, next) { | ||||
| 		fs.readFile(path.join(dirname, 'node_modules', module, 'package.json'), { encoding: 'utf-8' }, function (err, pkg) { | ||||
| 			if (err) { | ||||
| 				return next(err); | ||||
| 			} | ||||
|  | ||||
| 			try { | ||||
| 				pkg = JSON.parse(pkg); | ||||
| 				versionHash[module] = pkg.version; | ||||
| 				next(); | ||||
| 			} catch (err) { | ||||
| 				next(err); | ||||
| 			} | ||||
| 		}); | ||||
| 	}, function (err) { | ||||
| 		callback(err, versionHash); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function getInstalledPlugins(callback) { | ||||
| 	async.parallel({ | ||||
| 		files: async.apply(fs.readdir, path.join(dirname, 'node_modules')), | ||||
| 		deps: async.apply(fs.readFile, path.join(dirname, 'package.json'), { encoding: 'utf-8' }), | ||||
| 	}, function (err, payload) { | ||||
| 		if (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
|  | ||||
| 		var isNbbModule = /^nodebb-(?:plugin|theme|widget|rewards)-[\w-]+$/; | ||||
| 		var moduleName; | ||||
| 		var isGitRepo; | ||||
|  | ||||
| 		payload.files = payload.files.filter(function (file) { | ||||
| 			return isNbbModule.test(file); | ||||
| 		}); | ||||
|  | ||||
| 		try { | ||||
| 			payload.deps = JSON.parse(payload.deps).dependencies; | ||||
| 			payload.bundled = []; | ||||
| 			payload.installed = []; | ||||
| 		} catch (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
|  | ||||
| 		for (moduleName in payload.deps) { | ||||
| 			if (isNbbModule.test(moduleName)) { | ||||
| 				payload.bundled.push(moduleName); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// Whittle down deps to send back only extraneously installed plugins/themes/etc | ||||
| 		payload.files.forEach(function (moduleName) { | ||||
| 			try { | ||||
| 				fs.accessSync(path.join(dirname, 'node_modules', moduleName, '.git')); | ||||
| 				isGitRepo = true; | ||||
| 			} catch (e) { | ||||
| 				isGitRepo = false; | ||||
| 			} | ||||
|  | ||||
| 			if ( | ||||
| 				payload.files.indexOf(moduleName) !== -1 &&	// found in `node_modules/` | ||||
| 				payload.bundled.indexOf(moduleName) === -1 &&	// not found in `package.json` | ||||
| 				!fs.lstatSync(path.join(dirname, 'node_modules', moduleName)).isSymbolicLink() &&	// is not a symlink | ||||
| 				!isGitRepo	// .git/ does not exist, so it is not a git repository | ||||
| 			) { | ||||
| 				payload.installed.push(moduleName); | ||||
| 			} | ||||
| 		}); | ||||
|  | ||||
| 		getModuleVersions(payload.installed, callback); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function getCurrentVersion(callback) { | ||||
| 	fs.readFile(path.join(dirname, 'package.json'), { encoding: 'utf-8' }, function (err, pkg) { | ||||
| 		if (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
|  | ||||
| 		try { | ||||
| 			pkg = JSON.parse(pkg); | ||||
| 		} catch (err) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
| 		callback(null, pkg.version); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function checkPlugins(standalone, callback) { | ||||
| 	if (standalone) { | ||||
| 		process.stdout.write('Checking installed plugins and themes for updates... '); | ||||
| 	} | ||||
|  | ||||
| 	async.waterfall([ | ||||
| 		async.apply(async.parallel, { | ||||
| 			plugins: async.apply(getInstalledPlugins), | ||||
| 			version: async.apply(getCurrentVersion), | ||||
| 		}), | ||||
| 		function (payload, next) { | ||||
| 			var toCheck = Object.keys(payload.plugins); | ||||
|  | ||||
| 			if (!toCheck.length) { | ||||
| 				process.stdout.write('OK'.green + '\n'.reset); | ||||
| 				return next(null, []);	// no extraneous plugins installed | ||||
| 			} | ||||
|  | ||||
| 			request({ | ||||
| 				method: 'GET', | ||||
| 				url: 'https://packages.nodebb.org/api/v1/suggest?version=' + payload.version + '&package[]=' + toCheck.join('&package[]='), | ||||
| 				json: true, | ||||
| 			}, function (err, res, body) { | ||||
| 				if (err) { | ||||
| 					process.stdout.write('error'.red + '\n'.reset); | ||||
| 					return next(err); | ||||
| 				} | ||||
| 				process.stdout.write('OK'.green + '\n'.reset); | ||||
|  | ||||
| 				if (!Array.isArray(body) && toCheck.length === 1) { | ||||
| 					body = [body]; | ||||
| 				} | ||||
|  | ||||
| 				var current; | ||||
| 				var suggested; | ||||
| 				var upgradable = body.map(function (suggestObj) { | ||||
| 					current = payload.plugins[suggestObj.package]; | ||||
| 					suggested = suggestObj.version; | ||||
|  | ||||
| 					if (suggestObj.code === 'match-found' && semver.gt(suggested, current)) { | ||||
| 						return { | ||||
| 							name: suggestObj.package, | ||||
| 							current: current, | ||||
| 							suggested: suggested, | ||||
| 						}; | ||||
| 					} | ||||
| 					return null; | ||||
| 				}).filter(Boolean); | ||||
|  | ||||
| 				next(null, upgradable); | ||||
| 			}); | ||||
| 		}, | ||||
| 	], callback); | ||||
| } | ||||
|  | ||||
| function upgradePlugins(callback) { | ||||
| 	var standalone = false; | ||||
| 	if (typeof callback !== 'function') { | ||||
| 		callback = function () {}; | ||||
| 		standalone = true; | ||||
| 	} | ||||
|  | ||||
| 	checkPlugins(standalone, function (err, found) { | ||||
| 		if (err) { | ||||
| 			process.stdout.write('Warning'.yellow + ': An unexpected error occured when attempting to verify plugin upgradability\n'.reset); | ||||
| 			return callback(err); | ||||
| 		} | ||||
|  | ||||
| 		if (found && found.length) { | ||||
| 			process.stdout.write('\nA total of ' + String(found.length).bold + ' package(s) can be upgraded:\n'); | ||||
| 			found.forEach(function (suggestObj) { | ||||
| 				process.stdout.write('  * '.yellow + suggestObj.name.reset + ' (' + suggestObj.current.yellow + ' -> '.reset + suggestObj.suggested.green + ')\n'.reset); | ||||
| 			}); | ||||
| 			process.stdout.write('\n'); | ||||
| 		} else { | ||||
| 			if (standalone) { | ||||
| 				process.stdout.write('\nAll packages up-to-date!'.green + '\n'.reset); | ||||
| 			} | ||||
| 			return callback(); | ||||
| 		} | ||||
|  | ||||
| 		prompt.message = ''; | ||||
| 		prompt.delimiter = ''; | ||||
|  | ||||
| 		prompt.start(); | ||||
| 		prompt.get({ | ||||
| 			name: 'upgrade', | ||||
| 			description: 'Proceed with upgrade (y|n)?'.reset, | ||||
| 			type: 'string', | ||||
| 		}, function (err, result) { | ||||
| 			if (err) { | ||||
| 				return callback(err); | ||||
| 			} | ||||
|  | ||||
| 			if (['y', 'Y', 'yes', 'YES'].indexOf(result.upgrade) !== -1) { | ||||
| 				process.stdout.write('\nUpgrading packages...'); | ||||
| 				var args = ['i']; | ||||
| 				found.forEach(function (suggestObj) { | ||||
| 					args.push(suggestObj.name + '@' + suggestObj.suggested); | ||||
| 				}); | ||||
|  | ||||
| 				cproc.execFile((process.platform === 'win32') ? 'npm.cmd' : 'npm', args, { stdio: 'ignore' }, callback); | ||||
| 			} else { | ||||
| 				process.stdout.write('\nPackage upgrades skipped'.yellow + '. Check for upgrades at any time by running "'.reset + './nodebb upgrade-plugins'.green + '".\n'.reset); | ||||
| 				callback(); | ||||
| 			} | ||||
| 		}); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| exports.upgradePlugins = upgradePlugins; | ||||
							
								
								
									
										117
									
								
								src/cli/upgrade.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								src/cli/upgrade.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,117 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var async = require('async'); | ||||
| var nconf = require('nconf'); | ||||
|  | ||||
| var packageInstall = require('../meta/package-install'); | ||||
| var upgrade = require('../upgrade'); | ||||
| var build = require('../meta/build'); | ||||
| var db = require('../database'); | ||||
| var meta = require('../meta'); | ||||
| var upgradePlugins = require('./upgrade-plugins').upgradePlugins; | ||||
|  | ||||
| var steps = { | ||||
| 	package: function (next) { | ||||
| 		process.stdout.write('Updating package.json file with defaults... \n'.yellow); | ||||
| 		packageInstall.updatePackageFile(); | ||||
| 		packageInstall.preserveExtraneousPlugins(); | ||||
| 		process.stdout.write('OK\n'.green); | ||||
| 		next(); | ||||
| 	}, | ||||
| 	install: function (next) { | ||||
| 		process.stdout.write('Bringing base dependencies up to date... \n'.yellow); | ||||
| 		packageInstall.npmInstallProduction(); | ||||
| 		process.stdout.write('OK\n'.green); | ||||
| 		next(); | ||||
| 	}, | ||||
| 	plugins: function (next) { | ||||
| 		process.stdout.write('Checking installed plugins for updates... \n'.yellow); | ||||
| 		async.series([ | ||||
| 			db.init, | ||||
| 			upgradePlugins, | ||||
| 			function (next) { | ||||
| 				process.stdout.write('OK\n'.green); | ||||
| 				next(); | ||||
| 			}, | ||||
| 		], next); | ||||
| 	}, | ||||
| 	schema: function (next) { | ||||
| 		process.stdout.write('Updating NodeBB data store schema...\n'.yellow); | ||||
| 		async.series([ | ||||
| 			db.init, | ||||
| 			upgrade.run, | ||||
| 			function (next) { | ||||
| 				process.stdout.write('OK\n'.green); | ||||
| 				next(); | ||||
| 			}, | ||||
| 		], next); | ||||
| 	}, | ||||
| 	build: function (next) { | ||||
| 		process.stdout.write('Rebuilding assets...\n'.yellow); | ||||
| 		async.series([ | ||||
| 			build.buildAll, | ||||
| 			function (next) { | ||||
| 				process.stdout.write('OK\n'.green); | ||||
| 				next(); | ||||
| 			}, | ||||
| 		], next); | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| function runSteps(tasks) { | ||||
| 	tasks = tasks.map(function (key, i) { | ||||
| 		return function (next) { | ||||
| 			process.stdout.write(((i + 1) + '. ').bold); | ||||
| 			return steps[key](next); | ||||
| 		}; | ||||
| 	}); | ||||
|  | ||||
| 	async.series(tasks, function (err) { | ||||
| 		if (err) { | ||||
| 			process.stdout.write('Error occurred during upgrade'); | ||||
| 			throw err; | ||||
| 		} | ||||
|  | ||||
| 		var message = 'NodeBB Upgrade Complete!'; | ||||
| 		// some consoles will return undefined/zero columns, so just use 2 spaces in upgrade script if we can't get our column count | ||||
| 		var columns = process.stdout.columns; | ||||
| 		var spaces = columns ? new Array(Math.floor(columns / 2) - (message.length / 2) + 1).join(' ') : '  '; | ||||
|  | ||||
| 		process.stdout.write('\n' + spaces + message.green.bold + '\n\n'.reset); | ||||
|  | ||||
| 		process.exit(); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function runUpgrade(upgrades, options) { | ||||
| 	process.stdout.write('\nUpdating NodeBB...\n'.cyan); | ||||
|  | ||||
| 	// disable mongo timeouts during upgrade | ||||
| 	nconf.set('mongo:options:socketTimeoutMS', 0); | ||||
|  | ||||
| 	if (upgrades === true) { | ||||
| 		var tasks = Object.keys(steps); | ||||
| 		if (options.package || options.install || | ||||
| 				options.plugins || options.schema || options.build) { | ||||
| 			tasks = tasks.filter(function (key) { | ||||
| 				return options[key]; | ||||
| 			}); | ||||
| 		} | ||||
| 		runSteps(tasks); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	async.series([ | ||||
| 		db.init, | ||||
| 		meta.configs.init, | ||||
| 		async.apply(upgrade.runParticular, upgrades), | ||||
| 	], function (err) { | ||||
| 		if (err) { | ||||
| 			throw err; | ||||
| 		} | ||||
|  | ||||
| 		process.exit(0); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| exports.upgrade = runUpgrade; | ||||
| @@ -83,6 +83,8 @@ var aliases = { | ||||
| 	sounds: ['sound'], | ||||
| }; | ||||
|  | ||||
| exports.aliases = aliases; | ||||
|  | ||||
| aliases = Object.keys(aliases).reduce(function (prev, key) { | ||||
| 	var arr = aliases[key]; | ||||
| 	arr.forEach(function (alias) { | ||||
|   | ||||
							
								
								
									
										84
									
								
								src/prestart.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/prestart.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| var nconf = require('nconf'); | ||||
| var url = require('url'); | ||||
| var winston = require('winston'); | ||||
| var path = require('path'); | ||||
|  | ||||
| var pkg = require('../package.json'); | ||||
| var dirname = require('./cli/paths').baseDir; | ||||
|  | ||||
| function setupWinston() { | ||||
| 	winston.remove(winston.transports.Console); | ||||
| 	winston.add(winston.transports.Console, { | ||||
| 		colorize: true, | ||||
| 		timestamp: function () { | ||||
| 			var date = new Date(); | ||||
| 			return nconf.get('json-logging') ? date.toJSON() : | ||||
| 				date.getDate() + '/' + (date.getMonth() + 1) + ' ' + | ||||
| 				date.toTimeString().substr(0, 8) + ' [' + global.process.pid + ']'; | ||||
| 		}, | ||||
| 		level: nconf.get('log-level') || (global.env === 'production' ? 'info' : 'verbose'), | ||||
| 		json: !!nconf.get('json-logging'), | ||||
| 		stringify: !!nconf.get('json-logging'), | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| function loadConfig(configFile) { | ||||
| 	winston.verbose('* using configuration stored in: %s', configFile); | ||||
|  | ||||
| 	nconf.file({ | ||||
| 		file: configFile, | ||||
| 	}); | ||||
|  | ||||
| 	nconf.defaults({ | ||||
| 		base_dir: dirname, | ||||
| 		themes_path: path.join(dirname, 'node_modules'), | ||||
| 		upload_path: 'public/uploads', | ||||
| 		views_dir: path.join(dirname, 'build/public/templates'), | ||||
| 		version: pkg.version, | ||||
| 	}); | ||||
|  | ||||
| 	if (!nconf.get('isCluster')) { | ||||
| 		nconf.set('isPrimary', 'true'); | ||||
| 		nconf.set('isCluster', 'false'); | ||||
| 	} | ||||
|  | ||||
| 	// Ensure themes_path is a full filepath | ||||
| 	nconf.set('themes_path', path.resolve(dirname, nconf.get('themes_path'))); | ||||
| 	nconf.set('core_templates_path', path.join(dirname, 'src/views')); | ||||
| 	nconf.set('base_templates_path', path.join(nconf.get('themes_path'), 'nodebb-theme-persona/templates')); | ||||
|  | ||||
| 	nconf.set('upload_path', path.resolve(nconf.get('base_dir'), nconf.get('upload_path'))); | ||||
|  | ||||
| 	if (nconf.get('url')) { | ||||
| 		nconf.set('url_parsed', url.parse(nconf.get('url'))); | ||||
| 	} | ||||
|  | ||||
| 	// Explicitly cast 'jobsDisabled' as Bool | ||||
| 	var castAsBool = ['jobsDisabled']; | ||||
| 	nconf.stores.env.readOnly = false; | ||||
| 	castAsBool.forEach(function (prop) { | ||||
| 		var value = nconf.get(prop); | ||||
| 		if (value) { | ||||
| 			nconf.set(prop, typeof value === 'boolean' ? value : String(value).toLowerCase() === 'true'); | ||||
| 		} | ||||
| 	}); | ||||
| 	nconf.stores.env.readOnly = true; | ||||
| } | ||||
|  | ||||
| function versionCheck() { | ||||
| 	var version = process.version.slice(1); | ||||
| 	var range = pkg.engines.node; | ||||
| 	var semver = require('semver'); | ||||
| 	var compatible = semver.satisfies(version, range); | ||||
|  | ||||
| 	if (!compatible) { | ||||
| 		winston.warn('Your version of Node.js is too outdated for NodeBB. Please update your version of Node.js.'); | ||||
| 		winston.warn('Recommended ' + range.green + ', '.reset + version.yellow + ' provided\n'.reset); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| exports.setupWinston = setupWinston; | ||||
| exports.loadConfig = loadConfig; | ||||
| exports.versionCheck = versionCheck; | ||||
| @@ -18,7 +18,7 @@ var file = require('../src/file'); | ||||
|  * 3. Add your script under the "method" property | ||||
|  */ | ||||
|  | ||||
| var Upgrade = {}; | ||||
| var Upgrade = module.exports; | ||||
|  | ||||
| Upgrade.getAll = function (callback) { | ||||
| 	async.waterfall([ | ||||
| @@ -212,4 +212,3 @@ Upgrade.incrementProgress = function (value) { | ||||
| 	process.stdout.write('    [' + (filled ? new Array(filled).join('#') : '') + new Array(unfilled).join(' ') + '] (' + this.current + '/' + (this.total || '??') + ') ' + percentage + ' '); | ||||
| }; | ||||
|  | ||||
| module.exports = Upgrade; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user