From 79c52dfe8453327b0135dad6488ce63b83fcf67f Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 13 Nov 2013 12:46:55 -0500 Subject: [PATCH 1/9] updated schema handling so that nodebb won't run unless you are up to date -- fixed #498 --- app.js | 45 +++++++----- src/upgrade.js | 191 ++++++++++++++++++++++++++++--------------------- 2 files changed, 138 insertions(+), 98 deletions(-) diff --git a/app.js b/app.js index 2a4fb645ec..896dd82750 100644 --- a/app.js +++ b/app.js @@ -88,30 +88,41 @@ websockets = require('./src/websockets.js'), posts = require('./src/posts.js'), plugins = require('./src/plugins'), // Don't remove this - plugins initializes itself - Notifications = require('./src/notifications'); + Notifications = require('./src/notifications'), + Upgrade = require('./src/upgrade'); - websockets.init(SocketIO); + Upgrade.check(function(schema_ok) { + if (schema_ok || nconf.get('check-schema') === false) { + websockets.init(SocketIO); - global.templates = {}; - global.translator = translator; + global.templates = {}; + global.translator = translator; - translator.loadServer(); - - var customTemplates = meta.config['theme:templates'] ? path.join(__dirname, 'node_modules', meta.config['theme:id'], meta.config['theme:templates']) : false; + translator.loadServer(); - // todo: replace below with read directory code, derp. - 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', + var customTemplates = meta.config['theme:templates'] ? path.join(__dirname, 'node_modules', meta.config['theme:id'], meta.config['theme:templates']) : false; - 'noscript/header', 'noscript/home', 'noscript/category', 'noscript/topic' - ], customTemplates); - + // todo: replace below with read directory code, derp. + 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', - templates.ready(webserver.init); + 'noscript/header', 'noscript/home', 'noscript/category', 'noscript/topic' + ], customTemplates); - Notifications.init(); + + templates.ready(webserver.init); + + Notifications.init(); + } else { + winston.warn('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:'); + winston.warn(' node app --upgrade'); + winston.warn('To ignore this error (not recommended):'); + winston.warn(' node app --no-check-schema') + process.exit(); + } + }); }); } else if (nconf.get('setup') || !fs.existsSync(__dirname + '/config.json')) { // New install, ask setup questions diff --git a/src/upgrade.js b/src/upgrade.js index f4040a5886..5dcdbabda3 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -1,110 +1,139 @@ +"use strict"; + var RDB = require('./redis.js'), async = require('async'), winston = require('winston'), - notifications = require('./notifications') - Upgrade = {}; + notifications = require('./notifications'), + Upgrade = {}, + + schemaDate, thisSchemaDate; + +Upgrade.check = function(callback) { + var latestSchema = new Date(2013, 10, 11).getTime(); + + RDB.get('schemaDate', function(err, value) { + if (parseInt(value, 10) > latestSchema) { + callback(true); + } else { + callback(false); + } + }); +}; Upgrade.upgrade = function() { winston.info('Beginning Redis database schema update'); async.series([ function(next) { - RDB.hget('notifications:1', 'score', function(err, score) { - if (score) { - async.series([ - function(next) { - RDB.keys('uid:*:notifications:flag', function(err, keys) { - if (keys.length > 0) { - winston.info('[2013/10/03] Removing deprecated Notification Flags'); - async.each(keys, function(key, next) { - RDB.del(key, next); - }, next); - } else { - winston.info('[2013/10/03] No Notification Flags found. Good.'); - next(); - } - }); - }, - function(next) { - winston.info('[2013/10/03] Updating Notifications'); - RDB.keys('uid:*:notifications:*', function(err, keys) { - async.each(keys, function(key, next) { - RDB.zrange(key, 0, -1, function(err, nids) { - async.each(nids, function(nid, next) { - notifications.get(nid, null, function(notif_data) { - RDB.zadd(key, notif_data.datetime, nid, next); - }); - }, next); - }); - }, next); - }); - }, - function(next) { - RDB.keys('notifications:*', function(err, keys) { - if (keys.length > 0) { - winston.info('[2013/10/03] Removing Notification Scores'); - async.each(keys, function(key, next) { - if (key === 'notifications:next_nid') return next(); - RDB.hdel(key, 'score', next); - }, next); - } else { - winston.info('[2013/10/03] No Notification Scores found. Good.'); - next(); - } - }); - } - ], next); - } else { - winston.info('[2013/10/03] Updates to Notifications skipped.'); - next(); - } + RDB.get('schemaDate', function(err, value) { + schemaDate = value; + next(); }); }, function(next) { - RDB.exists('notifications', function(err, exists) { - if (!exists) { - RDB.keys('notifications:*', function(err, keys) { - var multi = RDB.multi(); + thisSchemaDate = new Date(2013, 9, 3).getTime(); + if (schemaDate < thisSchemaDate) { + async.series([ + function(next) { + RDB.keys('uid:*:notifications:flag', function(err, keys) { + if (keys.length > 0) { + winston.info('[2013/10/03] Removing deprecated Notification Flags'); + async.each(keys, function(key, next) { + RDB.del(key, next); + }, next); + } else { + winston.info('[2013/10/03] No Notification Flags found. Good.'); + next(); + } + }); + }, + function(next) { + winston.info('[2013/10/03] Updating Notifications'); + RDB.keys('uid:*:notifications:*', function(err, keys) { + async.each(keys, function(key, next) { + RDB.zrange(key, 0, -1, function(err, nids) { + async.each(nids, function(nid, next) { + notifications.get(nid, null, function(notif_data) { + RDB.zadd(key, notif_data.datetime, nid, next); + }); + }, next); + }); + }, next); + }); + }, + function(next) { + RDB.keys('notifications:*', function(err, keys) { + if (keys.length > 0) { + winston.info('[2013/10/03] Removing Notification Scores'); + async.each(keys, function(key, next) { + if (key === 'notifications:next_nid') { + return next(); + } - keys = keys.filter(function(key) { - if (key === 'notifications:next_nid') { - return false; + RDB.hdel(key, 'score', next); + }, next); } else { - return true; + winston.info('[2013/10/03] No Notification Scores found. Good.'); + next(); } - }).map(function(key) { - return key.slice(14); }); + } + ], next); + } else { + winston.info('[2013/10/03] Updates to Notifications skipped.'); + next(); + } + }, + function(next) { + thisSchemaDate = new Date(2013, 9, 23).getTime(); + if (schemaDate < thisSchemaDate) { + RDB.keys('notifications:*', function(err, keys) { + var multi = RDB.multi(); - winston.info('[2013/10/23] Adding existing notifications to set'); - RDB.sadd('notifications', keys, next); + keys = keys.filter(function(key) { + if (key === 'notifications:next_nid') { + return false; + } else { + return true; + } + }).map(function(key) { + return key.slice(14); }); - } else { - winston.info('[2013/10/23] Updates to Notifications skipped.'); - next(); - } - }); + + winston.info('[2013/10/23] Adding existing notifications to set'); + RDB.sadd('notifications', keys, next); + }); + } else { + winston.info('[2013/10/23] Updates to Notifications skipped.'); + next(); + } }, function(next) { - RDB.hget('config', 'postDelay', function(err, postDelay) { - if(parseInt(postDelay, 10) > 150) { - RDB.hset('config', 'postDelay', 10, function(err, success) { - winston.info('[2013/11/11] Updated postDelay to 10 seconds.'); - next(); - }); - } else { - winston.info('[2013/11/11] Update to postDelay skipped.'); + thisSchemaDate = new Date(2013, 10, 11).getTime(); + if (schemaDate < thisSchemaDate) { + RDB.hset('config', 'postDelay', 10, function(err, success) { + winston.info('[2013/11/11] Updated postDelay to 10 seconds.'); next(); - } - }); + }); + } else { + winston.info('[2013/11/11] Update to postDelay skipped.'); + next(); + } } // Add new schema updates here + // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 12!!! ], function(err) { if (!err) { - winston.info('Redis schema update complete!'); - process.exit(); + RDB.set('schemaDate', thisSchemaDate, function(err) { + if (!err) { + winston.info('[upgrade] Redis schema update complete!'); + process.exit(); + } else { + winston.error('[upgrade] Could not update NodeBB schema date!'); + } + }); } else { - winston.error('Errors were encountered while updating the NodeBB schema: ' + err.message); + winston.error('[upgrade] Errors were encountered while updating the NodeBB schema: ' + err.message); } }); }; From 1c80a1bad53f56c1c71d53990bf25c391e1ff2ee Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 13 Nov 2013 13:31:36 -0500 Subject: [PATCH 2/9] ninjafix --- src/upgrade.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upgrade.js b/src/upgrade.js index 5dcdbabda3..0e77918b86 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -12,7 +12,7 @@ Upgrade.check = function(callback) { var latestSchema = new Date(2013, 10, 11).getTime(); RDB.get('schemaDate', function(err, value) { - if (parseInt(value, 10) > latestSchema) { + if (parseInt(value, 10) >= latestSchema) { callback(true); } else { callback(false); From 88154c3ebf8fa4a71d03762990d472e79d70f00d Mon Sep 17 00:00:00 2001 From: Baris Soner Usakli Date: Wed, 13 Nov 2013 13:51:40 -0500 Subject: [PATCH 3/9] closes #497 --- src/routes/user.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/routes/user.js b/src/routes/user.js index b43d770f5c..c0a96e80bb 100644 --- a/src/routes/user.js +++ b/src/routes/user.js @@ -207,11 +207,6 @@ var user = require('./../user.js'), is.on('end', function () { fs.unlinkSync(tempPath); - var imageUrl = nconf.get('upload_url') + filename; - - user.setUserField(uid, 'uploadedpicture', imageUrl); - user.setUserField(uid, 'picture', imageUrl); - require('node-imagemagick').crop({ srcPath: uploadPath, dstPath: uploadPath, @@ -220,8 +215,17 @@ var user = require('./../user.js'), }, function (err, stdout, stderr) { if (err) { winston.err(err); + res.send({ + error: 'Invalid image file!' + }); + return; } + var imageUrl = nconf.get('upload_url') + filename; + + user.setUserField(uid, 'uploadedpicture', imageUrl); + user.setUserField(uid, 'picture', imageUrl); + res.json({ path: imageUrl }); From e6bb66705d3d491600890158ac7dc2f87d157da2 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 13 Nov 2013 15:15:00 -0500 Subject: [PATCH 4/9] 0.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1eeaf8e3f7..767d955a12 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPLv3 or later", "description": "NodeBB Forum", - "version": "0.0.7", + "version": "0.1.0", "homepage": "http://www.nodebb.org", "repository": { "type": "git", From 77c2f551d3897746e3bdfae37b552aeb232c6bef Mon Sep 17 00:00:00 2001 From: Baris Soner Usakli Date: Wed, 13 Nov 2013 20:03:44 -0500 Subject: [PATCH 5/9] upgrade script fix --- src/upgrade.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/upgrade.js b/src/upgrade.js index 0e77918b86..67b349f226 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -88,7 +88,6 @@ Upgrade.upgrade = function() { thisSchemaDate = new Date(2013, 9, 23).getTime(); if (schemaDate < thisSchemaDate) { RDB.keys('notifications:*', function(err, keys) { - var multi = RDB.multi(); keys = keys.filter(function(key) { if (key === 'notifications:next_nid') { @@ -101,7 +100,13 @@ Upgrade.upgrade = function() { }); winston.info('[2013/10/23] Adding existing notifications to set'); - RDB.sadd('notifications', keys, next); + + if(keys && Array.isArray(keys)) { + async.each(keys, function(key, cb) { + RDB.sadd('notifications', key, cb); + }, next); + } else next(); + }); } else { winston.info('[2013/10/23] Updates to Notifications skipped.'); From 01340c87bd906113b991c6143797439eef99a955 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 13 Nov 2013 23:08:44 -0500 Subject: [PATCH 6/9] handling missing notifications in update script --- src/upgrade.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/upgrade.js b/src/upgrade.js index 67b349f226..c2d98fbb40 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -54,7 +54,9 @@ Upgrade.upgrade = function() { RDB.zrange(key, 0, -1, function(err, nids) { async.each(nids, function(nid, next) { notifications.get(nid, null, function(notif_data) { - RDB.zadd(key, notif_data.datetime, nid, next); + if (notif_data) { + RDB.zadd(key, notif_data.datetime, nid, next); + } }); }, next); }); From 27fce2363dcb44110b94fb66ba8a6f4a6360d331 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 13 Nov 2013 23:09:22 -0500 Subject: [PATCH 7/9] ... and again --- src/upgrade.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/upgrade.js b/src/upgrade.js index c2d98fbb40..26ac52cc19 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -56,6 +56,8 @@ Upgrade.upgrade = function() { notifications.get(nid, null, function(notif_data) { if (notif_data) { RDB.zadd(key, notif_data.datetime, nid, next); + } else { + next(); } }); }, next); From bd04b2f921eec8627613b5e9c53c48668b6c3ca2 Mon Sep 17 00:00:00 2001 From: Jacob Gable Date: Thu, 14 Nov 2013 12:52:00 -0600 Subject: [PATCH 8/9] Add admin password confirmation on setup Closes #419 - Introduce a password:confirm question - Isolate password questions to they can be re-asked - Verify matching password, re-ask if not --- src/install.js | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/install.js b/src/install.js index 5795a4d605..b32b55622d 100644 --- a/src/install.js +++ b/src/install.js @@ -277,18 +277,32 @@ var async = require('async'), description: 'Administrator email address', pattern: /.+@.+/, required: true - }, { + }], + passwordQuestions = [{ name: 'password', description: 'Password', required: true, hidden: true, type: 'string' + }, { + name: 'password:confirm', + description: 'Confirm Password', + required: true, + hidden: true, + type: 'string' }], success = function(err, results) { if (!results) { return callback(new Error('aborted')); } + // Check if the passwords match + if (results['password:confirm'] !== results.password) { + winston.warn("Passwords did not match, please try again"); + // Re-prompt password questions. + return retryPassword(results); + } + nconf.set('bcrypt_rounds', 12); User.create(results.username, results.password, results.email, function (err, uid) { if (err) { @@ -306,8 +320,26 @@ var async = require('async'), } }); }); + }, + retryPassword = function (originalResults) { + // Ask only the password questions + prompt.get(passwordQuestions, function (err, results) { + if (!results) { + return callback(new Error('aborted')); + } + + // Update the original data with newly collected password + originalResults.password = results.password; + originalResults['password:confirm'] = results['password:confirm']; + + // Send back to success to handle + success(err, originalResults); + }); }; + // Add the password questions + questions = questions.concat(passwordQuestions); + if (!install.values) prompt.get(questions, success); else { var results = { From 535379d9d77d1b71c76e72ef73ee847819b979ec Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 14 Nov 2013 18:45:17 -0500 Subject: [PATCH 9/9] added password confirmation to automated setup --- src/install.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/install.js b/src/install.js index b32b55622d..8ce2a059ed 100644 --- a/src/install.js +++ b/src/install.js @@ -63,13 +63,14 @@ var async = require('async'), } if (setupVal && setupVal instanceof Object) { - if (setupVal['admin:username'] && setupVal['admin:password'] && setupVal['admin:email']) { + if (setupVal['admin:username'] && setupVal['admin:password'] && setupVal['admin:password:confirm'] && setupVal['admin:email']) { install.values = setupVal; next(); } else { winston.error('Required values are missing for automated setup:'); if (!setupVal['admin:username']) winston.error(' admin:username'); if (!setupVal['admin:password']) winston.error(' admin:password'); + if (!setupVal['admin:password:confirm']) winston.error(' admin:password:confirm'); if (!setupVal['admin:email']) winston.error(' admin:email'); process.exit(); } @@ -345,7 +346,8 @@ var async = require('async'), var results = { username: install.values['admin:username'], email: install.values['admin:email'], - password: install.values['admin:password'] + password: install.values['admin:password'], + 'password:confirm': install.values['admin:password:confirm'] }; success(null, results);