feat: #7743, finish user module

v1.18.x
Barış Soner Uşaklı 6 years ago
parent 6fea46b6e2
commit a51ec591ee

@ -129,8 +129,8 @@ Notifications.push = async function (notification, uids) {
} }
setTimeout(function () { setTimeout(function () {
batch.processArray(uids, function (uids, next) { batch.processArray(uids, async function (uids) {
pushToUids(uids, notification, next); await pushToUids(uids, notification);
}, { interval: 1000 }, function (err) { }, { interval: 1000 }, function (err) {
if (err) { if (err) {
winston.error(err.stack); winston.error(err.stack);

@ -1,6 +1,5 @@
'use strict'; 'use strict';
var async = require('async');
var nconf = require('nconf'); var nconf = require('nconf');
var winston = require('winston'); var winston = require('winston');
@ -16,185 +15,119 @@ var UserReset = module.exports;
var twoHours = 7200000; var twoHours = 7200000;
UserReset.validate = function (code, callback) { UserReset.validate = async function (code) {
async.waterfall([ const uid = await db.getObjectField('reset:uid', code);
function (next) { if (!uid) {
db.getObjectField('reset:uid', code, next); return false;
}, }
function (uid, next) { const issueDate = await db.sortedSetScore('reset:issueDate', code);
if (!uid) { return parseInt(issueDate, 10) > Date.now() - twoHours;
return callback(null, false);
}
db.sortedSetScore('reset:issueDate', code, next);
},
function (issueDate, next) {
next(null, parseInt(issueDate, 10) > Date.now() - twoHours);
},
], callback);
}; };
UserReset.generate = function (uid, callback) { UserReset.generate = async function (uid) {
var code = utils.generateUUID(); const code = utils.generateUUID();
async.parallel([ await Promise.all([
async.apply(db.setObjectField, 'reset:uid', code, uid), db.setObjectField('reset:uid', code, uid),
async.apply(db.sortedSetAdd, 'reset:issueDate', Date.now(), code), db.sortedSetAdd('reset:issueDate', Date.now(), code),
], function (err) { ]);
callback(err, code); return code;
});
}; };
function canGenerate(uid, callback) { async function canGenerate(uid) {
async.waterfall([ const score = await db.sortedSetScore('reset:issueDate:uid', uid);
function (next) { if (score > Date.now() - (1000 * 60)) {
db.sortedSetScore('reset:issueDate:uid', uid, next); throw new Error('[[error:reset-rate-limited]]');
}, }
function (score, next) {
if (score > Date.now() - (1000 * 60)) {
return next(new Error('[[error:reset-rate-limited]]'));
}
next();
},
], callback);
} }
UserReset.send = function (email, callback) { UserReset.send = async function (email) {
var uid; const uid = await user.getUidByEmail(email);
async.waterfall([ if (!uid) {
function (next) { throw new Error('[[error:invalid-email]]');
user.getUidByEmail(email, next); }
}, await canGenerate(uid);
function (_uid, next) { await db.sortedSetAdd('reset:issueDate:uid', Date.now(), uid);
if (!_uid) { const code = await UserReset.generate(uid);
return next(new Error('[[error:invalid-email]]')); await emailer.send('reset', uid, {
} reset_link: nconf.get('url') + '/reset/' + code,
subject: '[[email:password-reset-requested]]',
uid = _uid; template: 'reset',
canGenerate(uid, next); uid: uid,
}, });
function (next) {
db.sortedSetAdd('reset:issueDate:uid', Date.now(), uid, next);
},
function (next) {
UserReset.generate(uid, next);
},
function (code, next) {
emailer.send('reset', uid, {
reset_link: nconf.get('url') + '/reset/' + code,
subject: '[[email:password-reset-requested]]',
template: 'reset',
uid: uid,
}, next);
},
], callback);
}; };
UserReset.commit = function (code, password, callback) { UserReset.commit = async function (code, password) {
var uid; await user.isPasswordValid(password);
async.waterfall([ const validated = await UserReset.validate(code);
function (next) { if (!validated) {
user.isPasswordValid(password, next); throw new Error('[[error:reset-code-not-valid]]');
}, }
function (next) { const uid = await db.getObjectField('reset:uid', code);
UserReset.validate(code, next); if (!uid) {
}, throw new Error('[[error:reset-code-not-valid]]');
function (validated, next) { }
if (!validated) {
return next(new Error('[[error:reset-code-not-valid]]')); const hash = await user.hashPassword(password);
}
db.getObjectField('reset:uid', code, next); await user.setUserFields(uid, { password: hash, 'email:confirmed': 1 });
}, await db.deleteObjectField('reset:uid', code);
function (_uid, next) { await db.sortedSetRemoveBulk([
uid = _uid; ['reset:issueDate', code],
if (!uid) { ['reset:issueDate:uid', uid],
return next(new Error('[[error:reset-code-not-valid]]')); ['users:notvalidated', uid],
} ]);
await user.reset.updateExpiry(uid);
user.hashPassword(password, next); await user.auth.resetLockout(uid);
}, await db.delete('uid:' + uid + ':confirm:email:sent');
function (hash, next) { await UserReset.cleanByUid(uid);
async.series([
async.apply(user.setUserFields, uid, { password: hash, 'email:confirmed': 1 }),
async.apply(db.deleteObjectField, 'reset:uid', code),
async.apply(db.sortedSetRemove, 'reset:issueDate', code),
async.apply(db.sortedSetRemove, 'reset:issueDate:uid', uid),
async.apply(user.reset.updateExpiry, uid),
async.apply(user.auth.resetLockout, uid),
async.apply(db.delete, 'uid:' + uid + ':confirm:email:sent'),
async.apply(db.sortedSetRemove, 'users:notvalidated', uid),
async.apply(UserReset.cleanByUid, uid),
], function (err) {
next(err);
});
},
], callback);
}; };
UserReset.updateExpiry = function (uid, callback) { UserReset.updateExpiry = async function (uid) {
var oneDay = 1000 * 60 * 60 * 24; const oneDay = 1000 * 60 * 60 * 24;
var expireDays = meta.config.passwordExpiryDays; const expireDays = meta.config.passwordExpiryDays;
var expiry = Date.now() + (oneDay * expireDays); const expiry = Date.now() + (oneDay * expireDays);
await user.setUserField(uid, 'passwordExpiry', expireDays > 0 ? expiry : 0);
callback = callback || function () {};
user.setUserField(uid, 'passwordExpiry', expireDays > 0 ? expiry : 0, callback);
}; };
UserReset.clean = function (callback) { UserReset.clean = async function () {
async.waterfall([ const [tokens, uids] = await Promise.all([
function (next) { db.getSortedSetRangeByScore('reset:issueDate', 0, -1, '-inf', Date.now() - twoHours),
async.parallel({ db.getSortedSetRangeByScore('reset:issueDate:uid', 0, -1, '-inf', Date.now() - twoHours),
tokens: function (next) { ]);
db.getSortedSetRangeByScore('reset:issueDate', 0, -1, '-inf', Date.now() - twoHours, next); if (!tokens.length && !uids.length) {
}, return;
uids: function (next) { }
db.getSortedSetRangeByScore('reset:issueDate:uid', 0, -1, '-inf', Date.now() - twoHours, next);
}, winston.verbose('[UserReset.clean] Removing ' + tokens.length + ' reset tokens from database');
}, next); await cleanTokensAndUids(tokens, uids);
},
function (results, next) {
if (!results.tokens.length && !results.uids.length) {
return next();
}
winston.verbose('[UserReset.clean] Removing ' + results.tokens.length + ' reset tokens from database');
async.parallel([
async.apply(db.deleteObjectFields, 'reset:uid', results.tokens),
async.apply(db.sortedSetRemove, 'reset:issueDate', results.tokens),
async.apply(db.sortedSetRemove, 'reset:issueDate:uid', results.uids),
], next);
},
], callback);
}; };
UserReset.cleanByUid = function (uid, callback) { UserReset.cleanByUid = async function (uid) {
var toClean = []; const tokensToClean = [];
uid = parseInt(uid, 10); uid = parseInt(uid, 10);
async.waterfall([ await batch.processSortedSet('reset:issueDate', async function (tokens) {
function (next) { const results = await db.getObjectFields('reset:uid', tokens);
batch.processSortedSet('reset:issueDate', function (tokens, next) { for (var code in results) {
db.getObjectFields('reset:uid', tokens, function (err, results) { if (results.hasOwnProperty(code) && parseInt(results[code], 10) === uid) {
for (var code in results) { tokensToClean.push(code);
if (results.hasOwnProperty(code) && parseInt(results[code], 10) === uid) {
toClean.push(code);
}
}
next(err);
});
}, next);
},
function (next) {
if (!toClean.length) {
winston.verbose('[UserReset.cleanByUid] No tokens found for uid (' + uid + ').');
return setImmediate(next);
} }
}
}, { batch: 500 });
winston.verbose('[UserReset.cleanByUid] Found ' + toClean.length + ' token(s), removing...'); if (!tokensToClean.length) {
async.parallel([ winston.verbose('[UserReset.cleanByUid] No tokens found for uid (' + uid + ').');
async.apply(db.deleteObjectFields, 'reset:uid', toClean), return;
async.apply(db.sortedSetRemove, 'reset:issueDate', toClean), }
async.apply(db.sortedSetRemove, 'reset:issueDate:uid', uid),
], next); winston.verbose('[UserReset.cleanByUid] Found ' + tokensToClean.length + ' token(s), removing...');
}, await cleanTokensAndUids(tokensToClean, uid);
], callback);
}; };
async function cleanTokensAndUids(tokens, uids) {
await Promise.all([
db.deleteObjectFields('reset:uid', tokens),
db.sortedSetRemove('reset:issueDate', tokens),
db.sortedSetRemove('reset:issueDate:uid', uids),
]);
}

@ -1,95 +1,78 @@
'use strict'; 'use strict';
var async = require('async'); const meta = require('../meta');
var meta = require('../meta'); const plugins = require('../plugins');
var plugins = require('../plugins'); const db = require('../database');
var db = require('../database');
module.exports = function (User) { module.exports = function (User) {
User.search = function (data, callback) { User.search = async function (data) {
var query = data.query || ''; const query = data.query || '';
var searchBy = data.searchBy || 'username'; const searchBy = data.searchBy || 'username';
var page = data.page || 1; const page = data.page || 1;
var uid = data.uid || 0; const uid = data.uid || 0;
var paginate = data.hasOwnProperty('paginate') ? data.paginate : true; const paginate = data.hasOwnProperty('paginate') ? data.paginate : true;
var startTime = process.hrtime(); const startTime = process.hrtime();
var searchResult = {}; let uids = [];
async.waterfall([ if (searchBy === 'ip') {
function (next) { uids = await searchByIP(query);
if (searchBy === 'ip') { } else if (searchBy === 'uid') {
searchByIP(query, next); uids = [query];
} else if (searchBy === 'uid') { } else {
next(null, [query]); const searchMethod = data.findUids || findUids;
} else { uids = await searchMethod(query, searchBy, data.hardCap);
var searchMethod = data.findUids || findUids; }
searchMethod(query, searchBy, data.hardCap, next);
} uids = await filterAndSortUids(uids, data);
}, const result = await plugins.fireHook('filter:users.search', { uids: uids, uid: uid });
function (uids, next) { uids = result.uids;
filterAndSortUids(uids, data, next);
}, const searchResult = {
function (uids, next) { matchCount: uids.length,
plugins.fireHook('filter:users.search', { uids: uids, uid: uid }, next); };
},
function (data, next) {
var uids = data.uids;
searchResult.matchCount = uids.length;
if (paginate) {
var resultsPerPage = meta.config.userSearchResultsPerPage;
var start = Math.max(0, page - 1) * resultsPerPage;
var stop = start + resultsPerPage;
searchResult.pageCount = Math.ceil(uids.length / resultsPerPage);
uids = uids.slice(start, stop);
}
User.getUsers(uids, uid, next); if (paginate) {
}, var resultsPerPage = meta.config.userSearchResultsPerPage;
function (userData, next) { var start = Math.max(0, page - 1) * resultsPerPage;
searchResult.timing = (process.elapsedTimeSince(startTime) / 1000).toFixed(2); var stop = start + resultsPerPage;
searchResult.users = userData; searchResult.pageCount = Math.ceil(uids.length / resultsPerPage);
next(null, searchResult); uids = uids.slice(start, stop);
}, }
], callback);
const userData = await User.getUsers(uids, uid);
searchResult.timing = (process.elapsedTimeSince(startTime) / 1000).toFixed(2);
searchResult.users = userData;
return searchResult;
}; };
function findUids(query, searchBy, hardCap, callback) { async function findUids(query, searchBy, hardCap) {
if (!query) { if (!query) {
return callback(null, []); return [];
} }
query = query.toLowerCase(); query = String(query).toLowerCase();
var min = query; const min = query;
var max = query.substr(0, query.length - 1) + String.fromCharCode(query.charCodeAt(query.length - 1) + 1); const max = query.substr(0, query.length - 1) + String.fromCharCode(query.charCodeAt(query.length - 1) + 1);
var resultsPerPage = meta.config.userSearchResultsPerPage; const resultsPerPage = meta.config.userSearchResultsPerPage;
hardCap = hardCap || resultsPerPage * 10; hardCap = hardCap || resultsPerPage * 10;
async.waterfall([ const data = await db.getSortedSetRangeByLex(searchBy + ':sorted', min, max, 0, hardCap);
function (next) { const uids = data.map(data => data.split(':')[1]);
db.getSortedSetRangeByLex(searchBy + ':sorted', min, max, 0, hardCap, next); return uids;
},
function (data, next) {
var uids = data.map(function (data) {
return data.split(':')[1];
});
next(null, uids);
},
], callback);
} }
function filterAndSortUids(uids, data, callback) { async function filterAndSortUids(uids, data) {
uids = uids.filter(uid => parseInt(uid, 10)); uids = uids.filter(uid => parseInt(uid, 10));
var fields = []; const fields = [];
if (data.sortBy) { if (data.sortBy) {
fields.push(data.sortBy); fields.push(data.sortBy);
} }
if (data.onlineOnly) { if (data.onlineOnly) {
fields = fields.concat(['status', 'lastonline']); fields.push('status', 'lastonline');
} }
if (data.bannedOnly) { if (data.bannedOnly) {
fields.push('banned'); fields.push('banned');
@ -99,45 +82,34 @@ module.exports = function (User) {
} }
if (!fields.length) { if (!fields.length) {
return callback(null, uids); return uids;
} }
fields = ['uid'].concat(fields); fields.push('uid');
let userData = await User.getUsersFields(uids, fields);
async.waterfall([ userData = userData.filter(Boolean);
function (next) { if (data.onlineOnly) {
User.getUsersFields(uids, fields, next); userData = userData.filter(user => user.status !== 'offline' && (Date.now() - user.lastonline < 300000));
}, }
function (userData, next) {
userData = userData.filter(Boolean);
if (data.onlineOnly) {
userData = userData.filter(user => user.status !== 'offline' && (Date.now() - user.lastonline < 300000));
}
if (data.bannedOnly) {
userData = userData.filter(user => user.banned);
}
if (data.flaggedOnly) { if (data.bannedOnly) {
userData = userData.filter(user => parseInt(user.flags, 10) > 0); userData = userData.filter(user => user.banned);
} }
if (data.sortBy) { if (data.flaggedOnly) {
sortUsers(userData, data.sortBy); userData = userData.filter(user => parseInt(user.flags, 10) > 0);
} }
uids = userData.map(user => user.uid); if (data.sortBy) {
sortUsers(userData, data.sortBy);
}
next(null, uids); return userData.map(user => user.uid);
},
], callback);
} }
function sortUsers(userData, sortBy) { function sortUsers(userData, sortBy) {
if (sortBy === 'joindate' || sortBy === 'postcount' || sortBy === 'reputation') { if (sortBy === 'joindate' || sortBy === 'postcount' || sortBy === 'reputation') {
userData.sort(function (u1, u2) { userData.sort((u1, u2) => u2[sortBy] - u1[sortBy]);
return u2[sortBy] - u1[sortBy];
});
} else { } else {
userData.sort(function (u1, u2) { userData.sort(function (u1, u2) {
if (u1[sortBy] < u2[sortBy]) { if (u1[sortBy] < u2[sortBy]) {
@ -150,7 +122,7 @@ module.exports = function (User) {
} }
} }
function searchByIP(ip, callback) { async function searchByIP(ip) {
db.getSortedSetRevRange('ip:' + ip + ':uid', 0, -1, callback); return await db.getSortedSetRevRange('ip:' + ip + ':uid', 0, -1);
} }
}; };

@ -1,96 +1,73 @@
'use strict'; 'use strict';
var async = require('async'); const meta = require('../meta');
const db = require('../database');
var meta = require('../meta'); const plugins = require('../plugins');
var db = require('../database'); const notifications = require('../notifications');
var plugins = require('../plugins');
var notifications = require('../notifications');
module.exports = function (User) { module.exports = function (User) {
User.getSettings = function (uid, callback) { User.getSettings = async function (uid) {
if (parseInt(uid, 10) <= 0) { if (parseInt(uid, 10) <= 0) {
return onSettingsLoaded(0, {}, callback); return await onSettingsLoaded(0, {});
} }
let settings = await db.getObject('user:' + uid + ':settings');
async.waterfall([ settings = settings || {};
function (next) { settings.uid = uid;
db.getObject('user:' + uid + ':settings', next); return await onSettingsLoaded(uid, settings);
},
function (settings, next) {
settings = settings || {};
settings.uid = uid;
onSettingsLoaded(uid, settings, next);
},
], callback);
}; };
User.getMultipleUserSettings = function (uids, callback) { User.getMultipleUserSettings = async function (uids) {
if (!Array.isArray(uids) || !uids.length) { if (!Array.isArray(uids) || !uids.length) {
return callback(null, []); return [];
} }
var keys = uids.map(uid => 'user:' + uid + ':settings'); const keys = uids.map(uid => 'user:' + uid + ':settings');
let settings = await db.getObjects(keys);
async.waterfall([ settings = settings.map(function (userSettings, index) {
function (next) { userSettings = userSettings || {};
db.getObjects(keys, next); userSettings.uid = uids[index];
}, return userSettings;
function (settings, next) { });
settings = settings.map(function (userSettings, index) { return await Promise.all(settings.map(s => onSettingsLoaded(s.uid, s)));
userSettings = userSettings || {}; // async.map(settings, function (userSettings, next) {
userSettings.uid = uids[index]; // onSettingsLoaded(userSettings.uid, userSettings, next);
return userSettings; // }, next);
});
async.map(settings, function (userSettings, next) {
onSettingsLoaded(userSettings.uid, userSettings, next);
}, next);
},
], callback);
}; };
function onSettingsLoaded(uid, settings, callback) { async function onSettingsLoaded(uid, settings) {
async.waterfall([ const data = await plugins.fireHook('filter:user.getSettings', { uid: uid, settings: settings });
function (next) { settings = data.settings;
plugins.fireHook('filter:user.getSettings', { uid: uid, settings: settings }, next);
}, const defaultTopicsPerPage = meta.config.topicsPerPage;
function (data, next) { const defaultPostsPerPage = meta.config.postsPerPage;
settings = data.settings;
settings.showemail = parseInt(getSetting(settings, 'showemail', 0), 10) === 1;
var defaultTopicsPerPage = meta.config.topicsPerPage; settings.showfullname = parseInt(getSetting(settings, 'showfullname', 0), 10) === 1;
var defaultPostsPerPage = meta.config.postsPerPage; settings.openOutgoingLinksInNewTab = parseInt(getSetting(settings, 'openOutgoingLinksInNewTab', 0), 10) === 1;
settings.dailyDigestFreq = getSetting(settings, 'dailyDigestFreq', 'off');
settings.showemail = parseInt(getSetting(settings, 'showemail', 0), 10) === 1; settings.usePagination = parseInt(getSetting(settings, 'usePagination', 0), 10) === 1;
settings.showfullname = parseInt(getSetting(settings, 'showfullname', 0), 10) === 1; settings.topicsPerPage = Math.min(settings.topicsPerPage ? parseInt(settings.topicsPerPage, 10) : defaultTopicsPerPage, defaultTopicsPerPage);
settings.openOutgoingLinksInNewTab = parseInt(getSetting(settings, 'openOutgoingLinksInNewTab', 0), 10) === 1; settings.postsPerPage = Math.min(settings.postsPerPage ? parseInt(settings.postsPerPage, 10) : defaultPostsPerPage, defaultPostsPerPage);
settings.dailyDigestFreq = getSetting(settings, 'dailyDigestFreq', 'off'); settings.userLang = settings.userLang || meta.config.defaultLang || 'en-GB';
settings.usePagination = parseInt(getSetting(settings, 'usePagination', 0), 10) === 1; settings.acpLang = settings.acpLang || settings.userLang;
settings.topicsPerPage = Math.min(settings.topicsPerPage ? parseInt(settings.topicsPerPage, 10) : defaultTopicsPerPage, defaultTopicsPerPage); settings.topicPostSort = getSetting(settings, 'topicPostSort', 'oldest_to_newest');
settings.postsPerPage = Math.min(settings.postsPerPage ? parseInt(settings.postsPerPage, 10) : defaultPostsPerPage, defaultPostsPerPage); settings.categoryTopicSort = getSetting(settings, 'categoryTopicSort', 'newest_to_oldest');
settings.userLang = settings.userLang || meta.config.defaultLang || 'en-GB'; settings.followTopicsOnCreate = parseInt(getSetting(settings, 'followTopicsOnCreate', 1), 10) === 1;
settings.acpLang = settings.acpLang || settings.userLang; settings.followTopicsOnReply = parseInt(getSetting(settings, 'followTopicsOnReply', 0), 10) === 1;
settings.topicPostSort = getSetting(settings, 'topicPostSort', 'oldest_to_newest'); settings.upvoteNotifFreq = getSetting(settings, 'upvoteNotifFreq', 'all');
settings.categoryTopicSort = getSetting(settings, 'categoryTopicSort', 'newest_to_oldest'); settings.restrictChat = parseInt(getSetting(settings, 'restrictChat', 0), 10) === 1;
settings.followTopicsOnCreate = parseInt(getSetting(settings, 'followTopicsOnCreate', 1), 10) === 1; settings.topicSearchEnabled = parseInt(getSetting(settings, 'topicSearchEnabled', 0), 10) === 1;
settings.followTopicsOnReply = parseInt(getSetting(settings, 'followTopicsOnReply', 0), 10) === 1; settings.bootswatchSkin = settings.bootswatchSkin || '';
settings.upvoteNotifFreq = getSetting(settings, 'upvoteNotifFreq', 'all'); settings.scrollToMyPost = parseInt(getSetting(settings, 'scrollToMyPost', 1), 10) === 1;
settings.restrictChat = parseInt(getSetting(settings, 'restrictChat', 0), 10) === 1; settings.categoryWatchState = getSetting(settings, 'categoryWatchState', 'notwatching');
settings.topicSearchEnabled = parseInt(getSetting(settings, 'topicSearchEnabled', 0), 10) === 1;
settings.bootswatchSkin = settings.bootswatchSkin || ''; const notificationTypes = await notifications.getAllNotificationTypes();
settings.scrollToMyPost = parseInt(getSetting(settings, 'scrollToMyPost', 1), 10) === 1; notificationTypes.forEach(function (notificationType) {
settings.categoryWatchState = getSetting(settings, 'categoryWatchState', 'notwatching'); settings[notificationType] = getSetting(settings, notificationType, 'notification');
});
notifications.getAllNotificationTypes(next);
}, return settings;
function (notificationTypes, next) {
notificationTypes.forEach(function (notificationType) {
settings[notificationType] = getSetting(settings, notificationType, 'notification');
});
next(null, settings);
},
], callback);
} }
function getSetting(settings, key, defaultValue) { function getSetting(settings, key, defaultValue) {
@ -102,22 +79,22 @@ module.exports = function (User) {
return defaultValue; return defaultValue;
} }
User.saveSettings = function (uid, data, callback) { User.saveSettings = async function (uid, data) {
var maxPostsPerPage = meta.config.maxPostsPerPage || 20; var maxPostsPerPage = meta.config.maxPostsPerPage || 20;
if (!data.postsPerPage || parseInt(data.postsPerPage, 10) <= 1 || parseInt(data.postsPerPage, 10) > maxPostsPerPage) { if (!data.postsPerPage || parseInt(data.postsPerPage, 10) <= 1 || parseInt(data.postsPerPage, 10) > maxPostsPerPage) {
return callback(new Error('[[error:invalid-pagination-value, 2, ' + maxPostsPerPage + ']]')); throw new Error('[[error:invalid-pagination-value, 2, ' + maxPostsPerPage + ']]');
} }
var maxTopicsPerPage = meta.config.maxTopicsPerPage || 20; const maxTopicsPerPage = meta.config.maxTopicsPerPage || 20;
if (!data.topicsPerPage || parseInt(data.topicsPerPage, 10) <= 1 || parseInt(data.topicsPerPage, 10) > maxTopicsPerPage) { if (!data.topicsPerPage || parseInt(data.topicsPerPage, 10) <= 1 || parseInt(data.topicsPerPage, 10) > maxTopicsPerPage) {
return callback(new Error('[[error:invalid-pagination-value, 2, ' + maxTopicsPerPage + ']]')); throw new Error('[[error:invalid-pagination-value, 2, ' + maxTopicsPerPage + ']]');
} }
data.userLang = data.userLang || meta.config.defaultLang; data.userLang = data.userLang || meta.config.defaultLang;
plugins.fireHook('action:user.saveSettings', { uid: uid, settings: data }); plugins.fireHook('action:user.saveSettings', { uid: uid, settings: data });
var settings = { const settings = {
showemail: data.showemail, showemail: data.showemail,
showfullname: data.showfullname, showfullname: data.showfullname,
openOutgoingLinksInNewTab: data.openOutgoingLinksInNewTab, openOutgoingLinksInNewTab: data.openOutgoingLinksInNewTab,
@ -140,51 +117,30 @@ module.exports = function (User) {
bootswatchSkin: data.bootswatchSkin, bootswatchSkin: data.bootswatchSkin,
categoryWatchState: data.categoryWatchState, categoryWatchState: data.categoryWatchState,
}; };
const notificationTypes = await notifications.getAllNotificationTypes();
async.waterfall([ notificationTypes.forEach(function (notificationType) {
function (next) { if (data[notificationType]) {
notifications.getAllNotificationTypes(next); settings[notificationType] = data[notificationType];
}, }
function (notificationTypes, next) { });
notificationTypes.forEach(function (notificationType) { const result = await plugins.fireHook('filter:user.saveSettings', { settings: settings, data: data });
if (data[notificationType]) { await db.setObject('user:' + uid + ':settings', result.settings);
settings[notificationType] = data[notificationType]; await User.updateDigestSetting(uid, data.dailyDigestFreq);
} return await User.getSettings(uid);
});
plugins.fireHook('filter:user.saveSettings', { settings: settings, data: data }, next);
},
function (result, next) {
db.setObject('user:' + uid + ':settings', result.settings, next);
},
function (next) {
User.updateDigestSetting(uid, data.dailyDigestFreq, next);
},
function (next) {
User.getSettings(uid, next);
},
], callback);
}; };
User.updateDigestSetting = function (uid, dailyDigestFreq, callback) { User.updateDigestSetting = async function (uid, dailyDigestFreq) {
async.waterfall([ await db.sortedSetsRemove(['digest:day:uids', 'digest:week:uids', 'digest:month:uids'], uid);
function (next) { if (['day', 'week', 'month'].includes(dailyDigestFreq)) {
db.sortedSetsRemove(['digest:day:uids', 'digest:week:uids', 'digest:month:uids'], uid, next); await db.sortedSetAdd('digest:' + dailyDigestFreq + ':uids', Date.now(), uid);
}, }
function (next) {
if (['day', 'week', 'month'].includes(dailyDigestFreq)) {
db.sortedSetAdd('digest:' + dailyDigestFreq + ':uids', Date.now(), uid, next);
} else {
next();
}
},
], callback);
}; };
User.setSetting = function (uid, key, value, callback) { User.setSetting = async function (uid, key, value) {
if (parseInt(uid, 10) <= 0) { if (parseInt(uid, 10) <= 0) {
return setImmediate(callback); return;
} }
db.setObjectField('user:' + uid + ':settings', key, value, callback); await db.setObjectField('user:' + uid + ':settings', key, value);
}; };
}; };

@ -1,17 +1,16 @@
'use strict'; 'use strict';
var async = require('async'); const db = require('../database');
var db = require('../database');
module.exports = function (User) { module.exports = function (User) {
User.getIgnoredTids = function (uid, start, stop, callback) { User.getIgnoredTids = async function (uid, start, stop) {
db.getSortedSetRevRange('uid:' + uid + ':ignored_tids', start, stop, callback); return await db.getSortedSetRevRange('uid:' + uid + ':ignored_tids', start, stop);
}; };
User.addTopicIdToUser = function (uid, tid, timestamp, callback) { User.addTopicIdToUser = async function (uid, tid, timestamp) {
async.parallel([ await Promise.all([
async.apply(db.sortedSetAdd, 'uid:' + uid + ':topics', timestamp, tid), db.sortedSetAdd('uid:' + uid + ':topics', timestamp, tid),
async.apply(User.incrementUserFieldBy, uid, 'topiccount', 1), User.incrementUserFieldBy(uid, 'topiccount', 1),
], callback); ]);
}; };
}; };

@ -1,6 +1,5 @@
'use strict'; 'use strict';
var async = require('async');
var path = require('path'); var path = require('path');
var nconf = require('nconf'); var nconf = require('nconf');
var winston = require('winston'); var winston = require('winston');
@ -10,40 +9,25 @@ var file = require('../file');
var batch = require('../batch'); var batch = require('../batch');
module.exports = function (User) { module.exports = function (User) {
User.deleteUpload = function (callerUid, uid, uploadName, callback) { User.deleteUpload = async function (callerUid, uid, uploadName) {
async.waterfall([ const [isUsersUpload, isAdminOrGlobalMod] = await Promise.all([
function (next) { db.isSortedSetMember('uid:' + callerUid + ':uploads', uploadName),
async.parallel({ User.isAdminOrGlobalMod(callerUid),
isUsersUpload: function (next) { ]);
db.isSortedSetMember('uid:' + callerUid + ':uploads', uploadName, next); if (!isAdminOrGlobalMod && !isUsersUpload) {
}, throw new Error('[[error:no-privileges]]');
isAdminOrGlobalMod: function (next) { }
User.isAdminOrGlobalMod(callerUid, next);
},
}, next);
},
function (results, next) {
if (!results.isAdminOrGlobalMod && !results.isUsersUpload) {
return next(new Error('[[error:no-privileges]]'));
}
winston.verbose('[user/deleteUpload] Deleting ' + uploadName); winston.verbose('[user/deleteUpload] Deleting ' + uploadName);
async.parallel([ await Promise.all([
async.apply(file.delete, path.join(nconf.get('upload_path'), uploadName)), file.delete(path.join(nconf.get('upload_path'), uploadName)),
async.apply(file.delete, path.join(nconf.get('upload_path'), path.dirname(uploadName), path.basename(uploadName, path.extname(uploadName)) + '-resized' + path.extname(uploadName))), file.delete(path.join(nconf.get('upload_path'), path.dirname(uploadName), path.basename(uploadName, path.extname(uploadName)) + '-resized' + path.extname(uploadName))),
], function (err) { ]);
// Only return err, not the parallel'd result set await db.sortedSetRemove('uid:' + uid + ':uploads', uploadName);
next(err);
});
},
function (next) {
db.sortedSetRemove('uid:' + uid + ':uploads', uploadName, next);
},
], callback);
}; };
User.collateUploads = function (uid, archive, callback) { User.collateUploads = async function (uid, archive) {
batch.processSortedSet('uid:' + uid + ':uploads', function (files, next) { await batch.processSortedSet('uid:' + uid + ':uploads', function (files, next) {
files.forEach(function (file) { files.forEach(function (file) {
archive.file(path.join(nconf.get('upload_path'), file), { archive.file(path.join(nconf.get('upload_path'), file), {
name: path.basename(file), name: path.basename(file),
@ -51,8 +35,6 @@ module.exports = function (User) {
}); });
setImmediate(next); setImmediate(next);
}, function (err) {
callback(err);
}); });
}; };
}; };

Loading…
Cancel
Save