mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-12 08:55:47 +01:00
feat: #7743, finish user module
This commit is contained in:
@@ -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) {
|
|
||||||
db.getObjectField('reset:uid', code, next);
|
|
||||||
},
|
|
||||||
function (uid, next) {
|
|
||||||
if (!uid) {
|
if (!uid) {
|
||||||
return callback(null, false);
|
return false;
|
||||||
}
|
}
|
||||||
db.sortedSetScore('reset:issueDate', code, next);
|
const issueDate = await db.sortedSetScore('reset:issueDate', code);
|
||||||
},
|
return parseInt(issueDate, 10) > Date.now() - twoHours;
|
||||||
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) {
|
|
||||||
db.sortedSetScore('reset:issueDate:uid', uid, next);
|
|
||||||
},
|
|
||||||
function (score, next) {
|
|
||||||
if (score > Date.now() - (1000 * 60)) {
|
if (score > Date.now() - (1000 * 60)) {
|
||||||
return next(new Error('[[error:reset-rate-limited]]'));
|
throw 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);
|
|
||||||
},
|
|
||||||
function (_uid, next) {
|
|
||||||
if (!_uid) {
|
|
||||||
return next(new Error('[[error:invalid-email]]'));
|
|
||||||
}
|
}
|
||||||
|
await canGenerate(uid);
|
||||||
uid = _uid;
|
await db.sortedSetAdd('reset:issueDate:uid', Date.now(), uid);
|
||||||
canGenerate(uid, next);
|
const code = await UserReset.generate(uid);
|
||||||
},
|
await emailer.send('reset', 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,
|
reset_link: nconf.get('url') + '/reset/' + code,
|
||||||
subject: '[[email:password-reset-requested]]',
|
subject: '[[email:password-reset-requested]]',
|
||||||
template: 'reset',
|
template: 'reset',
|
||||||
uid: uid,
|
uid: uid,
|
||||||
}, next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
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.updateExpiry = function (uid, callback) {
|
UserReset.commit = async function (code, password) {
|
||||||
var oneDay = 1000 * 60 * 60 * 24;
|
await user.isPasswordValid(password);
|
||||||
var expireDays = meta.config.passwordExpiryDays;
|
const validated = await UserReset.validate(code);
|
||||||
var expiry = Date.now() + (oneDay * expireDays);
|
if (!validated) {
|
||||||
|
throw new Error('[[error:reset-code-not-valid]]');
|
||||||
callback = callback || function () {};
|
}
|
||||||
user.setUserField(uid, 'passwordExpiry', expireDays > 0 ? expiry : 0, callback);
|
const uid = await db.getObjectField('reset:uid', code);
|
||||||
};
|
if (!uid) {
|
||||||
|
throw new Error('[[error:reset-code-not-valid]]');
|
||||||
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');
|
const hash = await user.hashPassword(password);
|
||||||
async.parallel([
|
|
||||||
async.apply(db.deleteObjectFields, 'reset:uid', results.tokens),
|
await user.setUserFields(uid, { password: hash, 'email:confirmed': 1 });
|
||||||
async.apply(db.sortedSetRemove, 'reset:issueDate', results.tokens),
|
await db.deleteObjectField('reset:uid', code);
|
||||||
async.apply(db.sortedSetRemove, 'reset:issueDate:uid', results.uids),
|
await db.sortedSetRemoveBulk([
|
||||||
], next);
|
['reset:issueDate', code],
|
||||||
},
|
['reset:issueDate:uid', uid],
|
||||||
], callback);
|
['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.cleanByUid = function (uid, callback) {
|
UserReset.updateExpiry = async function (uid) {
|
||||||
var toClean = [];
|
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 = 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 = async function (uid) {
|
||||||
|
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) {
|
|
||||||
db.getObjectFields('reset:uid', tokens, function (err, results) {
|
|
||||||
for (var code in results) {
|
for (var code in results) {
|
||||||
if (results.hasOwnProperty(code) && parseInt(results[code], 10) === uid) {
|
if (results.hasOwnProperty(code) && parseInt(results[code], 10) === uid) {
|
||||||
toClean.push(code);
|
tokensToClean.push(code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}, { batch: 500 });
|
||||||
|
|
||||||
next(err);
|
if (!tokensToClean.length) {
|
||||||
});
|
|
||||||
}, next);
|
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
if (!toClean.length) {
|
|
||||||
winston.verbose('[UserReset.cleanByUid] No tokens found for uid (' + uid + ').');
|
winston.verbose('[UserReset.cleanByUid] No tokens found for uid (' + uid + ').');
|
||||||
return setImmediate(next);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
winston.verbose('[UserReset.cleanByUid] Found ' + toClean.length + ' token(s), removing...');
|
winston.verbose('[UserReset.cleanByUid] Found ' + tokensToClean.length + ' token(s), removing...');
|
||||||
async.parallel([
|
await cleanTokensAndUids(tokensToClean, uid);
|
||||||
async.apply(db.deleteObjectFields, 'reset:uid', toClean),
|
|
||||||
async.apply(db.sortedSetRemove, 'reset:issueDate', toClean),
|
|
||||||
async.apply(db.sortedSetRemove, 'reset:issueDate:uid', uid),
|
|
||||||
], next);
|
|
||||||
},
|
|
||||||
], 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,42 +1,37 @@
|
|||||||
|
|
||||||
'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([
|
|
||||||
function (next) {
|
|
||||||
if (searchBy === 'ip') {
|
if (searchBy === 'ip') {
|
||||||
searchByIP(query, next);
|
uids = await searchByIP(query);
|
||||||
} else if (searchBy === 'uid') {
|
} else if (searchBy === 'uid') {
|
||||||
next(null, [query]);
|
uids = [query];
|
||||||
} else {
|
} else {
|
||||||
var searchMethod = data.findUids || findUids;
|
const searchMethod = data.findUids || findUids;
|
||||||
searchMethod(query, searchBy, data.hardCap, next);
|
uids = await searchMethod(query, searchBy, data.hardCap);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
function (uids, next) {
|
uids = await filterAndSortUids(uids, data);
|
||||||
filterAndSortUids(uids, data, next);
|
const result = await plugins.fireHook('filter:users.search', { uids: uids, uid: uid });
|
||||||
},
|
uids = result.uids;
|
||||||
function (uids, next) {
|
|
||||||
plugins.fireHook('filter:users.search', { uids: uids, uid: uid }, next);
|
const searchResult = {
|
||||||
},
|
matchCount: uids.length,
|
||||||
function (data, next) {
|
};
|
||||||
var uids = data.uids;
|
|
||||||
searchResult.matchCount = uids.length;
|
|
||||||
|
|
||||||
if (paginate) {
|
if (paginate) {
|
||||||
var resultsPerPage = meta.config.userSearchResultsPerPage;
|
var resultsPerPage = meta.config.userSearchResultsPerPage;
|
||||||
@@ -46,50 +41,38 @@ module.exports = function (User) {
|
|||||||
uids = uids.slice(start, stop);
|
uids = uids.slice(start, stop);
|
||||||
}
|
}
|
||||||
|
|
||||||
User.getUsers(uids, uid, next);
|
const userData = await User.getUsers(uids, uid);
|
||||||
},
|
|
||||||
function (userData, next) {
|
|
||||||
searchResult.timing = (process.elapsedTimeSince(startTime) / 1000).toFixed(2);
|
searchResult.timing = (process.elapsedTimeSince(startTime) / 1000).toFixed(2);
|
||||||
searchResult.users = userData;
|
searchResult.users = userData;
|
||||||
next(null, searchResult);
|
return searchResult;
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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,16 +82,11 @@ 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([
|
|
||||||
function (next) {
|
|
||||||
User.getUsersFields(uids, fields, next);
|
|
||||||
},
|
|
||||||
function (userData, next) {
|
|
||||||
userData = userData.filter(Boolean);
|
userData = userData.filter(Boolean);
|
||||||
if (data.onlineOnly) {
|
if (data.onlineOnly) {
|
||||||
userData = userData.filter(user => user.status !== 'offline' && (Date.now() - user.lastonline < 300000));
|
userData = userData.filter(user => user.status !== 'offline' && (Date.now() - user.lastonline < 300000));
|
||||||
@@ -126,18 +104,12 @@ module.exports = function (User) {
|
|||||||
sortUsers(userData, data.sortBy);
|
sortUsers(userData, data.sortBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
uids = userData.map(user => user.uid);
|
return userData.map(user => user.uid);
|
||||||
|
|
||||||
next(null, uids);
|
|
||||||
},
|
|
||||||
], 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,65 +1,46 @@
|
|||||||
|
|
||||||
'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([
|
|
||||||
function (next) {
|
|
||||||
db.getObject('user:' + uid + ':settings', next);
|
|
||||||
},
|
|
||||||
function (settings, next) {
|
|
||||||
settings = settings || {};
|
settings = settings || {};
|
||||||
settings.uid = uid;
|
settings.uid = uid;
|
||||||
onSettingsLoaded(uid, settings, next);
|
return await onSettingsLoaded(uid, settings);
|
||||||
},
|
|
||||||
], 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([
|
|
||||||
function (next) {
|
|
||||||
db.getObjects(keys, next);
|
|
||||||
},
|
|
||||||
function (settings, next) {
|
|
||||||
settings = settings.map(function (userSettings, index) {
|
settings = settings.map(function (userSettings, index) {
|
||||||
userSettings = userSettings || {};
|
userSettings = userSettings || {};
|
||||||
userSettings.uid = uids[index];
|
userSettings.uid = uids[index];
|
||||||
return userSettings;
|
return userSettings;
|
||||||
});
|
});
|
||||||
async.map(settings, function (userSettings, next) {
|
return await Promise.all(settings.map(s => onSettingsLoaded(s.uid, s)));
|
||||||
onSettingsLoaded(userSettings.uid, userSettings, next);
|
// async.map(settings, function (userSettings, next) {
|
||||||
}, 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) {
|
|
||||||
plugins.fireHook('filter:user.getSettings', { uid: uid, settings: settings }, next);
|
|
||||||
},
|
|
||||||
function (data, next) {
|
|
||||||
settings = data.settings;
|
settings = data.settings;
|
||||||
|
|
||||||
var defaultTopicsPerPage = meta.config.topicsPerPage;
|
const defaultTopicsPerPage = meta.config.topicsPerPage;
|
||||||
var defaultPostsPerPage = meta.config.postsPerPage;
|
const defaultPostsPerPage = meta.config.postsPerPage;
|
||||||
|
|
||||||
settings.showemail = parseInt(getSetting(settings, 'showemail', 0), 10) === 1;
|
settings.showemail = parseInt(getSetting(settings, 'showemail', 0), 10) === 1;
|
||||||
settings.showfullname = parseInt(getSetting(settings, 'showfullname', 0), 10) === 1;
|
settings.showfullname = parseInt(getSetting(settings, 'showfullname', 0), 10) === 1;
|
||||||
@@ -81,16 +62,12 @@ module.exports = function (User) {
|
|||||||
settings.scrollToMyPost = parseInt(getSetting(settings, 'scrollToMyPost', 1), 10) === 1;
|
settings.scrollToMyPost = parseInt(getSetting(settings, 'scrollToMyPost', 1), 10) === 1;
|
||||||
settings.categoryWatchState = getSetting(settings, 'categoryWatchState', 'notwatching');
|
settings.categoryWatchState = getSetting(settings, 'categoryWatchState', 'notwatching');
|
||||||
|
|
||||||
notifications.getAllNotificationTypes(next);
|
const notificationTypes = await notifications.getAllNotificationTypes();
|
||||||
},
|
|
||||||
function (notificationTypes, next) {
|
|
||||||
notificationTypes.forEach(function (notificationType) {
|
notificationTypes.forEach(function (notificationType) {
|
||||||
settings[notificationType] = getSetting(settings, notificationType, 'notification');
|
settings[notificationType] = getSetting(settings, notificationType, 'notification');
|
||||||
});
|
});
|
||||||
|
|
||||||
next(null, settings);
|
return 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([
|
|
||||||
function (next) {
|
|
||||||
notifications.getAllNotificationTypes(next);
|
|
||||||
},
|
|
||||||
function (notificationTypes, next) {
|
|
||||||
notificationTypes.forEach(function (notificationType) {
|
notificationTypes.forEach(function (notificationType) {
|
||||||
if (data[notificationType]) {
|
if (data[notificationType]) {
|
||||||
settings[notificationType] = data[notificationType];
|
settings[notificationType] = data[notificationType];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
plugins.fireHook('filter:user.saveSettings', { settings: settings, data: data }, next);
|
const result = await plugins.fireHook('filter:user.saveSettings', { settings: settings, data: data });
|
||||||
},
|
await db.setObject('user:' + uid + ':settings', result.settings);
|
||||||
function (result, next) {
|
await User.updateDigestSetting(uid, data.dailyDigestFreq);
|
||||||
db.setObject('user:' + uid + ':settings', result.settings, next);
|
return await User.getSettings(uid);
|
||||||
},
|
|
||||||
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) {
|
|
||||||
db.sortedSetsRemove(['digest:day:uids', 'digest:week:uids', 'digest:month:uids'], uid, next);
|
|
||||||
},
|
|
||||||
function (next) {
|
|
||||||
if (['day', 'week', 'month'].includes(dailyDigestFreq)) {
|
if (['day', 'week', 'month'].includes(dailyDigestFreq)) {
|
||||||
db.sortedSetAdd('digest:' + dailyDigestFreq + ':uids', Date.now(), uid, next);
|
await db.sortedSetAdd('digest:' + dailyDigestFreq + ':uids', Date.now(), uid);
|
||||||
} 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);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user