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 () {
batch.processArray(uids, function (uids, next) {
pushToUids(uids, notification, next);
batch.processArray(uids, async function (uids) {
await pushToUids(uids, notification);
}, { interval: 1000 }, function (err) {
if (err) {
winston.error(err.stack);

@ -1,6 +1,5 @@
'use strict';
var async = require('async');
var nconf = require('nconf');
var winston = require('winston');
@ -16,185 +15,119 @@ var UserReset = module.exports;
var twoHours = 7200000;
UserReset.validate = function (code, callback) {
async.waterfall([
function (next) {
db.getObjectField('reset:uid', code, next);
},
function (uid, next) {
if (!uid) {
return callback(null, false);
}
db.sortedSetScore('reset:issueDate', code, next);
},
function (issueDate, next) {
next(null, parseInt(issueDate, 10) > Date.now() - twoHours);
},
], callback);
UserReset.validate = async function (code) {
const uid = await db.getObjectField('reset:uid', code);
if (!uid) {
return false;
}
const issueDate = await db.sortedSetScore('reset:issueDate', code);
return parseInt(issueDate, 10) > Date.now() - twoHours;
};
UserReset.generate = function (uid, callback) {
var code = utils.generateUUID();
async.parallel([
async.apply(db.setObjectField, 'reset:uid', code, uid),
async.apply(db.sortedSetAdd, 'reset:issueDate', Date.now(), code),
], function (err) {
callback(err, code);
});
UserReset.generate = async function (uid) {
const code = utils.generateUUID();
await Promise.all([
db.setObjectField('reset:uid', code, uid),
db.sortedSetAdd('reset:issueDate', Date.now(), code),
]);
return code;
};
function canGenerate(uid, callback) {
async.waterfall([
function (next) {
db.sortedSetScore('reset:issueDate:uid', uid, next);
},
function (score, next) {
if (score > Date.now() - (1000 * 60)) {
return next(new Error('[[error:reset-rate-limited]]'));
}
next();
},
], callback);
async function canGenerate(uid) {
const score = await db.sortedSetScore('reset:issueDate:uid', uid);
if (score > Date.now() - (1000 * 60)) {
throw new Error('[[error:reset-rate-limited]]');
}
}
UserReset.send = function (email, callback) {
var uid;
async.waterfall([
function (next) {
user.getUidByEmail(email, next);
},
function (_uid, next) {
if (!_uid) {
return next(new Error('[[error:invalid-email]]'));
}
uid = _uid;
canGenerate(uid, next);
},
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.send = async function (email) {
const uid = await user.getUidByEmail(email);
if (!uid) {
throw new Error('[[error:invalid-email]]');
}
await canGenerate(uid);
await db.sortedSetAdd('reset:issueDate:uid', Date.now(), uid);
const code = await UserReset.generate(uid);
await emailer.send('reset', uid, {
reset_link: nconf.get('url') + '/reset/' + code,
subject: '[[email:password-reset-requested]]',
template: 'reset',
uid: uid,
});
};
UserReset.commit = function (code, password, callback) {
var uid;
async.waterfall([
function (next) {
user.isPasswordValid(password, next);
},
function (next) {
UserReset.validate(code, next);
},
function (validated, next) {
if (!validated) {
return next(new Error('[[error:reset-code-not-valid]]'));
}
db.getObjectField('reset:uid', code, next);
},
function (_uid, next) {
uid = _uid;
if (!uid) {
return next(new Error('[[error:reset-code-not-valid]]'));
}
user.hashPassword(password, next);
},
function (hash, next) {
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.commit = async function (code, password) {
await user.isPasswordValid(password);
const validated = await UserReset.validate(code);
if (!validated) {
throw new Error('[[error:reset-code-not-valid]]');
}
const uid = await db.getObjectField('reset:uid', code);
if (!uid) {
throw new Error('[[error:reset-code-not-valid]]');
}
const hash = await user.hashPassword(password);
await user.setUserFields(uid, { password: hash, 'email:confirmed': 1 });
await db.deleteObjectField('reset:uid', code);
await db.sortedSetRemoveBulk([
['reset:issueDate', code],
['reset:issueDate:uid', uid],
['users:notvalidated', uid],
]);
await user.reset.updateExpiry(uid);
await user.auth.resetLockout(uid);
await db.delete('uid:' + uid + ':confirm:email:sent');
await UserReset.cleanByUid(uid);
};
UserReset.updateExpiry = function (uid, callback) {
var oneDay = 1000 * 60 * 60 * 24;
var expireDays = meta.config.passwordExpiryDays;
var expiry = Date.now() + (oneDay * expireDays);
callback = callback || function () {};
user.setUserField(uid, 'passwordExpiry', expireDays > 0 ? expiry : 0, callback);
UserReset.updateExpiry = async function (uid) {
const oneDay = 1000 * 60 * 60 * 24;
const expireDays = meta.config.passwordExpiryDays;
const expiry = Date.now() + (oneDay * expireDays);
await user.setUserField(uid, 'passwordExpiry', expireDays > 0 ? expiry : 0);
};
UserReset.clean = function (callback) {
async.waterfall([
function (next) {
async.parallel({
tokens: function (next) {
db.getSortedSetRangeByScore('reset:issueDate', 0, -1, '-inf', Date.now() - twoHours, next);
},
uids: function (next) {
db.getSortedSetRangeByScore('reset:issueDate:uid', 0, -1, '-inf', Date.now() - twoHours, next);
},
}, next);
},
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.clean = async function () {
const [tokens, uids] = await Promise.all([
db.getSortedSetRangeByScore('reset:issueDate', 0, -1, '-inf', Date.now() - twoHours),
db.getSortedSetRangeByScore('reset:issueDate:uid', 0, -1, '-inf', Date.now() - twoHours),
]);
if (!tokens.length && !uids.length) {
return;
}
winston.verbose('[UserReset.clean] Removing ' + tokens.length + ' reset tokens from database');
await cleanTokensAndUids(tokens, uids);
};
UserReset.cleanByUid = function (uid, callback) {
var toClean = [];
UserReset.cleanByUid = async function (uid) {
const tokensToClean = [];
uid = parseInt(uid, 10);
async.waterfall([
function (next) {
batch.processSortedSet('reset:issueDate', function (tokens, next) {
db.getObjectFields('reset:uid', tokens, function (err, results) {
for (var code in results) {
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);
await batch.processSortedSet('reset:issueDate', async function (tokens) {
const results = await db.getObjectFields('reset:uid', tokens);
for (var code in results) {
if (results.hasOwnProperty(code) && parseInt(results[code], 10) === uid) {
tokensToClean.push(code);
}
}
}, { batch: 500 });
winston.verbose('[UserReset.cleanByUid] Found ' + toClean.length + ' token(s), removing...');
async.parallel([
async.apply(db.deleteObjectFields, 'reset:uid', toClean),
async.apply(db.sortedSetRemove, 'reset:issueDate', toClean),
async.apply(db.sortedSetRemove, 'reset:issueDate:uid', uid),
], next);
},
], callback);
if (!tokensToClean.length) {
winston.verbose('[UserReset.cleanByUid] No tokens found for uid (' + uid + ').');
return;
}
winston.verbose('[UserReset.cleanByUid] Found ' + tokensToClean.length + ' token(s), removing...');
await cleanTokensAndUids(tokensToClean, uid);
};
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';
var async = require('async');
var meta = require('../meta');
var plugins = require('../plugins');
var db = require('../database');
const meta = require('../meta');
const plugins = require('../plugins');
const db = require('../database');
module.exports = function (User) {
User.search = function (data, callback) {
var query = data.query || '';
var searchBy = data.searchBy || 'username';
var page = data.page || 1;
var uid = data.uid || 0;
var paginate = data.hasOwnProperty('paginate') ? data.paginate : true;
var startTime = process.hrtime();
var searchResult = {};
async.waterfall([
function (next) {
if (searchBy === 'ip') {
searchByIP(query, next);
} else if (searchBy === 'uid') {
next(null, [query]);
} else {
var searchMethod = data.findUids || findUids;
searchMethod(query, searchBy, data.hardCap, next);
}
},
function (uids, next) {
filterAndSortUids(uids, data, next);
},
function (uids, next) {
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.search = async function (data) {
const query = data.query || '';
const searchBy = data.searchBy || 'username';
const page = data.page || 1;
const uid = data.uid || 0;
const paginate = data.hasOwnProperty('paginate') ? data.paginate : true;
const startTime = process.hrtime();
let uids = [];
if (searchBy === 'ip') {
uids = await searchByIP(query);
} else if (searchBy === 'uid') {
uids = [query];
} else {
const searchMethod = data.findUids || findUids;
uids = await searchMethod(query, searchBy, data.hardCap);
}
uids = await filterAndSortUids(uids, data);
const result = await plugins.fireHook('filter:users.search', { uids: uids, uid: uid });
uids = result.uids;
const searchResult = {
matchCount: uids.length,
};
User.getUsers(uids, uid, next);
},
function (userData, next) {
searchResult.timing = (process.elapsedTimeSince(startTime) / 1000).toFixed(2);
searchResult.users = userData;
next(null, searchResult);
},
], callback);
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);
}
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) {
return callback(null, []);
return [];
}
query = query.toLowerCase();
var min = query;
var max = query.substr(0, query.length - 1) + String.fromCharCode(query.charCodeAt(query.length - 1) + 1);
query = String(query).toLowerCase();
const min = query;
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;
async.waterfall([
function (next) {
db.getSortedSetRangeByLex(searchBy + ':sorted', min, max, 0, hardCap, next);
},
function (data, next) {
var uids = data.map(function (data) {
return data.split(':')[1];
});
next(null, uids);
},
], callback);
const data = await db.getSortedSetRangeByLex(searchBy + ':sorted', min, max, 0, hardCap);
const uids = data.map(data => data.split(':')[1]);
return uids;
}
function filterAndSortUids(uids, data, callback) {
async function filterAndSortUids(uids, data) {
uids = uids.filter(uid => parseInt(uid, 10));
var fields = [];
const fields = [];
if (data.sortBy) {
fields.push(data.sortBy);
}
if (data.onlineOnly) {
fields = fields.concat(['status', 'lastonline']);
fields.push('status', 'lastonline');
}
if (data.bannedOnly) {
fields.push('banned');
@ -99,45 +82,34 @@ module.exports = function (User) {
}
if (!fields.length) {
return callback(null, uids);
return uids;
}
fields = ['uid'].concat(fields);
async.waterfall([
function (next) {
User.getUsersFields(uids, fields, next);
},
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);
}
fields.push('uid');
let userData = await User.getUsersFields(uids, fields);
userData = userData.filter(Boolean);
if (data.onlineOnly) {
userData = userData.filter(user => user.status !== 'offline' && (Date.now() - user.lastonline < 300000));
}
if (data.flaggedOnly) {
userData = userData.filter(user => parseInt(user.flags, 10) > 0);
}
if (data.bannedOnly) {
userData = userData.filter(user => user.banned);
}
if (data.sortBy) {
sortUsers(userData, data.sortBy);
}
if (data.flaggedOnly) {
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);
},
], callback);
return userData.map(user => user.uid);
}
function sortUsers(userData, sortBy) {
if (sortBy === 'joindate' || sortBy === 'postcount' || sortBy === 'reputation') {
userData.sort(function (u1, u2) {
return u2[sortBy] - u1[sortBy];
});
userData.sort((u1, u2) => u2[sortBy] - u1[sortBy]);
} else {
userData.sort(function (u1, u2) {
if (u1[sortBy] < u2[sortBy]) {
@ -150,7 +122,7 @@ module.exports = function (User) {
}
}
function searchByIP(ip, callback) {
db.getSortedSetRevRange('ip:' + ip + ':uid', 0, -1, callback);
async function searchByIP(ip) {
return await db.getSortedSetRevRange('ip:' + ip + ':uid', 0, -1);
}
};

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

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

Loading…
Cancel
Save