commit
9f2196abfb
@ -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));
|
@ -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));
|
@ -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;
|
@ -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<data.length; ++i) {
|
||||
if(data[i]._key === key) {
|
||||
var item = data.splice(i, 1);
|
||||
if(item && item.length) {
|
||||
return item[0];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
for(var i=0; i<keys.length; ++i) {
|
||||
returnData.push(findData(keys[i]));
|
||||
}
|
||||
|
||||
callback(err, returnData);
|
||||
});
|
||||
}
|
||||
|
||||
module.getObjectField = function(key, field, callback) {
|
||||
module.getObjectFields(key, [field], function(err, data) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
callback(null, data[field]);
|
||||
});
|
||||
}
|
||||
|
||||
module.getObjectFields = function(key, fields, callback) {
|
||||
|
||||
var _fields = {};
|
||||
for(var i=0; i<fields.length; ++i) {
|
||||
_fields[fields[i].replace(/\./g, '\uff0E')] = 1;
|
||||
}
|
||||
|
||||
db.collection('objects').findOne({_key:key}, _fields, function(err, item) {
|
||||
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if(item === null) {
|
||||
item = {};
|
||||
}
|
||||
|
||||
for(var i=0; i<fields.length; ++i) {
|
||||
if(item[fields[i]] === null || item[fields[i]] === undefined) {
|
||||
item[fields[i]] = null;
|
||||
}
|
||||
}
|
||||
|
||||
removeHiddenFields(item);
|
||||
|
||||
callback(err, item);
|
||||
});
|
||||
}
|
||||
|
||||
module.getObjectValues = function(key, callback) {
|
||||
module.getObject(key, function(err, data) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var values = [];
|
||||
for(var key in data) {
|
||||
values.push(data[key]);
|
||||
}
|
||||
callback(null, values);
|
||||
});
|
||||
}
|
||||
|
||||
module.isObjectField = function(key, field, callback) {
|
||||
var data = {};
|
||||
field = field.replace(/\./g, '\uff0E');
|
||||
data[field] = '';
|
||||
db.collection('objects').findOne({_key:key}, {fields:data}, function(err, item) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(err, !!item && item[field] !== undefined && item[field] !== null);
|
||||
});
|
||||
}
|
||||
|
||||
module.deleteObjectField = function(key, field, callback) {
|
||||
var data = {};
|
||||
field = field.replace(/\./g, '\uff0E');
|
||||
data[field] = "";
|
||||
db.collection('objects').update({_key:key}, {$unset : data}, function(err, result) {
|
||||
if(callback) {
|
||||
callback(err, result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.incrObjectField = function(key, field, callback) {
|
||||
module.incrObjectFieldBy(key, field, 1, callback);
|
||||
}
|
||||
|
||||
module.decrObjectField = function(key, field, callback) {
|
||||
module.incrObjectFieldBy(key, field, -1, callback);
|
||||
}
|
||||
|
||||
module.incrObjectFieldBy = function(key, field, value, callback) {
|
||||
var data = {};
|
||||
field = field.replace(/\./g, '\uff0E');
|
||||
data[field] = value;
|
||||
db.collection('objects').update({_key:key}, {$inc : data}, {upsert:true}, function(err, result) {
|
||||
module.getObjectField(key, field, function(err, value) {
|
||||
if(callback) {
|
||||
callback(err, value);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// sets
|
||||
|
||||
module.setAdd = function(key, value, callback) {
|
||||
if(value !== null && value !== undefined) {
|
||||
value = value.toString();
|
||||
}
|
||||
db.collection('objects').update({_key:key}, {$addToSet: { members: value }}, {upsert:true, w: 1}, function(err, result) {
|
||||
if(callback) {
|
||||
callback(err, result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.setRemove = function(key, value, callback) {
|
||||
if(value !== null && value !== undefined) {
|
||||
value = value.toString();
|
||||
}
|
||||
db.collection('objects').update({_key:key, members: value}, {$pull : {members: value}}, function(err, result) {
|
||||
if(callback) {
|
||||
callback(err, result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.isSetMember = function(key, value, callback) {
|
||||
if(value !== null && value !== undefined) {
|
||||
value = value.toString();
|
||||
}
|
||||
db.collection('objects').findOne({_key:key, members: value}, function(err, item) {
|
||||
callback(err, item !== null && item !== undefined);
|
||||
});
|
||||
}
|
||||
|
||||
module.isMemberOfSets = function(sets, value, callback) {
|
||||
function iterator(set, next) {
|
||||
module.isSetMember(set, value, function(err, result) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
next(null, result?1:0);
|
||||
});
|
||||
}
|
||||
|
||||
async.map(sets, iterator, function(err, result) {
|
||||
callback(err, result);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
module.getSetMembers = function(key, callback) {
|
||||
db.collection('objects').findOne({_key:key}, {members:1}, function(err, data) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if(!data) {
|
||||
callback(null, []);
|
||||
} else {
|
||||
callback(null, data.members);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.setCount = function(key, callback) {
|
||||
db.collection('objects').findOne({_key:key}, function(err, data) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
if(!data) {
|
||||
return callback(null, 0);
|
||||
}
|
||||
|
||||
callback(null, data.members.length);
|
||||
});
|
||||
}
|
||||
|
||||
module.setRemoveRandom = function(key, callback) {
|
||||
db.collection('objects').findOne({_key:key}, function(err, data) {
|
||||
if(err) {
|
||||
if(callback) {
|
||||
return callback(err);
|
||||
} else {
|
||||
return winston.error(err.message);
|
||||
}
|
||||
}
|
||||
|
||||
if(!data) {
|
||||
if(callback) {
|
||||
callback(null, 0);
|
||||
}
|
||||
} else {
|
||||
var randomIndex = Math.floor(Math.random() * data.members.length);
|
||||
var value = data.members[randomIndex];
|
||||
module.setRemove(data._key, value, function(err, result) {
|
||||
if(callback) {
|
||||
callback(err, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// sorted sets
|
||||
|
||||
module.sortedSetAdd = function(key, score, value, callback) {
|
||||
if(value !== null && value !== undefined) {
|
||||
value = value.toString();
|
||||
}
|
||||
var data = {
|
||||
score:score,
|
||||
value:value
|
||||
};
|
||||
|
||||
data.setName = key;
|
||||
module.setObject(key + ':' + value, data, callback);
|
||||
}
|
||||
|
||||
module.sortedSetRemove = function(key, value, callback) {
|
||||
if(value !== null && value !== undefined) {
|
||||
value = value.toString();
|
||||
}
|
||||
db.collection('objects').remove({setName:key, value:value}, function(err, result) {
|
||||
if(callback) {
|
||||
callback(err, result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getSortedSetRange(key, start, stop, sort, callback) {
|
||||
db.collection('objects').find({setName:key}, {fields:{value:1}})
|
||||
.limit(stop - start + 1)
|
||||
.skip(start)
|
||||
.sort({score: sort})
|
||||
.toArray(function(err, data) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// maybe this can be done with mongo?
|
||||
data = data.map(function(item) {
|
||||
return item.value;
|
||||
});
|
||||
|
||||
callback(err, data);
|
||||
});
|
||||
}
|
||||
|
||||
module.getSortedSetRange = function(key, start, stop, callback) {
|
||||
getSortedSetRange(key, start, stop, 1, callback);
|
||||
}
|
||||
|
||||
module.getSortedSetRevRange = function(key, start, stop, callback) {
|
||||
getSortedSetRange(key, start, stop, -1, callback);
|
||||
}
|
||||
|
||||
module.getSortedSetRevRangeByScore = function(args, callback) {
|
||||
|
||||
//var args = ['topics:recent', '+inf', timestamp - since, 'LIMIT', start, end - start + 1];
|
||||
var key = args[0],
|
||||
max = (args[1] === '+inf')?Number.MAX_VALUE:args[1],
|
||||
min = args[2],
|
||||
start = args[4],
|
||||
stop = args[5];
|
||||
|
||||
|
||||
db.collection('objects').find({setName:key, score: {$gt:min, $lt:max}}, {fields:{value:1}})
|
||||
.limit(stop - start + 1)
|
||||
.skip(start)
|
||||
.sort({score: -1})
|
||||
.toArray(function(err, data) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// maybe this can be done with mongo?
|
||||
data = data.map(function(item) {
|
||||
return item.value;
|
||||
});
|
||||
|
||||
callback(err, data);
|
||||
});
|
||||
}
|
||||
|
||||
module.sortedSetCount = function(key, min, max, callback) {
|
||||
db.collection('objects').count({setName:key, score: {$gt:min, $lt:max}}, function(err, count) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if(!count) {
|
||||
return callback(null, 0);
|
||||
}
|
||||
callback(null,count);
|
||||
});
|
||||
}
|
||||
|
||||
module.sortedSetRank = function(key, value, callback) {
|
||||
if(value !== null && value !== undefined) {
|
||||
value = value.toString();
|
||||
}
|
||||
module.getSortedSetRange(key, 0, -1, function(err, result) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
var rank = result.indexOf(value);
|
||||
if(rank === -1) {
|
||||
return callback(null, null);
|
||||
}
|
||||
|
||||
callback(null, rank);
|
||||
});
|
||||
}
|
||||
|
||||
module.sortedSetScore = function(key, value, callback) {
|
||||
if(value !== null && value !== undefined) {
|
||||
value = value.toString();
|
||||
}
|
||||
db.collection('objects').findOne({setName:key, value: value}, {fields:{score:1}}, function(err, result) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
if(result) {
|
||||
return callback(null, result.score);
|
||||
}
|
||||
|
||||
callback(err, null);
|
||||
});
|
||||
}
|
||||
|
||||
module.sortedSetsScore = function(keys, value, callback) {
|
||||
if(value !== null && value !== undefined) {
|
||||
value = value.toString();
|
||||
}
|
||||
db.collection('objects').find({setName:{$in:keys}, value: value}).toArray(function(err, result) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var returnData = [],
|
||||
resultIndex = 0;
|
||||
|
||||
for(var i=0; i<keys.length; ++i) {
|
||||
|
||||
if(result && resultIndex < result.length && keys[i] === result[resultIndex].setName) {
|
||||
returnData.push(result[resultIndex].score);
|
||||
++resultIndex;
|
||||
} else {
|
||||
returnData.push(null);
|
||||
}
|
||||
}
|
||||
|
||||
callback(null, returnData);
|
||||
});
|
||||
}
|
||||
|
||||
// lists
|
||||
module.listPrepend = function(key, value, callback) {
|
||||
if(value !== null && value !== undefined) {
|
||||
value = value.toString();
|
||||
}
|
||||
module.isObjectField(key, 'array', function(err, exists) {
|
||||
if(err) {
|
||||
if(callback) {
|
||||
return callback(err);
|
||||
} else {
|
||||
return winston.error(err.message);
|
||||
}
|
||||
}
|
||||
|
||||
if(exists) {
|
||||
db.collection('objects').update({_key:key}, {'$set': {'array.-1': value}}, {upsert:true, w:1 }, function(err, result) {
|
||||
if(callback) {
|
||||
callback(err, result);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
module.listAppend(key, value, callback);
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
module.listAppend = function(key, value, callback) {
|
||||
if(value !== null && value !== undefined) {
|
||||
value = value.toString();
|
||||
}
|
||||
db.collection('objects').update({ _key: key }, { $push: { array: value } }, {upsert:true, w:1}, function(err, result) {
|
||||
if(callback) {
|
||||
callback(err, result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.listRemoveLast = function(key, callback) {
|
||||
module.getListRange(key, -1, 0, function(err, value) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
db.collection('objects').update({_key: key }, { $pop: { array: 1 } }, function(err, result) {
|
||||
if(err) {
|
||||
if(callback) {
|
||||
return callback(err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(value && value.length) {
|
||||
if(callback) {
|
||||
callback(err, value[0]);
|
||||
}
|
||||
} else {
|
||||
if(callback) {
|
||||
callback(err, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.getListRange = function(key, start, stop, callback) {
|
||||
|
||||
if(stop === -1) {
|
||||
// mongo doesnt allow -1 as the count argument in slice
|
||||
// pass in a large value to retrieve the whole array
|
||||
stop = Math.pow(2, 31) - 2;
|
||||
}
|
||||
|
||||
db.collection('objects').findOne({_key:key}, { array: { $slice: [start, stop - start + 1] }}, function(err, data) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
if(data && data.array) {
|
||||
callback(null, data.array);
|
||||
} else {
|
||||
callback(null, []);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}(exports));
|
||||
|
@ -0,0 +1,393 @@
|
||||
|
||||
|
||||
(function(module) {
|
||||
'use strict';
|
||||
var redisClient,
|
||||
redis = require('redis'),
|
||||
winston = require('winston'),
|
||||
nconf = require('nconf'),
|
||||
express = require('express'),
|
||||
connectRedis = require('connect-redis')(express),
|
||||
reds = require('reds'),
|
||||
|
||||
redis_socket_or_host = nconf.get('redis:host'),
|
||||
utils = require('./../../public/src/utils.js');
|
||||
|
||||
|
||||
if (redis_socket_or_host && redis_socket_or_host.indexOf('/')>=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<keys.length; ++x) {
|
||||
multi.hgetall(keys[x]);
|
||||
}
|
||||
|
||||
multi.exec(function (err, replies) {
|
||||
callback(err, replies);
|
||||
});
|
||||
}
|
||||
|
||||
module.getObjectField = function(key, field, callback) {
|
||||
module.getObjectFields(key, [field], function(err, data) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
callback(null, data[field]);
|
||||
});
|
||||
}
|
||||
|
||||
module.getObjectFields = function(key, fields, callback) {
|
||||
redisClient.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.getObjectValues = function(key, callback) {
|
||||
redisClient.hvals(key, callback);
|
||||
}
|
||||
|
||||
module.isObjectField = function(key, field, callback) {
|
||||
redisClient.hexists(key, field, function(err, exists) {
|
||||
callback(err, exists === 1);
|
||||
});
|
||||
}
|
||||
|
||||
module.deleteObjectField = function(key, field, callback) {
|
||||
redisClient.hdel(key, field, callback);
|
||||
}
|
||||
|
||||
module.incrObjectField = function(key, field, callback) {
|
||||
redisClient.hincrby(key, field, 1, callback);
|
||||
}
|
||||
|
||||
module.decrObjectField = function(key, field, callback) {
|
||||
redisClient.hincrby(key, field, -1, callback);
|
||||
}
|
||||
|
||||
module.incrObjectFieldBy = function(key, field, value, callback) {
|
||||
redisClient.hincrby(key, field, value, callback);
|
||||
}
|
||||
|
||||
|
||||
// sets
|
||||
|
||||
module.setAdd = function(key, value, callback) {
|
||||
redisClient.sadd(key, value, callback);
|
||||
}
|
||||
|
||||
module.setRemove = function(key, value, callback) {
|
||||
redisClient.srem(key, value, callback);
|
||||
}
|
||||
|
||||
module.isSetMember = function(key, value, callback) {
|
||||
redisClient.sismember(key, value, function(err, result) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
callback(null, result === 1);
|
||||
});
|
||||
}
|
||||
|
||||
module.isMemberOfSets = function(sets, value, callback) {
|
||||
var batch = redisClient.multi();
|
||||
|
||||
for (var i = 0, ii = sets.length; i < ii; i++) {
|
||||
batch.sismember(sets[i], value);
|
||||
}
|
||||
|
||||
batch.exec(callback);
|
||||
}
|
||||
|
||||
module.getSetMembers = function(key, callback) {
|
||||
redisClient.smembers(key, callback);
|
||||
}
|
||||
|
||||
module.setCount = function(key, callback) {
|
||||
redisClient.scard(key, callback);
|
||||
}
|
||||
|
||||
module.setRemoveRandom = function(key, callback) {
|
||||
redisClient.spop(key, callback);
|
||||
}
|
||||
|
||||
// sorted sets
|
||||
|
||||
module.sortedSetAdd = function(key, score, value, callback) {
|
||||
redisClient.zadd(key, score, value, callback);
|
||||
}
|
||||
|
||||
module.sortedSetRemove = function(key, value, callback) {
|
||||
redisClient.zrem(key, value, callback);
|
||||
}
|
||||
|
||||
module.getSortedSetRange = function(key, start, stop, callback) {
|
||||
redisClient.zrange(key, start, stop, callback);
|
||||
}
|
||||
|
||||
module.getSortedSetRevRange = function(key, start, stop, callback) {
|
||||
redisClient.zrevrange(key, start, stop, callback);
|
||||
}
|
||||
|
||||
module.getSortedSetRevRangeByScore = function(args, callback) {
|
||||
redisClient.zrevrangebyscore(args, callback);
|
||||
}
|
||||
|
||||
module.sortedSetCount = function(key, min, max, callback) {
|
||||
redisClient.zcount(key, min, max, callback);
|
||||
}
|
||||
|
||||
module.sortedSetRank = function(key, value, callback) {
|
||||
redisClient.zrank(key, value, callback);
|
||||
}
|
||||
|
||||
module.sortedSetScore = function(key, value, callback) {
|
||||
redisClient.zscore(key, value, callback);
|
||||
}
|
||||
|
||||
module.sortedSetsScore = function(keys, value, callback) {
|
||||
var multi = redisClient.multi();
|
||||
|
||||
for(var x=0; x<keys.length; ++x) {
|
||||
multi.zscore(keys[x], value);
|
||||
}
|
||||
|
||||
multi.exec(callback);
|
||||
}
|
||||
|
||||
// lists
|
||||
module.listPrepend = function(key, value, callback) {
|
||||
redisClient.lpush(key, value, callback);
|
||||
}
|
||||
|
||||
module.listAppend = function(key, value, callback) {
|
||||
redisClient.rpush(key, value, callback);
|
||||
}
|
||||
|
||||
module.listRemoveLast = function(key, callback) {
|
||||
redisClient.rpop(key, callback);
|
||||
}
|
||||
|
||||
module.getListRange = function(key, start, stop, callback) {
|
||||
redisClient.lrange(key, start, stop, callback);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}(exports));
|
||||
|
@ -1,326 +1,354 @@
|
||||
var fs = require('fs'),
|
||||
path = require('path'),
|
||||
RDB = require('./redis.js'),
|
||||
async = require('async'),
|
||||
winston = require('winston'),
|
||||
eventEmitter = require('events').EventEmitter,
|
||||
plugins = {
|
||||
libraries: {},
|
||||
loadedHooks: {},
|
||||
staticDirs: {},
|
||||
cssFiles: [],
|
||||
db = require('./database');
|
||||
|
||||
// Events
|
||||
readyEvent: new eventEmitter,
|
||||
(function(Plugins) {
|
||||
|
||||
init: function() {
|
||||
if (this.initialized) return;
|
||||
if (global.env === 'development') winston.info('[plugins] Initializing plugins system');
|
||||
Plugins.libraries = {};
|
||||
Plugins.loadedHooks = {};
|
||||
Plugins.staticDirs = {};
|
||||
Plugins.cssFiles = [];
|
||||
|
||||
this.reload(function(err) {
|
||||
if (err) {
|
||||
if (global.env === 'development') winston.info('[plugins] NodeBB encountered a problem while loading plugins', err.message);
|
||||
return;
|
||||
Plugins.initialized = false;
|
||||
|
||||
// Events
|
||||
Plugins.readyEvent = new eventEmitter;
|
||||
|
||||
Plugins.init = function() {
|
||||
if (Plugins.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (global.env === 'development') {
|
||||
winston.info('[plugins] Initializing plugins system');
|
||||
}
|
||||
|
||||
Plugins.reload(function(err) {
|
||||
if (err) {
|
||||
if (global.env === 'development') {
|
||||
winston.info('[plugins] NodeBB encountered a problem while loading plugins', err.message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (global.env === 'development') {
|
||||
winston.info('[plugins] Plugins OK');
|
||||
}
|
||||
Plugins.initialized = true;
|
||||
Plugins.readyEvent.emit('ready');
|
||||
});
|
||||
};
|
||||
|
||||
Plugins.ready = function(callback) {
|
||||
if (!Plugins.initialized) {
|
||||
Plugins.readyEvent.once('ready', callback);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
Plugins.reload = function(callback) {
|
||||
// Resetting all local plugin data
|
||||
Plugins.loadedHooks = {};
|
||||
Plugins.staticDirs = {};
|
||||
Plugins.cssFiles.length = 0;
|
||||
|
||||
// Read the list of activated plugins and require their libraries
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
db.getSetMembers('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)) {
|
||||
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 = '<i class="fa fa-power-off"></i> ' + (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 = '<i class="fa fa-power-off"></i> ' + (active ? 'Dea' : 'A') + 'ctivate';
|
||||
next(null, config);
|
||||
});
|
||||
}
|
||||
], function(err, config) {
|
||||
if (err) return next(); // Silently fail
|
||||
|
||||
module.exports = plugins;
|
||||
plugins.push(config);
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
next(null, plugins);
|
||||
});
|
||||
}
|
||||
], function(err, plugins) {
|
||||
callback(err, plugins);
|
||||
});
|
||||
}
|
||||
}(exports));
|
||||
|
@ -1,78 +0,0 @@
|
||||
(function(module) {
|
||||
'use strict';
|
||||
|
||||
var RedisDB,
|
||||
redis = require('redis'),
|
||||
utils = require('./../public/src/utils.js'),
|
||||
winston = require('winston'),
|
||||
nconf = require('nconf'),
|
||||
redis_socket_or_host = nconf.get('redis:host');
|
||||
|
||||
if (redis_socket_or_host && redis_socket_or_host.indexOf('/')>=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));
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
@ -1,10 +0,0 @@
|
||||
var assert = require('assert');
|
||||
|
||||
|
||||
describe('Test database', function() {
|
||||
it('should work', function(){
|
||||
assert.doesNotThrow(function(){
|
||||
var RDB = require('../mocks/redismock');
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue