mirror of
https://github.com/HabitRPG/habitica.git
synced 2026-04-04 05:33:52 -05:00
Compare commits
10 Commits
release
...
phillip/ap
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad8d0a3ed5 | ||
|
|
ac322b1925 | ||
|
|
ff46459159 | ||
|
|
e952a1dbaf | ||
|
|
e64428c89e | ||
|
|
b97dfdfa83 | ||
|
|
e1a68cd02a | ||
|
|
5d7a3bedf7 | ||
|
|
55cdd19215 | ||
|
|
ce24ce3079 |
@@ -1353,6 +1353,8 @@ api.getLookingForParty = {
|
||||
|
||||
const seekers = await User
|
||||
.find({
|
||||
'auth.blocked': { $ne: true },
|
||||
'flags.chatRevoked': { $ne: true },
|
||||
'party.seeking': { $exists: true },
|
||||
'invitations.party.id': { $exists: false },
|
||||
'auth.timestamps.loggedin': {
|
||||
@@ -1360,12 +1362,13 @@ api.getLookingForParty = {
|
||||
},
|
||||
})
|
||||
// eslint-disable-next-line no-multi-str
|
||||
.select('_id auth.blocked auth.local.username auth.timestamps backer contributor.level \
|
||||
flags.chatRevoked flags.classSelected inbox.blocks invitations.party items.gear.costume \
|
||||
.select('_id auth.local.username auth.timestamps backer contributor.level \
|
||||
flags.classSelected inbox.blocks invitations.party items.gear.costume \
|
||||
items.gear.equipped loginIncentives party._id preferences.background preferences.chair \
|
||||
preferences.costume preferences.hair preferences.shirt preferences.size preferences.skin \
|
||||
preferences.language profile.name stats.buffs stats.class stats.lvl')
|
||||
.sort('-auth.timestamps.loggedin')
|
||||
.lean()
|
||||
.exec();
|
||||
|
||||
const filteredSeekers = seekers.filter(seeker => {
|
||||
|
||||
@@ -24,7 +24,7 @@ const api = {};
|
||||
api.getInboxMessages = {
|
||||
method: 'GET',
|
||||
url: '/inbox/messages',
|
||||
middlewares: [authWithHeaders({ userFieldsToInclude: ['profile', 'contributor', 'backer', 'inbox'] })],
|
||||
middlewares: [authWithHeaders({ leanUser: true, userFieldsToInclude: ['profile', 'contributor', 'backer', 'inbox'] })],
|
||||
async handler (req, res) {
|
||||
const { user } = res.locals;
|
||||
const { page } = req.query;
|
||||
|
||||
@@ -40,7 +40,7 @@ const api = {};
|
||||
api.createTag = {
|
||||
method: 'POST',
|
||||
url: '/tags',
|
||||
middlewares: [authWithHeaders()],
|
||||
middlewares: [authWithHeaders({ userFieldsToInclude: ['tags'] })],
|
||||
async handler (req, res) {
|
||||
const { user } = res.locals;
|
||||
|
||||
@@ -69,7 +69,7 @@ api.createTag = {
|
||||
api.getTags = {
|
||||
method: 'GET',
|
||||
url: '/tags',
|
||||
middlewares: [authWithHeaders()],
|
||||
middlewares: [authWithHeaders({ leanUser: true, userFieldsToInclude: ['tags'] })],
|
||||
async handler (req, res) {
|
||||
const { user } = res.locals;
|
||||
res.respond(200, user.tags);
|
||||
@@ -95,7 +95,7 @@ api.getTags = {
|
||||
api.getTag = {
|
||||
method: 'GET',
|
||||
url: '/tags/:tagId',
|
||||
middlewares: [authWithHeaders()],
|
||||
middlewares: [authWithHeaders({ leanUser: true, userFieldsToInclude: ['tags'] })],
|
||||
async handler (req, res) {
|
||||
const { user } = res.locals;
|
||||
|
||||
@@ -133,7 +133,7 @@ api.getTag = {
|
||||
api.updateTag = {
|
||||
method: 'PUT',
|
||||
url: '/tags/:tagId',
|
||||
middlewares: [authWithHeaders()],
|
||||
middlewares: [authWithHeaders({ userFieldsToInclude: ['tags'] })],
|
||||
async handler (req, res) {
|
||||
const { user } = res.locals;
|
||||
|
||||
@@ -175,7 +175,7 @@ api.updateTag = {
|
||||
api.reorderTags = {
|
||||
method: 'POST',
|
||||
url: '/reorder-tags',
|
||||
middlewares: [authWithHeaders()],
|
||||
middlewares: [authWithHeaders({ userFieldsToInclude: ['tags'] })],
|
||||
async handler (req, res) {
|
||||
const { user } = res.locals;
|
||||
|
||||
@@ -215,7 +215,7 @@ api.reorderTags = {
|
||||
api.deleteTag = {
|
||||
method: 'DELETE',
|
||||
url: '/tags/:tagId',
|
||||
middlewares: [authWithHeaders()],
|
||||
middlewares: [authWithHeaders({ userFieldsToInclude: ['tags'] })],
|
||||
async handler (req, res) {
|
||||
const { user } = res.locals;
|
||||
|
||||
|
||||
@@ -388,7 +388,7 @@ api.getUserTasks = {
|
||||
method: 'GET',
|
||||
url: '/tasks/user',
|
||||
middlewares: [authWithHeaders({
|
||||
// Some fields (including _id, preferences) are always loaded (see middlewares/auth)
|
||||
leanUser: true,
|
||||
userFieldsToInclude: ['tasksOrder'],
|
||||
})],
|
||||
async handler (req, res) {
|
||||
@@ -953,7 +953,7 @@ api.addChecklistItem = {
|
||||
api.scoreCheckListItem = {
|
||||
method: 'POST',
|
||||
url: '/tasks/:taskId/checklist/:itemId/score',
|
||||
middlewares: [authWithHeaders()],
|
||||
middlewares: [authWithHeaders({ leanUser: true, userFieldsToInclude: ['_id'] })],
|
||||
async handler (req, res) {
|
||||
const { user } = res.locals;
|
||||
|
||||
|
||||
@@ -406,7 +406,7 @@ api.getUserAnonymized = {
|
||||
{ type: { $in: ['habit', 'daily', 'reward'] } },
|
||||
],
|
||||
};
|
||||
const tasks = await Tasks.Task.find(query).exec();
|
||||
const tasks = await Tasks.Task.find(query).lean().exec();
|
||||
|
||||
forEach(tasks, task => {
|
||||
task.text = 'task text';
|
||||
|
||||
@@ -22,6 +22,7 @@ api.purchaseHistory = {
|
||||
let transactions = await Transaction
|
||||
.find({ userId: req.params.memberId })
|
||||
.sort({ createdAt: -1 })
|
||||
.lean()
|
||||
.exec();
|
||||
|
||||
if (!res.locals.user.hasPermission('userSupport')) {
|
||||
|
||||
@@ -313,7 +313,9 @@ api.purchaseHistory = {
|
||||
url: '/user/purchase-history',
|
||||
async handler (req, res) {
|
||||
const { user } = res.locals;
|
||||
const transactions = await Transaction.find({ userId: user._id }).sort({ createdAt: -1 });
|
||||
const transactions = await Transaction.find({ userId: user._id })
|
||||
.sort({ createdAt: -1 })
|
||||
.lean();
|
||||
res.respond(200, transactions);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -53,7 +53,7 @@ api.exportUserHistory = {
|
||||
const tasks = await Tasks.Task.find({
|
||||
userId: user._id,
|
||||
type: { $in: ['habit', 'daily'] },
|
||||
}).exec();
|
||||
}).lean().exec();
|
||||
|
||||
const output = [
|
||||
['Task Name', 'Task ID', 'Task Type', 'Date', 'Value'],
|
||||
@@ -92,7 +92,7 @@ async function _getUserDataForExport (user) {
|
||||
const [tasks, messages] = await Promise.all([
|
||||
Tasks.Task.find({
|
||||
userId: user._id,
|
||||
}).exec(),
|
||||
}).lean().exec(),
|
||||
|
||||
inboxLib.getUserInbox(user, { asArray: false }),
|
||||
]);
|
||||
@@ -100,7 +100,6 @@ async function _getUserDataForExport (user) {
|
||||
userData.inbox.messages = messages;
|
||||
|
||||
_(tasks)
|
||||
.map(task => task.toJSON())
|
||||
.groupBy(task => task.type)
|
||||
.forEach((tasksPerType, taskType) => {
|
||||
userData.tasks[`${taskType}s`] = tasksPerType;
|
||||
|
||||
@@ -22,6 +22,7 @@ export async function sendChatPushNotifications (user, group, message, mentions,
|
||||
'party._id': group._id,
|
||||
_id: { $ne: user._id },
|
||||
})
|
||||
.lean()
|
||||
.select('preferences.pushNotifications preferences.language profile.name pushDevices auth.local.username')
|
||||
.exec();
|
||||
|
||||
|
||||
@@ -25,13 +25,13 @@ export async function getGroupChat (group, options = {}) {
|
||||
.sort('-timestamp');
|
||||
|
||||
if (before) {
|
||||
const beforeMessage = await Chat.findOne({ _id: before }).exec();
|
||||
const beforeMessage = await Chat.findOne({ _id: before }, { timestamp: 1 }).lean().exec();
|
||||
if (beforeMessage) {
|
||||
query = query.where('timestamp').lt(beforeMessage.timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
const groupChat = await query.limit(effectiveLimit).exec();
|
||||
const groupChat = await query.limit(effectiveLimit).lean().exec();
|
||||
|
||||
// @TODO: Concat old chat to keep continuity of chat stored on group object
|
||||
const currentGroupChat = group.chat || [];
|
||||
|
||||
@@ -22,7 +22,7 @@ async function usersMapByConversations (users) {
|
||||
stats: 1,
|
||||
flags: 1,
|
||||
inbox: 1,
|
||||
}).exec();
|
||||
}).lean().exec();
|
||||
|
||||
for (const usr of loadedUsers) {
|
||||
const loadedUserConversation = {
|
||||
|
||||
@@ -169,7 +169,10 @@ api.subscribe = async function subscribe (user, receipt, headers, nextPaymentPro
|
||||
{ 'purchased.plan.customerId': purchase.originalTransactionId },
|
||||
{ 'purchased.plan.customerId': purchase.transactionId },
|
||||
],
|
||||
}).exec();
|
||||
}, {
|
||||
_id: 1,
|
||||
'purchased.plan': 1,
|
||||
}).lean().exec();
|
||||
if (existingUsers.length > 0) {
|
||||
if (purchase.originalTransactionId === purchase.transactionId) {
|
||||
throw new NotAuthorized(this.constants.RESPONSE_ALREADY_USED);
|
||||
|
||||
@@ -178,7 +178,7 @@ async function getTasks (req, res, options = {}) {
|
||||
],
|
||||
},
|
||||
{ _id: 1 },
|
||||
).exec();
|
||||
).lean().exec();
|
||||
}
|
||||
if (upgradedGroups.length > 0) {
|
||||
for (const upgradedGroup of upgradedGroups) {
|
||||
@@ -270,7 +270,6 @@ async function getTasks (req, res, options = {}) {
|
||||
remove(taskOrder, taskId => tasks.findIndex(task => task._id === taskId) === -1);
|
||||
if (preLength !== taskOrder.length) {
|
||||
owner.tasksOrder[key] = taskOrder;
|
||||
owner.markModified('tasksOrder');
|
||||
ownerDirty = true;
|
||||
}
|
||||
});
|
||||
@@ -303,7 +302,17 @@ async function getTasks (req, res, options = {}) {
|
||||
}
|
||||
});
|
||||
|
||||
if (ownerDirty) await owner.save();
|
||||
if (ownerDirty) {
|
||||
let model;
|
||||
if (challenge) {
|
||||
model = Challenge;
|
||||
} else if (group) {
|
||||
model = Group;
|
||||
} else {
|
||||
model = User;
|
||||
}
|
||||
await model.updateOne({ _id: owner._id }, { tasksOrder: owner.tasksOrder }).exec();
|
||||
}
|
||||
|
||||
// Remove empty values from the array and add any unordered task
|
||||
orderedTasks = compact(orderedTasks).concat(unorderedTasks);
|
||||
|
||||
@@ -82,7 +82,7 @@ export function setNextDue (task, user, dueDateOption) {
|
||||
now = dateTaskIsDue;
|
||||
}
|
||||
|
||||
const optionsForShouldDo = user.preferences.toObject();
|
||||
const optionsForShouldDo = user.preferences;
|
||||
optionsForShouldDo.now = now;
|
||||
task.isDue = shared.shouldDo(dateTaskIsDue, task, optionsForShouldDo);
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ export async function update (req, res, { isV3 = false }) {
|
||||
],
|
||||
}, {
|
||||
_id: 1,
|
||||
}).exec();
|
||||
}).lean().exec();
|
||||
|
||||
matchingGroupsArray = _.map(matchingGroups, groupRecord => groupRecord._id);
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ schema.statics.getNews = async function getNews (isAdmin, options = { page: 0 })
|
||||
.sort({ publishDate: -1 })
|
||||
.limit(POSTS_PER_PAGE)
|
||||
.skip(POSTS_PER_PAGE * Number(page))
|
||||
.lean()
|
||||
.exec();
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
import logger from '../../libs/logger';
|
||||
import schema from './schema'; // eslint-disable-line import/no-cycle
|
||||
|
||||
import './hooks'; // eslint-disable-line import/no-cycle
|
||||
@@ -19,19 +18,3 @@ export const nameFields = 'profile.name auth.local.username flags.verifiedUserna
|
||||
export { schema };
|
||||
|
||||
export const model = mongoose.model('User', schema);
|
||||
|
||||
// Initially export an empty object so external requires will get
|
||||
// the right object by reference when it's defined later
|
||||
// Otherwise it would remain undefined if requested before the query executes
|
||||
export const mods = [];
|
||||
|
||||
mongoose.model('User')
|
||||
.find({ 'contributor.moderator': true })
|
||||
.sort('-contributor.level -backer.npc profile.name')
|
||||
.select('profile contributor backer')
|
||||
.exec()
|
||||
.then(foundMods => {
|
||||
// Using push to maintain the reference to mods
|
||||
mods.push(...foundMods);
|
||||
})
|
||||
.catch(err => logger.error(err));
|
||||
|
||||
@@ -367,14 +367,14 @@ schema.methods.getUtcOffset = function getUtcOffset () {
|
||||
return common.fns.getUtcOffset(this);
|
||||
};
|
||||
|
||||
schema.methods.daysUserHasMissed = function daysUserHasMissed (now, req = {}) {
|
||||
schema.statics.daysUserHasMissed = function daysUserHasMissed (user, now, req = {}) {
|
||||
// If the user's timezone has changed (due to travel or daylight savings),
|
||||
// cron can be triggered twice in one day, so we check for that and use
|
||||
// both timezones to work out if cron should run.
|
||||
// CDS = Custom Day Start time.
|
||||
let timezoneUtcOffsetFromUserPrefs = this.getUtcOffset();
|
||||
const timezoneUtcOffsetAtLastCron = Number.isFinite(this.preferences.timezoneOffsetAtLastCron)
|
||||
? -this.preferences.timezoneOffsetAtLastCron
|
||||
let timezoneUtcOffsetFromUserPrefs = common.fns.getUtcOffset(user);
|
||||
const timezoneUtcOffsetAtLastCron = Number.isFinite(user.preferences.timezoneOffsetAtLastCron)
|
||||
? -user.preferences.timezoneOffsetAtLastCron
|
||||
: timezoneUtcOffsetFromUserPrefs;
|
||||
|
||||
let timezoneUtcOffsetFromBrowser = typeof req.header === 'function' && -Number(req.header('x-user-timezoneoffset'));
|
||||
@@ -386,16 +386,16 @@ schema.methods.daysUserHasMissed = function daysUserHasMissed (now, req = {}) {
|
||||
if (timezoneUtcOffsetFromBrowser !== timezoneUtcOffsetFromUserPrefs) {
|
||||
// The user's browser has just told Habitica that the user's timezone has
|
||||
// changed so store and use the new zone.
|
||||
this.preferences.timezoneOffset = -timezoneUtcOffsetFromBrowser;
|
||||
user.preferences.timezoneOffset = -timezoneUtcOffsetFromBrowser;
|
||||
timezoneUtcOffsetFromUserPrefs = timezoneUtcOffsetFromBrowser;
|
||||
}
|
||||
|
||||
let lastCronTime = this.lastCron;
|
||||
if (this.auth.timestamps.loggedIn < lastCronTime) {
|
||||
lastCronTime = this.auth.timestamps.loggedIn;
|
||||
let lastCronTime = user.lastCron;
|
||||
if (user.auth.timestamps.loggedIn < lastCronTime) {
|
||||
lastCronTime = user.auth.timestamps.loggedIn;
|
||||
}
|
||||
// How many days have we missed using the user's current timezone:
|
||||
let daysMissed = daysSince(lastCronTime, defaults({ now }, this.preferences));
|
||||
let daysMissed = daysSince(lastCronTime, defaults({ now }, user.preferences));
|
||||
|
||||
if (timezoneUtcOffsetAtLastCron !== timezoneUtcOffsetFromUserPrefs) {
|
||||
// Give the user extra time based on the difference in timezones
|
||||
@@ -410,7 +410,7 @@ schema.methods.daysUserHasMissed = function daysUserHasMissed (now, req = {}) {
|
||||
const daysMissedOldZone = daysSince(lastCronTime, defaults({
|
||||
now,
|
||||
timezoneUtcOffsetOverride: timezoneUtcOffsetAtLastCron,
|
||||
}, this.preferences));
|
||||
}, user.preferences));
|
||||
|
||||
if (timezoneUtcOffsetAtLastCron > timezoneUtcOffsetFromUserPrefs) {
|
||||
// The timezone change was in the unsafe direction.
|
||||
@@ -447,12 +447,12 @@ schema.methods.daysUserHasMissed = function daysUserHasMissed (now, req = {}) {
|
||||
const timezoneOffsetDiff = timezoneUtcOffsetFromUserPrefs - timezoneUtcOffsetAtLastCron;
|
||||
// e.g., for dangerous zone change: -300 - -240 = -60 or 600 - 660= -60
|
||||
|
||||
this.lastCron = moment(lastCronTime).subtract(timezoneOffsetDiff, 'minutes');
|
||||
user.lastCron = moment(lastCronTime).subtract(timezoneOffsetDiff, 'minutes');
|
||||
// NB: We don't change this.auth.timestamps.loggedin so that will still record
|
||||
// the time that the previous cron actually ran.
|
||||
// From now on we can ignore the old timezone:
|
||||
// This is still timezoneOffset for backwards compatibility reasons.
|
||||
this.preferences.timezoneOffsetAtLastCron = -timezoneUtcOffsetAtLastCron;
|
||||
user.preferences.timezoneOffsetAtLastCron = -timezoneUtcOffsetAtLastCron;
|
||||
} else {
|
||||
// Both old and new timezones indicate that cron should
|
||||
// NOT run.
|
||||
@@ -474,6 +474,10 @@ schema.methods.daysUserHasMissed = function daysUserHasMissed (now, req = {}) {
|
||||
return { daysMissed, timezoneUtcOffsetFromUserPrefs };
|
||||
};
|
||||
|
||||
schema.methods.daysUserHasMissed = function daysUserHasMissed (now, req = {}) {
|
||||
return schema.statics.daysUserHasMissed(this, now, req);
|
||||
};
|
||||
|
||||
async function getUserGroupData (user) {
|
||||
const userGroups = user.getGroups();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user