2013-07-11 12:14:49 -04:00
/ *
2014-07-11 16:26:20 -04:00
NodeBB - A better forum platform for the modern web
https : //github.com/NodeBB/NodeBB/
Copyright ( C ) 2013 - 2014 NodeBB Inc .
2013-07-11 12:14:49 -04:00
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
* /
2014-02-02 01:38:46 -05:00
"use strict" ;
2014-04-23 21:20:40 -04:00
/*global require, global, process*/
2014-02-02 01:38:46 -05:00
var nconf = require ( 'nconf' ) ;
2015-02-26 17:39:23 +01:00
nconf . argv ( ) . env ( '__' ) ;
2014-02-02 01:38:46 -05:00
var fs = require ( 'fs' ) ,
os = require ( 'os' ) ,
2014-11-29 21:54:58 -05:00
url = require ( 'url' ) ,
2014-11-14 15:19:26 -05:00
async = require ( 'async' ) ,
2014-02-02 01:38:46 -05:00
semver = require ( 'semver' ) ,
winston = require ( 'winston' ) ,
2015-05-25 21:08:05 -04:00
colors = require ( 'colors' ) ,
2014-02-02 01:38:46 -05:00
path = require ( 'path' ) ,
pkg = require ( './package.json' ) ,
utils = require ( './public/src/utils.js' ) ;
global . env = process . env . NODE _ENV || 'production' ;
2013-07-11 12:10:45 -04:00
2014-02-02 01:38:46 -05:00
winston . remove ( winston . transports . Console ) ;
winston . add ( winston . transports . Console , {
2014-10-26 18:46:50 -04:00
colorize : true ,
2014-11-13 13:52:46 -05:00
timestamp : function ( ) {
2014-11-13 16:47:38 -05:00
var date = new Date ( ) ;
2014-11-14 16:16:06 -05:00
return date . getDate ( ) + '/' + ( date . getMonth ( ) + 1 ) + ' ' + date . toTimeString ( ) . substr ( 0 , 5 ) + ' [' + global . process . pid + ']' ;
2014-11-13 13:52:46 -05:00
} ,
2015-07-06 11:58:17 +02:00
level : nconf . get ( 'log-level' ) || ( global . env === 'production' ? 'info' : 'verbose' )
2014-02-02 01:38:46 -05:00
} ) ;
if ( os . platform ( ) === 'linux' ) {
2013-12-07 16:51:47 -05:00
require ( 'child_process' ) . exec ( '/usr/bin/which convert' , function ( err , stdout , stderr ) {
2013-12-07 17:03:07 -05:00
if ( err || ! stdout ) {
2013-12-08 14:49:47 -05:00
winston . warn ( 'Couldn\'t find convert. Did you install imagemagick?' ) ;
2013-12-07 16:51:47 -05:00
}
} ) ;
2014-02-02 01:38:46 -05:00
}
2013-12-07 16:51:47 -05:00
2014-02-14 11:49:16 -05:00
// Alternate configuration file support
2014-05-31 16:53:18 -04:00
var configFile = path . join ( _ _dirname , '/config.json' ) ,
2014-02-14 11:49:16 -05:00
configExists ;
2014-05-31 16:53:18 -04:00
2014-02-14 11:49:16 -05:00
if ( nconf . get ( 'config' ) ) {
2014-03-21 23:04:27 +00:00
configFile = path . resolve ( _ _dirname , nconf . get ( 'config' ) ) ;
2014-02-14 11:49:16 -05:00
}
configExists = fs . existsSync ( configFile ) ;
2013-09-10 16:26:26 -04:00
2014-10-03 15:28:02 -04:00
if ( ! nconf . get ( 'setup' ) && ! nconf . get ( 'install' ) && ! nconf . get ( 'upgrade' ) && ! nconf . get ( 'reset' ) && configExists ) {
2014-02-02 01:38:46 -05:00
start ( ) ;
2015-04-21 14:32:21 -04:00
} else if ( nconf . get ( 'setup' ) || nconf . get ( 'install' ) ) {
2014-02-02 01:38:46 -05:00
setup ( ) ;
2015-04-21 14:32:21 -04:00
} else if ( ! configExists ) {
2015-04-21 14:52:57 -04:00
require ( './install/web' ) . install ( nconf . get ( 'port' ) ) ;
2014-02-02 01:38:46 -05:00
} else if ( nconf . get ( 'upgrade' ) ) {
upgrade ( ) ;
2014-02-18 17:43:32 -05:00
} else if ( nconf . get ( 'reset' ) ) {
2014-04-08 15:29:51 -04:00
reset ( ) ;
2014-03-02 14:45:57 -05:00
}
2013-09-17 15:38:50 -04:00
2014-02-27 01:32:20 -05:00
function loadConfig ( ) {
2014-02-02 01:38:46 -05:00
nconf . file ( {
2014-02-16 05:35:44 +00:00
file : configFile
2014-02-02 01:38:46 -05:00
} ) ;
2013-07-16 15:22:59 -04:00
2014-02-14 05:01:02 +00:00
nconf . defaults ( {
2014-04-08 16:52:50 -04:00
base _dir : _ _dirname ,
themes _path : path . join ( _ _dirname , 'node_modules' ) ,
2015-04-16 11:30:53 +02:00
views _dir : path . join ( _ _dirname , 'public/templates' ) ,
version : pkg . version
2014-02-14 05:01:02 +00:00
} ) ;
2014-12-03 14:03:41 -05:00
if ( ! nconf . get ( 'isCluster' ) ) {
nconf . set ( 'isPrimary' , 'true' ) ;
nconf . set ( 'isCluster' , 'false' ) ;
}
2014-12-03 01:49:57 -05:00
// 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-vanilla/templates' ) ) ;
2015-04-16 11:30:53 +02:00
if ( ! process . send ) {
// If run using `node app`, log GNU copyright info along with server info
winston . info ( 'NodeBB v' + nconf . get ( 'version' ) + ' Copyright (C) 2013-2014 NodeBB Inc.' ) ;
winston . info ( 'This program comes with ABSOLUTELY NO WARRANTY.' ) ;
winston . info ( 'This is free software, and you are welcome to redistribute it under certain conditions.' ) ;
winston . info ( '' ) ;
}
2014-12-03 01:49:57 -05:00
}
function start ( ) {
loadConfig ( ) ;
2015-06-05 13:33:58 -04:00
var db = require ( './src/database' ) ;
2014-12-03 01:49:57 -05:00
2014-12-03 01:10:45 -05:00
// nconf defaults, if not set in config
if ( ! nconf . get ( 'upload_path' ) ) {
nconf . set ( 'upload_path' , '/public/uploads' ) ;
}
// Parse out the relative_url and other goodies from the configured URL
var urlObject = url . parse ( nconf . get ( 'url' ) ) ;
var relativePath = urlObject . pathname !== '/' ? urlObject . pathname : '' ;
2015-06-08 13:55:14 -04:00
nconf . set ( 'base_url' , urlObject . protocol + '//' + urlObject . host ) ;
2014-12-03 01:10:45 -05:00
nconf . set ( 'use_port' , ! ! urlObject . port ) ;
nconf . set ( 'relative_path' , relativePath ) ;
nconf . set ( 'port' , urlObject . port || nconf . get ( 'port' ) || nconf . get ( 'PORT' ) || 4567 ) ;
2015-02-10 13:45:29 -05:00
nconf . set ( 'upload_url' , '/uploads/' ) ;
2014-12-03 01:10:45 -05:00
2014-12-03 14:03:41 -05:00
if ( nconf . get ( 'isPrimary' ) === 'true' ) {
2014-11-24 20:09:23 -05:00
winston . info ( 'Time: %s' , ( new Date ( ) ) . toString ( ) ) ;
2015-04-16 11:30:53 +02:00
winston . info ( 'Initializing NodeBB v%s' , nconf . get ( 'version' ) ) ;
2014-11-26 13:28:10 -05:00
winston . verbose ( '* using configuration stored in: %s' , configFile ) ;
2014-04-11 01:38:47 -04:00
2014-10-17 01:47:13 -04:00
var host = nconf . get ( nconf . get ( 'database' ) + ':host' ) ,
storeLocation = host ? 'at ' + host + ( host . indexOf ( '/' ) === - 1 ? ':' + nconf . get ( nconf . get ( 'database' ) + ':port' ) : '' ) : '' ;
2014-01-05 23:57:43 -05:00
2014-11-26 13:28:10 -05:00
winston . verbose ( '* using %s store %s' , nconf . get ( 'database' ) , storeLocation ) ;
winston . verbose ( '* using themes stored in: %s' , nconf . get ( 'themes_path' ) ) ;
2014-02-02 01:38:46 -05:00
}
2015-04-27 20:26:02 -04:00
process . on ( 'SIGTERM' , shutdown ) ;
process . on ( 'SIGINT' , shutdown ) ;
process . on ( 'SIGHUP' , restart ) ;
process . on ( 'message' , function ( message ) {
if ( typeof message !== 'object' ) {
return ;
}
var meta = require ( './src/meta' ) ;
var emitter = require ( './src/emitter' ) ;
switch ( message . action ) {
case 'reload' :
meta . reload ( ) ;
break ;
case 'js-propagate' :
meta . js . cache = message . cache ;
meta . js . map = message . map ;
meta . js . hash = message . hash ;
emitter . emit ( 'meta:js.compiled' ) ;
winston . verbose ( '[cluster] Client-side javascript and mapping propagated to worker %s' , process . pid ) ;
break ;
case 'css-propagate' :
meta . css . cache = message . cache ;
meta . css . acpCache = message . acpCache ;
meta . css . hash = message . hash ;
emitter . emit ( 'meta:css.compiled' ) ;
winston . verbose ( '[cluster] Stylesheets propagated to worker %s' , process . pid ) ;
break ;
case 'templates:compiled' :
emitter . emit ( 'templates:compiled' ) ;
break ;
}
} ) ;
2014-11-27 15:36:05 -05:00
2015-04-27 20:26:02 -04:00
process . on ( 'uncaughtException' , function ( err ) {
winston . error ( err . stack ) ;
console . log ( err . stack ) ;
2014-11-27 15:36:05 -05:00
2015-04-27 20:26:02 -04:00
require ( './src/meta' ) . js . killMinifier ( ) ;
shutdown ( 1 ) ;
} ) ;
async . waterfall ( [
2015-06-05 13:33:58 -04:00
async . apply ( db . init ) ,
async . apply ( db . checkCompatibility ) ,
2015-04-27 20:26:02 -04:00
function ( next ) {
require ( './src/meta' ) . configs . init ( next ) ;
} ,
2015-07-06 15:44:52 -04:00
function ( next ) {
2015-07-06 16:11:20 -04:00
require ( './src/meta' ) . dependencies . check ( next ) ;
2015-07-06 15:44:52 -04:00
} ,
2015-04-27 20:26:02 -04:00
function ( next ) {
require ( './src/upgrade' ) . check ( next ) ;
} ,
2015-07-06 15:44:52 -04:00
function ( next ) {
2015-04-27 20:26:02 -04:00
var webserver = require ( './src/webserver' ) ;
require ( './src/socket.io' ) . init ( webserver . server ) ;
if ( nconf . get ( 'isPrimary' ) === 'true' && ! nconf . get ( 'jobsDisabled' ) ) {
require ( './src/notifications' ) . init ( ) ;
require ( './src/user' ) . startJobs ( ) ;
}
webserver . listen ( ) ;
}
] , function ( err ) {
2014-07-05 00:11:21 -04:00
if ( err ) {
2015-07-06 15:44:52 -04:00
switch ( err . message ) {
case 'schema-out-of-date' :
winston . warn ( 'Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:' ) ;
winston . warn ( ' ./nodebb upgrade' ) ;
break ;
case 'dependencies-out-of-date' :
winston . warn ( 'One or more of NodeBB\'s dependent packages are out-of-date. Please run the following command to update them:' ) ;
winston . warn ( ' ./nodebb upgrade' ) ;
break ;
default :
if ( err . stacktrace !== false ) {
winston . error ( err . stack ) ;
} else {
winston . error ( err . message ) ;
}
break ;
2015-06-05 13:33:58 -04:00
}
2015-07-06 15:44:52 -04:00
// Either way, bad stuff happened. Abort start.
2014-07-05 00:11:21 -04:00
process . exit ( ) ;
}
2014-02-02 01:38:46 -05:00
} ) ;
}
2013-05-27 14:02:57 -04:00
2014-02-02 01:38:46 -05:00
function setup ( ) {
2014-02-27 01:32:20 -05:00
loadConfig ( ) ;
2015-04-21 14:32:21 -04:00
winston . info ( 'NodeBB Setup Triggered via Command Line' ) ;
2013-11-03 11:53:44 -05:00
2014-02-02 01:38:46 -05:00
var install = require ( './src/install' ) ;
2013-09-10 16:26:26 -04:00
2015-04-22 10:27:54 -04:00
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' ) ;
install . setup ( function ( err , data ) {
var separator = ' ' ;
if ( process . stdout . columns > 10 ) {
for ( var x = 0 , cols = process . stdout . columns - 10 ; x < cols ; x ++ ) {
separator += '=' ;
}
}
process . stdout . write ( '\n' + separator + '\n\n' ) ;
2013-08-23 13:14:36 -04:00
2014-02-02 01:38:46 -05:00
if ( err ) {
winston . error ( 'There was a problem completing NodeBB setup: ' , err . message ) ;
} else {
2015-04-22 10:27:54 -04:00
if ( data . hasOwnProperty ( 'password' ) ) {
2015-04-27 20:26:02 -04:00
process . stdout . write ( 'An administrative user was automatically created for you:\n' ) ;
2015-04-22 10:27:54 -04:00
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 ) ;
}
2014-02-02 01:38:46 -05:00
}
2013-10-28 15:24:21 -04:00
2014-02-02 01:38:46 -05:00
process . exit ( ) ;
} ) ;
}
2013-10-26 10:56:05 -06:00
2014-02-02 01:38:46 -05:00
function upgrade ( ) {
2014-02-27 01:32:20 -05:00
loadConfig ( ) ;
2014-02-02 01:38:46 -05:00
require ( './src/database' ) . init ( function ( err ) {
2014-07-05 00:11:21 -04:00
if ( err ) {
winston . error ( err . stack ) ;
process . exit ( ) ;
}
2014-07-05 00:19:19 -04:00
require ( './src/meta' ) . configs . init ( function ( ) {
2014-02-02 01:38:46 -05:00
require ( './src/upgrade' ) . upgrade ( ) ;
2013-10-26 10:56:05 -06:00
} ) ;
2014-02-02 01:38:46 -05:00
} ) ;
}
2014-04-08 15:29:51 -04:00
function reset ( ) {
loadConfig ( ) ;
require ( './src/database' ) . init ( function ( err ) {
if ( err ) {
2014-04-08 16:03:42 -04:00
winston . error ( err . message ) ;
2014-04-08 15:29:51 -04:00
process . exit ( ) ;
}
2014-04-08 16:03:42 -04:00
2015-05-25 21:08:05 -04:00
if ( nconf . get ( 't' ) ) {
2014-04-08 16:03:42 -04:00
resetThemes ( ) ;
2015-05-25 21:08:05 -04:00
} else if ( nconf . get ( 'p' ) ) {
if ( nconf . get ( 'p' ) === true ) {
resetPlugins ( ) ;
} else {
resetPlugin ( nconf . get ( 'p' ) ) ;
}
} else if ( nconf . get ( 'w' ) ) {
2014-04-08 16:03:42 -04:00
resetWidgets ( ) ;
2015-05-25 21:08:05 -04:00
} else if ( nconf . get ( 's' ) ) {
2014-04-08 16:03:42 -04:00
resetSettings ( ) ;
2015-05-25 21:08:05 -04:00
} else if ( nconf . get ( 'a' ) ) {
2014-04-08 16:03:42 -04:00
require ( 'async' ) . series ( [ resetWidgets , resetThemes , resetPlugins , resetSettings ] , function ( err ) {
if ( ! err ) {
winston . info ( '[reset] Reset complete.' ) ;
} else {
2014-11-24 20:09:23 -05:00
winston . error ( '[reset] Errors were encountered while resetting your forum settings: %s' , err . message ) ;
2014-04-08 16:03:42 -04:00
}
2014-04-08 15:29:51 -04:00
process . exit ( ) ;
2014-04-08 16:03:42 -04:00
} ) ;
} else {
2015-05-25 21:08:05 -04:00
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 . stdout . write ( '\nPlugin reset flag (-p) can take a single argument\n' ) ;
process . stdout . write ( ' e.g. ./nodebb reset -p nodebb-plugin-mentions\n' ) ;
2014-11-14 22:14:55 -05:00
process . exit ( ) ;
2014-04-08 16:03:42 -04:00
}
} ) ;
}
function resetSettings ( callback ) {
var meta = require ( './src/meta' ) ;
meta . configs . set ( 'allowLocalLogin' , 1 , function ( err ) {
2014-04-08 16:50:23 -04:00
winston . info ( '[reset] Settings reset to default' ) ;
2014-04-08 16:03:42 -04:00
if ( typeof callback === 'function' ) {
callback ( err ) ;
} else {
process . exit ( ) ;
}
2014-04-08 15:29:51 -04:00
} ) ;
}
2014-04-04 22:47:35 -04:00
function resetThemes ( callback ) {
2014-04-08 16:03:42 -04:00
var meta = require ( './src/meta' ) ;
2014-02-18 17:43:32 -05:00
2014-04-08 16:03:42 -04:00
meta . themes . set ( {
type : 'local' ,
id : 'nodebb-theme-vanilla'
} , function ( err ) {
winston . info ( '[reset] Theme reset to Vanilla' ) ;
if ( typeof callback === 'function' ) {
callback ( err ) ;
} else {
process . exit ( ) ;
}
2014-04-04 22:47:35 -04:00
} ) ;
}
2014-05-14 11:20:23 -04:00
function resetPlugin ( pluginId ) {
var db = require ( './src/database' ) ;
2015-02-23 14:57:22 -05:00
db . sortedSetRemove ( 'plugins:active' , pluginId , function ( err ) {
2014-12-30 22:06:48 -05:00
if ( err ) {
winston . error ( '[reset] Could not disable plugin: %s encountered error %s' , pluginId , err . message ) ;
2014-05-14 11:20:23 -04:00
} else {
2014-11-24 20:09:23 -05:00
winston . info ( '[reset] Plugin `%s` disabled' , pluginId ) ;
2014-05-14 11:20:23 -04:00
}
process . exit ( ) ;
} ) ;
}
2014-04-04 22:47:35 -04:00
function resetPlugins ( callback ) {
var db = require ( './src/database' ) ;
2014-04-08 16:03:42 -04:00
db . delete ( 'plugins:active' , function ( err ) {
winston . info ( '[reset] All Plugins De-activated' ) ;
if ( typeof callback === 'function' ) {
callback ( err ) ;
} else {
process . exit ( ) ;
}
2014-02-18 17:43:32 -05:00
} ) ;
}
2014-04-07 17:52:48 -04:00
function resetWidgets ( callback ) {
2014-04-08 16:03:42 -04:00
require ( './src/widgets' ) . reset ( function ( err ) {
winston . info ( '[reset] All Widgets moved to Draft Zone' ) ;
if ( typeof callback === 'function' ) {
callback ( err ) ;
} else {
process . exit ( ) ;
}
2014-04-07 17:52:48 -04:00
} ) ;
}
2014-03-02 13:56:46 -05:00
function shutdown ( code ) {
winston . info ( '[app] Shutdown (SIGTERM/SIGINT) Initialised.' ) ;
2014-03-02 14:00:28 -05:00
require ( './src/database' ) . close ( ) ;
2014-03-02 13:56:46 -05:00
winston . info ( '[app] Database connection closed.' ) ;
2014-08-25 11:56:48 -04:00
require ( './src/webserver' ) . server . close ( ) ;
winston . info ( '[app] Web server closed to connections.' ) ;
2014-03-02 13:56:46 -05:00
winston . info ( '[app] Shutdown complete.' ) ;
2014-04-17 22:35:49 -04:00
process . exit ( code || 0 ) ;
2014-03-02 13:56:46 -05:00
}
function restart ( ) {
if ( process . send ) {
winston . info ( '[app] Restarting...' ) ;
2014-04-09 13:10:28 -04:00
process . send ( {
action : 'restart'
} ) ;
2014-03-02 13:56:46 -05:00
} else {
winston . error ( '[app] Could not restart server. Shutting down.' ) ;
2014-04-17 22:35:49 -04:00
shutdown ( 1 ) ;
2014-03-02 13:56:46 -05:00
}
2015-07-06 11:58:17 +02:00
}