diff --git a/app.js b/app.js
index 818968e0fb..38bcdfd222 100644
--- a/app.js
+++ b/app.js
@@ -1,54 +1,131 @@
-var categories = require('./src/categories.js'),
- templates = require('./public/src/templates.js'),
- webserver = require('./src/webserver.js'),
- websockets = require('./src/websockets.js'),
- admin = {
- 'categories': require('./src/admin/categories.js')
- },
- fs = require('fs');
-
-DEVELOPMENT = true;
-
-global.configuration = {};
-global.templates = {};
-
-(function(config) {
- config['ROOT_DIRECTORY'] = __dirname;
-
- templates.init([
- 'header', 'footer', 'logout', 'admin/header', 'admin/footer', 'admin/index',
- 'emails/reset', 'emails/reset_plaintext', 'emails/email_confirm', 'emails/email_confirm_plaintext',
- 'emails/header.tpl', 'emails/footer.tpl'
- ]);
-
- templates.ready(function() {
- webserver.init();
- });
-
- //setup scripts to be moved outside of the app in future.
- function setup_categories() {
- console.log('Checking categories...');
- categories.getAllCategories(function(data) {
- if (data.categories.length === 0) {
- console.log('Setting up default categories...');
-
- 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]);
- }
- });
-
- } else {
- console.log('Good.');
- }
- });
- }
-
-
- setup_categories();
-
-
-
-}(global.configuration));
\ No newline at end of file
+// Read config.js to grab redis info
+var fs = require('fs'),
+ path = require('path'),
+ utils = require('./public/src/utils.js');
+
+console.log('Info: Checking for valid base configuration file');
+fs.readFile(path.join(__dirname, 'config.json'), function(err, data) {
+ if (!err) {
+ global.config = JSON.parse(data);
+ global.config.url = global.config.base_url + (global.config.use_port ? ':' + global.config.port : '') + '/';
+ global.config.upload_url = global.config.url + 'uploads/';
+ console.log('Info: Base Configuration OK.');
+
+ var meta = require('./src/meta.js');
+ meta.config.get(function(config) {
+ for(c in config) {
+ if (config.hasOwnProperty(c)) {
+ global.config[c] = config[c];
+ }
+ }
+
+ var categories = require('./src/categories.js'),
+ templates = require('./public/src/templates.js'),
+ webserver = require('./src/webserver.js'),
+ websockets = require('./src/websockets.js'),
+ admin = {
+ 'categories': require('./src/admin/categories.js')
+ };
+
+ DEVELOPMENT = true;
+
+ global.configuration = {};
+ global.templates = {};
+
+ (function(config) {
+ config['ROOT_DIRECTORY'] = __dirname;
+
+ templates.init([
+ 'header', 'footer', 'logout', 'admin/header', 'admin/footer', 'admin/index',
+ 'emails/reset', 'emails/reset_plaintext', 'emails/email_confirm', 'emails/email_confirm_plaintext',
+ 'emails/header', 'emails/footer', 'install/header', 'install/footer', 'install/redis'
+ ]);
+
+ templates.ready(function() {
+ webserver.init();
+ });
+
+ //setup scripts to be moved outside of the app in future.
+ function setup_categories() {
+ console.log('Info: Checking categories...');
+ categories.getAllCategories(function(data) {
+ if (data.categories.length === 0) {
+ console.log('Info: Setting up default categories...');
+
+ 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]);
+ }
+ });
+
+ } else {
+ console.log('Info: Good.');
+ }
+ });
+ }
+ setup_categories();
+ }(global.configuration));
+ });
+ } else {
+ // New install, ask setup questions
+ console.log('Info: Configuration not found, starting NodeBB setup');
+ var ask = function(question, callback) {
+ process.stdin.resume();
+ process.stdout.write(question + ': ');
+
+ process.stdin.once('data', function(data) {
+ callback(data.toString().trim());
+ });
+ }
+
+ 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 with the setup.\n\n\nWhat is...\n\n"
+ );
+
+ ask('... the publically accessible URL of this installation? (http://localhost)', function(base_url) {
+ ask('... the port number of your install? (4567)', function(port) {
+ ask('Will you be using a port number to access NodeBB? (y)', function(use_port) {
+ ask('... the host IP or address of your Redis instance? (127.0.0.1)', function(redis_host) {
+ ask('... the host port of your Redis instance? (6379)', function(redis_port) {
+ ask('... your NodeBB secret? (keyboard mash for a bit here)', function(secret) {
+ if (!base_url) base_url = 'http://localhost';
+ if (!port) port = 4567;
+ if (!use_port) use_port = true; else use_port = (use_port === 'y' ? true : false);
+ if (!redis_host) redis_host = '127.0.0.1';
+ if (!redis_port) redis_port = 6379;
+ if (!secret) secret = utils.generateUUID();
+
+ var fs = require('fs'),
+ path = require('path'),
+ config = {
+ secret: secret,
+ base_url: base_url,
+ port: port,
+ use_port: use_port,
+ redis: {
+ host: redis_host,
+ port: redis_port
+ }
+ }
+
+ fs.writeFile(path.join(__dirname, 'config.json'), JSON.stringify(config, null, 4), function(err) {
+ if (err) throw err;
+ else {
+ process.stdout.write(
+ "\n\nConfiguration Saved OK\n\nPlease start NodeBB again and navigate to " +
+ base_url + (use_port ? ':' + port : '') + "/install to continue setup.\n\n"
+ );
+ process.exit();
+ }
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ }
+});
\ No newline at end of file
diff --git a/public/templates/config.json b/public/templates/config.json
index 8a35b78e0b..551606ba59 100644
--- a/public/templates/config.json
+++ b/public/templates/config.json
@@ -1,23 +1,27 @@
{
- "custom_mapping": {
- "admin/topics[^]*": "admin/topics",
- "admin/categories[^]*": "admin/categories",
- "admin/users[^]*": "admin/users",
- "admin/redis[^]*": "admin/redis",
- "admin/index[^]*": "admin/index",
- "admin/themes[^]*": "admin/themes",
- "admin/settings[^]*": "admin/settings",
- "admin/twitter[^]*": "admin/twitter",
- "admin/facebook[^]*": "admin/facebook",
- "admin/gplus[^]*": "admin/gplus",
- "users[^]*edit": "accountedit",
- "users[^]*friends": "friends",
- "users/[^]*": "account",
- "latest": "category",
- "popular": "category",
- "active": "category"
- },
- "force_refresh": {
- "logout": true
- }
+ "custom_mapping": {
+ "admin/topics[^]*": "admin/topics",
+ "admin/categories[^]*": "admin/categories",
+ "admin/users[^]*": "admin/users",
+ "admin/redis[^]*": "admin/redis",
+ "admin/index[^]*": "admin/index",
+ "admin/themes[^]*": "admin/themes",
+ "admin/settings[^]*": "admin/settings",
+ "admin/twitter[^]*": "admin/twitter",
+ "admin/facebook[^]*": "admin/facebook",
+ "admin/gplus[^]*": "admin/gplus",
+ "install/?$": "install/mail",
+ "install/mail/?": "install/mail",
+ "install/social/?": "install/social",
+ "install/privileges/?": "install/privileges",
+ "users[^]*edit": "accountedit",
+ "users[^]*friends": "friends",
+ "users/[^]*": "account",
+ "latest": "category",
+ "popular": "category",
+ "active": "category"
+ },
+ "force_refresh": {
+ "logout": true
+ }
}
\ No newline at end of file
diff --git a/public/templates/install/basic.tpl b/public/templates/install/basic.tpl
new file mode 100644
index 0000000000..5a656fc21d
--- /dev/null
+++ b/public/templates/install/basic.tpl
@@ -0,0 +1,51 @@
+
+
+ This "secret" is used to encode user sessions, so they are not stored in plaintext. Enter a bunch of random characters below:
+
+
\ No newline at end of file
diff --git a/public/templates/install/mail.tpl b/public/templates/install/mail.tpl
new file mode 100644
index 0000000000..f8dcb261f2
--- /dev/null
+++ b/public/templates/install/mail.tpl
@@ -0,0 +1,33 @@
+
+
Mailer Information
+
+
+
+
+
+ Next – Social
+
+
+
\ No newline at end of file
diff --git a/public/templates/install/privileges.tpl b/public/templates/install/privileges.tpl
new file mode 100644
index 0000000000..5a3659e5e1
--- /dev/null
+++ b/public/templates/install/privileges.tpl
@@ -0,0 +1,46 @@
+
+
User Privilege Thresholds
+
+
+
+
+
+ Start using NodeBB!
+
+
+ Previous – Social
+
+
+
\ No newline at end of file
diff --git a/public/templates/install/redis.tpl b/public/templates/install/redis.tpl
new file mode 100644
index 0000000000..e60954ee89
--- /dev/null
+++ b/public/templates/install/redis.tpl
@@ -0,0 +1,81 @@
+
+
Step 1 – Establish Redis Connection
+
+
+ Thanks for choosing to install NodeBB! We'll need some information to set up your installation
+ configuration...
+
+
+ Please enter the details of your Redis server here. If redis is hosted on the same
+ server as NodeBB, you can leave the default values as-is.
+
+
+
+
+
+
+ Next – Basic
+
+
+
diff --git a/public/templates/install/social.tpl b/public/templates/install/social.tpl
new file mode 100644
index 0000000000..16654f8f93
--- /dev/null
+++ b/public/templates/install/social.tpl
@@ -0,0 +1,47 @@
+
+
Social Media Logins
+
+
+
+
+
+ Next – Privileges
+
+
+ Previous – Mail
+
+
+
\ No newline at end of file
diff --git a/src/meta.js b/src/meta.js
new file mode 100644
index 0000000000..44d31f2abc
--- /dev/null
+++ b/src/meta.js
@@ -0,0 +1,58 @@
+var utils = require('./../public/src/utils.js'),
+ RDB = require('./redis.js'),
+ async = require('async');
+
+(function(Meta) {
+ Meta.testRedis = function(callback) {
+ RDB.set('nodebb-redis-test', 'foobar', function(err, res) {
+ if (!err) {
+ RDB.get('nodebb-redis-test', function(err, res) {
+ if (!err && res === 'foobar') {
+ callback(true);
+ } else {
+ callback(false);
+ }
+ });
+ } else {
+ callback(false);
+ }
+ });
+ }
+
+ Meta.config = {
+ get: function(callback) {
+ var config = {};
+
+ async.waterfall([
+ function(next) {
+ RDB.hkeys('config', function(err, keys) {
+ next(err, keys);
+ });
+ },
+ function(keys, next) {
+ async.each(keys, function(key, next) {
+ RDB.hget('config', key, function(err, value) {
+ if (!err) {
+ config[key] = value;
+ }
+
+ next(err);
+ });
+ }, next);
+ }
+ ], function(err) {
+ if (!err) {
+ config.status = 'ok';
+ callback(config);
+ } else callback({
+ status: 'error'
+ });
+ });
+ },
+ set: function(field, value, callback) {
+ RDB.hset('config', field, value, function(err, res) {
+ callback(err);
+ });
+ }
+ }
+}(exports));
\ No newline at end of file
diff --git a/src/notifications.js b/src/notifications.js
index 8ea7dc9f24..3e63628068 100644
--- a/src/notifications.js
+++ b/src/notifications.js
@@ -1,5 +1,4 @@
-var config = require('../config.js'),
- RDB = require('./redis.js'),
+var RDB = require('./redis.js'),
async = require('async'),
utils = require('../public/src/utils.js');
diff --git a/src/postTools.js b/src/postTools.js
index 682ae1d51d..74cce37873 100644
--- a/src/postTools.js
+++ b/src/postTools.js
@@ -2,7 +2,6 @@ var RDB = require('./redis.js'),
posts = require('./posts.js'),
threadTools = require('./threadTools.js'),
user = require('./user.js'),
- config = require('../config.js'),
async = require('async'),
marked = require('marked');
@@ -35,8 +34,8 @@ marked.setOptions({
// DRY fail in threadTools.
user.getUserField(uid, 'reputation', function(reputation) {
- next(null, reputation >= config.privilege_thresholds.manage_content);
- });
+ next(null, reputation >= global.config['privileges:manage_content']);
+ });
}
async.parallel([getThreadPrivileges, isOwnPost, hasEnoughRep], function(err, results) {
diff --git a/src/posts.js b/src/posts.js
index 7cc19c5c8c..58c6b38618 100644
--- a/src/posts.js
+++ b/src/posts.js
@@ -4,7 +4,6 @@ var RDB = require('./redis.js'),
user = require('./user.js'),
topics = require('./topics.js'),
favourites = require('./favourites.js'),
- config = require('../config.js'),
threadTools = require('./threadTools.js'),
feed = require('./feed.js'),
async = require('async');
diff --git a/src/redis.js b/src/redis.js
index 935e8afe06..5ac950f057 100644
--- a/src/redis.js
+++ b/src/redis.js
@@ -3,11 +3,9 @@
ERROR_LOGS = true,
redis = require('redis'),
- config = require('../config.js'),
utils = require('./../public/src/utils.js');
-
- RedisDB.exports = redis.createClient(config.redis.port, config.redis.host, config.redis.options);
+ RedisDB.exports = redis.createClient(global.config.redis.port, global.config.redis.host);
RedisDB.exports.handle = function(error) {
if (error !== null) {
diff --git a/src/routes/authentication.js b/src/routes/authentication.js
index e9c17d29ea..3777e2f478 100644
--- a/src/routes/authentication.js
+++ b/src/routes/authentication.js
@@ -1,5 +1,4 @@
(function(Auth) {
-
var passport = require('passport'),
passportLocal = require('passport-local').Strategy,
passportTwitter = require('passport-twitter').Strategy,
@@ -7,11 +6,7 @@
passportFacebook = require('passport-facebook').Strategy,
login_strategies = [],
- user_module = require('./../user.js'),
- config = require('./../../config.js');
-
-
-
+ user_module = require('./../user.js');
passport.use(new passportLocal(function(user, password, next) {
user_module.loginViaLocal(user, password, function(login) {
@@ -20,10 +15,10 @@
});
}));
- if (config.twitter && config.twitter.key && config.twitter.key.length > 0 && config.twitter.secret.length > 0) {
+ if (global.config['social:twitter:key'] && global.config['social:twitter:secret']) {
passport.use(new passportTwitter({
- consumerKey: config.twitter.key,
- consumerSecret: config.twitter.secret,
+ consumerKey: global.config['social:twitter:key'],
+ consumerSecret: global.config['social:twitter:secret'],
callbackURL: config.url + 'auth/twitter/callback'
}, function(token, tokenSecret, profile, done) {
user_module.loginViaTwitter(profile.id, profile.username, function(err, user) {
@@ -35,10 +30,10 @@
login_strategies.push('twitter');
}
- if (config.google && config.google.id.length > 0 && config.google.secret.length > 0) {
+ if (global.config['social:google:id'] && global.config['social:google:secret']) {
passport.use(new passportGoogle({
- clientID: config.google.id,
- clientSecret: config.google.secret,
+ clientID: global.config['social:google:id'],
+ clientSecret: global.config['social:google:secret'],
callbackURL: config.url + 'auth/google/callback'
}, function(accessToken, refreshToken, profile, done) {
user_module.loginViaGoogle(profile.id, profile.displayName, profile.emails[0].value, function(err, user) {
@@ -50,10 +45,10 @@
login_strategies.push('google');
}
- if (config.facebook && config.facebook.app_id.length > 0 && config.facebook.secret.length > 0) {
+ if (global.config['social:facebook:app_id'] && global.config['social:facebook:secret']) {
passport.use(new passportFacebook({
- clientID: config.facebook.app_id,
- clientSecret: config.facebook.secret,
+ clientID: global.config['social:facebook:app_id'],
+ clientSecret: global.config['social:facebook:secret'],
callbackURL: config.url + 'auth/facebook/callback'
}, function(accessToken, refreshToken, profile, done) {
user_module.loginViaFacebook(profile.id, profile.displayName, profile.emails[0].value, function(err, user) {
diff --git a/src/routes/install.js b/src/routes/install.js
new file mode 100644
index 0000000000..5b352fcd71
--- /dev/null
+++ b/src/routes/install.js
@@ -0,0 +1,44 @@
+
+var RDB = require('../redis.js');
+
+(function(Install) {
+ Install.create_routes = function(app) {
+
+ (function() {
+ var routes = ['basic', 'redis', 'mail', 'social', 'privileges'];
+
+ for (var i=0, ii=routes.length; i
= config.privilege_thresholds.manage_thread);
+ next(null, reputation >= global.config['privileges:manage_topic']);
});
}
diff --git a/src/topics.js b/src/topics.js
index c6e0732fef..7c2a55ab10 100644
--- a/src/topics.js
+++ b/src/topics.js
@@ -3,7 +3,6 @@ var RDB = require('./redis.js')
posts = require('./posts.js'),
utils = require('./../public/src/utils.js'),
user = require('./user.js'),
- config = require('../config.js'),
categories = require('./categories.js'),
posts = require('./posts.js'),
marked = require('marked'),
diff --git a/src/user.js b/src/user.js
index aa59326810..008e93838c 100644
--- a/src/user.js
+++ b/src/user.js
@@ -1,5 +1,4 @@
-var config = require('../config.js'),
- utils = require('./../public/src/utils.js'),
+var utils = require('./../public/src/utils.js'),
RDB = require('./redis.js'),
crypto = require('crypto'),
emailjs = require('emailjs'),
diff --git a/src/webserver.js b/src/webserver.js
index 04a08dc03f..4f3a25f606 100644
--- a/src/webserver.js
+++ b/src/webserver.js
@@ -3,9 +3,8 @@ var express = require('express'),
server = require('http').createServer(WebServer),
RedisStore = require('connect-redis')(express),
path = require('path'),
- config = require('../config.js'),
redis = require('redis'),
- redisServer = redis.createClient(config.redis.port, config.redis.host, config.redis.options),
+ redisServer = redis.createClient(global.config.redis.port, global.config.redis.host),
marked = require('marked'),
utils = require('../public/src/utils.js'),
fs = require('fs'),
@@ -17,7 +16,9 @@ var express = require('express'),
notifications = require('./notifications.js'),
admin = require('./routes/admin.js'),
userRoute = require('./routes/user.js'),
- auth = require('./routes/authentication.js');
+ installRoute = require('./routes/install.js'),
+ auth = require('./routes/authentication.js'),
+ meta = require('./meta.js');
(function(app) {
var templates = null;
@@ -34,7 +35,7 @@ var express = require('express'),
client: redisServer,
ttl: 60*60*24*14
}),
- secret: config.secret,
+ secret: global.config.secret,
key: 'express.sid'
}));
@@ -62,6 +63,7 @@ var express = require('express'),
auth.create_routes(app);
admin.create_routes(app);
userRoute.create_routes(app);
+ installRoute.create_routes(app);
app.create_route = function(url, tpl) { // to remove
@@ -220,13 +222,8 @@ var express = require('express'),
app.get('/api/:method/:id*', api_method);
app.get('/test', function(req, res) {
- // notifications.remove_by_uniqueId('foobar', 1, function(success) {
- // res.send('remove: ' + success);
- // });
- notifications.create('a bunch more text', 5, '/category/2/general-discussion', 'foobar', function(nid) {
- notifications.push(nid, 1, function() {
- res.send('nid: ' + nid)
- });
+ meta.config.get(function(config) {
+ res.send(JSON.stringify(config, null, 4));
});
});
diff --git a/src/websockets.js b/src/websockets.js
index 5eea821fb1..05bdfb0a2d 100644
--- a/src/websockets.js
+++ b/src/websockets.js
@@ -2,7 +2,6 @@
var SocketIO = require('socket.io').listen(global.server, { log:false }),
cookie = require('cookie'),
connect = require('connect'),
- config = require('../config.js'),
user = require('./user.js'),
posts = require('./posts.js'),
favourites = require('./favourites.js'),
@@ -11,7 +10,9 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
categories = require('./categories.js'),
notifications = require('./notifications.js'),
threadTools = require('./threadTools.js'),
- postTools = require('./postTools.js');
+ postTools = require('./postTools.js'),
+ meta = require('./meta.js'),
+ async = require('async');
(function(io) {
var users = {},
@@ -24,7 +25,7 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
io.set('authorization', function(handshakeData, accept) {
if (handshakeData.headers.cookie) {
handshakeData.cookie = cookie.parse(handshakeData.headers.cookie);
- handshakeData.sessionID = connect.utils.parseSignedCookie(handshakeData.cookie['express.sid'], config.secret);
+ handshakeData.sessionID = connect.utils.parseSignedCookie(handshakeData.cookie['express.sid'], global.config.secret);
if (handshakeData.cookie['express.sid'] == handshakeData.sessionID) {
return accept('Cookie is invalid.', false);
@@ -291,6 +292,26 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
});
}
});
+
+ socket.on('api:config.redisTest', function() {
+ meta.testRedis(function(success) {
+ socket.emit('api:config.redisTest', {
+ status: success ? 'ok' : 'error'
+ });
+ });
+ });
+
+ socket.on('api:config.get', function(data) {
+ meta.config.get(function(config) {
+ socket.emit('api:config.get', config);
+ });
+ });
+
+ socket.on('api:config.set', function(data) {
+ meta.config.set(data.key, data.value, function(err) {
+ if (!err) socket.emit('api:config.set', { status: 'ok' });
+ });
+ });
});
}(SocketIO));