From 3f5c51174d852314f3c47da11e9ea8a80c541201 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 12 Mar 2014 18:00:27 -0400 Subject: [PATCH] refactored user.js took out notifications, email and reset code to separate files --- src/socket.io/meta.js | 2 +- src/topics.js | 4 +- src/user.js | 314 +-------------------------------------- src/useremail.js | 89 +++++++++++ src/usernotifications.js | 174 ++++++++++++++++++++++ src/userreset.js | 92 ++++++++++++ 6 files changed, 363 insertions(+), 312 deletions(-) create mode 100644 src/useremail.js create mode 100644 src/usernotifications.js create mode 100644 src/userreset.js diff --git a/src/socket.io/meta.js b/src/socket.io/meta.js index 5a394937fa..d55721c9c6 100644 --- a/src/socket.io/meta.js +++ b/src/socket.io/meta.js @@ -17,7 +17,7 @@ SocketMeta.reconnected = function(socket) { if (uid) { topics.pushUnreadCount(uid); - user.pushNotifCount(uid); + user.notifications.pushCount(uid); } if (process.env.NODE_ENV === 'development') { diff --git a/src/topics.js b/src/topics.js index 1ac9e6b7d1..0dd7ea9452 100644 --- a/src/topics.js +++ b/src/topics.js @@ -206,7 +206,7 @@ var async = require('async'), postData = data; threadTools.notifyFollowers(tid, postData.pid, uid); - user.sendPostNotificationToFollowers(uid, tid, postData.pid); + user.notifications.sendPostNotificationToFollowers(uid, tid, postData.pid); next(); }, @@ -878,7 +878,7 @@ var async = require('async'), user.notifications.getUnreadByUniqueId(uid, 'topic:' + tid, function(err, nids) { notifications.mark_read_multiple(nids, uid, function() { - user.pushNotifCount(uid); + user.notifications.pushCount(uid); }); }); }; diff --git a/src/user.js b/src/user.js index 0162e58cc5..089158e75a 100644 --- a/src/user.js +++ b/src/user.js @@ -13,13 +13,16 @@ var bcrypt = require('bcryptjs'), db = require('./database'), meta = require('./meta'), groups = require('./groups'), - notifications = require('./notifications'), topics = require('./topics'), events = require('./events'), Emailer = require('./emailer'); (function(User) { + User.email = require('./useremail'); + User.notifications = require('./usernotifications'); + User.reset = require('./userreset'); + User.create = function(userData, callback) { userData = userData || {}; userData.userslug = utils.slugify(userData.username); @@ -753,26 +756,7 @@ var bcrypt = require('bcryptjs'), }; - User.sendPostNotificationToFollowers = function(uid, tid, pid) { - User.getUserField(uid, 'username', function(err, username) { - db.getSetMembers('followers:' + uid, function(err, followers) { - if (followers && followers.length) { - topics.getTopicField(tid, 'slug', function(err, slug) { - var message = '' + username + ' made a new post'; - - notifications.create({ - text: message, - path: nconf.get('relative_path') + '/topic/' + slug + '#' + pid, - uniqueId: 'topic:' + tid, - from: uid - }, function(nid) { - notifications.push(nid, followers); - }); - }); - } - }); - }); - }; + User.isFollowing = function(uid, theirid, callback) { db.isSetMember('following:' + uid, theirid, callback); @@ -841,102 +825,6 @@ var bcrypt = require('bcryptjs'), groups.isMemberByGroupName(uid, 'administrators', callback); }; - User.reset = { - validate: function(socket, code, callback) { - - db.getObjectField('reset:uid', code, function(err, uid) { - if (err) { - return callback(err); - } - - if (uid !== null) { - db.getObjectField('reset:expiry', code, function(err, expiry) { - if (err) { - return callback(err); - } - - if (parseInt(expiry, 10) >= Date.now() / 1000) { - callback(null, true); - } else { - // Expired, delete from db - db.deleteObjectField('reset:uid', code); - db.deleteObjectField('reset:expiry', code); - callback(null, false); - } - }); - } else { - callback(null, false); - } - }); - }, - send: function(socket, email, callback) { - User.getUidByEmail(email, function(err, uid) { - if(err) { - return callback(err); - } - - if(!uid) { - return callback(new Error('invalid-email')); - } - - // Generate a new reset code - var reset_code = utils.generateUUID(); - db.setObjectField('reset:uid', reset_code, uid); - db.setObjectField('reset:expiry', reset_code, (60 * 60) + Math.floor(Date.now() / 1000)); - - var reset_link = nconf.get('url') + '/reset/' + reset_code; - - Emailer.send('reset', uid, { - 'site_title': (meta.config.title || 'NodeBB'), - 'reset_link': reset_link, - - subject: 'Password Reset Requested - ' + (meta.config.title || 'NodeBB') + '!', - template: 'reset', - uid: uid - }); - - callback(null); - }); - }, - commit: function(socket, code, password, callback) { - this.validate(socket, code, function(err, validated) { - if(err) { - return callback(err); - } - - if (validated) { - db.getObjectField('reset:uid', code, function(err, uid) { - if (err) { - return callback(err); - } - - User.hashPassword(password, function(err, hash) { - User.setUserField(uid, 'password', hash); - events.logPasswordReset(uid); - }); - - db.deleteObjectField('reset:uid', code); - db.deleteObjectField('reset:expiry', code); - - callback(null); - }); - } - }); - } - }; - - User.pushNotifCount = function(uid) { - var websockets = require('./socket.io'); - - User.notifications.getUnreadCount(uid, function(err, count) { - if (!err) { - websockets.in('uid_' + uid).emit('event:notifications.updateCount', count); - } else { - winston.warn('[User.pushNotifCount] Count not retrieve unread notifications count to push to uid ' + uid + '\'s client(s)'); - } - }); - }; - User.logIP = function(uid, ip) { db.sortedSetAdd('uid:' + uid + ':ip', +new Date(), ip || 'Unknown'); }; @@ -953,197 +841,5 @@ var bcrypt = require('bcryptjs'), }); }; - User.email = { - verify: function(uid, email) { - if (!plugins.hasListeners('action:email.send')) { - return; - } - - var confirm_code = utils.generateUUID(), - confirm_link = nconf.get('url') + '/confirm/' + confirm_code; - - async.series([ - function(next) { - db.setObject('confirm:' + confirm_code, { - email: email, - uid: uid - }, next); - }, - function(next) { - db.expireAt('confirm:' + confirm_code, Math.floor(Date.now() / 1000 + 60 * 60 * 2), next); - } - ], function(err) { - // Send intro email w/ confirm code - User.getUserField(uid, 'username', function(err, username) { - Emailer.send('welcome', uid, { - 'site_title': (meta.config.title || 'NodeBB'), - username: username, - 'confirm_link': confirm_link, - - subject: 'Welcome to ' + (meta.config.title || 'NodeBB') + '!', - template: 'welcome', - uid: uid - }); - }); - }); - }, - exists: function(email, callback) { - User.getUidByEmail(email, function(err, exists) { - callback(err, !!exists); - }); - }, - confirm: function(code, callback) { - db.getObject('confirm:' + code, function(err, confirmObj) { - if (err) { - return callback({ - status:'error' - }); - } - - if (confirmObj && confirmObj.uid && confirmObj.email) { - db.setObjectField('email:confirmed', confirmObj.email, '1', function() { - callback({ - status: 'ok' - }); - }); - } else { - callback({ - status: 'not_ok' - }); - } - }); - }, - available: function(email, callback) { - db.isObjectField('email:uid', email, function(err, exists) { - callback(err, !exists); - }); - } - }; - - User.notifications = { - get: function(uid, callback) { - function getNotifications(set, start, stop, iterator, done) { - db.getSortedSetRevRange(set, start, stop, function(err, nids) { - if(err) { - return done(err); - } - - if(!nids || nids.length === 0) { - return done(null, []); - } - - if (nids.length > maxNotifs) { - nids.length = maxNotifs; - } - - async.map(nids, function(nid, next) { - notifications.get(nid, uid, function(notif_data) { - if(typeof iterator === 'function') { - iterator(notif_data); - } - - next(null, notif_data); - }); - }, done); - }); - } - - var maxNotifs = 15; - - async.parallel({ - unread: function(next) { - getNotifications('uid:' + uid + ':notifications:unread', 0, 9, function(notif_data) { - if (notif_data) { - notif_data.readClass = !notif_data.read ? 'label-warning' : ''; - } - }, next); - }, - read: function(next) { - getNotifications('uid:' + uid + ':notifications:read', 0, 9, null, next); - } - }, function(err, notifications) { - if(err) { - return callback(err); - } - - // Remove empties - notifications.read = notifications.read.filter(function(notifObj) { - return notifObj; - }); - notifications.unread = notifications.unread.filter(function(notifObj) { - return notifObj; - }); - - // Limit the number of notifications to `maxNotifs`, prioritising unread notifications - if (notifications.read.length + notifications.unread.length > maxNotifs) { - notifications.read.length = maxNotifs - notifications.unread.length; - } - - callback(null, notifications); - }); - }, - getAll: function(uid, limit, before, callback) { - var now = new Date(); - - if (!limit || parseInt(limit, 10) <= 0) { - limit = 25; - } - if (before) { - before = new Date(parseInt(before, 10)); - } - - 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); - }); - }, function(err, notifs) { - notifs = notifs.filter(function(notif) { - return notif !== null; - }).sort(function(a, b) { - return parseInt(b.datetime, 10) - parseInt(a.datetime, 10); - }).map(function(notif) { - notif.datetimeISO = utils.toISOString(notif.datetime); - notif.readClass = !notif.read ? 'label-warning' : ''; - - return notif; - }); - - callback(err, notifs); - }); - }); - }); - - }, - getUnreadCount: function(uid, callback) { - db.sortedSetCount('uid:' + uid + ':notifications:unread', -Infinity, Infinity, callback); - }, - getUnreadByUniqueId: function(uid, uniqueId, callback) { - 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) { - return next(false); - } - - if (notifObj.uniqueId === uniqueId) { - next(true); - } else { - next(false); - } - }); - }, function(nids) { - callback(null, nids); - }); - }); - } - }; }(exports)); diff --git a/src/useremail.js b/src/useremail.js new file mode 100644 index 0000000000..603dc7eee0 --- /dev/null +++ b/src/useremail.js @@ -0,0 +1,89 @@ + +'use strict'; + +var async = require('async'), + nconf = require('nconf'), + winston = require('winston'), + + user = require('./user'), + utils = require('./../public/src/utils'), + plugins = require('./plugins'), + db = require('./database'), + meta = require('./meta'), + emailer = require('./emailer'); + +(function(UserEmail) { + + UserEmail.exists = function(email, callback) { + user.getUidByEmail(email, function(err, exists) { + callback(err, !!exists); + }); + }; + + UserEmail.available = function(email, callback) { + db.isObjectField('email:uid', email, function(err, exists) { + callback(err, !exists); + }); + }; + + UserEmail.verify = function(uid, email) { + if (!plugins.hasListeners('action:email.send')) { + return; + } + + var confirm_code = utils.generateUUID(), + confirm_link = nconf.get('url') + '/confirm/' + confirm_code; + + async.series([ + function(next) { + db.setObject('confirm:' + confirm_code, { + email: email, + uid: uid + }, next); + }, + function(next) { + db.expireAt('confirm:' + confirm_code, Math.floor(Date.now() / 1000 + 60 * 60 * 2), next); + } + ], function(err) { + // Send intro email w/ confirm code + user.getUserField(uid, 'username', function(err, username) { + if (err) { + return winston.error(err.message); + } + + emailer.send('welcome', uid, { + site_title: (meta.config.title || 'NodeBB'), + username: username, + confirm_link: confirm_link, + + subject: 'Welcome to ' + (meta.config.title || 'NodeBB') + '!', + template: 'welcome', + uid: uid + }); + }); + }); + }; + + UserEmail.confirm = function(code, callback) { + db.getObject('confirm:' + code, function(err, confirmObj) { + if (err) { + return callback({ + status:'error' + }); + } + + if (confirmObj && confirmObj.uid && confirmObj.email) { + db.setObjectField('email:confirmed', confirmObj.email, '1', function() { + callback({ + status: 'ok' + }); + }); + } else { + callback({ + status: 'not_ok' + }); + } + }); + }; + +}(exports)); diff --git a/src/usernotifications.js b/src/usernotifications.js new file mode 100644 index 0000000000..8228074b12 --- /dev/null +++ b/src/usernotifications.js @@ -0,0 +1,174 @@ + +'use strict'; + +var async = require('async'), + nconf = require('nconf'), + winston = require('winston'), + + user = require('./user'), + utils = require('./../public/src/utils'), + db = require('./database'), + notifications = require('./notifications'), + topics = require('./topics'), + websockets = require('./socket.io'); + +(function(UserNotifications) { + + UserNotifications.get = function(uid, callback) { + function getNotifications(set, start, stop, iterator, done) { + db.getSortedSetRevRange(set, start, stop, function(err, nids) { + if(err) { + return done(err); + } + + if(!nids || nids.length === 0) { + return done(null, []); + } + + if (nids.length > maxNotifs) { + nids.length = maxNotifs; + } + + async.map(nids, function(nid, next) { + notifications.get(nid, uid, function(notif_data) { + if(typeof iterator === 'function') { + iterator(notif_data); + } + + next(null, notif_data); + }); + }, done); + }); + } + + var maxNotifs = 15; + + async.parallel({ + unread: function(next) { + getNotifications('uid:' + uid + ':notifications:unread', 0, 9, function(notif_data) { + if (notif_data) { + notif_data.readClass = !notif_data.read ? 'label-warning' : ''; + } + }, next); + }, + read: function(next) { + getNotifications('uid:' + uid + ':notifications:read', 0, 9, null, next); + } + }, function(err, notifications) { + function filterDeleted(notifObj) { + return !!notifObj; + } + if(err) { + return callback(err); + } + + notifications.read = notifications.read.filter(filterDeleted); + notifications.unread = notifications.unread.filter(filterDeleted); + + // Limit the number of notifications to `maxNotifs`, prioritising unread notifications + if (notifications.read.length + notifications.unread.length > maxNotifs) { + notifications.read.length = maxNotifs - notifications.unread.length; + } + + callback(null, notifications); + }); + }; + + UserNotifications.getAll = function(uid, limit, before, callback) { + var now = new Date(); + + if (!limit || parseInt(limit, 10) <= 0) { + limit = 25; + } + if (before) { + before = new Date(parseInt(before, 10)); + } + + 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); + }); + }, function(err, notifs) { + notifs = notifs.filter(function(notif) { + return notif !== null; + }).sort(function(a, b) { + return parseInt(b.datetime, 10) - parseInt(a.datetime, 10); + }).map(function(notif) { + notif.datetimeISO = utils.toISOString(notif.datetime); + notif.readClass = !notif.read ? 'label-warning' : ''; + + return notif; + }); + + callback(err, notifs); + }); + }); + }); + + }; + + UserNotifications.getUnreadCount = function(uid, callback) { + db.sortedSetCount('uid:' + uid + ':notifications:unread', -Infinity, Infinity, callback); + }; + + UserNotifications.getUnreadByUniqueId = function(uid, uniqueId, callback) { + 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) { + return next(false); + } + + if (notifObj.uniqueId === uniqueId) { + next(true); + } else { + next(false); + } + }); + }, function(nids) { + callback(null, nids); + }); + }); + }; + + UserNotifications.sendPostNotificationToFollowers = function(uid, tid, pid) { + user.getUserField(uid, 'username', function(err, username) { + db.getSetMembers('followers:' + uid, function(err, followers) { + if (followers && followers.length) { + topics.getTopicField(tid, 'slug', function(err, slug) { + var message = '' + username + ' made a new post'; + + notifications.create({ + text: message, + path: nconf.get('relative_path') + '/topic/' + slug + '#' + pid, + uniqueId: 'topic:' + tid, + from: uid + }, function(nid) { + notifications.push(nid, followers); + }); + }); + } + }); + }); + }; + + UserNotifications.pushCount = function(uid) { + + UserNotifications.getUnreadCount(uid, function(err, count) { + if (err) { + return winston.warn('[User.pushNotifCount] Count not retrieve unread notifications count to push to uid ' + uid + '\'s client(s)'); + } + + websockets.in('uid_' + uid).emit('event:notifications.updateCount', count); + }); + }; + +}(exports)); \ No newline at end of file diff --git a/src/userreset.js b/src/userreset.js new file mode 100644 index 0000000000..9561a1b48e --- /dev/null +++ b/src/userreset.js @@ -0,0 +1,92 @@ + +'use strict'; + +var async = require('async'), + nconf = require('nconf'), + + user = require('./user'), + utils = require('./../public/src/utils'), + + db = require('./database'), + meta = require('./meta'), + events = require('./events'), + emailer = require('./emailer'); + +(function(UserReset) { + + UserReset.validate = function(socket, code, callback) { + db.getObjectField('reset:uid', code, function(err, uid) { + if (err || !uid) { + return callback(err, false); + } + + db.getObjectField('reset:expiry', code, function(err, expiry) { + if (err) { + return callback(err); + } + + if (parseInt(expiry, 10) >= Date.now() / 1000) { + callback(null, true); + } else { + // Expired, delete from db + db.deleteObjectField('reset:uid', code); + db.deleteObjectField('reset:expiry', code); + callback(null, false); + } + }); + }); + }; + + UserReset.send = function(socket, email, callback) { + user.getUidByEmail(email, function(err, uid) { + if(err || !uid) { + return callback(err || new Error('invalid-email')); + } + + // Generate a new reset code + var reset_code = utils.generateUUID(); + db.setObjectField('reset:uid', reset_code, uid); + db.setObjectField('reset:expiry', reset_code, (60 * 60) + Math.floor(Date.now() / 1000)); + + var reset_link = nconf.get('url') + '/reset/' + reset_code; + + emailer.send('reset', uid, { + site_title: (meta.config.title || 'NodeBB'), + reset_link: reset_link, + + subject: 'Password Reset Requested - ' + (meta.config.title || 'NodeBB') + '!', + template: 'reset', + uid: uid + }); + + callback(); + }); + }; + + UserReset.commit = function(socket, code, password, callback) { + UserReset.validate(socket, code, function(err, validated) { + if(err) { + return callback(err); + } + + if (validated) { + db.getObjectField('reset:uid', code, function(err, uid) { + if (err) { + return callback(err); + } + + user.hashPassword(password, function(err, hash) { + user.setUserField(uid, 'password', hash); + events.logPasswordReset(uid); + }); + + db.deleteObjectField('reset:uid', code); + db.deleteObjectField('reset:expiry', code); + + callback(null); + }); + } + }); + }; + +}(exports)); \ No newline at end of file