mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-01-20 22:43:01 +01:00
89
app.js
89
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');
|
||||
require('./src/database').init(function(err) {
|
||||
meta.configs.init(function () {
|
||||
|
||||
reds.createClient = function () {
|
||||
return reds.client || (reds.client = RDB);
|
||||
};
|
||||
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');
|
||||
|
||||
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);
|
||||
|
||||
Upgrade.check(function(schema_ok) {
|
||||
if (schema_ok || nconf.get('check-schema') === false) {
|
||||
websockets.init(SocketIO);
|
||||
plugins.init();
|
||||
global.templates = {};
|
||||
global.translator = translator;
|
||||
|
||||
global.templates = {};
|
||||
global.translator = translator;
|
||||
translator.loadServer();
|
||||
|
||||
translator.loadServer();
|
||||
var customTemplates = meta.config['theme:templates'] ? path.join(__dirname, 'node_modules', meta.config['theme:id'], meta.config['theme:templates']) : false;
|
||||
|
||||
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',
|
||||
|
||||
// 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);
|
||||
'noscript/header', 'noscript/home', 'noscript/category', 'noscript/topic'
|
||||
], customTemplates);
|
||||
|
||||
|
||||
plugins.ready(function() {
|
||||
templates.ready(webserver.init);
|
||||
});
|
||||
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();
|
||||
}
|
||||
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]');
|
||||
|
||||
81
mocks/databasemock.js
Normal file
81
mocks/databasemock.js
Normal file
@@ -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));
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
|
||||
|
||||
<!-- IF redis -->
|
||||
<h1>Redis</h1>
|
||||
<hr />
|
||||
<div id="admin-redis-info">
|
||||
@@ -21,4 +24,29 @@
|
||||
<span>Keyspace Hits</span> <span class="text-right">{keyspace_hits}</span><br/>
|
||||
<span>Keyspace Misses</span> <span class="text-right">{keyspace_misses}</span><br/>
|
||||
</div>
|
||||
<hr />
|
||||
<h3>Raw Info </h3>
|
||||
<div class="highlight">
|
||||
<pre>{raw}</pre>
|
||||
</div>
|
||||
<!-- ENDIF redis -->
|
||||
|
||||
<!-- IF mongo -->
|
||||
<h1>Mongo</h1>
|
||||
<hr />
|
||||
<div id="admin-redis-info">
|
||||
|
||||
<span>Collections</span> <span class="text-right">{collections}</span><br/>
|
||||
<span>Objects</span> <span class="text-right">{objects}</span><br/>
|
||||
<span>Avg. Object Size</span> <span class="text-right">{avgObjSize} kb</span><br/>
|
||||
<hr/>
|
||||
<span>Data Size</span> <span class="text-right">{dataSize} kb</span><br/>
|
||||
<span>Storage Size</span> <span class="text-right">{storageSize} kb</span><br/>
|
||||
<span>File Size</span> <span class="text-right">{fileSize} kb</span><br/>
|
||||
</div>
|
||||
<hr />
|
||||
<h3>Raw Info </h3>
|
||||
<div class="highlight">
|
||||
<pre>{raw}</pre>
|
||||
</div>
|
||||
<!-- ENDIF mongo -->
|
||||
@@ -105,7 +105,7 @@
|
||||
<li><a href='{relative_path}/admin/themes'><i class='fa fa-th'></i> Themes</a></li>
|
||||
<li><a href='{relative_path}/admin/plugins'><i class='fa fa-code-fork'></i> Plugins</a></li>
|
||||
<li><a href='{relative_path}/admin/settings'><i class='fa fa-cogs'></i> Settings</a></li>
|
||||
<li><a href='{relative_path}/admin/redis'><i class='fa fa-hdd-o'></i> Redis</a></li>
|
||||
<li><a href='{relative_path}/admin/database'><i class='fa fa-hdd-o'></i> Database</a></li>
|
||||
<li><a href='{relative_path}/admin/logger'><i class='fa fa-th'></i> Logger</a></li>
|
||||
<li><a href="{relative_path}/admin/motd"><i class="fa fa-comment"></i> MOTD</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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));
|
||||
14
src/database.js
Normal file
14
src/database.js
Normal file
@@ -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;
|
||||
737
src/database/mongo.js
Normal file
737
src/database/mongo.js
Normal file
@@ -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));
|
||||
|
||||
393
src/database/redis.js
Normal file
393
src/database/redis.js
Normal file
@@ -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,4 +1,4 @@
|
||||
var RDB = require('./redis'),
|
||||
var db = require('./database'),
|
||||
posts = require('./posts'),
|
||||
user = require('./user'),
|
||||
websockets = require('./websockets')
|
||||
@@ -8,6 +8,7 @@ var RDB = require('./redis'),
|
||||
"use strict";
|
||||
|
||||
Favourites.favourite = function (pid, room_id, uid, socket) {
|
||||
|
||||
if (uid === 0) {
|
||||
|
||||
translator.mget(['topic:favourites.not_logged_in.message', 'topic:favourites.not_logged_in.title'], function(err, results) {
|
||||
@@ -25,15 +26,16 @@ var RDB = require('./redis'),
|
||||
posts.getPostFields(pid, ['uid', 'timestamp'], function (err, postData) {
|
||||
|
||||
Favourites.hasFavourited(pid, uid, function (hasFavourited) {
|
||||
if (hasFavourited === 0) {
|
||||
RDB.sadd('pid:' + pid + ':users_favourited', uid);
|
||||
RDB.zadd('uid:' + uid + ':favourites', postData.timestamp, pid);
|
||||
|
||||
RDB.hincrby('post:' + pid, 'reputation', 1);
|
||||
if (!hasFavourited) {
|
||||
db.setAdd('pid:' + pid + ':users_favourited', uid);
|
||||
db.sortedSetAdd('uid:' + uid + ':favourites', postData.timestamp, pid);
|
||||
|
||||
db.incrObjectFieldBy('post:' + pid, 'reputation', 1);
|
||||
|
||||
if (uid !== postData.uid) {
|
||||
user.incrementUserFieldBy(postData.uid, 'reputation', 1, function (err, newreputation) {
|
||||
RDB.zadd('users:reputation', newreputation, postData.uid);
|
||||
db.sortedSetAdd('users:reputation', newreputation, postData.uid);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -60,15 +62,15 @@ var RDB = require('./redis'),
|
||||
|
||||
posts.getPostField(pid, 'uid', function (err, uid_of_poster) {
|
||||
Favourites.hasFavourited(pid, uid, function (hasFavourited) {
|
||||
if (hasFavourited === 1) {
|
||||
RDB.srem('pid:' + pid + ':users_favourited', uid);
|
||||
RDB.zrem('uid:' + uid + ':favourites', pid);
|
||||
if (hasFavourited) {
|
||||
db.setRemove('pid:' + pid + ':users_favourited', uid);
|
||||
db.sortedSetRemove('uid:' + uid + ':favourites', pid);
|
||||
|
||||
RDB.hincrby('post:' + pid, 'reputation', -1);
|
||||
db.incrObjectFieldBy('post:' + pid, 'reputation', -1);
|
||||
|
||||
if (uid !== uid_of_poster) {
|
||||
user.incrementUserFieldBy(uid_of_poster, 'reputation', -1, function (err, newreputation) {
|
||||
RDB.zadd('users:reputation', newreputation, uid_of_poster);
|
||||
db.sortedSetAdd('users:reputation', newreputation, uid_of_poster);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -89,8 +91,7 @@ var RDB = require('./redis'),
|
||||
};
|
||||
|
||||
Favourites.hasFavourited = function (pid, uid, callback) {
|
||||
RDB.sismember('pid:' + pid + ':users_favourited', uid, function (err, hasFavourited) {
|
||||
RDB.handle(err);
|
||||
db.isSetMember('pid:' + pid + ':users_favourited', uid, function (err, hasFavourited) {
|
||||
|
||||
callback(hasFavourited);
|
||||
});
|
||||
|
||||
17
src/feed.js
17
src/feed.js
@@ -1,7 +1,7 @@
|
||||
(function (Feed) {
|
||||
var RDB = require('./redis.js'),
|
||||
posts = require('./posts.js'),
|
||||
topics = require('./topics.js'),
|
||||
var db = require('./database'),
|
||||
posts = require('./posts'),
|
||||
topics = require('./topics'),
|
||||
categories = require('./categories'),
|
||||
|
||||
fs = require('fs'),
|
||||
@@ -30,7 +30,12 @@
|
||||
Feed.updateTopic = function (tid, callback) {
|
||||
topics.getTopicWithPosts(tid, 0, 0, -1, true, function (err, topicData) {
|
||||
if (err) {
|
||||
return callback(new Error('topic-invalid'));
|
||||
if(callback) {
|
||||
return callback(new Error('topic-invalid'));
|
||||
} else {
|
||||
winston.error(err.message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var feed = new rss({
|
||||
@@ -50,8 +55,8 @@
|
||||
}
|
||||
|
||||
async.each(topicData.posts, function(postData, next) {
|
||||
if (postData.deleted === '0') {
|
||||
dateStamp = new Date(parseInt(postData.edited === '0' ? postData.timestamp : postData.edited, 10)).toUTCString();
|
||||
if (parseInt(postData.deleted, 10) === 0) {
|
||||
dateStamp = new Date(parseInt(parseInt(postData.edited, 10) === 0 ? postData.timestamp : postData.edited, 10)).toUTCString();
|
||||
|
||||
feed.item({
|
||||
title: 'Reply to ' + topicData.topic_name + ' on ' + dateStamp,
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
"use strict";
|
||||
|
||||
var async = require('async'),
|
||||
User = require('./user'),
|
||||
RDB = RDB || require('./redis');
|
||||
user = require('./user'),
|
||||
db = require('./database');
|
||||
|
||||
Groups.list = function(options, callback) {
|
||||
RDB.hvals('group:gid', function (err, gids) {
|
||||
db.getObjectValues('group:gid', function (err, gids) {
|
||||
if (gids.length > 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));
|
||||
|
||||
133
src/install.js
133
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
|
||||
}, {
|
||||
name: 'bind_address',
|
||||
description: 'IP or Hostname to bind to',
|
||||
'default': nconf.get('bind_address') || '0.0.0.0'
|
||||
}],
|
||||
mongoQuestions : [{
|
||||
name: 'mongo:host',
|
||||
description: 'Host IP or address of your MongoDB instance',
|
||||
'default': nconf.get('mongo:host') || '127.0.0.1'
|
||||
}, {
|
||||
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'];
|
||||
function dbQuestionsSuccess(err, databaseConfig) {
|
||||
if (!databaseConfig) {
|
||||
return next(new Error('aborted'));
|
||||
}
|
||||
|
||||
// Add hardcoded values
|
||||
config.bcrypt_rounds = 12;
|
||||
config.upload_path = '/public/uploads';
|
||||
config.use_port = (config.use_port.slice(0, 1) === 'y') ? true : false;
|
||||
// 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));
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
config.bcrypt_rounds = 12;
|
||||
config.upload_path = '/public/uploads';
|
||||
config.use_port = (config.use_port.slice(0, 1) === 'y') ? true : false;
|
||||
|
||||
server_conf.base_url = protocol + '//' + host;
|
||||
server_conf.relative_path = relative_path;
|
||||
var urlObject = url.parse(config.base_url),
|
||||
relative_path = (urlObject.pathname && urlObject.pathname.length > 1) ? urlObject.pathname : '',
|
||||
host = urlObject.host,
|
||||
protocol = urlObject.protocol,
|
||||
server_conf = config,
|
||||
client_conf = {
|
||||
relative_path: relative_path
|
||||
};
|
||||
|
||||
install.save(server_conf, client_conf, next);
|
||||
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);
|
||||
});
|
||||
|
||||
102
src/login.js
102
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);
|
||||
}
|
||||
|
||||
callback(null, {
|
||||
uid: uid
|
||||
});
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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 {
|
||||
|
||||
78
src/meta.js
78
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));
|
||||
@@ -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);
|
||||
}
|
||||
db.exists('notifications:' + nid, function(err, exists) {
|
||||
if(!exists) {
|
||||
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.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;
|
||||
|
||||
async.eachSeries(results.nids, function(nid, next) {
|
||||
var multi = RDB.multi();
|
||||
|
||||
for(x=0;x<numInboxes;x++) {
|
||||
multi.zscore(results.inboxes[x], nid);
|
||||
}
|
||||
|
||||
multi.exec(function(err, results) {
|
||||
// If the notification is not present in any inbox, delete it altogether
|
||||
var expired = results.every(function(present) {
|
||||
if (present === null) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (expired) {
|
||||
destroy(nid);
|
||||
numPruned++;
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.info('[notifications.prune] Notification pruning completed. ' + numPruned + ' expired notification' + (numPruned !== 1 ? 's' : '') + ' removed.');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
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.expiredNids, function(nid, next) {
|
||||
|
||||
db.sortedSetsScore(results.inboxes, nid, function(err, results) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
// If the notification is not present in any inbox, delete it altogether
|
||||
var expired = results.every(function(present) {
|
||||
return present === null;
|
||||
});
|
||||
|
||||
if (expired) {
|
||||
destroy(nid);
|
||||
numPruned++;
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.info('[notifications.prune] Notification pruning completed. ' + numPruned + ' expired notification' + (numPruned !== 1 ? 's' : '') + ' removed.');
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
596
src/plugins.js
596
src/plugins.js
@@ -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');
|
||||
if (global.env === 'development') {
|
||||
winston.info('[plugins] Plugins OK');
|
||||
}
|
||||
Plugins.initialized = true;
|
||||
Plugins.readyEvent.emit('ready');
|
||||
});
|
||||
};
|
||||
|
||||
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;
|
||||
Plugins.ready = function(callback) {
|
||||
if (!Plugins.initialized) {
|
||||
Plugins.readyEvent.once('ready', callback);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
// Resetting all local plugin data
|
||||
this.loadedHooks = {};
|
||||
this.staticDirs = {};
|
||||
this.cssFiles.length = 0;
|
||||
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) {
|
||||
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
|
||||
// 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);
|
||||
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;
|
||||
});
|
||||
});
|
||||
|
||||
next();
|
||||
}
|
||||
], callback);
|
||||
};
|
||||
|
||||
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) {
|
||||
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();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
winston.warn('[plugins.reload] Library not found for plugin: ' + pluginData.id);
|
||||
next();
|
||||
}
|
||||
},
|
||||
function(next) {
|
||||
// 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();
|
||||
});
|
||||
} else 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;
|
||||
});
|
||||
});
|
||||
|
||||
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();
|
||||
// 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);
|
||||
}
|
||||
Plugins.cssFiles = Plugins.cssFiles.concat(pluginData.css.map(function(file) {
|
||||
return path.join('/plugins', pluginData.id, file);
|
||||
}));
|
||||
|
||||
_self.cssFiles = _self.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]);
|
||||
}
|
||||
], 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'));
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
_self.loadedHooks[data.hook] = _self.loadedHooks[data.hook] || [];
|
||||
_self.loadedHooks[data.hook].push(data);
|
||||
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 (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 (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]);
|
||||
|
||||
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.');
|
||||
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;
|
||||
}
|
||||
},
|
||||
isActive: function(id, callback) {
|
||||
RDB.sismember('plugins:active', id, callback);
|
||||
},
|
||||
toggleActive: function(id, callback) {
|
||||
this.isActive(id, function(err, active) {
|
||||
} else {
|
||||
// Otherwise, this hook contains no methods
|
||||
var returnVal = args;
|
||||
if (callback) {
|
||||
callback(null, returnVal);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
if (callback) {
|
||||
callback({
|
||||
id: id,
|
||||
active: !active
|
||||
});
|
||||
}
|
||||
|
||||
// Reload meta data
|
||||
plugins.reload(function() {
|
||||
// (De)activation Hooks
|
||||
plugins.fireHook('action:plugin.' + (active ? 'de' : '') + 'activate', id);
|
||||
|
||||
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);
|
||||
});
|
||||
},
|
||||
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);
|
||||
});
|
||||
}
|
||||
], 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.init();
|
||||
Plugins.showInstalled = function(callback) {
|
||||
npmPluginPath = path.join(__dirname, '../node_modules');
|
||||
|
||||
module.exports = plugins;
|
||||
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);
|
||||
});
|
||||
},
|
||||
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);
|
||||
}
|
||||
|
||||
Plugins.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);
|
||||
});
|
||||
}
|
||||
], function(err, config) {
|
||||
if (err) return next(); // Silently fail
|
||||
|
||||
plugins.push(config);
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
next(null, plugins);
|
||||
});
|
||||
}
|
||||
], function(err, plugins) {
|
||||
callback(err, plugins);
|
||||
});
|
||||
}
|
||||
}(exports));
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
|
||||
|
||||
76
src/posts.js
76
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<numPids; x++) {
|
||||
multi.hgetall("post:" + pids[x]);
|
||||
keys.push('post:' + pids[x]);
|
||||
}
|
||||
|
||||
multi.exec(function (err, replies) {
|
||||
async.map(replies, function(postData, _callback) {
|
||||
db.getObjects(keys, function(err, data) {
|
||||
async.map(data, function(postData, _callback) {
|
||||
if (postData) {
|
||||
|
||||
try {
|
||||
postData.relativeTime = new Date(parseInt(postData.timestamp,10)).toISOString();
|
||||
postData.relativeEditTime = postData.edited !== '0' ? (new Date(parseInt(postData.edited,10)).toISOString()) : '';
|
||||
postData.relativeEditTime = parseInt(postData.edited, 10) !== 0 ? (new Date(parseInt(postData.edited, 10)).toISOString()) : '';
|
||||
} catch(e) {
|
||||
winston.err('invalid time value');
|
||||
}
|
||||
|
||||
postTools.parse(postData.content, function(err, content) {
|
||||
postData.content = content;
|
||||
postTools.parse(postData.content, function(err, content) {
|
||||
postData.content = content;
|
||||
_callback(null, postData);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
_callback(null);
|
||||
}
|
||||
@@ -399,7 +395,7 @@ var RDB = require('./redis'),
|
||||
return callback(err, null);
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
Posts.getCidByPid = function(pid, callback) {
|
||||
@@ -460,8 +456,10 @@ var RDB = require('./redis'),
|
||||
|
||||
Posts.getPostsByUid = function(uid, start, end, callback) {
|
||||
user.getPostIds(uid, start, end, function(pids) {
|
||||
|
||||
if (pids && pids.length) {
|
||||
plugins.fireHook('filter:post.getTopic', pids, function(err, posts) {
|
||||
|
||||
if (!err & 0 < posts.length) {
|
||||
Posts.getPostsByPids(pids, function(err, posts) {
|
||||
plugins.fireHook('action:post.gotTopic', posts);
|
||||
@@ -482,10 +480,10 @@ var RDB = require('./redis'),
|
||||
function reIndex(pid, callback) {
|
||||
|
||||
Posts.getPostField(pid, 'content', function(err, content) {
|
||||
postSearch.remove(pid, function() {
|
||||
db.searchRemove('post', pid, function() {
|
||||
|
||||
if (content && content.length) {
|
||||
postSearch.index(content, pid);
|
||||
db.searchIndex('post', content, pid);
|
||||
}
|
||||
callback(null);
|
||||
});
|
||||
@@ -502,7 +500,7 @@ var RDB = require('./redis'),
|
||||
}
|
||||
|
||||
Posts.getFavourites = function(uid, callback) {
|
||||
RDB.zrevrange('uid:' + uid + ':favourites', 0, -1, function(err, pids) {
|
||||
db.getSortedSetRevRange('uid:' + uid + ':favourites', 0, -1, function(err, pids) {
|
||||
if (err)
|
||||
return callback(err, null);
|
||||
|
||||
|
||||
78
src/redis.js
78
src/redis.js
@@ -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));
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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 || "<div class=\"pull-right btn-group\"><a target=\"_blank\" href=\"http://www.nodebb.org\" class=\"btn btn-default btn-lg\"><i class=\"fa fa-comment\"></i><span class='hidden-mobile'> Get NodeBB</span></a> <a target=\"_blank\" href=\"https://github.com/designcreateplay/NodeBB\" class=\"btn btn-default btn-lg\"><i class=\"fa fa-github\"></i><span class='hidden-mobile'> Fork us on Github</span></a> <a target=\"_blank\" href=\"https://twitter.com/dcplabs\" class=\"btn btn-default btn-lg\"><i class=\"fa fa-twitter\"></i><span class='hidden-mobile'> @dcplabs</span></a></div>\n\n# NodeBB <span>v" + pkg.version + "</span>\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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
@@ -78,11 +78,6 @@ var DebugRoute = function(app) {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/test', function(req, res) {
|
||||
topics.pushUnreadCount();
|
||||
res.send();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
114
src/upgrade.js
114
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;
|
||||
315
src/user.js
315
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 = '<strong>' + username + '</strong> 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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
})(module.exports);
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
330
tests/database.js
Normal file
330
tests/database.js
Normal file
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user