diff --git a/app.js b/app.js
index 7507ee4ac1..8defae9ab2 100644
--- a/app.js
+++ b/app.js
@@ -60,7 +60,7 @@
nconf.file({
file: __dirname + '/config.json'
});
- meta = require('./src/meta.js');
+ meta = require('./src/meta');
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/');
@@ -73,58 +73,53 @@
winston.info('Base Configuration OK.');
}
- meta.configs.init(function () {
- // Initial setup for Redis & Reds
- var reds = require('reds'),
- RDB = require('./src/redis.js');
-
- reds.createClient = function () {
- return reds.client || (reds.client = RDB);
- };
-
- var templates = require('./public/src/templates.js'),
- translator = require('./public/src/translator.js'),
- webserver = require('./src/webserver.js'),
- SocketIO = require('socket.io').listen(global.server, { log: false, transports: ['websocket', 'xhr-polling', 'jsonp-polling', 'flashsocket'], 'browser client minification': true}),
- 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'),
- Upgrade = require('./src/upgrade');
-
- Upgrade.check(function(schema_ok) {
- if (schema_ok || nconf.get('check-schema') === false) {
- websockets.init(SocketIO);
-
- 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;
-
- // 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',
-
- 'noscript/header', 'noscript/home', 'noscript/category', 'noscript/topic'
- ], customTemplates);
-
-
- plugins.ready(function() {
- 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();
- }
+ require('./src/database').init(function(err) {
+ meta.configs.init(function () {
+
+ var templates = require('./public/src/templates'),
+ translator = require('./public/src/translator'),
+ webserver = require('./src/webserver'),
+ SocketIO = require('socket.io').listen(global.server, { log: false, transports: ['websocket', 'xhr-polling', 'jsonp-polling', 'flashsocket'], 'browser client minification': true}),
+ websockets = require('./src/websockets'),
+ plugins = require('./src/plugins'),
+ notifications = require('./src/notifications'),
+ upgrade = require('./src/upgrade');
+
+ upgrade.check(function(schema_ok) {
+ if (schema_ok || nconf.get('check-schema') === false) {
+ websockets.init(SocketIO);
+
+ plugins.init();
+ 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;
+
+ // 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',
+
+ 'noscript/header', 'noscript/home', 'noscript/category', 'noscript/topic'
+ ], customTemplates);
+
+
+ plugins.ready(function() {
+ 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') || nconf.get('install') || !fs.existsSync(__dirname + '/config.json')) {
@@ -159,10 +154,12 @@
nconf.file({
file: __dirname + '/config.json'
});
- meta = require('./src/meta.js');
+ require('./src/database').init(function(err) {
+ meta = require('./src/meta.js');
- meta.configs.init(function () {
- require('./src/upgrade').upgrade();
+ meta.configs.init(function () {
+ require('./src/upgrade').upgrade();
+ });
});
} else/* if (nconf.get('help') */{
winston.info('Usage: node app [options] [arguments]');
diff --git a/mocks/databasemock.js b/mocks/databasemock.js
new file mode 100644
index 0000000000..89e5f850e7
--- /dev/null
+++ b/mocks/databasemock.js
@@ -0,0 +1,81 @@
+/**
+ * Database Mock - wrapper for database.js, makes system use separate test db, instead of production
+ * ATTENTION: testing db is flushed before every use!
+ */
+
+(function(module) {
+ 'use strict';
+
+ var utils = require('./../public/src/utils.js'),
+ path = require('path'),
+ nconf = require('nconf'),
+ winston = require('winston'),
+ errorText;
+
+
+ nconf.file({ file: path.join(__dirname, '../config.json') });
+
+ var dbType = nconf.get('database'),
+ testDbConfig = nconf.get('test_database'),
+ productionDbConfig = nconf.get(dbType);
+
+ if(!testDbConfig){
+ errorText = 'test_database is not defined';
+ winston.info(
+ "\n===========================================================\n"+
+ "Please, add parameters for test database in config.json\n"+
+ "For example (redis):\n"+
+ '"test_database": {' + '\n' +
+ ' "host": "127.0.0.1",' + '\n' +
+ ' "port": "6379",' + '\n' +
+ ' "password": "",' + '\n' +
+ ' "database": "1"' + '\n' +
+ '}\n'+
+ " or (mongo):\n" +
+ '"test_database": {' + '\n' +
+ ' "host": "127.0.0.1",' + '\n' +
+ ' "port": "27017",' + '\n' +
+ ' "password": "",' + '\n' +
+ ' "database": "1"' + '\n' +
+ '}\n'+
+ "==========================================================="
+ );
+ winston.error(errorText);
+ throw new Error(errorText);
+ }
+
+ if( testDbConfig.database === productionDbConfig.database &&
+ testDbConfig.host === productionDbConfig.host &&
+ testDbConfig.port === productionDbConfig.port
+ ){
+ errorText = 'test_database has the same config as production db';
+ winston.error(errorText);
+ throw new Error(errorText);
+ }
+
+ nconf.set(dbType, testDbConfig);
+
+ db = require('../src/database');
+ before(function(done) {
+
+ db.init(function(err) {
+ //Clean up
+ db.flushdb(function(err) {
+ if(err){
+ winston.error(err);
+ throw new Error(err);
+ } else {
+ winston.info('test_database flushed');
+ done();
+ }
+
+ //TODO: data seeding, if needed at all
+
+
+ });
+ });
+ });
+
+ module.exports = db;
+
+}(module));
diff --git a/mocks/redismock.js b/mocks/redismock.js
deleted file mode 100644
index cfdbb4d9fe..0000000000
--- a/mocks/redismock.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
- * Redis Mock - wrapper for redis.js, makes system use separate test db, instead of production
- * ATTENTION: testing db is flushed before every use!
- */
-
-(function(module) {
- 'use strict';
-
- var RedisDB,
- redis = require('redis'),
- utils = require('./../public/src/utils.js'),
- path = require('path'),
- nconf = require('nconf'),
- winston = require('winston'),
- errorText;
-
-
- nconf.file({ file: path.join(__dirname, '../config.json') });
-
- var testDbConfig = nconf.get('redis_test'),
- productionDbConfig = nconf.get('redis');
- if(!testDbConfig){
- errorText = 'redis_test database is not defined';
- winston.info(
- "\n===========================================================\n"+
- "Please, add parameters for test database in config.json\n"+
- "For example:\n"+
- '"redis_test": {' + '\n' +
- ' "host": "127.0.0.1",' + '\n' +
- ' "port": "6379",' + '\n' +
- ' "password": "",' + '\n' +
- ' "database": "1"' + '\n' +
- '}\n'+
- "==========================================================="
- );
- winston.error(errorText);
- throw new Error(errorText);
- }
-
- if( testDbConfig.database === productionDbConfig.database &&
- testDbConfig.host === productionDbConfig.host &&
- testDbConfig.port === productionDbConfig.port
- ){
- errorText = 'redis_test database has the same config as production db';
- winston.error(errorText);
- throw new Error(errorText);
- }
-
- nconf.set('redis',testDbConfig);
-
- RedisDB = require('../src/redis.js');
-
-
- //Clean up
- RedisDB.send_command('flushdb', [], function(error){
- if(error){
- winston.error(error);
- throw new Error(error);
- } else {
- winston.info('redis_test db flushed');
- }
- });
-
- //TODO: data seeding, if needed at all
-
-
- module.exports = RedisDB;
-
-}(module));
diff --git a/package.json b/package.json
index 4710b9daa0..af257f5775 100644
--- a/package.json
+++ b/package.json
@@ -15,11 +15,13 @@
"dependencies": {
"socket.io": "~0.9.16",
"redis": "0.8.3",
+ "mongodb": "1.3.20",
"express": "3.2.0",
"express-namespace": "~0.1.1",
"emailjs": "0.3.4",
"cookie": "0.0.6",
"connect-redis": "1.4.5",
+ "connect-mongo": "0.4.0",
"passport": "0.1.17",
"passport-local": "0.1.6",
"passport-twitter": "0.1.5",
diff --git a/public/src/forum/admin/settings.js b/public/src/forum/admin/settings.js
index 6c75e6457a..989e94a79c 100644
--- a/public/src/forum/admin/settings.js
+++ b/public/src/forum/admin/settings.js
@@ -32,7 +32,7 @@ define(['uploader'], function(uploader) {
break;
case 'checkbox':
- fields[x].checked = app.config[key] === '1' ? true : false;
+ fields[x].checked = parseInt(app.config[key], 10) === 1;
break;
}
}
diff --git a/public/src/forum/category.js b/public/src/forum/category.js
index 77daf9994c..44a11eaea9 100644
--- a/public/src/forum/category.js
+++ b/public/src/forum/category.js
@@ -140,10 +140,11 @@ define(function () {
jQuery('#topics-container, .category-sidebar').removeClass('hidden');
jQuery('#category-no-topics').remove();
+ html = $(html);
container.append(html);
$('#topics-container span.timeago').timeago();
- app.makeNumbersHumanReadable($(html).find('.human-readable-number'));
+ app.makeNumbersHumanReadable(html.find('.human-readable-number'));
}
Category.loadMoreTopics = function(cid) {
diff --git a/public/src/forum/recent.js b/public/src/forum/recent.js
index 4614c2f404..6b162db34f 100644
--- a/public/src/forum/recent.js
+++ b/public/src/forum/recent.js
@@ -89,9 +89,10 @@ define(function() {
$('#category-no-topics').remove();
+ html = $(html);
container.append(html);
$('span.timeago').timeago();
- app.makeNumbersHumanReadable($(html).find('.human-readable-number'));
+ app.makeNumbersHumanReadable(html.find('.human-readable-number'));
}
Recent.loadMoreTopics = function() {
diff --git a/public/src/forum/unread.js b/public/src/forum/unread.js
index 60c4ba4953..6debeeba84 100644
--- a/public/src/forum/unread.js
+++ b/public/src/forum/unread.js
@@ -79,9 +79,10 @@ define(function() {
$('#category-no-topics').remove();
+ html = $(html);
container.append(html);
$('span.timeago').timeago();
- app.makeNumbersHumanReadable($(html).find('.human-readable-number'));
+ app.makeNumbersHumanReadable(html.find('.human-readable-number'));
}
function loadMoreTopics() {
diff --git a/public/templates/admin/redis.tpl b/public/templates/admin/database.tpl
similarity index 62%
rename from public/templates/admin/redis.tpl
rename to public/templates/admin/database.tpl
index aa127ef893..40feb65c12 100644
--- a/public/templates/admin/redis.tpl
+++ b/public/templates/admin/database.tpl
@@ -1,3 +1,6 @@
+
+
+
Redis
@@ -21,4 +24,29 @@
Keyspace Hits {keyspace_hits}
Keyspace Misses {keyspace_misses}
+
+Raw Info
+
+
+
+Mongo
+
+
+
+ Collections {collections}
+ Objects {objects}
+ Avg. Object Size {avgObjSize} kb
+
+ Data Size {dataSize} kb
+ Storage Size {storageSize} kb
+ File Size {fileSize} kb
+
+
+Raw Info
+
+
diff --git a/public/templates/admin/header.tpl b/public/templates/admin/header.tpl
index 786b8bcfaa..66ef0fe982 100644
--- a/public/templates/admin/header.tpl
+++ b/public/templates/admin/header.tpl
@@ -105,7 +105,7 @@
Themes
Plugins
Settings
- Redis
+ Database
Logger
MOTD
diff --git a/public/templates/config.json b/public/templates/config.json
index fa435245ae..45e922c3df 100644
--- a/public/templates/config.json
+++ b/public/templates/config.json
@@ -4,7 +4,7 @@
"^admin/topics.*": "admin/topics",
"^admin/categories.*": "admin/categories",
"^admin/users.*": "admin/users",
- "^admin/redis.*": "admin/redis",
+ "^admin/database.*": "admin/database",
"^admin/index.*": "admin/index",
"^admin/themes.*": "admin/themes",
"^admin/plugins/?$": "admin/plugins",
diff --git a/src/admin/categories.js b/src/admin/categories.js
index 494a767e9c..333a22cf70 100644
--- a/src/admin/categories.js
+++ b/src/admin/categories.js
@@ -1,4 +1,4 @@
-var RDB = require('./../redis'),
+var db = require('./../database'),
utils = require('./../../public/src/utils'),
categories = require('./../categories');
@@ -11,12 +11,12 @@ var RDB = require('./../redis'),
var category = modified[cid];
for (var key in category) {
- RDB.hset('category:' + cid, key, category[key]);
+ db.setObjectField('category:' + cid, key, category[key]);
if (key == 'name') {
// reset slugs if name is updated
var slug = cid + '/' + utils.slugify(category[key]);
- RDB.hset('category:' + cid, 'slug', slug);
+ db.setObjectField('category:' + cid, 'slug', slug);
}
}
diff --git a/src/admin/user.js b/src/admin/user.js
index d9662749f1..0f37d8590e 100644
--- a/src/admin/user.js
+++ b/src/admin/user.js
@@ -1,5 +1,4 @@
-var RDB = require('../redis'),
- utils = require('../../public/src/utils'),
+var utils = require('../../public/src/utils'),
user = require('../user'),
groups = require('../groups');
diff --git a/src/categories.js b/src/categories.js
index 6aff4bfbe7..538bc0d5c3 100644
--- a/src/categories.js
+++ b/src/categories.js
@@ -1,4 +1,4 @@
-var RDB = require('./redis.js'),
+var db = require('./database.js'),
posts = require('./posts.js'),
utils = require('./../public/src/utils.js'),
user = require('./user.js'),
@@ -12,13 +12,13 @@ var RDB = require('./redis.js'),
"use strict";
Categories.create = function(data, callback) {
- RDB.incr('global:next_category_id', function(err, cid) {
+ db.incrObjectField('global', 'nextCid', function(err, cid) {
if (err) {
return callback(err, null);
}
var slug = cid + '/' + utils.slugify(data.name);
- RDB.rpush('categories:cid', cid);
+ db.listAppend('categories:cid', cid);
var category = {
cid: cid,
@@ -33,7 +33,7 @@ var RDB = require('./redis.js'),
order: data.order
};
- RDB.hmset('category:' + cid, category, function(err, data) {
+ db.setObject('category:' + cid, category, function(err, data) {
callback(err, category);
});
});
@@ -134,15 +134,15 @@ var RDB = require('./redis.js'),
};
Categories.getTopicIds = function(cid, start, stop, callback) {
- RDB.zrevrange('categories:' + cid + ':tid', start, stop, callback);
+ db.getSortedSetRevRange('categories:' + cid + ':tid', start, stop, callback);
};
Categories.getActiveUsers = function(cid, callback) {
- RDB.smembers('cid:' + cid + ':active_users', callback);
+ db.getSetMembers('cid:' + cid + ':active_users', callback);
};
Categories.getAllCategories = function(current_user, callback) {
- RDB.lrange('categories:cid', 0, -1, function(err, cids) {
+ db.getListRange('categories:cid', 0, -1, function(err, cids) {
if(err) {
return callback(err);
}
@@ -155,7 +155,7 @@ var RDB = require('./redis.js'),
};
Categories.getModerators = function(cid, callback) {
- RDB.smembers('cid:' + cid + ':moderators', function(err, mods) {
+ db.getSetMembers('cid:' + cid + ':moderators', function(err, mods) {
if (!err) {
if (mods && mods.length) {
user.getMultipleUserFields(mods, ['username'], function(err, moderators) {
@@ -172,7 +172,7 @@ var RDB = require('./redis.js'),
};
Categories.isTopicsRead = function(cid, uid, callback) {
- RDB.zrange('categories:' + cid + ':tid', 0, -1, function(err, tids) {
+ db.getSortedSetRange('categories:' + cid + ':tid', 0, -1, function(err, tids) {
topics.hasReadTopics(tids, uid, function(hasRead) {
@@ -189,31 +189,34 @@ var RDB = require('./redis.js'),
};
Categories.markAsRead = function(cid, uid) {
- RDB.sadd('cid:' + cid + ':read_by_uid', uid);
+ db.setAdd('cid:' + cid + ':read_by_uid', uid);
};
Categories.hasReadCategories = function(cids, uid, callback) {
- var batch = RDB.multi();
+
+ var sets = [];
for (var i = 0, ii = cids.length; i < ii; i++) {
- batch.sismember('cid:' + cids[i] + ':read_by_uid', uid);
+ sets.push('cid:' + cids[i] + ':read_by_uid');
}
- batch.exec(function(err, hasRead) {
+ db.isMemberOfSets(sets, uid, function(err, hasRead) {
callback(hasRead);
});
};
Categories.hasReadCategory = function(cid, uid, callback) {
- RDB.sismember('cid:' + cid + ':read_by_uid', uid, function(err, hasRead) {
- RDB.handle(err);
+ db.isSetMember('cid:' + cid + ':read_by_uid', uid, function(err, hasRead) {
+ if(err) {
+ return callback(false);
+ }
callback(hasRead);
});
};
Categories.getRecentReplies = function(cid, count, callback) {
- RDB.zrevrange('categories:recent_posts:cid:' + cid, 0, (count < 10) ? 10 : count, function(err, pids) {
+ db.getSortedSetRevRange('categories:recent_posts:cid:' + cid, 0, (count < 10) ? 10 : count, function(err, pids) {
if (err) {
winston.err(err);
@@ -242,8 +245,8 @@ var RDB = require('./redis.js'),
return callback(err);
}
- RDB.zrem('categories:recent_posts:cid:' + oldCid, pid);
- RDB.zadd('categories:recent_posts:cid:' + cid, timestamp, pid);
+ db.sortedSetRemove('categories:recent_posts:cid:' + oldCid, pid);
+ db.sortedSetAdd('categories:recent_posts:cid:' + cid, timestamp, pid);
callback(null);
});
}
@@ -283,9 +286,9 @@ var RDB = require('./redis.js'),
};
Categories.getCategoryData = function(cid, callback) {
- RDB.exists('category:' + cid, function(err, exists) {
+ db.exists('category:' + cid, function(err, exists) {
if (exists) {
- RDB.hgetall('category:' + cid, callback);
+ db.getObject('category:' + cid, callback);
} else {
callback(new Error('No category found!'));
}
@@ -293,19 +296,19 @@ var RDB = require('./redis.js'),
};
Categories.getCategoryField = function(cid, field, callback) {
- RDB.hget('category:' + cid, field, callback);
+ db.getObjectField('category:' + cid, field, callback);
};
Categories.getCategoryFields = function(cid, fields, callback) {
- RDB.hmgetObject('category:' + cid, fields, callback);
+ db.getObjectFields('category:' + cid, fields, callback);
};
Categories.setCategoryField = function(cid, field, value, callback) {
- RDB.hset('category:' + cid, field, value, callback);
+ db.setObjectField('category:' + cid, field, value, callback);
};
Categories.incrementCategoryFieldBy = function(cid, field, value, callback) {
- RDB.hincrby('category:' + cid, field, value, callback);
+ db.incrObjectFieldBy('category:' + cid, field, value, callback);
};
Categories.getCategories = function(cids, uid, callback) {
@@ -349,7 +352,7 @@ var RDB = require('./redis.js'),
Categories.isUserActiveIn = function(cid, uid, callback) {
- RDB.lrange('uid:' + uid + ':posts', 0, -1, function(err, pids) {
+ db.getListRange('uid:' + uid + ':posts', 0, -1, function(err, pids) {
if (err) {
return callback(err, null);
}
@@ -387,12 +390,13 @@ var RDB = require('./redis.js'),
};
Categories.addActiveUser = function(cid, uid) {
- if(parseInt(uid, 10))
- RDB.sadd('cid:' + cid + ':active_users', uid);
+ if(parseInt(uid, 10)) {
+ db.setAdd('cid:' + cid + ':active_users', uid);
+ }
};
Categories.removeActiveUser = function(cid, uid) {
- RDB.srem('cid:' + cid + ':active_users', uid);
+ db.setRemove('cid:' + cid + ':active_users', uid);
};
}(exports));
\ No newline at end of file
diff --git a/src/database.js b/src/database.js
new file mode 100644
index 0000000000..71775e8807
--- /dev/null
+++ b/src/database.js
@@ -0,0 +1,14 @@
+
+
+var nconf = require('nconf'),
+ databaseType = nconf.get('database'),
+ winston = require('winston');
+
+if(!databaseType) {
+ winston.info('Database type not set! Run node app --setup');
+ process.exit();
+}
+
+var db = require('./database/' + databaseType);
+
+module.exports = db;
\ No newline at end of file
diff --git a/src/database/mongo.js b/src/database/mongo.js
new file mode 100644
index 0000000000..9408aff75a
--- /dev/null
+++ b/src/database/mongo.js
@@ -0,0 +1,737 @@
+
+
+(function(module) {
+ 'use strict';
+ var mongoClient = require('mongodb').MongoClient,
+ winston = require('winston'),
+ async = require('async'),
+ nconf = require('nconf'),
+ express = require('express'),
+ mongoStore = require('connect-mongo')(express),
+ mongoHost = nconf.get('mongo:host'),
+ db;
+
+ module.init = function(callback) {
+ mongoClient.connect('mongodb://'+ mongoHost + ':' + nconf.get('mongo:port') + '/' + nconf.get('mongo:database'), function(err, _db) {
+ if(err) {
+ winston.error("NodeBB could not connect to your Mongo database. Mongo returned the following error: " + err.message);
+ process.exit();
+ }
+
+ db = _db;
+
+ module.client = db;
+
+ module.sessionStore = new mongoStore({
+ db: db
+ });
+
+
+ if(nconf.get('mongo:password') && nconf.get('mongo:username')) {
+ db.authenticate(nconf.get('mongo:username'), nconf.get('mongo:password'), function (err) {
+ if(err) {
+ winston.error(err.message);
+ process.exit();
+ }
+ createCollections();
+ });
+ } else {
+ createCollections();
+ }
+
+ function createCollections() {
+ db.createCollection('objects', function(err, collection) {
+ if(err) {
+ winston.error("Error creating collection " + err.message);
+ return;
+ }
+ if(collection) {
+ collection.ensureIndex({_key :1, setName:1}, {background:true}, function(err, name){
+ if(err) {
+ winston.error("Error creating index " + err.message);
+ }
+ });
+ }
+ });
+
+ db.createCollection('search', function(err, collection) {
+ if(err) {
+ winston.error("Error creating collection " + err.message);
+ return;
+ }
+ if(collection) {
+ collection.ensureIndex({content:'text'}, {background:true}, function(err, name){
+ if(err) {
+ winston.error("Error creating index " + err.message);
+ }
+ });
+ }
+ });
+
+ callback(null);
+ }
+ });
+ }
+
+
+ //
+ // Exported functions
+ //
+
+ module.searchIndex = function(key, content, id) {
+
+ var data = {
+ id:id,
+ key:key,
+ content:content
+ };
+
+ db.collection('search').update({id:id, key:key}, {$set:data}, {upsert:true, w: 1}, function(err, result) {
+ if(err) {
+ winston.error('Error indexing ' + err.message);
+ }
+ });
+ }
+
+ module.search = function(key, term, callback) {
+
+ db.command({text:"search" , search: term, filter: {key:key} }, function(err, result) {
+ if(err) {
+ return callback(err);
+ }
+
+ if(!result) {
+ return callback(null, []);
+ }
+
+ if(result.results && result.results.length) {
+ var data = result.results.map(function(item) {
+ return item.obj.id;
+ });
+ callback(null, data);
+ } else {
+ callback(null, []);
+ }
+ });
+ }
+
+ module.searchRemove = function(key, id) {
+ db.collection('search').remove({id:id, key:key}, function(err, result) {
+ if(err) {
+ winston.error('Error removing search ' + err.message);
+ }
+ });
+ }
+
+ module.flushdb = function(callback) {
+ db.dropDatabase(function(err, result) {
+ if(err){
+ winston.error(error);
+ if(callback) {
+ return callback(err);
+ }
+ }
+
+ if(callback) {
+ callback(null);
+ }
+ });
+ }
+
+
+ module.getFileName = function(callback) {
+ throw new Error('not-implemented');
+ }
+
+ module.info = function(callback) {
+ db.stats({scale:1024}, function(err, stats) {
+
+ // TODO : if this it not deleted the templates break,
+ // it is a nested object inside stats
+ delete stats.dataFileVersion;
+
+ stats.avgObjSize = (stats.avgObjSize / 1024).toFixed(2);
+
+ stats.raw = JSON.stringify(stats, null, 4);
+
+ stats.mongo = true;
+ //remove this when andrew adds in undefined checking to templates
+ stats.redis = false;
+ callback(err, stats);
+
+ });
+ }
+
+ // key
+
+ module.exists = function(key, callback) {
+ db.collection('objects').findOne({$or:[{_key:key}, {setName:key}]}, function(err, item) {
+ callback(err, item !== undefined && item !== null);
+ });
+ }
+
+ module.delete = function(key, callback) {
+ db.collection('objects').remove({_key:key}, function(err, result) {
+ if(err) {
+ if(callback) {
+ return callback(err);
+ } else {
+ return winston.error(err.message);
+ }
+ }
+
+ if(result === 0) {
+ db.collection('objects').remove({setName:key}, function(err, result) {
+ if(callback) {
+ callback(err, result);
+ }
+ });
+ } else {
+ if(callback) {
+ callback(null, result);
+ }
+ }
+ });
+ }
+
+ module.get = function(key, callback) {
+ module.getObjectField(key, 'value', callback);
+ }
+
+ module.set = function(key, value, callback) {
+ var data = {value:value};
+ module.setObject(key, data, callback);
+ }
+
+ module.keys = function(key, callback) {
+ db.collection('objects').find( { _key: { $regex: key /*, $options: 'i'*/ } }, function(err, result) {
+ callback(err, result);
+ });
+ }
+
+ //hashes
+ function removeHiddenFields(item) {
+ if(item) {
+ if(item._id) {
+ delete item._id;
+ }
+ if(item._key) {
+ delete item._key;
+ }
+ if(item.setName) {
+ delete item.setName;
+ }
+ }
+ return item;
+ }
+
+ module.setObject = function(key, data, callback) {
+ data['_key'] = key;
+ db.collection('objects').update({_key:key}, {$set:data}, {upsert:true, w: 1}, function(err, result) {
+ if(callback) {
+ callback(err, result);
+ }
+ });
+ }
+
+ module.setObjectField = function(key, field, value, callback) {
+ var data = {};
+ // if there is a '.' in the field name it inserts subdocument in mongo, replace '.'s with \uff0E
+ field = field.replace(/\./g, '\uff0E');
+ data[field] = value;
+ db.collection('objects').update({_key:key}, {$set:data}, {upsert:true, w: 1}, function(err, result) {
+ if(callback) {
+ callback(err, result);
+ }
+ });
+ }
+
+ module.getObject = function(key, callback) {
+ db.collection('objects').findOne({_key:key}, function(err, item) {
+ removeHiddenFields(item);
+
+ callback(err, item);
+ });
+ }
+
+ module.getObjects = function(keys, callback) {
+
+ db.collection('objects').find({_key:{$in:keys}}, {_id:0}).toArray(function(err, data) {
+
+ if(err) {
+ return callback(err);
+ }
+
+ var returnData = [],
+ resultIndex = 0;
+
+
+ function findData(key) {
+ if(!data) {
+ return null;
+ }
+
+ for(var i=0; i=0) {
+ /* If redis.host contains a path name character, use the unix dom sock connection. ie, /tmp/redis.sock */
+ redisClient = redis.createClient(nconf.get('redis:host'));
+ } else {
+ /* Else, connect over tcp/ip */
+ redisClient = redis.createClient(nconf.get('redis:port'), nconf.get('redis:host'));
+ }
+
+ module.client = redisClient;
+
+ module.sessionStore = new connectRedis({
+ client: redisClient,
+ ttl: 60 * 60 * 24 * 30
+ });
+
+ reds.createClient = function () {
+ return reds.client || (reds.client = redisClient);
+ };
+
+ var userSearch = reds.createSearch('nodebbusersearch'),
+ postSearch = reds.createSearch('nodebbpostsearch'),
+ topicSearch = reds.createSearch('nodebbtopicsearch');
+
+ if (nconf.get('redis:password')) {
+ redisClient.auth(nconf.get('redis:password'));
+ }
+
+ var db = parseInt(nconf.get('redis:database'), 10);
+
+ if (db){
+ redisClient.select(db, function(error) {
+ if(error) {
+ winston.error("NodeBB could not connect to your Redis database. Redis returned the following error: " + error.message);
+ process.exit();
+ }
+ });
+ }
+
+ module.init = function(callback) {
+ callback(null);
+ }
+
+
+ /*
+ * A possibly more efficient way of doing multiple sismember calls
+ */
+ function sismembers(key, needles, callback) {
+ var tempkey = key + ':temp:' + utils.generateUUID();
+ redisClient.sadd(tempkey, needles, function() {
+ redisClient.sinter(key, tempkey, function(err, data) {
+ redisClient.del(tempkey);
+ callback(err, data);
+ });
+ });
+ };
+
+ //
+ // Exported functions
+ //
+ module.searchIndex = function(key, content, id) {
+ if(key === 'post') {
+ postSearch.index(content, id);
+ } else if(key === 'topic') {
+ topicSearch.index(content, id);
+ } else if(key === 'user') {
+ userSearch.index(content, id);
+ }
+ }
+
+ module.search = function(key, term, callback) {
+ function search(searchObj, callback) {
+ searchObj
+ .query(term).type('or')
+ .end(callback);
+ }
+
+ if(key === 'post') {
+ search(postSearch, callback);
+ } else if(key === 'topic') {
+ search(topicSearch, callback);
+ } else if(key === 'user') {
+ search(userSearch, callback);
+ }
+ }
+
+ module.searchRemove = function(key, id) {
+ if(key === 'post') {
+ postSearch.remove(id);
+ } else if(key === 'topic') {
+ topicSearch.remove(id);
+ } else if(key === 'user') {
+ userSearch.remove(id);
+ }
+ }
+
+ module.flushdb = function(callback) {
+ redisClient.send_command('flushdb', [], function(err) {
+ if(err){
+ winston.error(error);
+ return callback(err);
+ }
+ callback(null);
+ });
+ }
+
+ module.getFileName = function(callback) {
+ var multi = redisClient.multi();
+
+ multi.config('get', 'dir');
+ multi.config('get', 'dbfilename');
+ multi.exec(function (err, results) {
+ if (err) {
+ return callback(err);
+ }
+
+ results = results.reduce(function (memo, config) {
+ memo[config[0]] = config[1];
+ return memo;
+ }, {});
+
+ var dbFile = path.join(results.dir, results.dbfilename);
+ callback(null, dbFile);
+ });
+ }
+
+
+ module.info = function(callback) {
+ redisClient.info(function (err, data) {
+ if(err) {
+ return callback(err);
+ }
+
+ data = data.split("\r\n");
+ var redisData = {};
+
+ for (var i in data) {
+
+ if (data[i].indexOf(':') == -1 || !data[i])
+ continue;
+
+ try {
+ data[i] = data[i].replace(/:/, "\":\"");
+ var json = "{\"" + data[i] + "\"}";
+
+ var jsonObject = JSON.parse(json);
+ for (var key in jsonObject) {
+ redisData[key] = jsonObject[key];
+ }
+ } catch (err) {
+ winston.warn('can\'t parse redis status variable, ignoring', i, data[i], err);
+ }
+ }
+ redisData.raw = JSON.stringify(redisData, null, 4);
+ redisData.redis = true;
+ //remove this when andrew adds in undefined checking to templates
+ redisData.mongo = false;
+
+ callback(null, redisData);
+ });
+ }
+
+ // key
+
+ module.exists = function(key, callback) {
+ redisClient.exists(key, function(err, exists) {
+ callback(err, exists === 1);
+ });
+ }
+
+ module.delete = function(key, callback) {
+ redisClient.del(key, callback);
+ }
+
+ module.get = function(key, callback) {
+ redisClient.get(key, callback);
+ }
+
+ module.set = function(key, value, callback) {
+ redisClient.set(key, value, callback);
+ }
+
+ module.keys = function(key, callback) {
+ redisClient.keys(key, callback);
+ }
+
+ //hashes
+
+ module.setObject = function(key, data, callback) {
+ // TODO: this crashes if callback isnt supplied -baris
+ redisClient.hmset(key, data, function(err, res) {
+ if(callback) {
+ callback(err, res);
+ }
+ });
+ }
+
+ module.setObjectField = function(key, field, value, callback) {
+ redisClient.hset(key, field, value, callback);
+ }
+
+ module.getObject = function(key, callback) {
+ redisClient.hgetall(key, callback);
+ }
+
+ module.getObjects = function(keys, callback) {
+ var multi = redisClient.multi();
+
+ for(var x=0; x 0) {
async.map(gids, function (gid, next) {
Groups.get(gid, {
@@ -15,7 +15,7 @@
}, function (err, groups) {
// Remove deleted and hidden groups from this list
callback(err, groups.filter(function (group) {
- if (group.deleted === '1' || group.hidden === '1') {
+ if (parseInt(group.deleted, 10) === 1 || parseInt(group.hidden, 10) === 1) {
return false;
} else {
return true;
@@ -31,17 +31,17 @@
Groups.get = function(gid, options, callback) {
async.parallel({
base: function (next) {
- RDB.hgetall('gid:' + gid, next);
+ db.getObject('gid:' + gid, next);
},
users: function (next) {
- RDB.smembers('gid:' + gid + ':members', function (err, uids) {
+ db.getSetMembers('gid:' + gid + ':members', function (err, uids) {
if (options.expand) {
if (err) {
return next(err);
}
async.map(uids, function (uid, next) {
- User.getUserData(uid, next);
+ user.getUserData(uid, next);
}, function (err, users) {
next(err, users);
});
@@ -58,7 +58,7 @@
results.base.count = results.users.length;
results.base.members = results.users;
- results.base.deletable = (results.base.gid !== '1');
+ results.base.deletable = parseInt(results.base.gid, 10) !== 1;
callback(err, results.base);
});
@@ -75,19 +75,19 @@
};
Groups.isDeleted = function(gid, callback) {
- RDB.hget('gid:' + gid, 'deleted', function(err, deleted) {
- callback(err, deleted === '1');
+ db.getObjectField('gid:' + gid, 'deleted', function(err, deleted) {
+ callback(err, parseInt(deleted, 10) === 1);
});
};
Groups.getGidFromName = function(name, callback) {
- RDB.hget('group:gid', name, callback);
+ db.getObjectField('group:gid', name, callback);
};
Groups.isMember = function(uid, gid, callback) {
Groups.isDeleted(gid, function(err, deleted) {
if (!deleted) {
- RDB.sismember('gid:' + gid + ':members', uid, callback);
+ db.isSetMember('gid:' + gid + ':members', uid, callback);
} else {
callback(err, false);
}
@@ -107,7 +107,7 @@
};
Groups.isEmpty = function(gid, callback) {
- RDB.scard('gid:' + gid + ':members', function(err, numMembers) {
+ db.setCount('gid:' + gid + ':members', function(err, numMembers) {
callback(err, numMembers === 0);
});
};
@@ -125,7 +125,7 @@
Groups.exists = function(name, callback) {
async.parallel({
exists: function(next) {
- RDB.hexists('group:gid', name, next);
+ db.isObjectField('group:gid', name, next);
},
deleted: function(next) {
Groups.getGidFromName(name, function(err, gid) {
@@ -144,19 +144,23 @@
Groups.exists(name, function (err, exists) {
if (!exists) {
- RDB.incr('next_gid', function (err, gid) {
- RDB.multi()
- .hset('group:gid', name, gid)
- .hmset('gid:' + gid, {
+ db.incrObjectField('global', 'nextGid', function (err, gid) {
+ db.setObjectField('group:gid', name, gid, function(err) {
+
+ var groupData = {
gid: gid,
name: name,
description: description,
deleted: '0',
hidden: '0'
- })
- .exec(function (err) {
+ };
+
+ db.setObject('gid:' + gid, groupData, function(err) {
+
Groups.get(gid, {}, callback);
+
});
+ });
});
} else {
callback(new Error('group-exists'));
@@ -171,22 +175,24 @@
};
Groups.update = function(gid, values, callback) {
- RDB.exists('gid:' + gid, function (err, exists) {
+ db.exists('gid:' + gid, function (err, exists) {
console.log('exists?', gid, exists, values);
if (!err && exists) {
- RDB.hmset('gid:' + gid, values, callback);
+ db.setObject('gid:' + gid, values, callback);
} else {
- if (callback) callback(new Error('gid-not-found'));
+ if (callback) {
+ callback(new Error('gid-not-found'));
+ }
}
});
};
Groups.destroy = function(gid, callback) {
- RDB.hset('gid:' + gid, 'deleted', '1', callback);
+ db.setObjectField('gid:' + gid, 'deleted', '1', callback);
};
Groups.join = function(gid, uid, callback) {
- RDB.sadd('gid:' + gid + ':members', uid, callback);
+ db.setAdd('gid:' + gid + ':members', uid, callback);
};
Groups.joinByGroupName = function(groupName, uid, callback) {
@@ -210,7 +216,7 @@
};
Groups.leave = function(gid, uid, callback) {
- RDB.srem('gid:' + gid + ':members', uid, callback);
+ db.setRemove('gid:' + gid + ':members', uid, callback);
};
Groups.leaveByGroupName = function(groupName, uid, callback) {
@@ -225,30 +231,36 @@
Groups.prune = function(callback) {
// Actually deletes groups (with the deleted flag) from the redis database
- RDB.hvals('group:gid', function (err, gids) {
- var multi = RDB.multi(),
- groupsDeleted = 0;
+ db.getObjectValues('group:gid', function (err, gids) {
+ var groupsDeleted = 0;
async.each(gids, function(gid, next) {
Groups.get(gid, {}, function(err, groupObj) {
- if (!err && groupObj.deleted === '1') {
- multi.hdel('group:gid', groupObj.name);
- multi.del('gid:' + gid);
- groupsDeleted++;
+ if(err) {
+ return next(err);
}
- next(null);
+ if (parseInt(groupObj.deleted, 10) === 1) {
+
+ db.deleteObjectField('group:gid', groupObj.name, function(err) {
+ db.delete('gid:' + gid, function(err) {
+ groupsDeleted++;
+ next(null);
+ });
+ });
+ } else {
+ next(null);
+ }
});
}, function(err) {
- multi.exec(function(err) {
- if (!err && process.env.NODE_ENV === 'development') {
- winston.info('[groups.prune] Pruned ' + groupsDeleted + ' deleted groups from Redis');
- }
- callback(err);
- });
+ if (!err && process.env.NODE_ENV === 'development') {
+ winston.info('[groups.prune] Pruned ' + groupsDeleted + ' deleted groups from Redis');
+ }
+
+ callback(err);
});
});
};
-
+
}(module.exports));
diff --git a/src/install.js b/src/install.js
index 3f12f4b9fb..601fdff077 100644
--- a/src/install.js
+++ b/src/install.js
@@ -19,8 +19,8 @@ var async = require('async'),
name: 'port',
description: 'Port number of your NodeBB',
'default': nconf.get('port') || 4567,
- pattern: /[0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]/,
- message: 'Please enter a value betweeen 1 and 65535'
+ pattern: /[0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]/,
+ message: 'Please enter a value betweeen 1 and 65535'
}, {
name: 'use_port',
description: 'Use a port number to access NodeBB?',
@@ -32,6 +32,15 @@ var async = require('async'),
description: 'Please enter a NodeBB secret',
'default': nconf.get('secret') || utils.generateUUID()
}, {
+ name: 'bind_address',
+ description: 'IP or Hostname to bind to',
+ 'default': nconf.get('bind_address') || '0.0.0.0'
+ }, {
+ name: 'database',
+ description: 'Which database to use',
+ 'default': nconf.get('database') || 'redis'
+ }],
+ redisQuestions : [{
name: 'redis:host',
description: 'Host IP or address of your Redis instance',
'default': nconf.get('redis:host') || '127.0.0.1'
@@ -46,11 +55,27 @@ var async = require('async'),
name: "redis:database",
description: "Which database to use (0..n)",
'default': nconf.get('redis:database') || 0
+ }],
+ mongoQuestions : [{
+ name: 'mongo:host',
+ description: 'Host IP or address of your MongoDB instance',
+ 'default': nconf.get('mongo:host') || '127.0.0.1'
}, {
- name: 'bind_address',
- description: 'IP or Hostname to bind to',
- 'default': nconf.get('bind_address') || '0.0.0.0'
+ name: 'mongo:port',
+ description: 'Host port of your MongoDB instance',
+ 'default': nconf.get('mongo:port') || 27017
+ }, {
+ name: 'mongo:user',
+ description: 'MongoDB username'
+ }, {
+ name: 'mongo:password',
+ description: 'Password of your MongoDB database'
+ }, {
+ name: "mongo:database",
+ description: "Which database to use (0..n)",
+ 'default': nconf.get('mongo:database') || 0
}],
+
setup: function (callback) {
async.series([
function(next) {
@@ -74,7 +99,9 @@ var async = require('async'),
if (!setupVal['admin:email']) winston.error(' admin:email');
process.exit();
}
- } else next();
+ } else {
+ next();
+ }
},
function (next) {
var success = function(err, config) {
@@ -82,36 +109,58 @@ var async = require('async'),
return next(new Error('aborted'));
}
- // Translate redis properties into redis object
- config.redis = {
- host: config['redis:host'],
- port: config['redis:port'],
- password: config['redis:password'],
- database: config['redis:database']
- };
- delete config['redis:host'];
- delete config['redis:port'];
- delete config['redis:password'];
- delete config['redis:database'];
-
- // Add hardcoded values
- config.bcrypt_rounds = 12;
- config.upload_path = '/public/uploads';
- config.use_port = (config.use_port.slice(0, 1) === 'y') ? true : false;
-
- 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 = {
- relative_path: relative_path
- };
-
- server_conf.base_url = protocol + '//' + host;
- server_conf.relative_path = relative_path;
-
- install.save(server_conf, client_conf, next);
+ function dbQuestionsSuccess(err, databaseConfig) {
+ if (!databaseConfig) {
+ return next(new Error('aborted'));
+ }
+
+ // Translate redis properties into redis object
+ if(config.database === 'redis') {
+ config.redis = {
+ host: databaseConfig['redis:host'],
+ port: databaseConfig['redis:port'],
+ password: databaseConfig['redis:password'],
+ database: databaseConfig['redis:database']
+ };
+ } else if (config.database === 'mongo') {
+ config.mongo = {
+ host: databaseConfig['mongo:host'],
+ port: databaseConfig['mongo:port'],
+ password: databaseConfig['mongo:password'],
+ database: databaseConfig['mongo:database']
+ };
+ } else {
+ return next(new Error('unknown database : ' + config.database));
+ }
+
+ config.bcrypt_rounds = 12;
+ config.upload_path = '/public/uploads';
+ config.use_port = (config.use_port.slice(0, 1) === 'y') ? true : false;
+
+ 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 = {
+ relative_path: relative_path
+ };
+
+ server_conf.base_url = protocol + '//' + host;
+ server_conf.relative_path = relative_path;
+
+ install.save(server_conf, client_conf, function(err) {
+ require('./database').init(next);
+ });
+ }
+
+ if(config.database === 'redis') {
+ prompt.get(install.redisQuestions, dbQuestionsSuccess);
+ } else if(config.database === 'mongo') {
+ prompt.get(install.mongoQuestions, dbQuestionsSuccess);
+ } else {
+ return next(new Error('unknown database : ' + config.database));
+ }
};
// prompt prepends "prompt: " to questions, let's clear that.
@@ -119,8 +168,9 @@ var async = require('async'),
prompt.message = '';
prompt.delimiter = '';
- if (!install.values) prompt.get(install.questions, success);
- else {
+ if (!install.values) {
+ prompt.get(install.questions, success);
+ } else {
// Use provided values, fall back to defaults
var config = {},
question, x, numQ;
@@ -252,9 +302,7 @@ var async = require('async'),
}, next);
},
function (next) {
- // Upgrading schema
- var Upgrade = require('./upgrade');
- Upgrade.upgrade(next);
+ require('./upgrade').upgrade(next);
}
], function (err) {
if (err) {
@@ -343,8 +391,9 @@ var async = require('async'),
// Add the password questions
questions = questions.concat(passwordQuestions);
- if (!install.values) prompt.get(questions, success);
- else {
+ if (!install.values) {
+ prompt.get(questions, success);
+ } else {
var results = {
username: install.values['admin:username'],
email: install.values['admin:email'],
@@ -375,10 +424,10 @@ var async = require('async'),
file: path.join(__dirname, '..', 'config.json')
});
- var RDB = require('./redis');
+ /*var RDB = require('./redis');
reds.createClient = function () {
return reds.client || (reds.client = RDB);
- };
+ };*/
callback(err);
});
diff --git a/src/login.js b/src/login.js
index 51f3ed5472..c4eb226586 100644
--- a/src/login.js
+++ b/src/login.js
@@ -1,9 +1,9 @@
-var user = require('./user.js'),
+var user = require('./user'),
bcrypt = require('bcrypt'),
- RDB = require('./redis.js'),
+ db = require('./database'),
path = require('path'),
winston = require('winston'),
- utils = require('./../public/src/utils.js');
+ utils = require('./../public/src/utils');
(function(Login) {
@@ -28,7 +28,7 @@ var user = require('./user.js'),
user.getUserFields(uid, ['password', 'banned'], function(err, userData) {
if (err) return next(err);
- if (userData.banned && userData.banned === '1') {
+ if (userData.banned && parseInt(userData.banned, 10) === 1) {
return next({
status: "error",
message: "user-banned"
@@ -58,7 +58,11 @@ var user = require('./user.js'),
}
Login.loginViaTwitter = function(twid, handle, photos, callback) {
- user.getUidByTwitterId(twid, function(uid) {
+ user.getUidByTwitterId(twid, function(err, uid) {
+ if(err) {
+ return callback(err);
+ }
+
if (uid !== null) {
// Existing User
callback(null, {
@@ -67,32 +71,36 @@ var user = require('./user.js'),
} else {
// New User
user.create(handle, undefined, undefined, function(err, uid) {
- if (err !== null) {
- callback(err);
- } else {
- // Save twitter-specific information to the user
- user.setUserField(uid, 'twid', twid);
- RDB.hset('twid:uid', twid, uid);
-
- // Save their photo, if present
- if (photos && photos.length > 0) {
- var photoUrl = photos[0].value;
- photoUrl = path.dirname(photoUrl) + '/' + path.basename(photoUrl, path.extname(photoUrl)).slice(0, -6) + 'bigger' + path.extname(photoUrl);
- user.setUserField(uid, 'uploadedpicture', photoUrl);
- user.setUserField(uid, 'picture', photoUrl);
- }
+ if(err) {
+ return callback(err);
+ }
- callback(null, {
- uid: uid
- });
+ // Save twitter-specific information to the user
+ user.setUserField(uid, 'twid', twid);
+ db.setObjectField('twid:uid', twid, uid);
+
+ // Save their photo, if present
+ if (photos && photos.length > 0) {
+ var photoUrl = photos[0].value;
+ photoUrl = path.dirname(photoUrl) + '/' + path.basename(photoUrl, path.extname(photoUrl)).slice(0, -6) + 'bigger' + path.extname(photoUrl);
+ user.setUserField(uid, 'uploadedpicture', photoUrl);
+ user.setUserField(uid, 'picture', photoUrl);
}
+
+ callback(null, {
+ uid: uid
+ });
});
}
});
}
Login.loginViaGoogle = function(gplusid, handle, email, callback) {
- user.getUidByGoogleId(gplusid, function(uid) {
+ user.getUidByGoogleId(gplusid, function(err, uid) {
+ if(err) {
+ return callback(err);
+ }
+
if (uid !== null) {
// Existing User
callback(null, {
@@ -103,27 +111,39 @@ var user = require('./user.js'),
var success = function(uid) {
// Save google-specific information to the user
user.setUserField(uid, 'gplusid', gplusid);
- RDB.hset('gplusid:uid', gplusid, uid);
+ db.setObjectField('gplusid:uid', gplusid, uid);
callback(null, {
uid: uid
});
}
- user.getUidByEmail(email, function(uid) {
+ user.getUidByEmail(email, function(err, uid) {
+ if(err) {
+ return callback(err);
+ }
+
if (!uid) {
user.create(handle, undefined, email, function(err, uid) {
- if (err !== null) {
- callback(err);
- } else success(uid);
+ if(err) {
+ return callback(err);
+ }
+
+ success(uid);
});
- } else success(uid); // Existing account -- merge
+ } else {
+ success(uid); // Existing account -- merge
+ }
});
}
});
}
Login.loginViaFacebook = function(fbid, name, email, callback) {
- user.getUidByFbid(fbid, function(uid) {
+ user.getUidByFbid(fbid, function(err, uid) {
+ if(err) {
+ return callback(err);
+ }
+
if (uid !== null) {
// Existing User
callback(null, {
@@ -134,20 +154,28 @@ var user = require('./user.js'),
var success = function(uid) {
// Save facebook-specific information to the user
user.setUserField(uid, 'fbid', fbid);
- RDB.hset('fbid:uid', fbid, uid);
+ db.setObjectField('fbid:uid', fbid, uid);
callback(null, {
uid: uid
});
}
- user.getUidByEmail(email, function(uid) {
+ user.getUidByEmail(email, function(err, uid) {
+ if(err) {
+ return callback(err);
+ }
+
if (!uid) {
user.create(name, undefined, email, function(err, uid) {
- if (err !== null) {
- callback(err);
- } else success(uid);
+ if(err) {
+ return callback(err);
+ }
+
+ success(uid);
});
- } else success(uid); // Existing account -- merge
+ } else {
+ success(uid); // Existing account -- merge
+ }
});
}
});
diff --git a/src/messaging.js b/src/messaging.js
index e6275ec9ed..948f715448 100644
--- a/src/messaging.js
+++ b/src/messaging.js
@@ -1,4 +1,4 @@
-var RDB = require('./redis'),
+var db = require('./database'),
async = require('async'),
user = require('./user');
@@ -14,9 +14,10 @@ var RDB = require('./redis'),
Messaging.addMessage = function(fromuid, touid, content, callback) {
var uids = sortUids(fromuid, touid);
- RDB.incr('global:next_message_id', function(err, mid) {
- if (err)
+ db.incrObjectField('global', 'nextMid', function(err, mid) {
+ if (err) {
return callback(err, null);
+ }
var message = {
content: content,
@@ -25,8 +26,8 @@ var RDB = require('./redis'),
touid: touid
};
- RDB.hmset('message:' + mid, message);
- RDB.rpush('messages:' + uids[0] + ':' + uids[1], mid);
+ db.setObject('message:' + mid, message);
+ db.listAppend('messages:' + uids[0] + ':' + uids[1], mid);
Messaging.updateChatTime(fromuid, touid);
Messaging.updateChatTime(touid, fromuid);
@@ -37,9 +38,10 @@ var RDB = require('./redis'),
Messaging.getMessages = function(fromuid, touid, callback) {
var uids = sortUids(fromuid, touid);
- RDB.lrange('messages:' + uids[0] + ':' + uids[1], 0, -1, function(err, mids) {
- if (err)
+ db.getListRange('messages:' + uids[0] + ':' + uids[1], 0, -1, function(err, mids) {
+ if (err) {
return callback(err, null);
+ }
if (!mids || !mids.length) {
return callback(null, []);
@@ -51,14 +53,16 @@ var RDB = require('./redis'),
var messages = [];
function getMessage(mid, next) {
- RDB.hgetall('message:' + mid, function(err, message) {
- if (err)
+ db.getObject('message:' + mid, function(err, message) {
+ if (err) {
return next(err);
+ }
- if (message.fromuid === fromuid)
+ if (message.fromuid === fromuid) {
message.content = 'You : ' + message.content;
- else
+ } else {
message.content = tousername + ' : ' + message.content;
+ }
messages.push(message);
next(null);
@@ -66,8 +70,9 @@ var RDB = require('./redis'),
}
async.eachSeries(mids, getMessage, function(err) {
- if (err)
+ if (err) {
return callback(err, null);
+ }
callback(null, messages);
});
@@ -76,7 +81,7 @@ var RDB = require('./redis'),
}
Messaging.updateChatTime = function(uid, toUid, callback) {
- RDB.zadd('uid:' + uid + ':chats', Date.now(), toUid, function(err) {
+ db.sortedSetAdd('uid:' + uid + ':chats', Date.now(), toUid, function(err) {
if (callback) {
callback(err);
}
@@ -84,7 +89,7 @@ var RDB = require('./redis'),
};
Messaging.getRecentChats = function(uid, callback) {
- RDB.zrevrange('uid:' + uid + ':chats', 0, 9, function(err, uids) {
+ db.getSortedSetRevRange('uid:' + uid + ':chats', 0, 9, function(err, uids) {
if (!err) {
user.getMultipleUserFields(uids, ['username', 'picture', 'uid'], callback);
} else {
diff --git a/src/meta.js b/src/meta.js
index a5e50fd552..fa50858515 100644
--- a/src/meta.js
+++ b/src/meta.js
@@ -1,11 +1,13 @@
-var utils = require('./../public/src/utils.js'),
- RDB = require('./redis.js'),
- plugins = require('./plugins'),
- async = require('async'),
+var fs = require('fs'),
path = require('path'),
- fs = require('fs'),
+ async = require('async'),
winston = require('winston'),
- nconf = require('nconf');
+ nconf = require('nconf'),
+
+ utils = require('./../public/src/utils'),
+ db = require('./database'),
+ plugins = require('./plugins');
+
(function (Meta) {
Meta.config = {};
@@ -15,33 +17,34 @@ var utils = require('./../public/src/utils.js'),
delete Meta.config;
Meta.configs.list(function (err, config) {
- if (!err) {
- Meta.config = config;
- callback();
- } else {
+ if(err) {
winston.error(err);
+ return callback(err);
}
+
+ Meta.config = config;
+ callback();
});
},
list: function (callback) {
- RDB.hgetall('config', function (err, config) {
- if (!err) {
- config = config || {};
- config.status = 'ok';
- callback(err, config);
- } else {
- callback(new Error('could-not-read-config'));
+ db.getObject('config', function (err, config) {
+ if(err) {
+ return callback(new Error('could-not-read-config'));
}
+
+ config = config || {};
+ config.status = 'ok';
+ callback(err, config);
});
},
get: function (field, callback) {
- RDB.hget('config', field, callback);
+ db.getObjectField('config', field, callback);
},
getFields: function (fields, callback) {
- RDB.hmgetObject('config', fields, callback);
+ db.getObjectFields('config', fields, callback);
},
set: function (field, value, callback) {
- RDB.hset('config', field, value, function (err, res) {
+ db.setObjectField('config', field, value, function(err, res) {
if (callback) {
if(!err && Meta.config)
Meta.config[field] = value;
@@ -50,7 +53,7 @@ var utils = require('./../public/src/utils.js'),
});
},
setOnEmpty: function (field, value, callback) {
- this.get(field, function (err, curValue) {
+ Meta.configs.get(field, function (err, curValue) {
if (!curValue) {
Meta.configs.set(field, value, callback);
} else {
@@ -59,7 +62,7 @@ var utils = require('./../public/src/utils.js'),
});
},
remove: function (field) {
- RDB.hdel('config', field);
+ db.deleteObjectField('config', field);
}
};
@@ -125,7 +128,7 @@ var utils = require('./../public/src/utils.js'),
themeData['theme:staticDir'] = config.staticDir ? config.staticDir : '';
themeData['theme:templates'] = config.templates ? config.templates : '';
- RDB.hmset('config', themeData, next);
+ db.setObject('config', themeData, next);
}
], function(err) {
callback(err);
@@ -134,7 +137,7 @@ var utils = require('./../public/src/utils.js'),
case 'bootswatch':
themeData['theme:src'] = data.src;
- RDB.hmset('config', themeData, callback);
+ db.setObject('config', themeData, callback);
break;
}
}
@@ -257,11 +260,16 @@ var utils = require('./../public/src/utils.js'),
}),
minified;
- if (process.env.NODE_ENV === 'development') winston.info('Minifying client-side libraries');
+ if (process.env.NODE_ENV === 'development') {
+ winston.info('Minifying client-side libraries');
+ }
+
minified = uglifyjs.minify(jsPaths);
fs.writeFile(Meta.js.minFile, minified.code, function (err) {
if (!err) {
- if (process.env.NODE_ENV === 'development') winston.info('Minified client-side libraries');
+ if (process.env.NODE_ENV === 'development') {
+ winston.info('Minified client-side libraries');
+ }
callback();
} else {
winston.error('Problem minifying client-side libraries, exiting.');
@@ -273,23 +281,7 @@ var utils = require('./../public/src/utils.js'),
Meta.db = {
getFile: function (callback) {
- var multi = RDB.multi();
-
- multi.config('get', 'dir');
- multi.config('get', 'dbfilename');
- multi.exec(function (err, results) {
- if (err) {
- return callback(err);
- } else {
- results = results.reduce(function (memo, config) {
- memo[config[0]] = config[1];
- return memo;
- }, {});
-
- var dbFile = path.join(results.dir, results.dbfilename);
- callback(null, dbFile);
- }
- });
+ db.getFileName(callback);
}
};
}(exports));
\ No newline at end of file
diff --git a/src/notifications.js b/src/notifications.js
index cff6fda563..030478701b 100644
--- a/src/notifications.js
+++ b/src/notifications.js
@@ -1,9 +1,9 @@
-var RDB = require('./redis'),
- async = require('async'),
- utils = require('../public/src/utils'),
+var async = require('async'),
winston = require('winston'),
cron = require('cron').CronJob,
+ db = require('./database'),
+ utils = require('../public/src/utils'),
websockets = require('./websockets');
@@ -19,28 +19,21 @@ var RDB = require('./redis'),
};
Notifications.get = function(nid, uid, callback) {
- RDB.multi()
- .hmget('notifications:' + nid, 'text', 'score', 'path', 'datetime', 'uniqueId')
- .zrank('uid:' + uid + ':notifications:read', nid)
- .exists('notifications:' + nid)
- .exec(function(err, results) {
- var notification = results[0],
- readIdx = results[1];
-
- if (!results[2]) {
- return callback(null);
- }
- callback({
- nid: nid,
- text: notification[0],
- score: notification[1],
- path: notification[2],
- datetime: notification[3],
- uniqueId: notification[4],
- read: readIdx !== null ? true : false
+ db.exists('notifications:' + nid, function(err, exists) {
+ if(!exists) {
+ return callback(null);
+ }
+
+ db.sortedSetRank('uid:' + uid + ':notifications:read', nid, function(err, rank) {
+
+ db.getObjectFields('notifications:' + nid, ['nid', 'text', 'score', 'path', 'datetime', 'uniqueId'], function(err, notification) {
+
+ notification.read = rank !== null ? true:false;
+ callback(notification);
});
});
+ });
};
Notifications.create = function(text, path, uniqueId, callback) {
@@ -50,9 +43,9 @@ var RDB = require('./redis'),
* (un)read list contains the same uniqueId, it will be removed, and
* the new one put in its place.
*/
- RDB.incr('notifications:next_nid', function(err, nid) {
- RDB.sadd('notifications', nid);
- RDB.hmset('notifications:' + nid, {
+ db.incrObjectField('global', 'nextNid', function(err, nid) {
+ db.setAdd('notifications', nid);
+ db.setObject('notifications:' + nid, {
text: text || '',
path: path || null,
datetime: Date.now(),
@@ -66,16 +59,14 @@ var RDB = require('./redis'),
};
function destroy(nid) {
- var multi = RDB.multi();
- multi.del('notifications:' + nid);
- multi.srem('notifications', nid);
-
- multi.exec(function(err) {
- if (err) {
- winston.error('Problem deleting expired notifications. Stack follows.');
- winston.error(err.stack);
- }
+ db.delete('notifications:' + nid, function(err, result) {
+ db.setRemove('notifications', nid, function(err, result) {
+ if (err) {
+ winston.error('Problem deleting expired notifications. Stack follows.');
+ winston.error(err.stack);
+ }
+ });
});
}
@@ -92,7 +83,7 @@ var RDB = require('./redis'),
if (parseInt(uids[x], 10) > 0) {
(function(uid) {
remove_by_uniqueId(notif_data.uniqueId, uid, function() {
- RDB.zadd('uid:' + uid + ':notifications:unread', notif_data.datetime, nid);
+ db.sortedSetAdd('uid:' + uid + ':notifications:unread', notif_data.datetime, nid);
websockets.in('uid_' + uid).emit('event:new_notification');
@@ -109,12 +100,12 @@ var RDB = require('./redis'),
function remove_by_uniqueId(uniqueId, uid, callback) {
async.parallel([
function(next) {
- RDB.zrange('uid:' + uid + ':notifications:unread', 0, -1, function(err, nids) {
+ db.getSortedSetRange('uid:' + uid + ':notifications:unread', 0, -1, function(err, nids) {
if (nids && nids.length > 0) {
async.each(nids, function(nid, next) {
Notifications.get(nid, uid, function(nid_info) {
if (nid_info.uniqueId === uniqueId) {
- RDB.zrem('uid:' + uid + ':notifications:unread', nid);
+ db.sortedSetRemove('uid:' + uid + ':notifications:unread', nid);
}
next();
@@ -128,12 +119,12 @@ var RDB = require('./redis'),
});
},
function(next) {
- RDB.zrange('uid:' + uid + ':notifications:read', 0, -1, function(err, nids) {
+ db.getSortedSetRange('uid:' + uid + ':notifications:read', 0, -1, function(err, nids) {
if (nids && nids.length > 0) {
async.each(nids, function(nid, next) {
Notifications.get(nid, uid, function(nid_info) {
if (nid_info && nid_info.uniqueId === uniqueId) {
- RDB.zrem('uid:' + uid + ':notifications:read', nid);
+ db.sortedSetRemove('uid:' + uid + ':notifications:read', nid);
}
next();
@@ -156,8 +147,8 @@ var RDB = require('./redis'),
Notifications.mark_read = function(nid, uid, callback) {
if (parseInt(uid, 10) > 0) {
Notifications.get(nid, uid, function(notif_data) {
- RDB.zrem('uid:' + uid + ':notifications:unread', nid);
- RDB.zadd('uid:' + uid + ':notifications:read', notif_data.datetime, nid);
+ db.sortedSetRemove('uid:' + uid + ':notifications:unread', nid);
+ db.sortedSetAdd('uid:' + uid + ':notifications:read', notif_data.datetime, nid);
if (callback) {
callback();
}
@@ -184,7 +175,7 @@ var RDB = require('./redis'),
};
Notifications.mark_all_read = function(uid, callback) {
- RDB.zrange('uid:' + uid + ':notifications:unread', 0, 10, function(err, nids) {
+ db.getSortedSetRange('uid:' + uid + ':notifications:unread', 0, 10, function(err, nids) {
if (err) {
return callback(err);
}
@@ -200,6 +191,7 @@ var RDB = require('./redis'),
};
Notifications.prune = function(cutoff) {
+
if (process.env.NODE_ENV === 'development') {
winston.info('[notifications.prune] Removing expired notifications from the database.');
}
@@ -215,12 +207,20 @@ var RDB = require('./redis'),
async.parallel({
"inboxes": function(next) {
- RDB.keys('uid:*:notifications:unread', next);
+ db.getSortedSetRange('users:joindate', 0, -1, function(err, uids) {
+ if(err) {
+ return next(err);
+ }
+ uids = uids.map(function(uid) {
+ return 'uid:' + uid + ':notifications:unread';
+ });
+ next(null, uids);
+ });
},
- "nids": function(next) {
- RDB.smembers('notifications', function(err, nids) {
+ "expiredNids": function(next) {
+ db.getSetMembers('notifications', function(err, nids) {
async.filter(nids, function(nid, next) {
- RDB.hget('notifications:' + nid, 'datetime', function(err, datetime) {
+ db.getObjectField('notifications:' + nid, 'datetime', function(err, datetime) {
if (parseInt(datetime, 10) < cutoffTime) {
next(true);
} else {
@@ -233,43 +233,39 @@ var RDB = require('./redis'),
});
}
}, function(err, results) {
- if (!err) {
- var numInboxes = results.inboxes.length,
- x;
+ if(err) {
+ if (process.env.NODE_ENV === 'development') {
+ winston.error('[notifications.prune] Ran into trouble pruning expired notifications. Stack trace to follow.');
+ winston.error(err.stack);
+ }
+ return;
+ }
- async.eachSeries(results.nids, function(nid, next) {
- var multi = RDB.multi();
+ async.eachSeries(results.expiredNids, function(nid, next) {
- for(x=0;x 0) {
+ async.each(plugins, function(plugin, next) {
+ var modulePath = path.join(__dirname, '../node_modules/', plugin);
+ if (fs.existsSync(modulePath)) {
+ Plugins.loadPlugin(modulePath, next);
+ } else {
+ if (global.env === 'development') {
+ winston.warn('[plugins] Plugin \'' + plugin + '\' not found');
+ }
+ next(); // Ignore this plugin silently
+ }
+ }, next);
+ } else next();
+ },
+ function(next) {
+ if (global.env === 'development') winston.info('[plugins] Sorting hooks to fire in priority sequence');
+ Object.keys(Plugins.loadedHooks).forEach(function(hook) {
+ var hooks = Plugins.loadedHooks[hook];
+ hooks = hooks.sort(function(a, b) {
+ return a.priority - b.priority;
+ });
+ });
- if (global.env === 'development') winston.info('[plugins] Plugins OK');
+ next();
+ }
+ ], callback);
+ };
- plugins.initialized = true;
- plugins.readyEvent.emit('ready');
- });
- },
- ready: function(callback) {
- if (!this.initialized) this.readyEvent.once('ready', callback);
- else callback();
- },
- initialized: false,
- reload: function(callback) {
- var _self = this;
-
- // Resetting all local plugin data
- this.loadedHooks = {};
- this.staticDirs = {};
- this.cssFiles.length = 0;
-
- // Read the list of activated plugins and require their libraries
- async.waterfall([
+ Plugins.loadPlugin = function(pluginPath, callback) {
+ fs.readFile(path.join(pluginPath, 'plugin.json'), function(err, data) {
+ if (err) {
+ return callback(err);
+ }
+
+ var pluginData = JSON.parse(data),
+ libraryPath, staticDir;
+
+ async.parallel([
function(next) {
- RDB.smembers('plugins:active', next);
- },
- function(plugins, next) {
- if (plugins && Array.isArray(plugins) && plugins.length > 0) {
- async.each(plugins, function(plugin, next) {
- var modulePath = path.join(__dirname, '../node_modules/', plugin);
- if (fs.existsSync(modulePath)) _self.loadPlugin(modulePath, next);
- else {
- if (global.env === 'development') winston.warn('[plugins] Plugin \'' + plugin + '\' not found');
- next(); // Ignore this plugin silently
+ if (pluginData.library) {
+ libraryPath = path.join(pluginPath, pluginData.library);
+
+ fs.exists(libraryPath, function(exists) {
+ if (exists) {
+ if (!Plugins.libraries[pluginData.id]) {
+ Plugins.libraries[pluginData.id] = require(libraryPath);
+ }
+
+ // Register hooks for this plugin
+ if (pluginData.hooks && Array.isArray(pluginData.hooks) && pluginData.hooks.length > 0) {
+ async.each(pluginData.hooks, function(hook, next) {
+ Plugins.registerHook(pluginData.id, hook, next);
+ }, next);
+ } else {
+ next(null);
+ }
+ } else {
+ winston.warn('[plugins.reload] Library not found for plugin: ' + pluginData.id);
+ next();
}
- }, next);
- } else next();
+ });
+ } else {
+ winston.warn('[plugins.reload] Library not found for plugin: ' + pluginData.id);
+ next();
+ }
},
function(next) {
- if (global.env === 'development') winston.info('[plugins] Sorting hooks to fire in priority sequence');
- Object.keys(_self.loadedHooks).forEach(function(hook) {
- var hooks = _self.loadedHooks[hook];
- hooks = hooks.sort(function(a, b) {
- return a.priority - b.priority;
+ // Static Directories for Plugins
+ if (pluginData.staticDir) {
+ staticDir = path.join(pluginPath, pluginData.staticDir);
+
+ fs.exists(staticDir, function(exists) {
+ if (exists) {
+ Plugins.staticDirs[pluginData.id] = staticDir;
+ next();
+ } else next();
});
- });
-
- next();
- }
- ], callback);
- },
- loadPlugin: function(pluginPath, callback) {
- var _self = this;
-
- fs.readFile(path.join(pluginPath, 'plugin.json'), function(err, data) {
- if (err) return callback(err);
-
- var pluginData = JSON.parse(data),
- libraryPath, staticDir;
-
- async.parallel([
- function(next) {
- if (pluginData.library) {
- libraryPath = path.join(pluginPath, pluginData.library);
-
- fs.exists(libraryPath, function(exists) {
- if (exists) {
- if (!_self.libraries[pluginData.id]) {
- _self.libraries[pluginData.id] = require(libraryPath);
- }
-
- // Register hooks for this plugin
- if (pluginData.hooks && Array.isArray(pluginData.hooks) && pluginData.hooks.length > 0) {
- async.each(pluginData.hooks, function(hook, next) {
- _self.registerHook(pluginData.id, hook, next);
- }, next);
- } else {
- next(null);
- }
- } else {
- winston.warn('[plugins.reload] Library not found for plugin: ' + pluginData.id);
- next();
- }
- });
- } else {
- winston.warn('[plugins.reload] Library not found for plugin: ' + pluginData.id);
- next();
+ } else next();
+ },
+ function(next) {
+ // CSS Files for plugins
+ if (pluginData.css && pluginData.css instanceof Array) {
+ if (global.env === 'development') {
+ winston.info('[plugins] Found ' + pluginData.css.length + ' CSS file(s) for plugin ' + pluginData.id);
}
- },
- function(next) {
- // Static Directories for Plugins
- if (pluginData.staticDir) {
- staticDir = path.join(pluginPath, pluginData.staticDir);
-
- fs.exists(staticDir, function(exists) {
- if (exists) {
- _self.staticDirs[pluginData.id] = staticDir;
- next();
- } else next();
- });
- } else next();
- },
- function(next) {
- // CSS Files for plugins
- if (pluginData.css && pluginData.css instanceof Array) {
- if (global.env === 'development') {
- winston.info('[plugins] Found ' + pluginData.css.length + ' CSS file(s) for plugin ' + pluginData.id);
- }
- _self.cssFiles = _self.cssFiles.concat(pluginData.css.map(function(file) {
- return path.join('/plugins', pluginData.id, file);
- }));
+ Plugins.cssFiles = Plugins.cssFiles.concat(pluginData.css.map(function(file) {
+ return path.join('/plugins', pluginData.id, file);
+ }));
- next();
- } else next();
- }
- ], function(err) {
- if (!err) {
- if (global.env === 'development') winston.info('[plugins] Loaded plugin: ' + pluginData.id);
- callback();
- } else callback(new Error('Could not load plugin system'))
- });
- });
- },
- registerHook: function(id, data, callback) {
- /*
- `data` is an object consisting of (* is required):
- `data.hook`*, the name of the NodeBB hook
- `data.method`*, the method called in that plugin
- `data.callbacked`, whether or not the hook expects a callback (true), or a return (false). Only used for filters. (Default: false)
- `data.priority`, the relative priority of the method when it is eventually called (default: 10)
- */
- var _self = this;
-
- if (data.hook && data.method) {
- data.id = id;
- if (!data.priority) data.priority = 10;
- data.method = data.method.split('.').reduce(function(memo, prop) {
- if (memo[prop]) {
- return memo[prop];
+ next();
} else {
- // Couldn't find method by path, assuming property with periods in it (evil!)
- _self.libraries[data.id][data.method];
+ next();
}
- }, _self.libraries[data.id]);
-
- _self.loadedHooks[data.hook] = _self.loadedHooks[data.hook] || [];
- _self.loadedHooks[data.hook].push(data);
-
- if (global.env === 'development') winston.info('[plugins] Hook registered: ' + data.hook + ' will call ' + id);
- callback();
- } else return;
- },
- fireHook: function(hook, args, callback) {
- var _self = this
- hookList = this.loadedHooks[hook];
-
- if (hookList && Array.isArray(hookList)) {
- //if (global.env === 'development') winston.info('[plugins] Firing hook: \'' + hook + '\'');
- var hookType = hook.split(':')[0];
- switch (hookType) {
- case 'filter':
- async.reduce(hookList, args, function(value, hookObj, next) {
- if (hookObj.method) {
- if (hookObj.callbacked) { // If a callback is present (asynchronous method)
- hookObj.method.call(_self.libraries[hookObj.id], value, next);
- } else { // Synchronous method
- value = hookObj.method.call(_self.libraries[hookObj.id], value);
- next(null, value);
- }
- } else {
- if (global.env === 'development') winston.info('[plugins] Expected method \'' + hookObj.method + '\' in plugin \'' + hookObj.id + '\' not found, skipping.');
+ }
+ ], function(err) {
+ if (!err) {
+ if (global.env === 'development') {
+ winston.info('[plugins] Loaded plugin: ' + pluginData.id);
+ }
+ callback();
+ } else {
+ callback(new Error('Could not load plugin system'));
+ }
+ });
+ });
+ };
+
+ Plugins.registerHook = function(id, data, callback) {
+ /*
+ `data` is an object consisting of (* is required):
+ `data.hook`*, the name of the NodeBB hook
+ `data.method`*, the method called in that plugin
+ `data.callbacked`, whether or not the hook expects a callback (true), or a return (false). Only used for filters. (Default: false)
+ `data.priority`, the relative priority of the method when it is eventually called (default: 10)
+ */
+
+ if (data.hook && data.method) {
+ data.id = id;
+ if (!data.priority) data.priority = 10;
+ data.method = data.method.split('.').reduce(function(memo, prop) {
+ if (memo[prop]) {
+ return memo[prop];
+ } else {
+ // Couldn't find method by path, assuming property with periods in it (evil!)
+ Plugins.libraries[data.id][data.method];
+ }
+ }, Plugins.libraries[data.id]);
+
+ Plugins.loadedHooks[data.hook] = Plugins.loadedHooks[data.hook] || [];
+ Plugins.loadedHooks[data.hook].push(data);
+
+ if (global.env === 'development') {
+ winston.info('[plugins] Hook registered: ' + data.hook + ' will call ' + id);
+ }
+ callback();
+ } else return;
+ };
+
+ Plugins.fireHook = function(hook, args, callback) {
+ hookList = Plugins.loadedHooks[hook];
+
+ if (hookList && Array.isArray(hookList)) {
+ //if (global.env === 'development') winston.info('[plugins] Firing hook: \'' + hook + '\'');
+ var hookType = hook.split(':')[0];
+ switch (hookType) {
+ case 'filter':
+ async.reduce(hookList, args, function(value, hookObj, next) {
+ if (hookObj.method) {
+ if (hookObj.callbacked) { // If a callback is present (asynchronous method)
+ hookObj.method.call(Plugins.libraries[hookObj.id], value, next);
+ } else { // Synchronous method
+ value = hookObj.method.call(Plugins.libraries[hookObj.id], value);
next(null, value);
}
- }, function(err, value) {
- if (err) {
- if (global.env === 'development') {
- winston.info('[plugins] Problem executing hook: ' + hook);
- }
+ } else {
+ if (global.env === 'development') {
+ winston.info('[plugins] Expected method \'' + hookObj.method + '\' in plugin \'' + hookObj.id + '\' not found, skipping.');
}
+ next(null, value);
+ }
+ }, function(err, value) {
+ if (err) {
+ if (global.env === 'development') {
+ winston.info('[plugins] Problem executing hook: ' + hook);
+ }
+ }
- callback.apply(plugins, arguments);
- });
- break;
- case 'action':
- async.each(hookList, function(hookObj) {
- if (hookObj.method) {
- hookObj.method.call(_self.libraries[hookObj.id], args);
- } else {
- if (global.env === 'development') {
- winston.info('[plugins] Expected method \'' + hookObj.method + '\' in plugin \'' + hookObj.id + '\' not found, skipping.');
- }
+ callback.apply(Plugins, arguments);
+ });
+ break;
+ case 'action':
+ async.each(hookList, function(hookObj) {
+ if (hookObj.method) {
+ hookObj.method.call(Plugins.libraries[hookObj.id], args);
+ } else {
+ if (global.env === 'development') {
+ winston.info('[plugins] Expected method \'' + hookObj.method + '\' in plugin \'' + hookObj.id + '\' not found, skipping.');
}
- });
- break;
- default:
- // Do nothing...
- break;
- }
- } else {
- // Otherwise, this hook contains no methods
- var returnVal = args;
- if (callback) {
- callback(null, returnVal);
- }
+ }
+ });
+ break;
+ default:
+ // Do nothing...
+ break;
+ }
+ } else {
+ // Otherwise, this hook contains no methods
+ var returnVal = args;
+ if (callback) {
+ callback(null, returnVal);
}
- },
- isActive: function(id, callback) {
- RDB.sismember('plugins:active', id, callback);
- },
- toggleActive: function(id, callback) {
- this.isActive(id, function(err, active) {
+ }
+ };
+
+ Plugins.isActive = function(id, callback) {
+ db.isSetMember('plugins:active', id, callback);
+ };
+
+ Plugins.toggleActive = function(id, callback) {
+ Plugins.isActive(id, function(err, active) {
+ if (err) {
+ if (global.env === 'development') winston.info('[plugins] Could not toggle active state on plugin \'' + id + '\'');
+ return;
+ }
+
+ db[(active ? 'setRemove' : 'setAdd')]('plugins:active', id, function(err, success) {
if (err) {
if (global.env === 'development') winston.info('[plugins] Could not toggle active state on plugin \'' + id + '\'');
return;
}
- RDB[(active ? 'srem' : 'sadd')]('plugins:active', id, function(err, success) {
- if (err) {
- if (global.env === 'development') winston.info('[plugins] Could not toggle active state on plugin \'' + id + '\'');
- return;
- }
-
- // Reload meta data
- plugins.reload(function() {
- // (De)activation Hooks
- plugins.fireHook('action:plugin.' + (active ? 'de' : '') + 'activate', id);
+ // Reload meta data
+ Plugins.reload(function() {
+ // (De)activation Hooks
+ Plugins.fireHook('action:plugin.' + (active ? 'de' : '') + 'activate', id);
- if (callback) {
- callback({
- id: id,
- active: !active
- });
- }
- });
+ if (callback) {
+ callback({
+ id: id,
+ active: !active
+ });
+ }
});
});
- },
- showInstalled: function(callback) {
- var _self = this;
- npmPluginPath = path.join(__dirname, '../node_modules');
-
- async.waterfall([
- function(next) {
- fs.readdir(npmPluginPath, function(err, dirs) {
- dirs = dirs.map(function(file) {
- return path.join(npmPluginPath, file);
- }).filter(function(file) {
- var stats = fs.statSync(file);
- if (stats.isDirectory() && file.substr(npmPluginPath.length + 1, 14) === 'nodebb-plugin-') return true;
- else return false;
- });
+ });
+ }
- next(err, dirs);
+ Plugins.showInstalled = function(callback) {
+ npmPluginPath = path.join(__dirname, '../node_modules');
+
+ async.waterfall([
+ function(next) {
+ fs.readdir(npmPluginPath, function(err, dirs) {
+ dirs = dirs.map(function(file) {
+ return path.join(npmPluginPath, file);
+ }).filter(function(file) {
+ var stats = fs.statSync(file);
+ if (stats.isDirectory() && file.substr(npmPluginPath.length + 1, 14) === 'nodebb-plugin-') return true;
+ else return false;
});
- },
- function(files, next) {
- var plugins = [];
-
- async.each(files, function(file, next) {
- var configPath;
-
- async.waterfall([
- function(next) {
- fs.readFile(path.join(file, 'plugin.json'), next);
- },
- function(configJSON, next) {
- try {
- var config = JSON.parse(configJSON);
- } catch (err) {
- winston.warn("Plugin: " + file + " is corrupted or invalid. Please check plugin.json for errors.")
- return next(err, null);
- }
-
- _self.isActive(config.id, function(err, active) {
- if (err) next(new Error('no-active-state'));
- delete config.library;
- delete config.hooks;
- config.active = active;
- config.activeText = ' ' + (active ? 'Dea' : 'A') + 'ctivate';
- next(null, config);
- });
+ next(err, dirs);
+ });
+ },
+ function(files, next) {
+ var plugins = [];
+
+ async.each(files, function(file, next) {
+ var configPath;
+
+ async.waterfall([
+ function(next) {
+ fs.readFile(path.join(file, 'plugin.json'), next);
+ },
+ function(configJSON, next) {
+ try {
+ var config = JSON.parse(configJSON);
+ } catch (err) {
+ winston.warn("Plugin: " + file + " is corrupted or invalid. Please check plugin.json for errors.")
+ return next(err, null);
}
- ], function(err, config) {
- if (err) return next(); // Silently fail
- plugins.push(config);
- next();
- });
- }, function(err) {
- next(null, plugins);
- });
- }
- ], function(err, plugins) {
- callback(err, plugins);
- });
- }
- }
+ Plugins.isActive(config.id, function(err, active) {
+ if (err) {
+ next(new Error('no-active-state'));
+ }
-plugins.init();
+ delete config.library;
+ delete config.hooks;
+ config.active = active;
+ config.activeText = ' ' + (active ? 'Dea' : 'A') + 'ctivate';
+ next(null, config);
+ });
+ }
+ ], function(err, config) {
+ if (err) return next(); // Silently fail
-module.exports = plugins;
\ No newline at end of file
+ plugins.push(config);
+ next();
+ });
+ }, function(err) {
+ next(null, plugins);
+ });
+ }
+ ], function(err, plugins) {
+ callback(err, plugins);
+ });
+ }
+}(exports));
diff --git a/src/postTools.js b/src/postTools.js
index c70e88f167..e31fa70552 100644
--- a/src/postTools.js
+++ b/src/postTools.js
@@ -1,4 +1,4 @@
-var RDB = require('./redis'),
+var db = require('./database'),
posts = require('./posts'),
topics = require('./topics'),
threadTools = require('./threadTools'),
@@ -10,16 +10,14 @@ var RDB = require('./redis'),
utils = require('../public/src/utils'),
plugins = require('./plugins'),
- reds = require('reds'),
- postSearch = reds.createSearch('nodebbpostsearch'),
- topicSearch = reds.createSearch('nodebbtopicsearch'),
+
winston = require('winston'),
meta = require('./meta'),
Feed = require('./feed');
(function(PostTools) {
PostTools.isMain = function(pid, tid, callback) {
- RDB.lrange('tid:' + tid + ':posts', 0, 0, function(err, pids) {
+ db.getListRange('tid:' + tid + ':posts', 0, 0, function(err, pids) {
if(err) {
return callback(err);
}
@@ -83,8 +81,8 @@ var RDB = require('./redis'),
}
]);
- postSearch.remove(pid, function() {
- postSearch.index(content, pid);
+ db.searchRemove('post', pid, function() {
+ db.searchIndex('post', content, pid);
});
async.parallel([
@@ -93,8 +91,8 @@ var RDB = require('./redis'),
PostTools.isMain(pid, tid, function(err, isMainPost) {
if (isMainPost) {
topics.setTopicField(tid, 'title', title);
- topicSearch.remove(tid, function() {
- topicSearch.index(title, tid);
+ db.searchRemove('topic', tid, function() {
+ db.searchIndex('topic', title, tid);
});
}
@@ -131,14 +129,14 @@ var RDB = require('./redis'),
PostTools.delete = function(uid, pid, callback) {
var success = function() {
posts.setPostField(pid, 'deleted', 1);
- RDB.decr('totalpostcount');
- postSearch.remove(pid);
+ db.decrObjectField('global', 'postCount');
+ db.searchRemove('post', pid);
posts.getPostFields(pid, ['tid', 'uid'], function(err, postData) {
- RDB.hincrby('topic:' + postData.tid, 'postcount', -1);
+ db.incrObjectFieldBy('topic:' + postData.tid, 'postcount', -1);
user.decrementUserFieldBy(postData.uid, 'postcount', 1, function(err, postcount) {
- RDB.zadd('users:postcount', postcount, postData.uid);
+ db.sortedSetAdd('users:postcount', postcount, postData.uid);
});
// Delete the thread if it is the last undeleted post
@@ -164,7 +162,7 @@ var RDB = require('./redis'),
};
posts.getPostField(pid, 'deleted', function(err, deleted) {
- if(deleted === '1') {
+ if(parseInt(deleted, 10) === 1) {
return callback(new Error('Post already deleted!'));
}
@@ -180,10 +178,10 @@ var RDB = require('./redis'),
PostTools.restore = function(uid, pid, callback) {
var success = function() {
posts.setPostField(pid, 'deleted', 0);
- RDB.incr('totalpostcount');
+ db.incrObjectField('global', 'postCount');
posts.getPostFields(pid, ['tid', 'uid', 'content'], function(err, postData) {
- RDB.hincrby('topic:' + postData.tid, 'postcount', 1);
+ db.incrObjectFieldBy('topic:' + postData.tid, 'postcount', 1);
user.incrementUserFieldBy(postData.uid, 'postcount', 1);
@@ -195,7 +193,7 @@ var RDB = require('./redis'),
// Restore topic if it is the only post
topics.getTopicField(postData.tid, 'postcount', function(err, count) {
- if (count === '1') {
+ if (parseInt(count, 10) === 1) {
threadTools.restore(postData.tid, uid);
}
});
@@ -203,14 +201,14 @@ var RDB = require('./redis'),
Feed.updateTopic(postData.tid);
Feed.updateRecent();
- postSearch.index(postData.content, pid);
+ db.searchIndex('post', postData.content, pid);
callback();
});
};
posts.getPostField(pid, 'deleted', function(err, deleted) {
- if(deleted === '0') {
+ if(parseInt(deleted, 10) === 0) {
return callback(new Error('Post already restored'));
}
diff --git a/src/posts.js b/src/posts.js
index ac4cf01400..c496afdd99 100644
--- a/src/posts.js
+++ b/src/posts.js
@@ -1,4 +1,4 @@
-var RDB = require('./redis'),
+var db = require('./database'),
utils = require('./../public/src/utils'),
user = require('./user'),
topics = require('./topics'),
@@ -12,8 +12,6 @@ var RDB = require('./redis'),
meta = require('./meta'),
async = require('async'),
- reds = require('reds'),
- postSearch = reds.createSearch('nodebbpostsearch'),
nconf = require('nconf'),
validator = require('validator'),
winston = require('winston');
@@ -23,18 +21,17 @@ var RDB = require('./redis'),
Posts.create = function(uid, tid, content, callback) {
if (uid === null) {
- callback(new Error('invalid-user'), null);
- return;
+ return callback(new Error('invalid-user'), null);
}
topics.isLocked(tid, function(err, locked) {
if(err) {
return callback(err, null);
} else if(locked) {
- callback(new Error('topic-locked'), null);
+ return callback(new Error('topic-locked'), null);
}
- RDB.incr('global:next_post_id', function(err, pid) {
+ db.incrObjectField('global', 'nextPid', function(err, pid) {
if(err) {
return callback(err, null);
}
@@ -57,7 +54,7 @@ var RDB = require('./redis'),
'deleted': 0
};
- RDB.hmset('post:' + pid, postData);
+ db.setObject('post:' + pid, postData);
postData.favourited = false;
postData.display_moderator_tools = true;
@@ -67,26 +64,24 @@ var RDB = require('./redis'),
topics.increasePostCount(tid);
topics.updateTimestamp(tid, timestamp);
- RDB.incr('totalpostcount');
+ db.incrObjectField('global', 'postCount');
topics.getTopicFields(tid, ['cid', 'pinned'], function(err, topicData) {
- RDB.handle(err);
-
var cid = topicData.cid;
feed.updateTopic(tid);
feed.updateRecent();
- RDB.zadd('categories:recent_posts:cid:' + cid, timestamp, pid);
+ db.sortedSetAdd('categories:recent_posts:cid:' + cid, timestamp, pid);
- if(topicData.pinned === '0') {
- RDB.zadd('categories:' + cid + ':tid', timestamp, tid);
+ if(parseInt(topicData.pinned, 10) === 0) {
+ db.sortedSetAdd('categories:' + cid + ':tid', timestamp, tid);
}
- RDB.scard('cid:' + cid + ':active_users', function(err, amount) {
+ db.setCount('cid:' + cid + ':active_users', function(err, amount) {
if (amount > 15) {
- RDB.spop('cid:' + cid + ':active_users');
+ db.setRemoveRandom('cid:' + cid + ':active_users');
}
categories.addActiveUser(cid, uid);
@@ -110,7 +105,7 @@ var RDB = require('./redis'),
plugins.fireHook('action:post.save', postData);
- postSearch.index(content, pid);
+ db.searchIndex('post', content, pid);
callback(null, postData);
});
@@ -158,7 +153,7 @@ var RDB = require('./redis'),
return next(err);
}
- RDB.del('cid:' + cid + ':read_by_uid');
+ db.delete('cid:' + cid + ':read_by_uid');
next();
});
},
@@ -185,8 +180,10 @@ var RDB = require('./redis'),
}
Posts.getPostsByTid = function(tid, start, end, callback) {
- RDB.lrange('tid:' + tid + ':posts', start, end, function(err, pids) {
- RDB.handle(err);
+ db.getListRange('tid:' + tid + ':posts', start, end, function(err, pids) {
+ if(err) {
+ return callback(err);
+ }
if (pids.length) {
plugins.fireHook('filter:post.getTopic', pids, function(err, posts) {
@@ -216,7 +213,7 @@ var RDB = require('./redis'),
post.userslug = userData.userslug || '';
post.user_rep = userData.reputation || 0;
post.user_postcount = userData.postcount || 0;
- post.user_banned = userData.banned === '1';
+ post.user_banned = parseInt(userData.banned, 10) === 1;
post.picture = userData.picture || require('gravatar').url('', {}, https = nconf.get('https'));
post.signature = signature;
@@ -253,7 +250,7 @@ var RDB = require('./redis'),
async.waterfall([
function(next) {
Posts.getPostFields(pid, ['pid', 'tid', 'content', 'uid', 'timestamp', 'deleted'], function(err, postData) {
- if (postData.deleted === '1') {
+ if (parseInt(postData.deleted, 10) === 1) {
return callback(null);
} else {
postData.relativeTime = new Date(parseInt(postData.timestamp || 0, 10)).toISOString();
@@ -270,7 +267,7 @@ var RDB = require('./redis'),
topics.getTopicFields(postData.tid, ['title', 'cid', 'slug', 'deleted'], function(err, topicData) {
if (err) {
return callback(err);
- } else if (topicData.deleted === '1') {
+ } else if (parseInt(topicData.deleted, 10) === 1) {
return callback(null);
}
categories.getCategoryFields(topicData.cid, ['name', 'icon', 'slug'], function(err, categoryData) {
@@ -313,7 +310,7 @@ var RDB = require('./redis'),
};
Posts.getPostData = function(pid, callback) {
- RDB.hgetall('post:' + pid, function(err, data) {
+ db.getObject('post:' + pid, function(err, data) {
if(err) {
return callback(err, null);
}
@@ -328,7 +325,7 @@ var RDB = require('./redis'),
}
Posts.getPostFields = function(pid, fields, callback) {
- RDB.hmgetObject('post:' + pid, fields, function(err, data) {
+ db.getObjectFields('post:' + pid, fields, function(err, data) {
if(err) {
return callback(err, null);
}
@@ -358,7 +355,7 @@ var RDB = require('./redis'),
}
Posts.setPostField = function(pid, field, value, callback) {
- RDB.hset('post:' + pid, field, value, callback);
+ db.setObjectField('post:' + pid, field, value, callback);
plugins.fireHook('action:post.setField', {
'pid': pid,
'field': field,
@@ -367,28 +364,27 @@ var RDB = require('./redis'),
}
Posts.getPostsByPids = function(pids, callback) {
- var posts = [],
- multi = RDB.multi();
+ var keys = [];
for(var x=0, numPids=pids.length; x=0) {
- /* If redis.host contains a path name character, use the unix dom sock connection. ie, /tmp/redis.sock */
- RedisDB = redis.createClient(nconf.get('redis:host'));
- } else {
- /* Else, connect over tcp/ip */
- RedisDB = redis.createClient(nconf.get('redis:port'), nconf.get('redis:host'));
- }
-
- if (nconf.get('redis:password')) {
- RedisDB.auth(nconf.get('redis:password'));
- }
-
- var db = parseInt(nconf.get('redis:database'), 10);
- if (db){
- RedisDB.select(db, function(error){
- if(error !== null){
- winston.error("NodeBB could not connect to your Redis database. Redis returned the following error: " + error.message);
- process.exit();
- }
- });
- }
-
- RedisDB.handle = function(error) {
- if (error !== null) {
- winston.err(error);
- if (global.env !== 'production') {
- throw new Error(error);
- }
- }
- };
-
-
- /*
- * A possibly more efficient way of doing multiple sismember calls
- */
- RedisDB.sismembers = function(key, needles, callback) {
- var tempkey = key + ':temp:' + utils.generateUUID();
- RedisDB.sadd(tempkey, needles, function() {
- RedisDB.sinter(key, tempkey, function(err, data) {
- RedisDB.del(tempkey);
- callback(err, data);
- });
- });
- };
-
- /*
- * gets fields of a hash as an object instead of an array
- */
- RedisDB.hmgetObject = function(key, fields, callback) {
- RedisDB.hmget(key, fields, function(err, data) {
-
- if(err) {
- return callback(err, null);
- }
-
- var returnData = {};
-
- for (var i = 0, ii = fields.length; i < ii; ++i) {
- returnData[fields[i]] = data[i];
- }
-
- callback(null, returnData);
- });
- };
-
- module.exports = RedisDB;
-
-}(module));
\ No newline at end of file
diff --git a/src/routes/admin.js b/src/routes/admin.js
index 736fb9c50d..2831787434 100644
--- a/src/routes/admin.js
+++ b/src/routes/admin.js
@@ -3,7 +3,7 @@ var nconf = require('nconf'),
path = require('path'),
winston = require('winston'),
- RDB = require('./../redis'),
+ db = require('./../database'),
user = require('./../user'),
groups = require('../groups'),
topics = require('./../topics'),
@@ -55,7 +55,7 @@ var nconf = require('nconf'),
(function () {
var routes = [
'categories/active', 'categories/disabled', 'users', 'topics', 'settings', 'themes',
- 'twitter', 'facebook', 'gplus', 'redis', 'motd', 'groups', 'plugins', 'logger',
+ 'twitter', 'facebook', 'gplus', 'database', 'motd', 'groups', 'plugins', 'logger',
'users/latest', 'users/sort-posts', 'users/sort-reputation',
'users/search'
];
@@ -241,7 +241,7 @@ var nconf = require('nconf'),
app.get('/categories/active', function (req, res) {
categories.getAllCategories(0, function (err, data) {
data.categories = data.categories.filter(function (category) {
- return (!category.disabled || category.disabled === "0");
+ return (!category.disabled || parseInt(category.disabled, 10) === 0);
});
res.json(data);
});
@@ -250,7 +250,7 @@ var nconf = require('nconf'),
app.get('/categories/disabled', function (req, res) {
categories.getAllCategories(0, function (err, data) {
data.categories = data.categories.filter(function (category) {
- return category.disabled === "1";
+ return parseInt(category.disabled, 10) === 1;
});
res.json(data);
});
@@ -265,31 +265,10 @@ var nconf = require('nconf'),
});
});
- app.namespace('/redis', function () {
+ app.namespace('/database', function () {
app.get('/', function (req, res) {
- RDB.info(function (err, data) {
- data = data.split("\r\n");
- var finalData = {};
-
- for (var i in data) {
-
- if (data[i].indexOf(':') == -1 || !data[i])
- continue;
-
- try {
- data[i] = data[i].replace(/:/, "\":\"");
- var json = "{\"" + data[i] + "\"}";
-
- var jsonObject = JSON.parse(json);
- for (var key in jsonObject) {
- finalData[key] = jsonObject[key];
- }
- } catch (err) {
- winston.warn('can\'t parse redis status variable, ignoring', i, data[i], err);
- }
- }
-
- res.json(finalData);
+ db.info(function (err, data) {
+ res.json(data);
});
});
diff --git a/src/routes/api.js b/src/routes/api.js
index 9ee9fb14c3..0bebd9f975 100644
--- a/src/routes/api.js
+++ b/src/routes/api.js
@@ -42,7 +42,7 @@ var path = require('path'),
var uid = (req.user) ? req.user.uid : 0;
categories.getAllCategories(uid, function (err, data) {
data.categories = data.categories.filter(function (category) {
- return (!category.disabled || category.disabled === "0");
+ return (!category.disabled || parseInt(category.disabled, 10) === 0);
});
function iterator(category, callback) {
@@ -54,7 +54,7 @@ var path = require('path'),
}
async.each(data.categories, iterator, function (err) {
- data.motd_class = (meta.config.show_motd === '1' || meta.config.show_motd === undefined) ? '' : ' none';
+ data.motd_class = (parseInt(meta.config.show_motd, 10) === 1 || meta.config.show_motd === undefined) ? '' : ' none';
data.motd_class += (meta.config.motd && meta.config.motd.length > 0 ? '' : ' default');
data.motd = require('marked')(meta.config.motd || "\n\n# NodeBB v" + pkg.version + "\nWelcome to NodeBB, the discussion platform of the future.");
@@ -117,7 +117,7 @@ var path = require('path'),
var uid = (req.user) ? req.user.uid : 0;
topics.getTopicWithPosts(req.params.id, uid, 0, 10, false, function (err, data) {
if (!err) {
- if (data.deleted === '1' && data.expose_tools === 0) {
+ if (parseInt(data.deleted, 10) === 1 && parseInt(data.expose_tools, 10) === 0) {
return res.json(404, {});
}
res.json(data);
@@ -132,10 +132,11 @@ var path = require('path'),
categoryTools.privileges(req.params.id, uid, function(err, privileges) {
if (!err && privileges.read) {
categories.getCategoryById(req.params.id, uid, function (err, data) {
- if (!err && data && data.disabled === "0")
+ if (!err && data && parseInt(data.disabled, 10) === 0) {
res.json(data);
- else
+ } else {
next();
+ }
}, req.params.id, uid);
} else {
res.send(403);
@@ -225,18 +226,8 @@ var path = require('path'),
app.get('/search/:term', function (req, res, next) {
- var reds = require('reds');
- var postSearch = reds.createSearch('nodebbpostsearch');
- var topicSearch = reds.createSearch('nodebbtopicsearch');
-
- function search(searchObj, callback) {
- searchObj
- .query(query = req.params.term).type('or')
- .end(callback);
- }
-
function searchPosts(callback) {
- search(postSearch, function (err, pids) {
+ db.search('post', req.params.term, function(err, pids) {
if (err) {
return callback(err, null);
}
@@ -247,11 +238,11 @@ var path = require('path'),
}
callback(null, posts);
});
- })
+ });
}
function searchTopics(callback) {
- search(topicSearch, function (err, tids) {
+ db.search('topic', req.params.term, function(err, tids) {
if (err) {
return callback(err, null);
}
diff --git a/src/routes/authentication.js b/src/routes/authentication.js
index 88fcc1fd23..743ad9f9b0 100644
--- a/src/routes/authentication.js
+++ b/src/routes/authentication.js
@@ -14,8 +14,11 @@
passport.use(new passportLocal(function(user, password, next) {
login_module.loginViaLocal(user, password, function(err, login) {
- if (!err) next(null, login.user);
- else next(null, false, err);
+ if (!err) {
+ next(null, login.user);
+ } else {
+ next(null, false, err);
+ }
});
}));
diff --git a/src/routes/debug.js b/src/routes/debug.js
index 104e48bd08..d089a48b2c 100644
--- a/src/routes/debug.js
+++ b/src/routes/debug.js
@@ -78,11 +78,6 @@ var DebugRoute = function(app) {
});
});
});
-
- app.get('/test', function(req, res) {
- topics.pushUnreadCount();
- res.send();
- });
});
};
diff --git a/src/routes/user.js b/src/routes/user.js
index b0de83ebcb..3eb0acd6b8 100644
--- a/src/routes/user.js
+++ b/src/routes/user.js
@@ -9,7 +9,7 @@ var fs = require('fs'),
postTools = require('../postTools'),
utils = require('./../../public/src/utils'),
meta = require('./../meta'),
- RDB = require('./../redis'),
+ db = require('./../database'),
websockets = require('./../websockets');
(function (User) {
@@ -318,10 +318,11 @@ var fs = require('fs'),
return next(err);
if (userData) {
- if (userData.showemail && userData.showemail === "1")
+ if (userData.showemail && parseInt(userData.showemail, 10) === 1) {
userData.showemail = "checked";
- else
+ } else {
userData.showemail = "";
+ }
res.json(userData);
} else {
res.json(404, {
@@ -382,13 +383,15 @@ var fs = require('fs'),
posts.getPostsByUid(userData.theirid, 0, 9, function (posts) {
userData.posts = posts.filter(function (p) {
- return p && p.deleted !== "1";
+ return p && parseInt(p.deleted, 10) !== 1;
});
userData.isFollowing = isFollowing;
- if (!userData.profileviews)
+ if (!userData.profileviews) {
userData.profileviews = 1;
- if (callerUID !== userData.uid)
+ }
+ if (callerUID !== userData.uid) {
user.incrementUserFieldBy(userData.uid, 'profileviews', 1);
+ }
postTools.parse(userData.signature, function (err, signature) {
userData.signature = signature;
@@ -454,7 +457,7 @@ var fs = require('fs'),
if(websockets.isUserOnline(user.uid)) {
onlineUsers.push(user);
} else {
- RDB.zrem('users:online', user.uid);
+ db.sortedSetRemove('users:online', user.uid);
}
callback(null);
}
@@ -501,21 +504,21 @@ var fs = require('fs'),
}
function canSeeEmail() {
- return callerUID == uid || (data.email && (data.showemail && data.showemail === "1"));
+ return callerUID == uid || (data.email && (data.showemail && parseInt(data.showemail, 10) === 1));
}
if (!canSeeEmail()) {
data.email = "";
}
- if (callerUID == uid && (!data.showemail || data.showemail === "0")) {
+ if (callerUID == uid && (!data.showemail || parseInt(data.showemail, 10) === 0)) {
data.emailClass = "";
} else {
data.emailClass = "hide";
}
data.websiteName = data.website.replace('http://', '').replace('https://', '');
- data.banned = data.banned === '1';
+ data.banned = parseInt(data.banned, 10) === 1;
data.uid = uid;
data.yourid = callerUID;
data.theirid = uid;
diff --git a/src/sitemap.js b/src/sitemap.js
index b359b90d73..5568944297 100644
--- a/src/sitemap.js
+++ b/src/sitemap.js
@@ -45,7 +45,7 @@ var path = require('path'),
var topicUrls = [];
topics.getAllTopics(null, null, function(err, topics) {
topics.forEach(function(topic) {
- if (topic.deleted !== '1') {
+ if (parseInt(topic.deleted, 10) !== 1) {
topicUrls.push({
url: path.join('topic', topic.slug),
changefreq: 'daily',
diff --git a/src/threadTools.js b/src/threadTools.js
index a7df6eeaf8..ed32f795be 100644
--- a/src/threadTools.js
+++ b/src/threadTools.js
@@ -1,4 +1,4 @@
-var RDB = require('./redis'),
+var db = require('./database'),
topics = require('./topics'),
categories = require('./categories'),
CategoryTools = require('./categoryTools'),
@@ -8,18 +8,20 @@ var RDB = require('./redis'),
posts = require('./posts'),
meta = require('./meta'),
websockets = require('./websockets');
-
- reds = require('reds'),
- topicSearch = reds.createSearch('nodebbtopicsearch'),
winston = require('winston'),
nconf = require('nconf'),
(function(ThreadTools) {
ThreadTools.exists = function(tid, callback) {
- RDB.sismember('topics:tid', tid, function(err, ismember) {
- if (err) RDB.handle(err);
- callback( !! ismember || false);
+
+ db.isSetMember('topics:tid', tid, function(err, ismember) {
+
+ if (err) {
+ callback(false);
+ }
+
+ callback(ismember);
});
}
@@ -85,11 +87,11 @@ var RDB = require('./redis'),
ThreadTools.delete = function(tid, callback) {
topics.delete(tid);
- RDB.decr('totaltopiccount');
+ db.decrObjectField('global', 'topicCount');
ThreadTools.lock(tid);
- topicSearch.remove(tid);
+ db.searchRemove('topic', tid);
websockets.in('topic_' + tid).emit('event:topic_deleted', {
tid: tid,
@@ -103,7 +105,7 @@ var RDB = require('./redis'),
ThreadTools.restore = function(tid, socket, callback) {
topics.restore(tid);
- RDB.incr('totaltopiccount');
+ db.incrObjectField('global', 'topicCount');
ThreadTools.unlock(tid);
websockets.in('topic_' + tid).emit('event:topic_restored', {
@@ -112,7 +114,7 @@ var RDB = require('./redis'),
});
topics.getTopicField(tid, 'title', function(err, title) {
- topicSearch.index(title, tid);
+ db.searchIndex('topic', title, tid);
});
if(callback) {
@@ -123,7 +125,7 @@ var RDB = require('./redis'),
ThreadTools.pin = function(tid, socket) {
topics.setTopicField(tid, 'pinned', 1);
topics.getTopicField(tid, 'cid', function(err, cid) {
- RDB.zadd('categories:' + cid + ':tid', Math.pow(2, 53), tid);
+ db.sortedSetAdd('categories:' + cid + ':tid', Math.pow(2, 53), tid);
});
if (socket) {
@@ -144,7 +146,7 @@ var RDB = require('./redis'),
ThreadTools.unpin = function(tid, socket) {
topics.setTopicField(tid, 'pinned', 0);
topics.getTopicFields(tid, ['cid', 'lastposttime'], function(err, topicData) {
- RDB.zadd('categories:' + topicData.cid + ':tid', topicData.lastposttime, tid);
+ db.sortedSetAdd('categories:' + topicData.cid + ':tid', topicData.lastposttime, tid);
});
if (socket) {
websockets.in('topic_' + tid).emit('event:topic_unpinned', {
@@ -165,14 +167,16 @@ var RDB = require('./redis'),
topics.getTopicFields(tid, ['cid', 'lastposttime'], function(err, topicData) {
var oldCid = topicData.cid;
- var multi = RDB.multi();
- multi.zrem('categories:' + oldCid + ':tid', tid);
- multi.zadd('categories:' + cid + ':tid', topicData.lastposttime, tid);
+ db.sortedSetRemove('categories:' + oldCid + ':tid', tid, function(err, result) {
+ db.sortedSetAdd('categories:' + cid + ':tid', topicData.lastposttime, tid, function(err, result) {
- multi.exec(function(err, result) {
-
- if (!err && result[0] === 1 && result[1] === 1) {
+ if(err) {
+ socket.emit('api:topic.move', {
+ status: 'error'
+ });
+ return;
+ }
topics.setTopicField(tid, 'cid', cid);
@@ -198,17 +202,13 @@ var RDB = require('./redis'),
websockets.in('topic_' + tid).emit('event:topic_moved', {
tid: tid
});
- } else {
- socket.emit('api:topic.move', {
- status: 'error'
- });
- }
+ });
});
});
}
ThreadTools.isFollowing = function(tid, current_user, callback) {
- RDB.sismember('tid:' + tid + ':followers', current_user, function(err, following) {
+ db.isSetMember('tid:' + tid + ':followers', current_user, function(err, following) {
callback(following);
});
}
@@ -216,7 +216,7 @@ var RDB = require('./redis'),
ThreadTools.toggleFollow = function(tid, current_user, callback) {
ThreadTools.isFollowing(tid, current_user, function(following) {
if (!following) {
- RDB.sadd('tid:' + tid + ':followers', current_user, function(err, success) {
+ db.setAdd('tid:' + tid + ':followers', current_user, function(err, success) {
if (callback) {
if (!err) {
callback({
@@ -229,7 +229,7 @@ var RDB = require('./redis'),
}
});
} else {
- RDB.srem('tid:' + tid + ':followers', current_user, function(err, success) {
+ db.setRemove('tid:' + tid + ':followers', current_user, function(err, success) {
if (callback) {
if (!err) {
callback({
@@ -246,7 +246,7 @@ var RDB = require('./redis'),
}
ThreadTools.getFollowers = function(tid, callback) {
- RDB.smembers('tid:' + tid + ':followers', function(err, followers) {
+ db.getSetMembers('tid:' + tid + ':followers', function(err, followers) {
callback(err, followers.map(function(follower) {
return parseInt(follower, 10);
}));
@@ -274,24 +274,33 @@ var RDB = require('./redis'),
});
}
], function(err, results) {
- if (!err) notifications.push(results[0], results[1]);
- // Otherwise, do nothing
+ if (!err) {
+ notifications.push(results[0], results[1]);
+ }
});
}
ThreadTools.getLatestUndeletedPid = function(tid, callback) {
- RDB.lrange('tid:' + tid + ':posts', 0, -1, function(err, pids) {
- if (pids.length === 0) return callback(new Error('no-undeleted-pids-found'));
+ db.getListRange('tid:' + tid + ':posts', 0, -1, function(err, pids) {
+ if (pids.length === 0) {
+ return callback(new Error('no-undeleted-pids-found'));
+ }
pids.reverse();
async.detectSeries(pids, function(pid, next) {
posts.getPostField(pid, 'deleted', function(err, deleted) {
- if (deleted === '0') next(true);
- else next(false);
+ if (parseInt(deleted, 10) === 0) {
+ next(true);
+ } else {
+ next(false);
+ }
});
}, function(pid) {
- if (pid) callback(null, pid);
- else callback(new Error('no-undeleted-pids-found'));
+ if (pid) {
+ callback(null, pid);
+ } else {
+ callback(new Error('no-undeleted-pids-found'));
+ }
});
});
}
diff --git a/src/topics.js b/src/topics.js
index 3b64676df9..f5a496bc77 100644
--- a/src/topics.js
+++ b/src/topics.js
@@ -2,10 +2,8 @@ var async = require('async'),
gravatar = require('gravatar'),
nconf = require('nconf'),
validator = require('validator'),
- reds = require('reds'),
- topicSearch = reds.createSearch('nodebbtopicsearch'),
- RDB = require('./redis'),
+ db = require('./database'),
posts = require('./posts'),
utils = require('./../public/src/utils'),
user = require('./user'),
@@ -60,16 +58,16 @@ var async = require('async'),
return callback(new Error('too-many-posts'), null);
}
- RDB.incr('next_topic_id', function(err, tid) {
+ db.incrObjectField('global', 'nextTid', function(err, tid) {
if(err) {
return callback(err);
}
- RDB.sadd('topics:tid', tid);
+ db.setAdd('topics:tid', tid);
var slug = tid + '/' + utils.slugify(title);
var timestamp = Date.now();
- RDB.hmset('topic:' + tid, {
+ db.setObject('topic:' + tid, {
'tid': tid,
'uid': uid,
'cid': cid,
@@ -84,19 +82,19 @@ var async = require('async'),
'pinned': 0
});
- topicSearch.index(title, tid);
+ db.searchIndex('topic', title, tid);
user.addTopicIdToUser(uid, tid);
// let everyone know that there is an unread topic in this category
- RDB.del('cid:' + cid + ':read_by_uid', function(err, data) {
+ db.delete('cid:' + cid + ':read_by_uid', function(err, data) {
Topics.markAsRead(tid, uid);
});
// in future it may be possible to add topics to several categories, so leaving the door open here.
- RDB.zadd('categories:' + cid + ':tid', timestamp, tid);
- RDB.hincrby('category:' + cid, 'topic_count', 1);
- RDB.incr('totaltopiccount');
+ db.sortedSetAdd('categories:' + cid + ':tid', timestamp, tid);
+ db.incrObjectField('category:' + cid, 'topic_count');
+ db.incrObjectField('global', 'topicCount');
feed.updateCategory(cid);
@@ -127,7 +125,7 @@ var async = require('async'),
};
Topics.getTopicData = function(tid, callback) {
- RDB.hgetall('topic:' + tid, function(err, data) {
+ db.getObject('topic:' + tid, function(err, data) {
if(err) {
return callback(err, null);
}
@@ -173,7 +171,7 @@ var async = require('async'),
}
postData = postData.filter(function(post) {
- return parseInt(current_user, 10) !== 0 || post.deleted === "0";
+ return parseInt(current_user, 10) !== 0 || parseInt(post.deleted, 10) === 0;
});
function getFavouritesData(next) {
@@ -209,7 +207,7 @@ var async = require('async'),
privileges = results[2];
for (var i = 0; i < postData.length; ++i) {
- postData[i].favourited = fav_data[postData[i].pid] === 1;
+ postData[i].favourited = fav_data[postData[i].pid];
postData[i].display_moderator_tools = ((current_user != 0) && (postData[i].uid == current_user || privileges.editable));
}
@@ -239,8 +237,7 @@ var async = require('async'),
since = terms[term];
var args = ['topics:recent', '+inf', timestamp - since, 'LIMIT', start, end - start + 1];
-
- RDB.zrevrangebyscore(args, function(err, tids) {
+ db.getSortedSetRevRangeByScore(args, function(err, tids) {
if (err) {
return callback(err);
}
@@ -275,7 +272,7 @@ var async = require('async'),
return unreadTids.length < 21 && !done;
},
function(callback) {
- RDB.zrevrange('topics:recent', start, stop, function(err, tids) {
+ db.getSortedSetRevRange('topics:recent', start, stop, function(err, tids) {
if (err)
return callback(err);
@@ -316,7 +313,7 @@ var async = require('async'),
}
async.whilst(continueCondition, function(callback) {
- RDB.zrevrange('topics:recent', start, stop, function(err, tids) {
+ db.getSortedSetRevRange('topics:recent', start, stop, function(err, tids) {
if (err) {
return callback(err);
}
@@ -489,18 +486,18 @@ var async = require('async'),
getTopicInfo(topicData, function(topicInfo) {
- topicData['pin-icon'] = topicData.pinned === '1' ? 'fa-thumb-tack' : 'none';
- topicData['lock-icon'] = topicData.locked === '1' ? 'fa-lock' : 'none';
- topicData['deleted-class'] = topicData.deleted === '1' ? 'deleted' : '';
+ topicData['pin-icon'] = parseInt(topicData.pinned, 10) === 1 ? 'fa-thumb-tack' : 'none';
+ topicData['lock-icon'] = parseInt(topicData.locked, 10) === 1 ? 'fa-lock' : 'none';
+ topicData['deleted-class'] = parseInt(topicData.deleted, 10) === 1 ? 'deleted' : '';
- topicData.unreplied = topicData.postcount === '1';
+ topicData.unreplied = parseInt(topicData.postcount, 10) === 1;
topicData.username = topicInfo.username || 'anonymous';
topicData.userslug = topicInfo.userslug || '';
topicData.picture = topicInfo.picture || gravatar.url('', {}, https = nconf.get('https'));
topicData.categoryIcon = topicInfo.categoryData.icon;
topicData.categoryName = topicInfo.categoryData.name;
topicData.categorySlug = topicInfo.categoryData.slug;
- topicData.badgeclass = (topicInfo.hasread && current_user != 0) ? '' : 'badge-important';
+ topicData.badgeclass = (topicInfo.hasread && parseInt(current_user, 10) !== 0) ? '' : 'badge-important';
topicData.teaser_text = topicInfo.teaserInfo.text || '',
topicData.teaser_username = topicInfo.teaserInfo.username || '';
topicData.teaser_userslug = topicInfo.teaserInfo.userslug || '';
@@ -577,7 +574,7 @@ var async = require('async'),
'slug': topicData.slug,
'postcount': topicData.postcount,
'viewcount': topicData.viewcount,
- 'unreplied': topicData.postcount > 1,
+ 'unreplied': parseInt(topicData.postcount, 10) > 1,
'topic_id': tid,
'expose_tools': privileges.editable ? 1 : 0,
'posts': topicPosts
@@ -594,7 +591,7 @@ var async = require('async'),
}
function getReadStatus(next) {
- if (uid && parseInt(uid) > 0) {
+ if (uid && parseInt(uid, 10) > 0) {
Topics.hasReadTopic(tid, uid, function(read) {
next(null, read);
});
@@ -619,8 +616,8 @@ var async = require('async'),
hasRead = results[1],
teaser = results[2];
- topicData['pin-icon'] = topicData.pinned === '1' ? 'fa-thumb-tack' : 'none';
- topicData['lock-icon'] = topicData.locked === '1' ? 'fa-lock' : 'none';
+ topicData['pin-icon'] = parseInt(topicData.pinned, 10) === 1 ? 'fa-thumb-tack' : 'none';
+ topicData['lock-icon'] = parseInt(topicData.locked, 10) === 1 ? 'fa-lock' : 'none';
topicData.badgeclass = hasRead ? '' : 'badge-important';
topicData.teaser_text = teaser.text || '';
@@ -635,7 +632,7 @@ var async = require('async'),
}
Topics.getAllTopics = function(limit, after, callback) {
- RDB.smembers('topics:tid', function(err, tids) {
+ db.getSetMembers('topics:tid', function(err, tids) {
if(err) {
return callback(err, null);
}
@@ -681,7 +678,7 @@ var async = require('async'),
}
Topics.markAllRead = function(uid, callback) {
- RDB.smembers('topics:tid', function(err, tids) {
+ db.getSetMembers('topics:tid', function(err, tids) {
if (err) {
return callback(err, null);
}
@@ -705,12 +702,12 @@ var async = require('async'),
}
Topics.markUnRead = function(tid, callback) {
- RDB.del('tid:' + tid + ':read_by_uid', callback);
+ db.delete('tid:' + tid + ':read_by_uid', callback);
}
Topics.markAsRead = function(tid, uid) {
- RDB.sadd('tid:' + tid + ':read_by_uid', uid);
+ db.setAdd('tid:' + tid + ':read_by_uid', uid);
Topics.getTopicField(tid, 'cid', function(err, cid) {
@@ -729,19 +726,19 @@ var async = require('async'),
}
Topics.hasReadTopics = function(tids, uid, callback) {
- var batch = RDB.multi();
+ var sets = [];
for (var i = 0, ii = tids.length; i < ii; i++) {
- batch.sismember('tid:' + tids[i] + ':read_by_uid', uid);
+ sets.push('tid:' + tids[i] + ':read_by_uid');
}
- batch.exec(function(err, hasRead) {
+ db.isMemberOfSets(sets, uid, function(err, hasRead) {
callback(hasRead);
});
}
Topics.hasReadTopic = function(tid, uid, callback) {
- RDB.sismember('tid:' + tid + ':read_by_uid', uid, function(err, hasRead) {
+ db.isSetMember('tid:' + tid + ':read_by_uid', uid, function(err, hasRead) {
if (err === null) {
callback(hasRead);
@@ -757,7 +754,9 @@ var async = require('async'),
if (Array.isArray(tids)) {
async.eachSeries(tids, function(tid, next) {
Topics.getTeaser(tid, function(err, teaser_info) {
- if (err) teaser_info = {};
+ if (err) {
+ teaser_info = {};
+ }
teasers.push(teaser_info);
next();
});
@@ -821,23 +820,23 @@ var async = require('async'),
}
Topics.getTopicField = function(tid, field, callback) {
- RDB.hget('topic:' + tid, field, callback);
+ db.getObjectField('topic:' + tid, field, callback);
}
Topics.getTopicFields = function(tid, fields, callback) {
- RDB.hmgetObject('topic:' + tid, fields, callback);
+ db.getObjectFields('topic:' + tid, fields, callback);
}
Topics.setTopicField = function(tid, field, value, callback) {
- RDB.hset('topic:' + tid, field, value, callback);
+ db.setObjectField('topic:' + tid, field, value, callback);
}
Topics.increasePostCount = function(tid, callback) {
- RDB.hincrby('topic:' + tid, 'postcount', 1, callback);
+ db.incrObjectField('topic:' + tid, 'postcount', callback);
}
Topics.increaseViewCount = function(tid, callback) {
- RDB.hincrby('topic:' + tid, 'viewcount', 1, callback);
+ db.incrObjectField('topic:' + tid, 'viewcount', callback);
}
Topics.isLocked = function(tid, callback) {
@@ -845,21 +844,21 @@ var async = require('async'),
if(err) {
return callback(err, null);
}
- callback(null, locked === "1");
+ callback(null, parseInt(locked, 10) === 1);
});
}
Topics.updateTimestamp = function(tid, timestamp) {
- RDB.zadd('topics:recent', timestamp, tid);
+ db.sortedSetAdd('topics:recent', timestamp, tid);
Topics.setTopicField(tid, 'lastposttime', timestamp);
}
Topics.addPostToTopic = function(tid, pid) {
- RDB.rpush('tid:' + tid + ':posts', pid);
+ db.listAppend('tid:' + tid + ':posts', pid);
}
Topics.getPids = function(tid, callback) {
- RDB.lrange('tid:' + tid + ':posts', 0, -1, callback);
+ db.getListRange('tid:' + tid + ':posts', 0, -1, callback);
}
Topics.getUids = function(tid, callback) {
@@ -886,23 +885,23 @@ var async = require('async'),
Topics.delete = function(tid) {
Topics.setTopicField(tid, 'deleted', 1);
- RDB.zrem('topics:recent', tid);
+ db.sortedSetRemove('topics:recent', tid);
Topics.getTopicField(tid, 'cid', function(err, cid) {
feed.updateCategory(cid);
- RDB.hincrby('category:' + cid, 'topic_count', -1);
+ db.incrObjectFieldBy('category:' + cid, 'topic_count', -1);
});
}
Topics.restore = function(tid) {
Topics.setTopicField(tid, 'deleted', 0);
Topics.getTopicField(tid, 'lastposttime', function(err, lastposttime) {
- RDB.zadd('topics:recent', lastposttime, tid);
+ db.sortedSetAdd('topics:recent', lastposttime, tid);
});
Topics.getTopicField(tid, 'cid', function(err, cid) {
feed.updateCategory(cid);
- RDB.hincrby('category:' + cid, 'topic_count', 1);
+ db.incrObjectFieldBy('category:' + cid, 'topic_count', 1);
});
}
@@ -923,7 +922,7 @@ var async = require('async'),
}
Topics.reIndexAll = function(callback) {
- RDB.smembers('topics:tid', function(err, tids) {
+ db.getSetMembers('topics:tid', function(err, tids) {
if (err) {
callback(err, null);
} else {
diff --git a/src/upgrade.js b/src/upgrade.js
index cec799914d..75265c4643 100644
--- a/src/upgrade.js
+++ b/src/upgrade.js
@@ -1,19 +1,20 @@
"use strict";
-var RDB = require('./redis.js'),
+var db = require('./database'),
async = require('async'),
winston = require('winston'),
notifications = require('./notifications'),
categories = require('./categories'),
+ nconf = require('nconf'),
Upgrade = {},
schemaDate, thisSchemaDate;
Upgrade.check = function(callback) {
// IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema
- var latestSchema = new Date(2013, 10, 26).getTime();
+ var latestSchema = new Date(2013, 11, 2).getTime();
- RDB.get('schemaDate', function(err, value) {
+ db.get('schemaDate', function(err, value) {
if (parseInt(value, 10) >= latestSchema) {
callback(true);
} else {
@@ -23,6 +24,22 @@ Upgrade.check = function(callback) {
};
Upgrade.upgrade = function(callback) {
+ var databaseType = nconf.get('database');
+
+ if(databaseType === 'redis') {
+ Upgrade.upgradeRedis(callback);
+ } else if(databaseType === 'mongo') {
+ Upgrade.upgradeMongo(callback);
+ } else {
+ winston.error('Unknown database type. Aborting upgrade');
+ callback(new Error('unknown-database'));
+ }
+};
+
+Upgrade.upgradeRedis = function(callback) {
+
+ var RDB = db.client;
+
winston.info('Beginning Redis database schema update');
async.series([
@@ -179,7 +196,7 @@ Upgrade.upgrade = function(callback) {
},
function(next) {
thisSchemaDate = new Date(2013, 10, 26).getTime();
- if (schemaDate < thisSchemaDate || 1) {
+ if (schemaDate < thisSchemaDate) {
categories.getAllCategories(0, function(err, categories) {
function updateIcon(category, next) {
@@ -209,7 +226,57 @@ Upgrade.upgrade = function(callback) {
winston.info('[2013/11/26] Update to Category icons skipped.');
next();
}
- }
+ },
+ function(next) {
+
+ function updateKeyToHash(key, next) {
+ RDB.get(key, function(err, value) {
+ RDB.hset('global', newKeys[key], value, next);
+ });
+ }
+
+ thisSchemaDate = new Date(2013, 11, 2).getTime();
+ if (schemaDate < thisSchemaDate) {
+
+ var keys = [
+ 'global:next_user_id',
+ 'next_topic_id',
+ 'next_gid',
+ 'notifications:next_nid',
+ 'global:next_category_id',
+ 'global:next_message_id',
+ 'global:next_post_id',
+ 'usercount',
+ 'totaltopiccount',
+ 'totalpostcount'
+ ];
+
+ var newKeys = {
+ 'global:next_user_id':'nextUid',
+ 'next_topic_id':'nextTid',
+ 'next_gid':'nextGid',
+ 'notifications:next_nid':'nextNid',
+ 'global:next_category_id':'nextCid',
+ 'global:next_message_id':'nextMid',
+ 'global:next_post_id':'nextPid',
+ 'usercount':'userCount',
+ 'totaltopiccount':'topicCount',
+ 'totalpostcount':'postCount'
+ };
+
+ async.each(keys, updateKeyToHash, function(err) {
+ if(err) {
+ return next(err);
+ }
+ winston.info('[2013/12/2] Updated global keys to hash.');
+ next();
+ });
+
+ } else {
+ winston.info('[2013/12/2] Update to global keys skipped');
+ next();
+ }
+ },
// Add new schema updates here
// IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 12!!!
], function(err) {
@@ -234,4 +301,41 @@ Upgrade.upgrade = function(callback) {
});
};
+Upgrade.upgradeMongo = function(callback) {
+ var MDB = db.client;
+
+ winston.info('Beginning Mongo database schema update');
+
+ async.series([
+ function(next) {
+ db.get('schemaDate', function(err, value) {
+ schemaDate = value;
+ thisSchemaDate = new Date(2013, 11, 6).getTime();
+ next();
+ });
+ }
+ // Add new schema updates here
+
+ ], function(err) {
+ if (!err) {
+ db.set('schemaDate', thisSchemaDate, function(err) {
+ if (!err) {
+ winston.info('[upgrade] Mongo schema update complete!');
+ if (callback) {
+ callback(err);
+ } else {
+ process.exit();
+ }
+ } else {
+ winston.error('[upgrade] Could not update NodeBB schema date!');
+ process.exit();
+ }
+ });
+ } else {
+ winston.error('[upgrade] Errors were encountered while updating the NodeBB schema: ' + err.message);
+ process.exit();
+ }
+ });
+}
+
module.exports = Upgrade;
\ No newline at end of file
diff --git a/src/user.js b/src/user.js
index 7dd14e2426..b45bf30275 100644
--- a/src/user.js
+++ b/src/user.js
@@ -4,16 +4,15 @@ var bcrypt = require('bcrypt'),
nconf = require('nconf'),
winston = require('winston'),
gravatar = require('gravatar'),
- userSearch = require('reds').createSearch('nodebbusersearch'),
check = require('validator').check,
sanitize = require('validator').sanitize,
utils = require('./../public/src/utils'),
plugins = require('./plugins'),
- RDB = require('./redis'),
+ db = require('./database'),
meta = require('./meta'),
emailjsServer = emailjs.server.connect(meta.config['email:smtp:host'] || '127.0.0.1'),
- Groups = require('./groups'),
+ groups = require('./groups'),
notifications = require('./notifications'),
topics = require('./topics');
@@ -65,16 +64,18 @@ var bcrypt = require('bcrypt'),
}
], function(err, results) {
if (err) {
- return callback(err, null);
+ return callback(err);
}
- RDB.incr('global:next_user_id', function(err, uid) {
- RDB.handle(err);
+ db.incrObjectField('global', 'nextUid', function(err, uid) {
+ if(err) {
+ return callback(err);
+ }
var gravatar = User.createGravatarURLFromEmail(email);
var timestamp = Date.now();
- RDB.hmset('user:' + uid, {
+ db.setObject('user:' + uid, {
'uid': uid,
'username': username,
'userslug': userslug,
@@ -96,22 +97,22 @@ var bcrypt = require('bcrypt'),
'showemail': 0
});
- RDB.hset('username:uid', username, uid);
- RDB.hset('userslug:uid', userslug, uid);
+ db.setObjectField('username:uid', username, uid);
+ db.setObjectField('userslug:uid', userslug, uid);
if (email !== undefined) {
- RDB.hset('email:uid', email, uid);
+ db.setObjectField('email:uid', email, uid);
User.sendConfirmationEmail(email);
}
plugins.fireHook('action:user.create', {uid: uid, username: username, email: email, picture: gravatar, timestamp: timestamp});
- RDB.incr('usercount');
+ db.incrObjectField('global', 'userCount');
- RDB.zadd('users:joindate', timestamp, uid);
- RDB.zadd('users:postcount', 0, uid);
- RDB.zadd('users:reputation', 0, uid);
+ db.sortedSetAdd('users:joindate', timestamp, uid);
+ db.sortedSetAdd('users:postcount', 0, uid);
+ db.sortedSetAdd('users:reputation', 0, uid);
- userSearch.index(username, uid);
+ db.searchIndex('user', username, uid);
if (password !== undefined) {
User.hashPassword(password, function(err, hash) {
@@ -134,11 +135,11 @@ var bcrypt = require('bcrypt'),
};
User.getUserField = function(uid, field, callback) {
- RDB.hget('user:' + uid, field, callback);
+ db.getObjectField('user:' + uid, field, callback);
};
User.getUserFields = function(uid, fields, callback) {
- RDB.hmgetObject('user:' + uid, fields, callback);
+ db.getObjectFields('user:' + uid, fields, callback);
};
User.getMultipleUserFields = function(uids, fields, callback) {
@@ -168,7 +169,10 @@ var bcrypt = require('bcrypt'),
};
User.getUserData = function(uid, callback) {
- RDB.hgetall('user:' + uid, function(err, data) {
+ db.getObject('user:' + uid, function(err, data) {
+ if(err) {
+ return callback(err);
+ }
if (data && data.password) {
delete data.password;
@@ -177,12 +181,6 @@ var bcrypt = require('bcrypt'),
});
};
- User.filterBannedUsers = function(users) {
- return users.filter(function(user) {
- return (!user.banned || user.banned === '0');
- });
- };
-
User.updateProfile = function(uid, data, callback) {
var fields = ['email', 'fullname', 'website', 'location', 'birthday', 'signature'];
@@ -253,8 +251,8 @@ var bcrypt = require('bcrypt'),
return next(err);
}
- RDB.hdel('email:uid', userData.email);
- RDB.hset('email:uid', data.email, uid);
+ db.deleteObjectField('email:uid', userData.email);
+ db.setObjectField('email:uid', data.email, uid);
User.setUserField(uid, field, data[field]);
if (userData.picture !== userData.uploadedpicture) {
returnData.picture = gravatarpicture;
@@ -282,7 +280,7 @@ var bcrypt = require('bcrypt'),
};
User.isEmailAvailable = function(email, callback) {
- RDB.hexists('email:uid', email, function(err, exists) {
+ db.isObjectField('email:uid', email, function(err, exists) {
callback(err, !exists);
});
};
@@ -316,25 +314,25 @@ var bcrypt = require('bcrypt'),
};
User.setUserField = function(uid, field, value, callback) {
- RDB.hset('user:' + uid, field, value, callback);
+ db.setObjectField('user:' + uid, field, value, callback);
};
User.setUserFields = function(uid, data, callback) {
- RDB.hmset('user:' + uid, data, callback);
+ db.setObject('user:' + uid, data, callback);
};
User.incrementUserFieldBy = function(uid, field, value, callback) {
- RDB.hincrby('user:' + uid, field, value, callback);
+ db.incrObjectFieldBy('user:' + uid, field, value, callback);
};
User.decrementUserFieldBy = function(uid, field, value, callback) {
- RDB.hincrby('user:' + uid, field, -value, callback);
+ db.incrObjectFieldBy('user:' + uid, field, -value, callback);
};
User.getUsers = function(set, start, stop, callback) {
var data = [];
- RDB.zrevrange(set, start, stop, function(err, uids) {
+ db.getSortedSetRevRange(set, start, stop, function(err, uids) {
if (err) {
return callback(err, null);
}
@@ -355,7 +353,6 @@ var bcrypt = require('bcrypt'),
callback(err, data);
});
});
-
};
User.createGravatarURLFromEmail = function(email) {
@@ -392,8 +389,8 @@ var bcrypt = require('bcrypt'),
}
function reIndexUser(uid, username) {
- userSearch.remove(uid, function() {
- userSearch.index(username, uid);
+ db.searchRemove('user', uid, function() {
+ db.searchIndex('user', username, uid);
});
}
@@ -409,7 +406,7 @@ var bcrypt = require('bcrypt'),
callback([]);
return;
}
- userSearch.query(username).type('or').end(function(err, uids) {
+ db.search('user', username, function(err, uids) {
if (err) {
console.log(err);
return;
@@ -428,7 +425,7 @@ var bcrypt = require('bcrypt'),
User.addPostIdToUser(uid, pid);
User.incrementUserFieldBy(uid, 'postcount', 1, function(err, newpostcount) {
- RDB.zadd('users:postcount', newpostcount, uid);
+ db.sortedSetAdd('users:postcount', newpostcount, uid);
});
User.setUserField(uid, 'lastposttime', timestamp);
@@ -437,15 +434,15 @@ var bcrypt = require('bcrypt'),
};
User.addPostIdToUser = function(uid, pid) {
- RDB.lpush('uid:' + uid + ':posts', pid);
+ db.listPrepend('uid:' + uid + ':posts', pid);
};
User.addTopicIdToUser = function(uid, tid) {
- RDB.lpush('uid:' + uid + ':topics', tid);
+ db.listPrepend('uid:' + uid + ':topics', tid);
};
- User.getPostIds = function(uid, start, end, callback) {
- RDB.lrange('uid:' + uid + ':posts', start, end, function(err, pids) {
+ User.getPostIds = function(uid, start, stop, callback) {
+ db.getListRange('uid:' + uid + ':posts', start, stop, function(err, pids) {
if (!err) {
if (pids && pids.length) {
callback(pids);
@@ -459,51 +456,12 @@ var bcrypt = require('bcrypt'),
});
};
- User.sendConfirmationEmail = function(email) {
- if (meta.config['email:smtp:host'] && meta.config['email:smtp:port'] && meta.config['email:from']) {
- var confirm_code = utils.generateUUID(),
- confirm_link = nconf.get('url') + 'confirm/' + confirm_code,
- confirm_email = global.templates['emails/header'] + global.templates['emails/email_confirm'].parse({
- 'CONFIRM_LINK': confirm_link
- }) + global.templates['emails/footer'],
- confirm_email_plaintext = global.templates['emails/email_confirm_plaintext'].parse({
- 'CONFIRM_LINK': confirm_link
- });
-
- // Email confirmation code
- var expiry_time = 60 * 60 * 2, // Expire after 2 hours
- email_key = 'email:' + email + ':confirm',
- confirm_key = 'confirm:' + confirm_code + ':email';
- RDB.set(email_key, confirm_code);
- RDB.expire(email_key, expiry_time);
- RDB.set(confirm_key, email);
- RDB.expire(confirm_key, expiry_time);
-
- // Send intro email w/ confirm code
- var message = emailjs.message.create({
- text: confirm_email_plaintext,
- from: meta.config['email:from'] || 'localhost@example.org',
- to: email,
- subject: '[NodeBB] Registration Email Verification',
- attachment: [{
- data: confirm_email,
- alternative: true
- }]
- });
-
- emailjsServer.send(message, function(err, success) {
- if (err) {
- console.log(err);
- }
- });
- }
- };
User.follow = function(uid, followid, callback) {
- RDB.sadd('following:' + uid, followid, function(err, data) {
+ db.setAdd('following:' + uid, followid, function(err, data) {
if (!err) {
- RDB.sadd('followers:' + followid, uid, function(err, data) {
+ db.setAdd('followers:' + followid, uid, function(err, data) {
if (!err) {
callback(true);
} else {
@@ -519,9 +477,9 @@ var bcrypt = require('bcrypt'),
};
User.unfollow = function(uid, unfollowid, callback) {
- RDB.srem('following:' + uid, unfollowid, function(err, data) {
+ db.setRemove('following:' + uid, unfollowid, function(err, data) {
if (!err) {
- RDB.srem('followers:' + unfollowid, uid, function(err, data) {
+ db.setRemove('followers:' + unfollowid, uid, function(err, data) {
callback(data);
});
} else {
@@ -531,7 +489,7 @@ var bcrypt = require('bcrypt'),
};
User.getFollowing = function(uid, callback) {
- RDB.smembers('following:' + uid, function(err, userIds) {
+ db.getSetMembers('following:' + uid, function(err, userIds) {
if (!err) {
User.getDataForUsers(userIds, callback);
} else {
@@ -541,7 +499,7 @@ var bcrypt = require('bcrypt'),
};
User.getFollowers = function(uid, callback) {
- RDB.smembers('followers:' + uid, function(err, userIds) {
+ db.getSetMembers('followers:' + uid, function(err, userIds) {
if (!err) {
User.getDataForUsers(userIds, callback);
} else {
@@ -551,12 +509,12 @@ var bcrypt = require('bcrypt'),
};
User.getFollowingCount = function(uid, callback) {
- RDB.smembers('following:' + uid, function(err, userIds) {
+ db.getSetMembers('following:' + uid, function(err, userIds) {
if (err) {
console.log(err);
} else {
userIds = userIds.filter(function(value) {
- return value !== '0';
+ return parseInt(value, 10) !== 0;
});
callback(userIds.length);
}
@@ -564,12 +522,12 @@ var bcrypt = require('bcrypt'),
};
User.getFollowerCount = function(uid, callback) {
- RDB.smembers('followers:' + uid, function(err, userIds) {
+ db.getSetMembers('followers:' + uid, function(err, userIds) {
if(err) {
console.log(err);
} else {
userIds = userIds.filter(function(value) {
- return value !== '0';
+ return parseInt(value, 10) !== 0;
});
callback(userIds.length);
}
@@ -585,7 +543,7 @@ var bcrypt = require('bcrypt'),
}
function iterator(uid, callback) {
- if(uid === "0") {
+ if(parseInt(uid, 10) === 0) {
return callback(null);
}
@@ -603,7 +561,7 @@ var bcrypt = require('bcrypt'),
User.sendPostNotificationToFollowers = function(uid, tid, pid) {
User.getUserField(uid, 'username', function(err, username) {
- RDB.smembers('followers:' + uid, function(err, followers) {
+ db.getSetMembers('followers:' + uid, function(err, followers) {
topics.getTopicField(tid, 'slug', function(err, slug) {
var message = '' + username + ' made a new post';
@@ -616,9 +574,9 @@ var bcrypt = require('bcrypt'),
};
User.isFollowing = function(uid, theirid, callback) {
- RDB.sismember('following:' + uid, theirid, function(err, data) {
+ db.isSetMember('following:' + uid, theirid, function(err, isMember) {
if (!err) {
- callback(data === 1);
+ callback(isMember);
} else {
console.log(err);
}
@@ -632,8 +590,10 @@ var bcrypt = require('bcrypt'),
};
User.count = function(socket) {
- RDB.get('usercount', function(err, count) {
- RDB.handle(err);
+ db.getObjectField('global', 'userCount', function(err, count) {
+ if(err) {
+ return;
+ }
socket.emit('user.count', {
count: count ? count : 0
@@ -642,11 +602,11 @@ var bcrypt = require('bcrypt'),
};
User.getUidByUsername = function(username, callback) {
- RDB.hget('username:uid', username, callback);
+ db.getObjectField('username:uid', username, callback);
};
User.getUidByUserslug = function(userslug, callback) {
- RDB.hget('userslug:uid', userslug, callback);
+ db.getObjectField('userslug:uid', userslug, callback);
};
User.getUsernamesByUids = function(uids, callback) {
@@ -688,52 +648,54 @@ var bcrypt = require('bcrypt'),
};
User.getUidByEmail = function(email, callback) {
- RDB.hget('email:uid', email, function(err, data) {
+ db.getObjectField('email:uid', email, function(err, data) {
if (err) {
- RDB.handle(err);
+ return callback(err);
}
- callback(data);
+ callback(null, data);
});
};
User.getUidByTwitterId = function(twid, callback) {
- RDB.hget('twid:uid', twid, function(err, uid) {
+ db.getObjectField('twid:uid', twid, function(err, uid) {
if (err) {
- RDB.handle(err);
+ return callback(err);
}
- callback(uid);
+ callback(null, uid);
});
};
User.getUidByGoogleId = function(gplusid, callback) {
- RDB.hget('gplusid:uid', gplusid, function(err, uid) {
+ db.getObjectField('gplusid:uid', gplusid, function(err, uid) {
if (err) {
- RDB.handle(err);
+ return callback(err);
}
- callback(uid);
+ callback(null, uid);
});
};
User.getUidByFbid = function(fbid, callback) {
- RDB.hget('fbid:uid', fbid, function(err, uid) {
+ db.getObjectField('fbid:uid', fbid, function(err, uid) {
if (err) {
- RDB.handle(err);
+ return callback(err);
}
- callback(uid);
+ callback(null, uid);
});
};
User.isModerator = function(uid, cid, callback) {
- RDB.sismember('cid:' + cid + ':moderators', uid, function(err, exists) {
- RDB.handle(err);
- callback(err, !! exists);
+ db.isSetMember('cid:' + cid + ':moderators', uid, function(err, exists) {
+ if(err) {
+ return calback(err);
+ }
+ callback(err, exists);
});
};
User.isAdministrator = function(uid, callback) {
- Groups.getGidFromName('Administrators', function(err, gid) {
- Groups.isMember(uid, gid, function(err, isAdmin) {
- callback(err, !! isAdmin);
+ groups.getGidFromName('Administrators', function(err, gid) {
+ groups.isMember(uid, gid, function(err, isAdmin) {
+ callback(err, isAdmin);
});
});
};
@@ -745,15 +707,15 @@ var bcrypt = require('bcrypt'),
callback = null;
}
- RDB.hget('reset:uid', code, function(err, uid) {
+ db.getObjectField('reset:uid', code, function(err, uid) {
if (err) {
- RDB.handle(err);
+ return callback(false);
}
if (uid !== null) {
- RDB.hget('reset:expiry', code, function(err, expiry) {
+ db.getObjectField('reset:expiry', code, function(err, expiry) {
if (err) {
- RDB.handle(err);
+ return callback(false);
}
if (expiry >= +Date.now() / 1000 | 0) {
@@ -766,8 +728,8 @@ var bcrypt = require('bcrypt'),
}
} else {
// Expired, delete from db
- RDB.hdel('reset:uid', code);
- RDB.hdel('reset:expiry', code);
+ db.deleteObjectField('reset:uid', code);
+ db.deleteObjectField('reset:expiry', code);
if (!callback) {
socket.emit('user:reset.valid', {
valid: false
@@ -789,12 +751,12 @@ var bcrypt = require('bcrypt'),
});
},
send: function(socket, email) {
- User.getUidByEmail(email, function(uid) {
+ User.getUidByEmail(email, function(err, uid) {
if (uid !== null) {
// Generate a new reset code
var reset_code = utils.generateUUID();
- RDB.hset('reset:uid', reset_code, uid);
- RDB.hset('reset:expiry', reset_code, (60 * 60) + new Date() / 1000 | 0); // Active for one hour
+ db.setObjectField('reset:uid', reset_code, uid);
+ db.setobjectField('reset:expiry', reset_code, (60 * 60) + new Date() / 1000 | 0); // Active for one hour
var reset_link = nconf.get('url') + 'reset/' + reset_code,
reset_email = global.templates['emails/reset'].parse({
@@ -842,17 +804,17 @@ var bcrypt = require('bcrypt'),
commit: function(socket, code, password) {
this.validate(socket, code, function(validated) {
if (validated) {
- RDB.hget('reset:uid', code, function(err, uid) {
+ db.getObjectField('reset:uid', code, function(err, uid) {
if (err) {
- RDB.handle(err);
+ return;
}
User.hashPassword(password, function(err, hash) {
User.setUserField(uid, 'password', hash);
});
- RDB.hdel('reset:uid', code);
- RDB.hdel('reset:expiry', code);
+ db.deleteObjectField('reset:uid', code);
+ db.deleteObjectField('reset:expiry', code);
socket.emit('user:reset.commit', {
status: 'ok'
@@ -863,9 +825,48 @@ var bcrypt = require('bcrypt'),
}
};
+ User.sendConfirmationEmail = function(email) {
+ if (meta.config['email:smtp:host'] && meta.config['email:smtp:port'] && meta.config['email:from']) {
+ var confirm_code = utils.generateUUID(),
+ confirm_link = nconf.get('url') + 'confirm/' + confirm_code,
+ confirm_email = global.templates['emails/header'] + global.templates['emails/email_confirm'].parse({
+ 'CONFIRM_LINK': confirm_link
+ }) + global.templates['emails/footer'],
+ confirm_email_plaintext = global.templates['emails/email_confirm_plaintext'].parse({
+ 'CONFIRM_LINK': confirm_link
+ });
+
+ // Email confirmation code
+ var expiry_time = Date.now() / 1000 + 60 * 60 * 2;
+
+ db.setObjectField('email:confirm', email, confirm_code);
+
+ db.setObjectField('confirm:email', confirm_code, email);
+ db.setObjectField('confirm:email', confirm_code + ':expire', expiry_time);
+
+ // Send intro email w/ confirm code
+ var message = emailjs.message.create({
+ text: confirm_email_plaintext,
+ from: meta.config['email:from'] || 'localhost@example.org',
+ to: email,
+ subject: '[NodeBB] Registration Email Verification',
+ attachment: [{
+ data: confirm_email,
+ alternative: true
+ }]
+ });
+
+ emailjsServer.send(message, function(err, success) {
+ if (err) {
+ console.log(err);
+ }
+ });
+ }
+ };
+
User.email = {
exists: function(socket, email, callback) {
- User.getUidByEmail(email, function(exists) {
+ User.getUidByEmail(email, function(err, exists) {
exists = !! exists;
if (typeof callback !== 'function') {
socket.emit('user.email.exists', {
@@ -877,14 +878,30 @@ var bcrypt = require('bcrypt'),
});
},
confirm: function(code, callback) {
- RDB.get('confirm:' + code + ':email', function(err, email) {
+ db.getObjectFields('confirm:email', [code, code + ':expire'], function(err, data) {
if (err) {
- RDB.handle(err);
+ return callback({
+ status:'error'
+ });
+ }
+
+ var email = data.email;
+ var expiry = data[code + ':expire'];
+ if (parseInt(expiry, 10) >= Date.now() / 1000) {
+
+ db.deleteObjectField('confirm:email', code);
+ db.deleteObjectField('confirm:email', code + ':expire');
+
+ return callback({
+ status: 'expired'
+ });
}
if (email !== null) {
- RDB.set('email:' + email + ':confirm', true);
- RDB.del('confirm:' + code + ':email');
+ db.setObjectField('email:confirm', email, true);
+
+ db.deleteObjectField('confirm:email', code);
+ db.deleteObjectField('confirm:email', code + ':expire');
callback({
status: 'ok'
});
@@ -903,7 +920,7 @@ var bcrypt = require('bcrypt'),
async.parallel({
unread: function(next) {
- RDB.zrevrange('uid:' + uid + ':notifications:unread', 0, 10, function(err, nids) {
+ db.getSortedSetRevRange('uid:' + uid + ':notifications:unread', 0, 10, function(err, nids) {
// @todo handle err
var unread = [];
@@ -919,7 +936,7 @@ var bcrypt = require('bcrypt'),
if (notif_data) {
unread.push(notif_data);
} else {
- RDB.zrem('uid:' + uid + ':notifications:unread', nid);
+ db.sortedSetRemove('uid:' + uid + ':notifications:unread', nid);
}
next();
@@ -933,7 +950,7 @@ var bcrypt = require('bcrypt'),
});
},
read: function(next) {
- RDB.zrevrange('uid:' + uid + ':notifications:read', 0, 10, function(err, nids) {
+ db.getSortedSetRevRange('uid:' + uid + ':notifications:read', 0, 10, function(err, nids) {
// @todo handle err
var read = [];
@@ -949,7 +966,7 @@ var bcrypt = require('bcrypt'),
if (notif_data) {
read.push(notif_data);
} else {
- RDB.zrem('uid:' + uid + ':notifications:read', nid);
+ db.sortedSetRemove('uid:' + uid + ':notifications:read', nid);
}
next();
@@ -981,13 +998,13 @@ var bcrypt = require('bcrypt'),
before = new Date(parseInt(before, 10));
}
- RDB.multi()
- .zrevrangebyscore('uid:' + uid + ':notifications:read', before ? before.getTime(): now.getTime(), -Infinity, 'LIMIT', 0, limit)
- .zrevrangebyscore('uid:' + uid + ':notifications:unread', before ? before.getTime(): now.getTime(), -Infinity, 'LIMIT', 0, limit)
- .exec(function(err, results) {
- // Merge the read and unread notifications
- var nids = results[0].concat(results[1]);
+ var args1 = ['uid:' + uid + ':notifications:read', before ? before.getTime(): now.getTime(), -Infinity, 'LIMIT', 0, limit];
+ var args2 = ['uid:' + uid + ':notifications:unread', before ? before.getTime(): now.getTime(), -Infinity, 'LIMIT', 0, limit];
+
+ db.getSortedSetRevRangeByScore(args1, function(err, results1) {
+ db.getSortedSetRevRangeByScore(args2, function(err, results2) {
+ var nids = results1.concat(results2);
async.map(nids, function(nid, next) {
notifications.get(nid, uid, function(notif_data) {
next(null, notif_data);
@@ -1007,14 +1024,20 @@ var bcrypt = require('bcrypt'),
callback(err, notifs);
});
});
+ });
+
},
getUnreadCount: function(uid, callback) {
- RDB.zcount('uid:' + uid + ':notifications:unread', -Infinity, Infinity, callback);
+ db.sortedSetCount('uid:' + uid + ':notifications:unread', -Infinity, Infinity, callback);
},
getUnreadByUniqueId: function(uid, uniqueId, callback) {
- RDB.zrange('uid:' + uid + ':notifications:unread', 0, -1, function(err, nids) {
+ db.getSortedSetRange('uid:' + uid + ':notifications:unread', 0, -1, function(err, nids) {
+
async.filter(nids, function(nid, next) {
notifications.get(nid, uid, function(notifObj) {
+ if(!notifObj) {
+ next(false);
+ }
if (notifObj.uniqueId === uniqueId) {
next(true);
} else {
diff --git a/src/webserver.js b/src/webserver.js
index 389255a0a7..ad8614911d 100644
--- a/src/webserver.js
+++ b/src/webserver.js
@@ -5,7 +5,6 @@ var path = require('path'),
express_namespace = require('express-namespace'),
WebServer = express(),
server = require('http').createServer(WebServer),
- RedisStore = require('connect-redis')(express),
nconf = require('nconf'),
winston = require('winston'),
validator = require('validator'),
@@ -14,7 +13,7 @@ var path = require('path'),
pkg = require('../package.json'),
utils = require('../public/src/utils'),
- RDB = require('./redis'),
+ db = require('./database'),
user = require('./user'),
categories = require('./categories'),
posts = require('./posts'),
@@ -141,17 +140,16 @@ var path = require('path'),
}));
app.use(express.bodyParser()); // Puts POST vars in request.body
app.use(express.cookieParser()); // If you want to parse cookies (res.cookies)
+
app.use(express.session({
- store: new RedisStore({
- client: RDB,
- ttl: 60 * 60 * 24 * 30
- }),
+ store: db.sessionStore,
secret: nconf.get('secret'),
key: 'express.sid',
cookie: {
maxAge: 60 * 60 * 24 * 30 * 1000 // 30 days
}
}));
+
app.use(express.csrf());
// Local vars, other assorted setup
@@ -173,33 +171,33 @@ var path = require('path'),
function(next) {
async.parallel([
function(next) {
- // Theme configuration
- RDB.hmget('config', 'theme:type', 'theme:id', 'theme:staticDir', 'theme:templates', function(err, themeData) {
- var themeId = (themeData[1] || 'nodebb-theme-vanilla');
+
+ db.getObjectFields('config', ['theme:type', 'theme:id', 'theme:staticDir', 'theme:templates'], function(err, themeData) {
+ var themeId = (themeData['theme:id'] || 'nodebb-theme-vanilla');
// Detect if a theme has been selected, and handle appropriately
- if (!themeData[0] || themeData[0] === 'local') {
+ if (!themeData['theme:type'] || themeData['theme:type'] === 'local') {
// Local theme
if (process.env.NODE_ENV === 'development') {
winston.info('[themes] Using theme ' + themeId);
}
// Theme's static directory
- if (themeData[2]) {
- app.use('/css/assets', express.static(path.join(__dirname, '../node_modules', themeData[1], themeData[2]), {
+ if (themeData['theme:staticDir']) {
+ app.use('/css/assets', express.static(path.join(__dirname, '../node_modules', themeData['theme:id'], themeData['theme:staticDir']), {
maxAge: app.enabled('cache') ? 5184000000 : 0
}));
if (process.env.NODE_ENV === 'development') {
- winston.info('Static directory routed for theme: ' + themeData[1]);
+ winston.info('Static directory routed for theme: ' + themeData['theme:id']);
}
}
- if (themeData[3]) {
- app.use('/templates', express.static(path.join(__dirname, '../node_modules', themeData[1], themeData[3]), {
+ if (themeData['theme:templates']) {
+ app.use('/templates', express.static(path.join(__dirname, '../node_modules', themeData['theme:id'], themeData['theme:templates']), {
maxAge: app.enabled('cache') ? 5184000000 : 0
}));
if (process.env.NODE_ENV === 'development') {
- winston.info('Custom templates directory routed for theme: ' + themeData[1]);
+ winston.info('Custom templates directory routed for theme: ' + themeData['theme:id']);
}
}
@@ -411,7 +409,7 @@ var path = require('path'),
"categories": function (next) {
categories.getAllCategories(0, function (err, returnData) {
returnData.categories = returnData.categories.filter(function (category) {
- if (category.disabled !== '1') {
+ if (parseInt(category.disabled, 10) !== 1) {
return true;
} else {
return false;
@@ -467,7 +465,7 @@ var path = require('path'),
function (next) {
topics.getTopicWithPosts(tid, ((req.user) ? req.user.uid : 0), 0, -1, true, function (err, topicData) {
if (topicData) {
- if (topicData.deleted === '1' && topicData.expose_tools === 0) {
+ if (parseInt(topicData.deleted, 10) === 1 && parseInt(topicData.expose_tools, 10) === 0) {
return next(new Error('Topic deleted'), null);
}
}
@@ -589,7 +587,7 @@ var path = require('path'),
categories.getCategoryById(cid, 0, function (err, categoryData) {
if (categoryData) {
- if (categoryData.disabled === '1') {
+ if (parseInt(categoryData.disabled, 10) === 1) {
return next(new Error('Category disabled'), null);
}
}
diff --git a/src/websockets.js b/src/websockets.js
index 6e18ee6edd..8a76ca3402 100644
--- a/src/websockets.js
+++ b/src/websockets.js
@@ -9,15 +9,10 @@ var cookie = require('cookie'),
gravatar = require('gravatar'),
winston = require('winston'),
- RedisStoreLib = require('connect-redis')(express),
- RDB = require('./redis'),
- RedisStore = new RedisStoreLib({
- client: RDB,
- ttl: 60 * 60 * 24 * 14
- }),
+ db = require('./database'),
user = require('./user'),
- Groups = require('./groups'),
+ groups = require('./groups'),
posts = require('./posts'),
favourites = require('./favourites'),
utils = require('../public/src/utils'),
@@ -70,9 +65,12 @@ websockets.init = function(io) {
// Validate the session, if present
socketCookieParser(hs, {}, function(err) {
sessionID = socket.handshake.signedCookies["express.sid"];
- RedisStore.get(sessionID, function(err, sessionData) {
- if (!err && sessionData && sessionData.passport && sessionData.passport.user) uid = users[sessionID] = sessionData.passport.user;
- else uid = users[sessionID] = 0;
+ db.sessionStore.get(sessionID, function(err, sessionData) {
+ if (!err && sessionData && sessionData.passport && sessionData.passport.user) {
+ uid = users[sessionID] = sessionData.passport.user;
+ } else {
+ uid = users[sessionID] = 0;
+ }
userSockets[uid] = userSockets[uid] || [];
userSockets[uid].push(socket);
@@ -89,7 +87,7 @@ websockets.init = function(io) {
if (uid) {
- RDB.zadd('users:online', Date.now(), uid, function(err, data) {
+ db.sortedSetAdd('users:online', Date.now(), uid, function(err, data) {
socket.join('uid_' + uid);
user.getUserField(uid, 'username', function(err, username) {
@@ -117,7 +115,7 @@ websockets.init = function(io) {
delete users[sessionID];
delete userSockets[uid];
if (uid) {
- RDB.zrem('users:online', uid, function(err, data) {
+ db.sortedSetRemove('users:online', uid, function(err, data) {
});
}
}
@@ -348,7 +346,7 @@ websockets.init = function(io) {
});
socket.on('api:topics.post', function(data) {
- if (uid < 1 && meta.config.allowGuestPosting === '0') {
+ if (uid < 1 && parseInt(meta.config.allowGuestPosting, 10) === 0) {
socket.emit('event:alert', {
title: 'Post Unsuccessful',
message: 'You don't seem to be logged in, so you cannot reply.',
@@ -419,7 +417,7 @@ websockets.init = function(io) {
});
socket.on('api:posts.reply', function(data) {
- if (uid < 1 && meta.config.allowGuestPosting === '0') {
+ if (uid < 1 && parseInt(meta.config.allowGuestPosting, 10) === 0) {
socket.emit('event:alert', {
title: 'Reply Unsuccessful',
message: 'You don't seem to be logged in, so you cannot reply.',
@@ -781,7 +779,7 @@ websockets.init = function(io) {
});
socket.on('api:composer.push', function(data) {
- if (uid > 0 || meta.config.allowGuestPosting === '1') {
+ if (parseInt(uid, 10) > 0 || parseInt(meta.config.allowGuestPosting, 10) === 1) {
if (parseInt(data.tid) > 0) {
topics.getTopicData(data.tid, function(err, topicData) {
if (data.body)
@@ -1029,16 +1027,16 @@ websockets.init = function(io) {
};
if (set) {
- Groups.joinByGroupName('cid:' + cid + ':privileges:' + privilege, uid, cb);
+ groups.joinByGroupName('cid:' + cid + ':privileges:' + privilege, uid, cb);
} else {
- Groups.leaveByGroupName('cid:' + cid + ':privileges:' + privilege, uid, cb);
+ groups.leaveByGroupName('cid:' + cid + ':privileges:' + privilege, uid, cb);
}
});
socket.on('api:admin.categories.getPrivilegeSettings', function(cid, callback) {
async.parallel({
"+r": function(next) {
- Groups.getByGroupName('cid:' + cid + ':privileges:+r', { expand: true }, function(err, groupObj) {
+ groups.getByGroupName('cid:' + cid + ':privileges:+r', { expand: true }, function(err, groupObj) {
if (!err) {
next.apply(this, arguments);
} else {
@@ -1049,7 +1047,7 @@ websockets.init = function(io) {
});
},
"+w": function(next) {
- Groups.getByGroupName('cid:' + cid + ':privileges:+w', { expand: true }, function(err, groupObj) {
+ groups.getByGroupName('cid:' + cid + ':privileges:+w', { expand: true }, function(err, groupObj) {
if (!err) {
next.apply(this, arguments);
} else {
@@ -1090,19 +1088,19 @@ websockets.init = function(io) {
*/
socket.on('api:groups.create', function(data, callback) {
- Groups.create(data.name, data.description, function(err, groupObj) {
+ groups.create(data.name, data.description, function(err, groupObj) {
callback(err ? err.message : null, groupObj || undefined);
});
});
socket.on('api:groups.delete', function(gid, callback) {
- Groups.destroy(gid, function(err) {
+ groups.destroy(gid, function(err) {
callback(err ? err.message : null, err ? null : 'OK');
});
});
socket.on('api:groups.get', function(gid, callback) {
- Groups.get(gid, {
+ groups.get(gid, {
expand: true
}, function(err, groupObj) {
callback(err ? err.message : null, groupObj || undefined);
@@ -1110,15 +1108,15 @@ websockets.init = function(io) {
});
socket.on('api:groups.join', function(data, callback) {
- Groups.join(data.gid, data.uid, callback);
+ groups.join(data.gid, data.uid, callback);
});
socket.on('api:groups.leave', function(data, callback) {
- Groups.leave(data.gid, data.uid, callback);
+ groups.leave(data.gid, data.uid, callback);
});
socket.on('api:groups.update', function(data, callback) {
- Groups.update(data.gid, data.values, function(err) {
+ groups.update(data.gid, data.values, function(err) {
callback(err ? err.message : null);
});
});
@@ -1128,14 +1126,14 @@ websockets.init = function(io) {
function emitTopicPostStats() {
- RDB.mget(['totaltopiccount', 'totalpostcount'], function(err, data) {
+ db.getObjectFields('global', ['topicCount', 'postCount'], function(err, data) {
if (err) {
return winston.err(err);
}
var stats = {
- topics: data[0] ? data[0] : 0,
- posts: data[1] ? data[1] : 0
+ topics: data.topicCount ? data.topicCount : 0,
+ posts: data.postCount ? data.postCount : 0
};
io.sockets.emit('post.stats', stats);
@@ -1143,7 +1141,7 @@ websockets.init = function(io) {
}
websockets.emitUserCount = function() {
- RDB.get('usercount', function(err, count) {
+ db.getObjectField('global', 'userCount', function(err, count) {
io.sockets.emit('user.count', {
count: count
});
@@ -1154,8 +1152,10 @@ websockets.init = function(io) {
return io.sockets.in(room);
};
+}
+
websockets.getConnectedClients = function() {
return userSockets;
}
-}
-})(module.exports);
\ No newline at end of file
+
+})(module.exports);
diff --git a/tests/categories.js b/tests/categories.js
index 11456b54a4..070a71e884 100644
--- a/tests/categories.js
+++ b/tests/categories.js
@@ -7,14 +7,7 @@ process.on('uncaughtException', function (err) {
});
var assert = require('assert'),
- RDB = require('../mocks/redismock');
-
-// Reds is not technically used in this test suite, but its invocation is required to stop the included
-// libraries from trying to connect to the default Redis host/port
-var reds = require('reds');
-reds.createClient = function () {
- return reds.client || (reds.client = RDB);
-};
+ db = require('../mocks/databasemock');
var Categories = require('../src/categories');
@@ -23,6 +16,7 @@ describe('Categories', function() {
describe('.create', function() {
it('should create a new category', function(done) {
+
Categories.create({
name: 'Test Category',
description: 'Test category created by testing script',
@@ -62,9 +56,7 @@ describe('Categories', function() {
});
after(function() {
- RDB.multi()
- .del('category:'+categoryObj.cid)
- .rpop('categories:cid')
- .exec();
+ db.delete('category:' + categoryObj.cid);
+ db.listRemoveLast('categories:cid');
});
});
\ No newline at end of file
diff --git a/tests/database.js b/tests/database.js
new file mode 100644
index 0000000000..071b291957
--- /dev/null
+++ b/tests/database.js
@@ -0,0 +1,330 @@
+var assert = require('assert'),
+ db = require('../mocks/databasemock'),
+ async = require('async');
+
+
+describe('Test database', function() {
+ it('should work', function(){
+ assert.doesNotThrow(function(){
+ var db = require('../mocks/databasemock');
+ });
+ });
+
+ it('should not throw err', function(done) {
+ var objectKey = 'testObj';
+
+ function setObject(callback) {
+ db.setObject(objectKey, {name:'baris', 'lastname':'usakli', age:3}, function(err, result) {
+ callback(err, {'setObject':result});
+ });
+ }
+
+ function getObject(callback) {
+ db.getObject(objectKey, function(err, data) {
+ callback(err, {'getObject':data});
+ });
+ }
+
+ function getObjects(callback) {
+ db.getObjects(['testing1', objectKey, 'doesntexist', 'user:1'], function(err, data) {
+ callback(err, {'getObjects':data});
+ });
+ }
+
+ function setObjectField(callback) {
+ db.setObjectField(objectKey, 'reputation', 5, function(err, result) {
+ callback(err, {'setObjectField': result});
+ });
+ }
+
+ function getObjectField(callback) {
+ db.getObjectField(objectKey, 'age', function(err, age) {
+ callback(err, {'getObjectField' : age});
+ });
+ }
+
+ function getObjectFields(callback) {
+ db.getObjectFields(objectKey, ['name', 'lastname'], function(err, data) {
+ callback(err, {'getObjectFields':data});
+ });
+ }
+
+ function getObjectValues(callback) {
+ db.getObjectValues(objectKey, function(err, data) {
+ callback(err, {'getObjectValues':data});
+ });
+ }
+
+ function isObjectField(callback) {
+ db.isObjectField(objectKey, 'age', function(err, data) {
+ callback(err, {'isObjectField':data});
+ });
+ }
+
+ function deleteObjectField(callback) {
+ db.deleteObjectField(objectKey, 'reputation', function(err, data) {
+ callback(err, {'deleteObjectField':data});
+ });
+ }
+
+ function incrObjectFieldBy(callback) {
+ db.incrObjectFieldBy(objectKey, 'age', 3, function(err, data) {
+ callback(err, {'incrObjectFieldBy':data});
+ });
+ }
+
+ var objectTasks = [
+ setObject,
+ getObject,
+ deleteObjectField,
+ getObject,
+ setObjectField,
+ getObject,
+ deleteObjectField,
+ getObject,
+ getObjectField,
+ getObjectFields,
+ getObjectValues,
+ isObjectField,
+ incrObjectFieldBy,
+ getObject,
+ getObjects
+ ];
+
+ async.series(objectTasks, function(err, results) {
+ assert.equal(err, null, 'error in object methods');
+ assert.ok(results);
+
+ done();
+ });
+ });
+
+ it('should not throw err', function(done) {
+
+ function sortedSetAdd(callback) {
+ db.sortedSetAdd('sortedSet3', 12, 5, function(err, data) {
+ callback(err, {'sortedSetAdd': data});
+ });
+ }
+
+ function sortedSetRemove(callback) {
+ db.sortedSetRemove('sortedSet3', 12, function(err, data) {
+ callback(err, {'sortedSetRemove': data});
+ });
+ }
+
+ function getSortedSetRange(callback) {
+ db.getSortedSetRevRange('sortedSet3', 0, -1, function(err, data) {
+ callback(err, {'getSortedSetRange': data});
+ });
+ }
+
+ function getSortedSetRevRangeByScore(callback) {
+ var args = ['sortedSet2', '+inf', 100, 'LIMIT', 0, 10];
+ db.getSortedSetRevRangeByScore(args, function(err, data) {
+ callback(err, {'getSortedSetRevRangeByScore': data});
+ });
+ }
+
+ function sortedSetCount(callback) {
+ db.sortedSetCount('sortedSet3', -Infinity, Infinity, function(err, data) {
+ callback(err, {'sortedSetCount': data});
+ });
+ }
+
+ function sortedSetScore(callback) {
+ db.sortedSetScore('users:joindate', 1, function(err, data) {
+ callback(err, {'sortedSetScore': data});
+ });
+ }
+
+ function sortedSetsScore(callback) {
+ db.sortedSetsScore(['users:joindate', 'users:derp', 'users:postcount'], 1, function(err, data) {
+ callback(err, {'sortedSetsScore': data});
+ });
+ }
+
+ var sortedSetTasks = [
+ sortedSetAdd,
+ getSortedSetRange,
+ sortedSetAdd,
+ getSortedSetRange,
+ sortedSetRemove,
+ getSortedSetRange,
+ sortedSetCount,
+ sortedSetScore,
+ sortedSetsScore,
+ getSortedSetRevRangeByScore
+ ];
+
+ async.series(sortedSetTasks, function(err, results) {
+ assert.equal(err, null, 'error in sorted set methods');
+ assert.ok(results);
+
+ done();
+ });
+
+ });
+
+ it('should not throw err', function(done) {
+
+ function listAppend(callback) {
+ db.listAppend('myList5', 5, function(err, data) {
+ callback(err, {'listAppend': data});
+ });
+ }
+
+ function listPrepend(callback) {
+ db.listPrepend('myList5', 4, function(err, data) {
+ callback(err, {'listPrepend': data});
+ });
+ }
+
+
+ function listRemoveLast(callback) {
+ db.listRemoveLast('myList5', function(err, data) {
+ callback(err, {'listRemoveLast': data});
+ });
+ }
+
+
+ function getListRange(callback) {
+ db.getListRange('myList5', 0, -1, function(err, data) {
+ callback(err, {'getListRange': data});
+ });
+ }
+
+ var listTasks = [
+ listAppend,
+ listPrepend,
+ getListRange,
+ listRemoveLast,
+ getListRange
+ ];
+
+ async.series(listTasks, function(err, results) {
+ assert.equal(err, null, 'error in list methods');
+ assert.ok(results);
+
+ done();
+ });
+
+ });
+
+ it('should not throw err', function(done) {
+
+ function get(callback) {
+ db.get('testingStr', function(err, data) {
+ callback(err, {'get': data});
+ });
+ }
+
+ function set(callback) {
+ db.set('testingStr', 'opppa gangastayla', function(err, data) {
+ callback(err, {'set': data});
+ });
+ }
+
+ function deleteKey(callback) {
+ db.delete('testingStr', function(err, data) {
+ callback(err, {'delete': data});
+ });
+ }
+
+ function exists(callback) {
+ db.exists('testingStr', function(err, data) {
+ callback(err, {'exists': data});
+ });
+ }
+
+ var keyTasks = [
+ get,
+ set,
+ get,
+ exists,
+ deleteKey,
+ deleteKey,
+ get,
+ exists
+ ];
+
+ async.series(keyTasks, function(err, results) {
+ assert.equal(err, null, 'error in key methods');
+ assert.ok(results);
+
+ done();
+ });
+
+ });
+
+ it('should not throw err', function(done) {
+
+
+ function setAdd(callback) {
+ db.setAdd('myTestSet', 15, function(err, data) {
+ callback(err, {'setAdd': data});
+ });
+ }
+
+ function setRemove(callback) {
+ db.setRemove('myTestSet', 15, function(err, data) {
+ callback(err, {'setRemove': data});
+ });
+ }
+
+ function getSetMembers(callback) {
+ db.getSetMembers('myTestSet', function(err, data) {
+ callback(err, {'getSetMembers': data});
+ });
+ }
+
+ function isSetMember(callback) {
+ db.isSetMember('myTestSet', 15, function(err, data) {
+ callback(err, {'isSetMember': data});
+ });
+ }
+
+ function isMemberOfSets(callback) {
+ db.isMemberOfSets(['doesntexist', 'myTestSet', 'nonexistingSet'], 15, function(err, data) {
+ callback(err, {'isMemberOfSets': data});
+ });
+ }
+
+ function setRemoveRandom(callback) {
+ db.setRemoveRandom('myTestSet', function(err, data) {
+ callback(err, {'setRemoveRandom': data});
+ });
+ }
+
+ function setCount(callback) {
+ db.setCount('myTestSet', function(err, data) {
+ callback(err, {'setCount': data});
+ });
+ }
+
+
+ var setTasks = [
+ getSetMembers,
+ setAdd,
+ getSetMembers,
+ setRemove,
+ getSetMembers,
+ isSetMember,
+ setAdd,
+ getSetMembers,
+ isSetMember,
+ setRemoveRandom,
+ getSetMembers,
+ setCount,
+ isMemberOfSets
+ ];
+
+
+ require('async').series(setTasks, function(err, results) {
+ assert.equal(err, null, 'error in set methods');
+ assert.ok(results);
+
+ done();
+ });
+ });
+});
diff --git a/tests/redis.js b/tests/redis.js
deleted file mode 100644
index 0af4f1a120..0000000000
--- a/tests/redis.js
+++ /dev/null
@@ -1,10 +0,0 @@
-var assert = require('assert');
-
-
-describe('Test database', function() {
- it('should work', function(){
- assert.doesNotThrow(function(){
- var RDB = require('../mocks/redismock');
- });
- });
-});
diff --git a/tests/topics.js b/tests/topics.js
index 063451cc3e..9d27db8cf7 100644
--- a/tests/topics.js
+++ b/tests/topics.js
@@ -5,14 +5,8 @@ process.on('uncaughtException', function (err) {
});
var assert = require('assert'),
- RDB = require('../mocks/redismock');
+ db = require('../mocks/databasemock');
-// Reds is not technically used in this test suite, but its invocation is required to stop the included
-// libraries from trying to connect to the default Redis host/port
-var reds = require('reds');
-reds.createClient = function () {
- return reds.client || (reds.client = RDB);
-};
var Topics = require('../src/topics');
@@ -43,7 +37,7 @@ describe('Topic\'s', function() {
topic.userId = null;
Topics.post(topic.userId, topic.title, topic.content, topic.categoryId, function(err, result) {
- assert.equal(err.message, 'not-logged-in');
+ assert.equal(err.message, 'invalid-user');
done();
});
});
@@ -75,11 +69,6 @@ describe('Topic\'s', function() {
});
after(function() {
- RDB.send_command('flushdb', [], function(error){
- if(error){
- winston.error(error);
- throw new Error(error);
- }
- });
+ db.flushdb();
});
});
\ No newline at end of file
diff --git a/tests/user.js b/tests/user.js
index cb89ac1fc9..7adfca61ee 100644
--- a/tests/user.js
+++ b/tests/user.js
@@ -7,7 +7,7 @@ process.on('uncaughtException', function (err) {
});
var assert = require('assert'),
- RDB = require('../mocks/redismock');
+ db = require('../mocks/databasemock');
var User = require('../src/user');
@@ -43,12 +43,6 @@ describe('User', function() {
});
after(function() {
- //Clean up
- RDB.send_command('flushdb', [], function(error){
- if(error){
- winston.error(error);
- throw new Error(error);
- }
- });
+ db.flushdb();
});
});
\ No newline at end of file