refactoring installation scripts to use node prompt module, lots of other fixes

fixed #263, fixed #264, fixed #265
v1.18.x
Julian Lam 12 years ago
parent a88ddc2a4d
commit bec0b46a2c

@ -50,15 +50,9 @@ winston.err = function(err) {
winston.info('NodeBB v' + pkg.version + ' Copyright (C) 2013 DesignCreatePlay 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('===');
winston.info('');
if(nconf.get('upgrade')) {
meta.configs.init(function() {
require('./src/upgrade').upgrade();
});
} else if (!nconf.get('setup') && nconf.get('base_url')) {
if (!nconf.get('setup') && !nconf.get('upgrade') && nconf.get('base_url')) {
nconf.set('url', nconf.get('base_url') + (nconf.get('use_port') ? ':' + nconf.get('port') : '') + nconf.get('relative_path') + '/');
nconf.set('upload_url', nconf.get('url') + 'uploads/');
@ -82,77 +76,39 @@ if(nconf.get('upgrade')) {
'categories': require('./src/admin/categories.js')
};
DEVELOPMENT = true;
global.configuration = {};
global.templates = {};
templates.init([
'header', 'footer', 'logout', 'outgoing', 'admin/header', 'admin/footer', 'admin/index',
'emails/reset', 'emails/reset_plaintext', 'emails/email_confirm', 'emails/email_confirm_plaintext',
'emails/header', 'emails/footer',
(function(config) {
config['ROOT_DIRECTORY'] = __dirname;
templates.init([
'header', 'footer', 'logout', 'outgoing', 'admin/header', 'admin/footer', 'admin/index',
'emails/reset', 'emails/reset_plaintext', 'emails/email_confirm', 'emails/email_confirm_plaintext',
'emails/header', 'emails/footer',
'noscript/header', 'noscript/home', 'noscript/category', 'noscript/topic'
]);
templates.ready(webserver.init);
//setup scripts to be moved outside of the app in future.
function setup_categories() {
if (process.env.NODE_ENV === 'development') winston.info('Checking categories...');
categories.getAllCategories(function(data) {
if (data.categories.length === 0) {
winston.info('Setting up default categories...');
'noscript/header', 'noscript/home', 'noscript/category', 'noscript/topic'
]);
fs.readFile(config.ROOT_DIRECTORY + '/install/data/categories.json', function(err, default_categories) {
default_categories = JSON.parse(default_categories);
for (var category in default_categories) {
admin.categories.create(default_categories[category]);
}
});
winston.info('Hardcoding uid 1 as an admin');
var user = require('./src/user.js');
user.makeAdministrator(1);
} else {
if (process.env.NODE_ENV === 'development') winston.info('Categories OK. Found ' + data.categories.length + ' categories.');
}
});
}
setup_categories();
}(global.configuration));
templates.ready(webserver.init);
});
} else if (nconf.get('upgrade')) {
meta.configs.init(function() {
require('./src/upgrade').upgrade();
});
} else {
// New install, ask setup questions
if (nconf.get('setup')) winston.info('NodeBB Setup Triggered via Command Line');
else winston.warn('Configuration not found, starting NodeBB setup');
meta.config = {};
var install = require('./src/install');
process.stdout.write(
"\nWelcome to NodeBB!\nThis looks like a new installation, so you'll have to answer a " +
"few questions about your environment before we can proceed.\n\n" +
"Press enter to accept the default setting (shown in brackets).\n\n\n"
);
winston.info('Welcome to NodeBB!');
winston.info('This looks like a new installation, so you\'ll have to answer a few questions about your environment before we can proceed.');
winston.info('Press enter to accept the default setting (shown in brackets).');
install.setup(function(err) {
if (err) {
winston.error('There was a problem completing NodeBB setup: ', err.message);
} else {
if (!nconf.get('setup')) {
process.stdout.write(
"Please start NodeBB again and register a new user. This user will automatically become an administrator.\n\n"
);
}
winston.info('NodeBB Setup Completed.');
}
process.exit();

@ -36,7 +36,8 @@
"winston": "~0.7.2",
"nodebb-plugin-mentions": "~0.1.0",
"nodebb-plugin-markdown": "~0.1.0",
"rss": "~0.2.0"
"rss": "~0.2.0",
"prompt": "~0.2.11"
},
"bugs": {
"url": "https://github.com/designcreateplay/NodeBB/issues"

@ -65,7 +65,7 @@
for (var t in templatesToLoad) {
(function(file) {
fs.readFile(global.configuration.ROOT_DIRECTORY + '/public/templates/' + file + '.tpl', function(err, html) {
fs.readFile(__dirname + '/../templates/' + file + '.tpl', function(err, html) {
var template = function() {
this.toString = function() {
return this.html;
@ -90,8 +90,6 @@
config = config_data[0];
available_templates = templates_data[0];
templates.ready();
});
}

@ -17,8 +17,8 @@
//Adapted from http://stackoverflow.com/questions/5827612/node-js-fs-readdir-recursive-directory-search
walk: function(dir, done) {
var main_dir = global.configuration.ROOT_DIRECTORY + '/public/templates/';
var results = [];
var results = [],
templateExtract = /\/([\w\d\-_]+)\.tpl$/;
fs.readdir(dir, function(err, list) {
if (err) return done(err);
var pending = list.length;
@ -32,7 +32,10 @@
if (!--pending) done(null, results);
});
} else {
results.push(file.replace(main_dir, '').replace('.tpl', ''));
var templateMatch = file.match(templateExtract);
if (templateMatch) results.push(templateMatch[1]);
// results.push(file.replace(main_dir, '').replace('.tpl', ''));
if (!--pending) done(null, results);
}
});

@ -24,7 +24,7 @@ var RDB = require('./../redis.js'),
RDB.set('categoryslug:' + slug + ':cid', cid);
if (callback) callback({'status': 1});
if (callback) callback(null);
});
};

@ -1,5 +1,6 @@
var async = require('async'),
User = require('./user'),
RDB = RDB || require('./redis'),
Groups = {
list: function(options, callback) {
RDB.hvals('group:gid', function(err, gids) {

@ -4,95 +4,186 @@ var async = require('async'),
url = require('url'),
path = require('path'),
meta = require('./meta'),
User = require('./user'),
Groups = require('./groups'),
Categories = require('./categories'),
prompt = require('prompt'),
admin = {
categories: require('./admin/categories')
},
winston = require('winston'),
install = {
questions: [
'base_url|Publically accessible URL of this installation? (http://localhost)',
'port|Port number of your install? (4567)',
'use_port|Will you be using a port number to access NodeBB? (y)',
'redis:host|Host IP or address of your Redis instance? (127.0.0.1)',
'redis:port|Host port of your Redis instance? (6379)',
'redis:password|Password of your Redis database? (no password)',
'secret|Your NodeBB secret? (keyboard mash for a bit here)'
],
defaults: {
"base_url": 'http://localhost',
"port": 4567,
"use_port": true,
"redis": {
"host": '127.0.0.1',
"port": 6379,
"password": ''
{
name: 'base_url',
description: 'URL of this installation',
'default': 'http://localhost',
pattern: /^http(?:s)?:\/\//,
message: 'Base URL must begin with \'http://\' or \'https://\'',
},
"secret": utils.generateUUID(),
"bcrypt_rounds": 12,
"upload_path": '/public/uploads'
},
ask: function(question, callback) {
process.stdin.resume();
process.stdout.write(question + ': ');
process.stdin.once('data', function(data) {
callback(data.toString().trim());
});
},
{
name: 'port',
description: 'Port number of your NodeBB',
'default': 4567
},
{
name: 'use_port',
description: 'Use a port number to access NodeBB?',
'default': 'y',
pattern: /y[es]*|n[o]?/,
message: 'Please enter \'yes\' or \'no\'',
},
{
name: 'secret',
description: 'Please enter a NodeBB secret',
'default': utils.generateUUID()
},
{
name: 'redis:host',
description: 'Host IP or address of your Redis instance',
'default': '127.0.0.1'
},
{
name: 'redis:port',
description: 'Host port of your Redis instance',
'default': 6379
},
{
name: 'redis:password',
description: 'Password of your Redis database'
}
],
setup: function(callback) {
var config = {};
for(d in install.defaults) config[d] = install.defaults[d];
async.series([
function(next) {
// prompt prepends "prompt: " to questions, let's clear that.
prompt.start();
prompt.message = '';
prompt.delimiter = '';
async.eachSeries(install.questions, function(question, next) {
var question = question.split('|');
install.ask(question[1], function(value) {
switch(question[0]) {
case 'use_port':
value = value.toLowerCase();
if (['y', 'yes', ''].indexOf(value) === -1) config[question[0]] = false;
break;
case 'redis:host':
config.redis = config.redis || {};
if (value !== '') config.redis.host = value;
break;
case 'redis:port':
config.redis = config.redis || {};
if (value !== '') config.redis.port = value;
break;
case 'redis:password':
config.redis = config.redis || {};
if (value !== '') config.redis.password = value;
break;
prompt.get(install.questions, function(err, config) {
if (!config) {
winston.warn('NodeBB Setup Aborted.');
process.exit();
}
default:
if (value !== '') config[question[0]] = value;
break;
}
// Translate redis properties into redis object
config.redis = {
host: config['redis:host'],
port: config['redis:port'],
password: config['redis:password']
};
delete config['redis:host'];
delete config['redis:port'];
delete config['redis:password'];
next();
});
}, function() {
var urlObject = url.parse(config.base_url),
relative_path = (urlObject.pathname && urlObject.pathname.length > 1) ? urlObject.pathname : '',
host = urlObject.host,
protocol = urlObject.protocol,
server_conf = config,
client_conf = {
socket: {
address: protocol + '//' + host + (config.use_port ? ':' + config.port : '')
},
api_url: protocol + '//' + host + (config.use_port ? ':' + config.port : '') + relative_path + '/api/',
relative_path: relative_path
};
// Add hardcoded values
config['bcrypt_rounds'] = 12,
config['upload_path'] = '/public/uploads';
server_conf.base_url = protocol + '//' + host;
server_conf.relative_path = relative_path;
var urlObject = url.parse(config.base_url),
relative_path = (urlObject.pathname && urlObject.pathname.length > 1) ? urlObject.pathname : '',
host = urlObject.host,
protocol = urlObject.protocol,
server_conf = config,
client_conf = {
socket: {
address: protocol + '//' + host + (config.use_port ? ':' + config.port : '')
},
api_url: protocol + '//' + host + (config.use_port ? ':' + config.port : '') + relative_path + '/api/',
relative_path: relative_path
};
meta.configs.set('postDelay', 10000);
meta.configs.set('minimumPostLength', 8);
meta.configs.set('minimumTitleLength', 3);
meta.configs.set('minimumUsernameLength', 2);
meta.configs.set('maximumUsernameLength', 16);
meta.configs.set('minimumPasswordLength', 6);
meta.configs.set('imgurClientID', '');
server_conf.base_url = protocol + '//' + host;
server_conf.relative_path = relative_path;
install.save(server_conf, client_conf, callback);
meta.configs.set('postDelay', 10000);
meta.configs.set('minimumPostLength', 8);
meta.configs.set('minimumTitleLength', 3);
meta.configs.set('minimumUsernameLength', 2);
meta.configs.set('maximumUsernameLength', 16);
meta.configs.set('minimumPasswordLength', 6);
meta.configs.set('imgurClientID', '');
install.save(server_conf, client_conf, next);
});
},
function(next) {
// Check if an administrator needs to be created
Groups.getGidFromName('Administrators', function(err, gid) {
if (err) return next(new Error('Could not save configs'));
if (gid) {
Groups.get(gid, {}, function(err, groupObj) {
if (groupObj.count > 0) {
winston.info('Administrator found, skipping Admin setup');
next();
} else install.createAdmin(next);
});
} else install.createAdmin(next);
});
},
function(next) {
// Categories
categories.getAllCategories(function(data) {
if (data.categories.length === 0) {
winston.warn('No categories found, populating instance with default categories')
fs.readFile(path.join(__dirname, '../', 'install/data/categories.json'), function(err, default_categories) {
default_categories = JSON.parse(default_categories);
async.eachSeries(default_categories, function(category, next) {
admin.categories.create(category, next);
}, function(err) {
if (!err) next();
else winston.error('Could not set up categories');
});
});
} else {
winston.info('Categories OK. Found ' + data.categories.length + ' categories.');
next();
}
});
}
], callback);
},
createAdmin: function(callback) {
winston.warn('No administrators have been detected, running initial user setup');
var questions = [
{
name: 'username',
description: 'Administrator username',
required: true,
type: 'string'
},
{
name: 'email',
description: 'Administrator email address',
pattern: /.+@.+/,
required: true
},
{
name: 'password',
description: 'Password',
required: true,
hidden: true,
type: 'string'
}
];
prompt.get(questions, function(err, results) {
nconf.set('bcrypt_rounds', 12);
User.create(results.username, results.password, results.email, function(err, uid) {
Groups.getGidFromName('Administrators', function(err, gid) {
if (gid) Groups.join(gid, uid, callback);
else {
Groups.create('Administrators', 'Forum Administrators', function(err, groupObj) {
Groups.join(groupObj.gid, uid, callback);
});
}
});
});
});
},
save: function(server_conf, client_conf, callback) {
@ -109,10 +200,7 @@ var async = require('async'),
});
}
], function(err) {
process.stdout.write(
"\n\nConfiguration Saved OK\n\n"
);
winston.info('Configuration Saved OK');
callback(err);
});
}

@ -8,7 +8,6 @@ var user = require('./user.js'),
(function(Login){
Login.loginViaLocal = function(username, password, next) {
if (!username || !password) {
return next({
status: 'error',
@ -26,8 +25,7 @@ var user = require('./user.js'),
}
user.getUserFields(uid, ['password', 'banned'], function(err, userData) {
if(err)
return next(err);
if(err) return next(err);
if(userData.banned && userData.banned === '1') {
return next({
@ -38,7 +36,7 @@ var user = require('./user.js'),
bcrypt.compare(password, userData.password, function(err, res) {
if(err) {
winston.err(err);
winston.err(err.message);
next({
status: "error",
message: 'bcrypt compare error'

@ -4,13 +4,14 @@ var user = require('./../user.js'),
categories = require('./../categories.js')
utils = require('./../../public/src/utils.js'),
pkg = require('../../package.json'),
meta = require('./../meta.js');
meta = require('./../meta.js'),
path = require('path');
(function(Api) {
Api.create_routes = function(app) {
app.get('/api/get_templates_listing', function(req, res) {
utils.walk(global.configuration.ROOT_DIRECTORY + '/public/templates', function(err, data) {
utils.walk(path.join(__dirname, '../../', 'public/templates'), function(err, data) {
res.json(data);
});
});

@ -131,7 +131,7 @@ var user = require('./../user.js'),
return;
}
var absolutePath = path.join(global.configuration['ROOT_DIRECTORY'], global.nconf.get('upload_path'), path.basename(oldpicture));
var absolutePath = path.join(__dirname, '../', global.nconf.get('upload_path'), path.basename(oldpicture));
fs.unlink(absolutePath, function(err) {
if(err) {
@ -152,7 +152,7 @@ var user = require('./../user.js'),
}
var filename = uid + '-profileimg' + extension;
var uploadPath = path.join(global.configuration['ROOT_DIRECTORY'], global.nconf.get('upload_path'), filename);
var uploadPath = path.join(__dirname, '../', global.nconf.get('upload_path'), filename);
winston.info('Attempting upload to: '+ uploadPath);

@ -84,7 +84,7 @@ var utils = require('./../public/src/utils.js'),
RDB.incr('usercount', function(err, count) {
RDB.handle(err);
io.sockets.emit('user.count', {count: count});
if (typeof io !== 'undefined') io.sockets.emit('user.count', {count: count});
});
RDB.zadd('users:joindate', timestamp, uid);
@ -93,15 +93,14 @@ var utils = require('./../public/src/utils.js'),
userSearch.index(username, uid);
io.sockets.emit('user.latest', {userslug: userslug, username: username});
if (typeof io !== 'undefined') io.sockets.emit('user.latest', {userslug: userslug, username: username});
if (password !== undefined) {
User.hashPassword(password, function(hash) {
User.hashPassword(password, function(err, hash) {
User.setUserField(uid, 'password', hash);
callback(null, uid);
});
}
callback(null, uid);
} else callback(null, uid);
});
});
};
@ -309,7 +308,7 @@ var utils = require('./../public/src/utils.js'),
}
if (res) {
User.hashPassword(data.newPassword, function(hash) {
User.hashPassword(data.newPassword, function(err, hash) {
User.setUserField(uid, 'password', hash);
callback({err:null});
@ -382,9 +381,7 @@ var utils = require('./../public/src/utils.js'),
}
bcrypt.genSalt(nconf.get('bcrypt_rounds'), function(err, salt) {
bcrypt.hash(password, salt, function(err, hash) {
callback(hash);
});
bcrypt.hash(password, salt, callback);
});
}
@ -906,7 +903,7 @@ var utils = require('./../public/src/utils.js'),
RDB.handle(err);
}
User.hashPassword(password, function(hash) {
User.hashPassword(password, function(err, hash) {
User.setUserField(uid, 'password', hash);
});

Loading…
Cancel
Save