diff --git a/models/accessibilitySettings.js b/models/accessibilitySettings.js index b9785bb5a..12be6e2ad 100644 --- a/models/accessibilitySettings.js +++ b/models/accessibilitySettings.js @@ -46,8 +46,8 @@ AccessibilitySettings.attachSchema( ); AccessibilitySettings.allow({ - update(userId) { - const user = ReactiveCache.getUser(userId); + async update(userId) { + const user = await ReactiveCache.getUser(userId); return user && user.isAdmin; }, }); diff --git a/models/accountSettings.js b/models/accountSettings.js index 6b5a6b246..38fed5c82 100644 --- a/models/accountSettings.js +++ b/models/accountSettings.js @@ -45,8 +45,8 @@ AccountSettings.attachSchema( ); AccountSettings.allow({ - update(userId) { - const user = ReactiveCache.getUser(userId); + async update(userId) { + const user = await ReactiveCache.getUser(userId); return user && user.isAdmin; }, }); diff --git a/models/actions.js b/models/actions.js index b7c946318..e8209febd 100644 --- a/models/actions.js +++ b/models/actions.js @@ -4,14 +4,14 @@ import { Meteor } from 'meteor/meteor'; Actions = new Mongo.Collection('actions'); Actions.allow({ - insert(userId, doc) { - return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId)); + async insert(userId, doc) { + return allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId)); }, - update(userId, doc) { - return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId)); + async update(userId, doc) { + return allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId)); }, - remove(userId, doc) { - return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId)); + async remove(userId, doc) { + return allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId)); }, }); diff --git a/models/activities.js b/models/activities.js index e3080c1da..03d3df499 100644 --- a/models/activities.js +++ b/models/activities.js @@ -13,54 +13,54 @@ import { ReactiveCache } from '/imports/reactiveCache'; Activities = new Mongo.Collection('activities'); Activities.helpers({ - board() { - return ReactiveCache.getBoard(this.boardId); + async board() { + return await ReactiveCache.getBoard(this.boardId); }, - oldBoard() { - return ReactiveCache.getBoard(this.oldBoardId); + async oldBoard() { + return await ReactiveCache.getBoard(this.oldBoardId); }, - user() { - return ReactiveCache.getUser(this.userId); + async user() { + return await ReactiveCache.getUser(this.userId); }, - member() { - return ReactiveCache.getUser(this.memberId); + async member() { + return await ReactiveCache.getUser(this.memberId); }, - list() { - return ReactiveCache.getList(this.listId); + async list() { + return await ReactiveCache.getList(this.listId); }, - swimlane() { - return ReactiveCache.getSwimlane(this.swimlaneId); + async swimlane() { + return await ReactiveCache.getSwimlane(this.swimlaneId); }, - oldSwimlane() { - return ReactiveCache.getSwimlane(this.oldSwimlaneId); + async oldSwimlane() { + return await ReactiveCache.getSwimlane(this.oldSwimlaneId); }, - oldList() { - return ReactiveCache.getList(this.oldListId); + async oldList() { + return await ReactiveCache.getList(this.oldListId); }, - card() { - return ReactiveCache.getCard(this.cardId); + async card() { + return await ReactiveCache.getCard(this.cardId); }, - comment() { - return ReactiveCache.getCardComment(this.commentId); + async comment() { + return await ReactiveCache.getCardComment(this.commentId); }, - attachment() { - return ReactiveCache.getAttachment(this.attachmentId); + async attachment() { + return await ReactiveCache.getAttachment(this.attachmentId); }, - checklist() { - return ReactiveCache.getChecklist(this.checklistId); + async checklist() { + return await ReactiveCache.getChecklist(this.checklistId); }, - checklistItem() { - return ReactiveCache.getChecklistItem(this.checklistItemId); + async checklistItem() { + return await ReactiveCache.getChecklistItem(this.checklistItemId); }, - subtasks() { - return ReactiveCache.getCard(this.subtaskId); + async subtasks() { + return await ReactiveCache.getCard(this.subtaskId); }, - customField() { - return ReactiveCache.getCustomField(this.customFieldId); + async customField() { + return await ReactiveCache.getCustomField(this.customFieldId); }, - label() { + async label() { // Label activity did not work yet, unable to edit labels when tried this. - return ReactiveCache.getCard(this.labelId); + return await ReactiveCache.getCard(this.labelId); }, }); @@ -105,12 +105,12 @@ if (Meteor.isServer) { //Activities._collection.dropIndex({ labelId: 1 }, { partialFilterExpression: { labelId: { $exists: true } } }); }); - Activities.after.insert((userId, doc) => { + Activities.after.insert(async (userId, doc) => { const activity = Activities._transform(doc); let participants = []; let watchers = []; let title = 'act-activity-notify'; - const board = ReactiveCache.getBoard(activity.boardId); + const board = await ReactiveCache.getBoard(activity.boardId); const description = `act-${activity.activityType}`; const params = { activityId: activity._id, @@ -203,14 +203,16 @@ if (Meteor.isServer) { let hasMentions = false; // Track if comment has @mentions if (board) { const comment = params.comment; - const knownUsers = board.members.map((member) => { - const u = ReactiveCache.getUser(member.userId); + // Build knownUsers with async user lookups + const knownUsers = []; + for (const member of board.members) { + const u = await ReactiveCache.getUser(member.userId); if (u) { member.username = u.username; member.emails = u.emails; } - return member; - }); + knownUsers.push(member); + } // Match @mentions including usernames with @ symbols (like email addresses) // Pattern matches: @username, @user@example.com, @"quoted username" const mentionRegex = /\B@(?:(?:"([\w.\s-]*)")|([\w.@-]+))/gi; @@ -225,30 +227,31 @@ if (Meteor.isServer) { if (activity.boardId && username === 'board_members') { // mentions all board members - const validUserIds = knownUsers - .map((u) => u.userId) - .filter((userId) => { - const user = ReactiveCache.getUser(userId); - return user && user._id; - }); + const validUserIds = []; + for (const u of knownUsers) { + const user = await ReactiveCache.getUser(u.userId); + if (user && user._id) { + validUserIds.push(u.userId); + } + } watchers = _.union(watchers, validUserIds); title = 'act-atUserComment'; hasMentions = true; } else if (activity.boardId && username === 'board_assignees') { // mentions all assignees of all cards on the board - const allCards = ReactiveCache.getCards({ boardId: activity.boardId }); + const allCards = await ReactiveCache.getCards({ boardId: activity.boardId }); const assigneeIds = []; - allCards.forEach((card) => { + for (const card of allCards) { if (card.assignees && card.assignees.length > 0) { - card.assignees.forEach((assigneeId) => { + for (const assigneeId of card.assignees) { // Only add if the user exists and is a board member - const user = ReactiveCache.getUser(assigneeId); + const user = await ReactiveCache.getUser(assigneeId); if (user && _.findWhere(knownUsers, { userId: assigneeId })) { assigneeIds.push(assigneeId); } - }); + } } - }); + } watchers = _.union(watchers, assigneeIds); title = 'act-atUserComment'; hasMentions = true; @@ -257,10 +260,13 @@ if (Meteor.isServer) { const card = activity.card(); if (card && card.members && card.members.length > 0) { // Filter to only valid users who are board members - const validMembers = card.members.filter((memberId) => { - const user = ReactiveCache.getUser(memberId); - return user && user._id && _.findWhere(knownUsers, { userId: memberId }); - }); + const validMembers = []; + for (const memberId of card.members) { + const user = await ReactiveCache.getUser(memberId); + if (user && user._id && _.findWhere(knownUsers, { userId: memberId })) { + validMembers.push(memberId); + } + } watchers = _.union(watchers, validMembers); } title = 'act-atUserComment'; @@ -270,10 +276,13 @@ if (Meteor.isServer) { const card = activity.card(); if (card && card.assignees && card.assignees.length > 0) { // Filter to only valid users who are board members - const validAssignees = card.assignees.filter((assigneeId) => { - const user = ReactiveCache.getUser(assigneeId); - return user && user._id && _.findWhere(knownUsers, { userId: assigneeId }); - }); + const validAssignees = []; + for (const assigneeId of card.assignees) { + const user = await ReactiveCache.getUser(assigneeId); + if (user && user._id && _.findWhere(knownUsers, { userId: assigneeId })) { + validAssignees.push(assigneeId); + } + } watchers = _.union(watchers, validAssignees); } title = 'act-atUserComment'; @@ -390,7 +399,7 @@ if (Meteor.isServer) { Notifications.getUsers(watchers).forEach((user) => { // Skip if user is undefined or doesn't have an _id (e.g., deleted user or invalid ID) if (!user || !user._id) return; - + // Don't notify a user of their own behavior, EXCEPT for self-mentions const isSelfMention = (user._id === userId && title === 'act-atUserComment'); if (user._id !== userId || isSelfMention) { @@ -398,7 +407,7 @@ if (Meteor.isServer) { } }); - const integrations = ReactiveCache.getIntegrations({ + const integrations = await ReactiveCache.getIntegrations({ boardId: { $in: [board._id, Integrations.Const.GLOBAL_WEBHOOK_ID] }, // type: 'outgoing-webhooks', // all types enabled: true, diff --git a/models/announcements.js b/models/announcements.js index b44f7a229..1c3f60e7a 100644 --- a/models/announcements.js +++ b/models/announcements.js @@ -50,8 +50,8 @@ Announcements.attachSchema( ); Announcements.allow({ - update(userId) { - const user = ReactiveCache.getUser(userId); + async update(userId) { + const user = await ReactiveCache.getUser(userId); return user && user.isAdmin; }, }); diff --git a/models/attachmentStorageSettings.js b/models/attachmentStorageSettings.js index f0a67271c..df7721cba 100644 --- a/models/attachmentStorageSettings.js +++ b/models/attachmentStorageSettings.js @@ -257,18 +257,18 @@ AttachmentStorageSettings.helpers({ if (Meteor.isServer) { // Get or create default settings Meteor.methods({ - 'getAttachmentStorageSettings'() { + async 'getAttachmentStorageSettings'() { if (!this.userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(this.userId); + const user = await ReactiveCache.getUser(this.userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } let settings = AttachmentStorageSettings.findOne({}); - + if (!settings) { // Create default settings settings = { @@ -299,20 +299,20 @@ if (Meteor.isServer) { createdBy: this.userId, updatedBy: this.userId }; - + AttachmentStorageSettings.insert(settings); settings = AttachmentStorageSettings.findOne({}); } - + return settings; }, - 'updateAttachmentStorageSettings'(settings) { + async 'updateAttachmentStorageSettings'(settings) { if (!this.userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(this.userId); + const user = await ReactiveCache.getUser(this.userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } @@ -320,7 +320,7 @@ if (Meteor.isServer) { // Validate settings const schema = AttachmentStorageSettings.simpleSchema(); schema.validate(settings); - + // Update settings const result = AttachmentStorageSettings.upsert( {}, @@ -332,7 +332,7 @@ if (Meteor.isServer) { } } ); - + return result; }, @@ -345,12 +345,12 @@ if (Meteor.isServer) { return settings ? settings.getDefaultStorage() : STORAGE_NAME_FILESYSTEM; }, - 'setDefaultAttachmentStorage'(storageName) { + async 'setDefaultAttachmentStorage'(storageName) { if (!this.userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(this.userId); + const user = await ReactiveCache.getUser(this.userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } @@ -369,18 +369,18 @@ if (Meteor.isServer) { } } ); - + return result; } }); // Publication for settings - Meteor.publish('attachmentStorageSettings', function() { + Meteor.publish('attachmentStorageSettings', async function() { if (!this.userId) { return this.ready(); } - const user = ReactiveCache.getUser(this.userId); + const user = await ReactiveCache.getUser(this.userId); if (!user || !user.isAdmin) { return this.ready(); } diff --git a/models/attachments.js b/models/attachments.js index 3ac87da3b..a2e52e1fc 100644 --- a/models/attachments.js +++ b/models/attachments.js @@ -179,13 +179,13 @@ Attachments = new FilesCollection({ // We authorize the attachment download either: // - if the board is public, everyone (even unconnected) can download it // - if the board is private, only board members can download it - protected(fileObj) { + async protected(fileObj) { // file may have been deleted already again after upload validation failed if (!fileObj) { return false; } - const board = ReactiveCache.getBoard(fileObj.meta.boardId); + const board = await ReactiveCache.getBoard(fileObj.meta.boardId); if (board.isPublic()) { return true; } @@ -196,11 +196,11 @@ Attachments = new FilesCollection({ if (Meteor.isServer) { Attachments.allow({ - insert(userId, fileObj) { + async insert(userId, fileObj) { // ReadOnly users cannot upload attachments - return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(fileObj.boardId)); + return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(fileObj.boardId)); }, - update(userId, fileObj, fields) { + async update(userId, fileObj, fields) { // SECURITY: The 'name' field is sanitized in onBeforeUpload and server-side methods, // but we block direct client-side $set operations on 'versions.*.path' to prevent // path traversal attacks via storage migration exploits. @@ -230,9 +230,9 @@ if (Meteor.isServer) { } // ReadOnly users cannot update attachments - return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(fileObj.boardId)); + return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(fileObj.boardId)); }, - remove(userId, fileObj) { + async remove(userId, fileObj) { // Additional security check: ensure the file belongs to the board the user has access to if (!fileObj || !fileObj.boardId) { if (process.env.DEBUG === 'true') { @@ -241,7 +241,7 @@ if (Meteor.isServer) { return false; } - const board = ReactiveCache.getBoard(fileObj.boardId); + const board = await ReactiveCache.getBoard(fileObj.boardId); if (!board) { if (process.env.DEBUG === 'true') { console.warn('Blocked attachment removal: board not found'); @@ -293,7 +293,7 @@ if (Meteor.isServer) { return { valid: true }; }, - moveAttachmentToStorage(fileObjId, storageDestination) { + async moveAttachmentToStorage(fileObjId, storageDestination) { check(fileObjId, String); check(storageDestination, String); @@ -301,12 +301,12 @@ if (Meteor.isServer) { throw new Meteor.Error('not-authorized', 'You must be logged in.'); } - const fileObj = ReactiveCache.getAttachment(fileObjId); + const fileObj = await ReactiveCache.getAttachment(fileObjId); if (!fileObj) { throw new Meteor.Error('attachment-not-found', 'Attachment not found'); } - const board = ReactiveCache.getBoard(fileObj.boardId); + const board = await ReactiveCache.getBoard(fileObj.boardId); if (!board || !board.isVisibleBy({ _id: this.userId })) { throw new Meteor.Error('not-authorized', 'You do not have access to this board.'); } @@ -319,7 +319,7 @@ if (Meteor.isServer) { moveToStorage(fileObj, storageDestination, fileStoreStrategyFactory); }, - renameAttachment(fileObjId, newName) { + async renameAttachment(fileObjId, newName) { check(fileObjId, String); check(newName, String); @@ -328,13 +328,13 @@ if (Meteor.isServer) { throw new Meteor.Error('not-authorized', 'User must be logged in'); } - const fileObj = ReactiveCache.getAttachment(fileObjId); + const fileObj = await ReactiveCache.getAttachment(fileObjId); if (!fileObj) { throw new Meteor.Error('file-not-found', 'Attachment not found'); } // Verify the user has permission to modify this attachment - const board = ReactiveCache.getBoard(fileObj.boardId); + const board = await ReactiveCache.getBoard(fileObj.boardId); if (!board) { throw new Meteor.Error('board-not-found', 'Board not found'); } @@ -348,30 +348,30 @@ if (Meteor.isServer) { rename(fileObj, newName, fileStoreStrategyFactory); }, - validateAttachment(fileObjId) { + async validateAttachment(fileObjId) { check(fileObjId, String); if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in.'); } - const fileObj = ReactiveCache.getAttachment(fileObjId); + const fileObj = await ReactiveCache.getAttachment(fileObjId); if (!fileObj) { throw new Meteor.Error('attachment-not-found', 'Attachment not found'); } - const board = ReactiveCache.getBoard(fileObj.boardId); + const board = await ReactiveCache.getBoard(fileObj.boardId); if (!board || !board.isVisibleBy({ _id: this.userId })) { throw new Meteor.Error('not-authorized', 'You do not have access to this board.'); } - const isValid = Promise.await(isFileValid(fileObj, attachmentUploadMimeTypes, attachmentUploadSize, attachmentUploadExternalProgram)); + const isValid = await isFileValid(fileObj, attachmentUploadMimeTypes, attachmentUploadSize, attachmentUploadExternalProgram); if (!isValid) { Attachments.remove(fileObjId); } }, - validateAttachmentAndMoveToStorage(fileObjId, storageDestination) { + async validateAttachmentAndMoveToStorage(fileObjId, storageDestination) { check(fileObjId, String); check(storageDestination, String); @@ -379,12 +379,12 @@ if (Meteor.isServer) { throw new Meteor.Error('not-authorized', 'You must be logged in.'); } - const fileObj = ReactiveCache.getAttachment(fileObjId); + const fileObj = await ReactiveCache.getAttachment(fileObjId); if (!fileObj) { throw new Meteor.Error('attachment-not-found', 'Attachment not found'); } - const board = ReactiveCache.getBoard(fileObj.boardId); + const board = await ReactiveCache.getBoard(fileObj.boardId); if (!board || !board.isVisibleBy({ _id: this.userId })) { throw new Meteor.Error('not-authorized', 'You do not have access to this board.'); } @@ -395,9 +395,9 @@ if (Meteor.isServer) { throw new Meteor.Error('invalid-storage-destination', 'Invalid storage destination'); } - Meteor.call('validateAttachment', fileObjId); + await Meteor.callAsync('validateAttachment', fileObjId); - const fileObjAfter = ReactiveCache.getAttachment(fileObjId); + const fileObjAfter = await ReactiveCache.getAttachment(fileObjId); if (fileObjAfter) { Meteor.defer(() => Meteor.call('moveAttachmentToStorage', fileObjId, storageDestination)); diff --git a/models/avatars.js b/models/avatars.js index da3033bc8..369eb473b 100644 --- a/models/avatars.js +++ b/models/avatars.js @@ -106,7 +106,7 @@ Avatars = new FilesCollection({ } return TAPi18n.__('avatar-too-big', {size: filesize(avatarsUploadSize)}); }, - onAfterUpload(fileObj) { + async onAfterUpload(fileObj) { // current storage is the filesystem, update object and database Object.keys(fileObj.versions).forEach(versionName => { fileObj.versions[versionName].storage = STORAGE_NAME_FILESYSTEM; @@ -114,12 +114,13 @@ Avatars = new FilesCollection({ Avatars.update({ _id: fileObj._id }, { $set: { "versions": fileObj.versions } }); - const isValid = Promise.await(isFileValid(fileObj, avatarsUploadMimeTypes, avatarsUploadSize, avatarsUploadExternalProgram)); + const isValid = await isFileValid(fileObj, avatarsUploadMimeTypes, avatarsUploadSize, avatarsUploadExternalProgram); if (isValid) { // Set avatar URL using universal URL generator (URL-agnostic) const universalUrl = generateUniversalAvatarUrl(fileObj._id); - ReactiveCache.getUser(fileObj.userId).setAvatarUrl(universalUrl); + const user = await ReactiveCache.getUser(fileObj.userId); + user.setAvatarUrl(universalUrl); } else { Avatars.remove(fileObj._id); } @@ -128,12 +129,13 @@ Avatars = new FilesCollection({ const ret = fileStoreStrategyFactory.getFileStrategy(fileObj, versionName).interceptDownload(http, this.cacheControl); return ret; }, - onBeforeRemove(files) { - files.forEach(fileObj => { + async onBeforeRemove(files) { + for (const fileObj of files) { if (fileObj.userId) { - ReactiveCache.getUser(fileObj.userId).setAvatarUrl(''); + const user = await ReactiveCache.getUser(fileObj.userId); + user.setAvatarUrl(''); } - }); + } return true; }, diff --git a/models/boards.js b/models/boards.js index 479b2530e..45bf1f6dd 100644 --- a/models/boards.js +++ b/models/boards.js @@ -703,12 +703,12 @@ Boards.attachSchema( ); Boards.helpers({ - copy() { + async copy() { const oldId = this._id; const oldWatchers = this.watchers ? this.watchers.slice() : []; delete this._id; delete this.slug; - this.title = this.copyTitle(); + this.title = await this.copyTitle(); const _id = Boards.insert(this); // Temporary remove watchers to disable notifications @@ -719,23 +719,26 @@ Boards.helpers({ }); // Copy all swimlanes in board - ReactiveCache.getSwimlanes({ + const swimlanes = await ReactiveCache.getSwimlanes({ boardId: oldId, archived: false, - }).forEach(swimlane => { + }); + for (const swimlane of swimlanes) { swimlane.type = 'swimlane'; swimlane.copy(_id); - }); + } // copy custom field definitions const cfMap = {}; - ReactiveCache.getCustomFields({ boardIds: oldId }).forEach(cf => { + const customFields = await ReactiveCache.getCustomFields({ boardIds: oldId }); + for (const cf of customFields) { const id = cf._id; delete cf._id; cf.boardIds = [_id]; cfMap[id] = CustomFields.insert(cf); - }); - ReactiveCache.getCards({ boardId: _id }).forEach(card => { + } + const cards = await ReactiveCache.getCards({ boardId: _id }); + for (const card of cards) { Cards.update(card._id, { $set: { customFields: card.customFields.map(cf => { @@ -744,30 +747,33 @@ Boards.helpers({ }), }, }); - }); + } // copy rules, actions, and triggers const actionsMap = {}; - ReactiveCache.getActions({ boardId: oldId }).forEach(action => { + const actions = await ReactiveCache.getActions({ boardId: oldId }); + for (const action of actions) { const id = action._id; delete action._id; action.boardId = _id; actionsMap[id] = Actions.insert(action); - }); + } const triggersMap = {}; - ReactiveCache.getTriggers({ boardId: oldId }).forEach(trigger => { + const triggers = await ReactiveCache.getTriggers({ boardId: oldId }); + for (const trigger of triggers) { const id = trigger._id; delete trigger._id; trigger.boardId = _id; triggersMap[id] = Triggers.insert(trigger); - }); - ReactiveCache.getRules({ boardId: oldId }).forEach(rule => { + } + const rules = await ReactiveCache.getRules({ boardId: oldId }); + for (const rule of rules) { delete rule._id; rule.boardId = _id; rule.actionId = actionsMap[rule.actionId]; rule.triggerId = triggersMap[rule.triggerId]; Rules.insert(rule); - }); + } // Re-set Watchers to reenable notification Boards.update(_id, { @@ -781,8 +787,8 @@ Boards.helpers({ * * @returns {string|null} */ - copyTitle() { - return Boards.uniqueTitle(this.title); + async copyTitle() { + return await Boards.uniqueTitle(this.title); }, /** @@ -823,8 +829,8 @@ Boards.helpers({ }, - cards() { - const ret = ReactiveCache.getCards( + async cards() { + const ret = await ReactiveCache.getCards( { boardId: this._id, archived: false }, { sort: { title: 1 } }, ); @@ -835,11 +841,12 @@ Boards.helpers({ return this.draggableLists(); }, - newestLists() { + async newestLists() { // sorted lists from newest to the oldest, by its creation date or its cards' last modification date - const value = ReactiveCache.getCurrentUser()._getListSortBy(); + const user = await ReactiveCache.getCurrentUser(); + const value = user._getListSortBy(); const sortKey = { starred: -1, [value[0]]: value[1] }; // [["starred",-1],value]; - return ReactiveCache.getLists( + return await ReactiveCache.getLists( { boardId: this._id, archived: false, @@ -848,8 +855,8 @@ Boards.helpers({ ); }, - draggableLists() { - return ReactiveCache.getLists( + async draggableLists() { + return await ReactiveCache.getLists( { boardId: this._id, }, @@ -860,28 +867,28 @@ Boards.helpers({ /** returns the last list * @returns Document the last list */ - getLastList() { - const ret = ReactiveCache.getList({ boardId: this._id }, { sort: { sort: 'desc' } }); + async getLastList() { + const ret = await ReactiveCache.getList({ boardId: this._id }, { sort: { sort: 'desc' } }); return ret; }, - nullSortLists() { - return ReactiveCache.getLists({ + async nullSortLists() { + return await ReactiveCache.getLists({ boardId: this._id, archived: false, sort: { $eq: null }, }); }, - swimlanes() { - return ReactiveCache.getSwimlanes( + async swimlanes() { + return await ReactiveCache.getSwimlanes( { boardId: this._id, archived: false }, { sort: { sort: 1 } }, ); }, - nextSwimlane(swimlane) { - return ReactiveCache.getSwimlane( + async nextSwimlane(swimlane) { + return await ReactiveCache.getSwimlane( { boardId: this._id, archived: false, @@ -894,16 +901,16 @@ Boards.helpers({ ); }, - nullSortSwimlanes() { - return ReactiveCache.getSwimlanes({ + async nullSortSwimlanes() { + return await ReactiveCache.getSwimlanes({ boardId: this._id, archived: false, sort: { $eq: null }, }); }, - hasOvertimeCards() { - const card = ReactiveCache.getCard({ + async hasOvertimeCards() { + const card = await ReactiveCache.getCard({ isOvertime: true, boardId: this._id, archived: false, @@ -911,8 +918,8 @@ Boards.helpers({ return card !== undefined; }, - hasSpentTimeCards() { - const card = ReactiveCache.getCard({ + async hasSpentTimeCards() { + const card = await ReactiveCache.getCard({ spentTime: { $gt: 0 }, boardId: this._id, archived: false, @@ -920,19 +927,20 @@ Boards.helpers({ return card !== undefined; }, - activities() { + async activities() { let linkedBoardId = [this._id]; - ReactiveCache.getCards({ + const cards = await ReactiveCache.getCards({ "type": "cardType-linkedBoard", - "boardId": this._id} - ).forEach(card => { - linkedBoardId.push(card.linkedId); + "boardId": this._id }); - const ret = ReactiveCache.getActivities({ boardId: { $in: linkedBoardId } }, { sort: { createdAt: -1 } }); + for (const card of cards) { + linkedBoardId.push(card.linkedId); + } + const ret = await ReactiveCache.getActivities({ boardId: { $in: linkedBoardId } }, { sort: { createdAt: -1 } }); return ret; }, - activeMembers(){ + async activeMembers(){ // Depend on the users collection for reactivity when users are loaded const memberUserIds = _.pluck(this.members, 'userId'); const dummy = Meteor.users.find({ _id: { $in: memberUserIds } }).count(); @@ -945,16 +953,21 @@ Boards.helpers({ return selected; }); // Filter out members where user is not loaded - const filteredMembers = uniqueMembers.filter(member => { - const user = ReactiveCache.getUser(member.userId); - return user !== undefined; - }); - + const filteredMembers = []; + for (const member of uniqueMembers) { + const user = await ReactiveCache.getUser(member.userId); + if (user !== undefined) { + filteredMembers.push(member); + } + } + // Sort by role priority first (admin, normal, normal-assigned, no-comments, comment-only, comment-assigned, worker, read-only, read-assigned), then by fullname - return _.sortBy(filteredMembers, member => { - const user = ReactiveCache.getUser(member.userId); + // Build sort keys with async user lookup + const membersWithSortKey = []; + for (const member of filteredMembers) { + const user = await ReactiveCache.getUser(member.userId); let rolePriority = 8; // Default for normal - + if (member.isAdmin) rolePriority = 0; else if (member.isReadAssignedOnly) rolePriority = 8; else if (member.isReadOnly) rolePriority = 7; @@ -964,10 +977,11 @@ Boards.helpers({ else if (member.isNoComments) rolePriority = 3; else if (member.isNormalAssignedOnly) rolePriority = 2; else rolePriority = 1; // Normal - + const fullname = user ? user.profile.fullname : ''; - return rolePriority + '-' + fullname; - }); + membersWithSortKey.push({ member, sortKey: rolePriority + '-' + fullname }); + } + return _.sortBy(membersWithSortKey, 'sortKey').map(item => item.member); }, activeOrgs() { @@ -990,8 +1004,8 @@ Boards.helpers({ return _.where(this.members, { isActive: true, isAdmin: true }); }, - memberUsers() { - return ReactiveCache.getUsers({ _id: { $in: _.pluck(this.members, 'userId') } }); + async memberUsers() { + return await ReactiveCache.getUsers({ _id: { $in: _.pluck(this.members, 'userId') } }); }, getLabel(name, color) { @@ -1111,8 +1125,8 @@ Boards.helpers({ return `board-color-${this.color}`; }, - customFields() { - const ret = ReactiveCache.getCustomFields( + async customFields() { + const ret = await ReactiveCache.getCustomFields( { boardIds: { $in: [this._id] } }, { sort: { name: 1 } }, ); @@ -1141,7 +1155,7 @@ Boards.helpers({ } }, - searchBoards(term) { + async searchBoards(term) { check(term, Match.OneOf(String, null, undefined)); const query = { boardId: this._id }; @@ -1156,11 +1170,11 @@ Boards.helpers({ query.$or = [{ title: regex }, { description: regex }]; } - const ret = ReactiveCache.getCards(query, projection); + const ret = await ReactiveCache.getCards(query, projection); return ret; }, - searchSwimlanes(term) { + async searchSwimlanes(term) { check(term, Match.OneOf(String, null, undefined)); const query = { boardId: this._id }; @@ -1178,10 +1192,10 @@ Boards.helpers({ query.$or = [{ title: regex }, { description: regex }]; } - return ReactiveCache.getSwimlanes(query, projection); + return await ReactiveCache.getSwimlanes(query, projection); }, - searchLists(term) { + async searchLists(term) { let ret = null; if (term) { check(term, Match.OneOf(String)); @@ -1203,12 +1217,12 @@ Boards.helpers({ query.$or = [{ title: regex }, { description: regex }]; } - ret = ReactiveCache.getLists(query, projection); + ret = await ReactiveCache.getLists(query, projection); } return ret; }, - searchCards(term, excludeLinked) { + async searchCards(term, excludeLinked) { let ret = null; if (term) { check(term, Match.OneOf(String)); @@ -1234,7 +1248,7 @@ Boards.helpers({ { description: regex }, { customFields: { $elemMatch: { value: regex } } }, ]; - ret = ReactiveCache.getCards(query, projection); + ret = await ReactiveCache.getCards(query, projection); } return ret; }, @@ -1268,8 +1282,8 @@ Boards.helpers({ return this.subtasksDefaultBoardId; }, - getDefaultSubtasksBoard() { - return ReactiveCache.getBoard(this.getDefaultSubtasksBoardId()); + async getDefaultSubtasksBoard() { + return await ReactiveCache.getBoard(this.getDefaultSubtasksBoardId()); }, //Date Settings option such as received date, start date and so on. @@ -1301,8 +1315,8 @@ Boards.helpers({ return this.dateSettingsDefaultBoardId; }, - getDefaultDateSettingsBoard() { - return ReactiveCache.getBoard(this.getDefaultDateSettingsBoardId()); + async getDefaultDateSettingsBoard() { + return await ReactiveCache.getBoard(this.getDefaultDateSettingsBoardId()); }, getDefaultSubtasksListId() { @@ -1320,8 +1334,8 @@ Boards.helpers({ return this.subtasksDefaultListId; }, - getDefaultSubtasksList() { - return ReactiveCache.getList(this.getDefaultSubtasksListId()); + async getDefaultSubtasksList() { + return await ReactiveCache.getList(this.getDefaultSubtasksListId()); }, getDefaultDateSettingsListId() { @@ -1339,15 +1353,15 @@ Boards.helpers({ return this.dateSettingsDefaultListId; }, - getDefaultDateSettingsList() { - return ReactiveCache.getList(this.getDefaultDateSettingsListId()); + async getDefaultDateSettingsList() { + return await ReactiveCache.getList(this.getDefaultDateSettingsListId()); }, - getDefaultSwimline() { - let result = ReactiveCache.getSwimlane({ boardId: this._id }); + async getDefaultSwimline() { + let result = await ReactiveCache.getSwimlane({ boardId: this._id }); if (result === undefined) { // Check if any swimlane exists for this board to avoid duplicates - const existingSwimlanes = ReactiveCache.getSwimlanes({ boardId: this._id }); + const existingSwimlanes = await ReactiveCache.getSwimlanes({ boardId: this._id }); if (existingSwimlanes.length > 0) { // Use the first existing swimlane result = existingSwimlanes[0]; @@ -1358,14 +1372,14 @@ Boards.helpers({ title: title, boardId: this._id, }); - result = ReactiveCache.getSwimlane({ boardId: this._id }); + result = await ReactiveCache.getSwimlane({ boardId: this._id }); } } return result; }, - getNextCardNumber() { - const boardCards = ReactiveCache.getCard( + async getNextCardNumber() { + const boardCards = await ReactiveCache.getCard( { boardId: this._id }, @@ -1384,16 +1398,16 @@ Boards.helpers({ return maxCardNr + 1; }, - cardsDueInBetween(start, end) { - const ret = ReactiveCache.getCards({ + async cardsDueInBetween(start, end) { + const ret = await ReactiveCache.getCards({ boardId: this._id, dueAt: { $gte: start, $lte: end }, }); return ret; }, - cardsInInterval(start, end) { - const ret = ReactiveCache.getCards({ + async cardsInInterval(start, end) { + const ret = await ReactiveCache.getCards({ boardId: this._id, $or: [ { @@ -1454,7 +1468,7 @@ Boards.helpers({ }, async setBackgroundImageURL(backgroundImageURL) { - const currentUser = ReactiveCache.getCurrentUser(); + const currentUser = await ReactiveCache.getCurrentUser(); if (currentUser.isBoardAdmin() || currentUser.isAdmin()) { return await Boards.updateAsync(this._id, { $set: { backgroundImageURL } }); } @@ -1717,32 +1731,31 @@ function boardRemover(userId, doc) { ); } -Boards.uniqueTitle = title => { +Boards.uniqueTitle = async title => { const m = title.match( new RegExp('^(?.*?)\\s*(\\[(?<num>\\d+)]\\s*$|\\s*$)'), ); const base = escapeForRegex(m.groups.title); const baseTitle = m.groups.title; - boards = ReactiveCache.getBoards({ title: new RegExp(`^${base}\\s*(\\[(?<num>\\d+)]\\s*$|\\s*$)`) }); + const boards = await ReactiveCache.getBoards({ title: new RegExp(`^${base}\\s*(\\[(?<num>\\d+)]\\s*$|\\s*$)`) }); if (boards.length > 0) { let num = 0; - ReactiveCache.getBoards({ title: new RegExp(`^${base}\\s*\\[\\d+]\\s*$`) }).forEach( - board => { - const m = board.title.match( - new RegExp('^(?<title>.*?)\\s*\\[(?<num>\\d+)]\\s*$'), - ); - if (m) { - const n = parseInt(m.groups.num, 10); - num = num < n ? n : num; - } - }, - ); + const numberedBoards = await ReactiveCache.getBoards({ title: new RegExp(`^${base}\\s*\\[\\d+]\\s*$`) }); + for (const board of numberedBoards) { + const m = board.title.match( + new RegExp('^(?<title>.*?)\\s*\\[(?<num>\\d+)]\\s*$'), + ); + if (m) { + const n = parseInt(m.groups.num, 10); + num = num < n ? n : num; + } + } return `${baseTitle} [${num + 1}]`; } return title; }; -Boards.userSearch = ( +Boards.userSearch = async ( userId, selector = {}, projection = {}, @@ -1756,17 +1769,17 @@ Boards.userSearch = ( if (userId) { selector.$or.push({ members: { $elemMatch: { userId, isActive: true } } }); } - const ret = ReactiveCache.getBoards(selector, projection); + const ret = await ReactiveCache.getBoards(selector, projection); return ret; }; -Boards.userBoards = ( +Boards.userBoards = async ( userId, archived = false, selector = {}, projection = {}, ) => { - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user) { return []; } @@ -1785,13 +1798,14 @@ Boards.userBoards = ( { teams: { $elemMatch: { teamId: { $in: user.teamIds() }, isActive: true } } }, ]; - return ReactiveCache.getBoards(selector, projection); + return await ReactiveCache.getBoards(selector, projection); }; -Boards.userBoardIds = (userId, archived = false, selector = {}) => { - return Boards.userBoards(userId, archived, selector, { +Boards.userBoardIds = async (userId, archived = false, selector = {}) => { + const boards = await Boards.userBoards(userId, archived, selector, { fields: { _id: 1 }, - }).map(board => { + }); + return boards.map(board => { return board._id; }); }; @@ -1810,7 +1824,7 @@ Boards.labelColors = () => { if (Meteor.isServer) { Boards.allow({ - insert(userId, doc) { + async insert(userId, doc) { // Check if user is logged in if (!userId) return false; @@ -1829,7 +1843,7 @@ if (Meteor.isServer) { // All logged in users are allowed to reorder boards by dragging at All Boards page and Public Boards page. Boards.allow({ - update(userId, board, fieldNames) { + async update(userId, board, fieldNames) { return canUpdateBoardSort(userId, board, fieldNames); }, // Need members to verify membership in policy @@ -1839,7 +1853,7 @@ if (Meteor.isServer) { // The number of users that have starred this board is managed by trusted code // and the user is not allowed to update it Boards.deny({ - update(userId, board, fieldNames) { + async update(userId, board, fieldNames) { return _.contains(fieldNames, 'stars'); }, fetch: [], @@ -1847,7 +1861,7 @@ if (Meteor.isServer) { // We can't remove a member if it is the last administrator Boards.deny({ - update(userId, doc, fieldNames, modifier) { + async update(userId, doc, fieldNames, modifier) { if (!_.contains(fieldNames, 'members')) return false; // We only care in case of a $pull operation, ie remove a member @@ -1873,7 +1887,7 @@ if (Meteor.isServer) { // Deny changing permission to public if allowPrivateOnly is enabled Boards.deny({ - update(userId, doc, fieldNames, modifier) { + async update(userId, doc, fieldNames, modifier) { if (!_.contains(fieldNames, 'permission')) return false; const allowPrivateOnly = TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly')?.booleanValue; @@ -1887,13 +1901,13 @@ if (Meteor.isServer) { }); Meteor.methods({ - getBackgroundImageURL(boardId) { + async getBackgroundImageURL(boardId) { check(boardId, String); - return ReactiveCache.getBoard(boardId, {}, { backgroundImageUrl: 1 }); + return await ReactiveCache.getBoard(boardId, {}, { backgroundImageUrl: 1 }); }, async quitBoard(boardId) { check(boardId, String); - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (board) { const userId = Meteor.userId(); const index = board.memberIndex(userId); @@ -1903,9 +1917,9 @@ if (Meteor.isServer) { } else throw new Meteor.Error('error-board-notAMember'); } else throw new Meteor.Error('error-board-doesNotExist'); }, - acceptInvite(boardId) { + async acceptInvite(boardId) { check(boardId, String); - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) { throw new Meteor.Error('error-board-doesNotExist'); } @@ -1927,9 +1941,10 @@ if (Meteor.isServer) { } }); }, - myLabelNames() { + async myLabelNames() { let names = []; - Boards.userBoards(Meteor.userId()).forEach(board => { + const boards = await Boards.userBoards(Meteor.userId()); + for (const board of boards) { // Only return labels when they exist. if (board.labels !== undefined) { names = names.concat( @@ -1939,21 +1954,21 @@ if (Meteor.isServer) { return label.name; }), ); - } else { - return []; } - }); + } return _.uniq(names).sort(); }, - myBoardNames() { + async myBoardNames() { + const boards = await Boards.userBoards(Meteor.userId()); return _.uniq( - Boards.userBoards(Meteor.userId()).map(board => { + boards.map(board => { return board.title; }), ).sort(); }, - setAllBoardsHideActivities() { - if ((ReactiveCache.getCurrentUser() || {}).isAdmin) { + async setAllBoardsHideActivities() { + const currentUser = await ReactiveCache.getCurrentUser(); + if ((currentUser || {}).isAdmin) { Boards.update( { showActivities: true @@ -1977,7 +1992,7 @@ if (Meteor.isServer) { Meteor.methods({ async archiveBoard(boardId) { check(boardId, String); - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (board) { const userId = Meteor.userId(); const index = board.memberIndex(userId); @@ -1987,14 +2002,14 @@ if (Meteor.isServer) { } else throw new Meteor.Error('error-board-notAMember'); } else throw new Meteor.Error('error-board-doesNotExist'); }, - setBoardOrgs(boardOrgsArray, currBoardId){ + async setBoardOrgs(boardOrgsArray, currBoardId){ check(boardOrgsArray, Array); check(currBoardId, String); const userId = Meteor.userId(); if (!userId) { throw new Meteor.Error('not-authorized', 'You must be logged in to perform this action.'); } - const board = ReactiveCache.getBoard(currBoardId); + const board = await ReactiveCache.getBoard(currBoardId); if (!board) { throw new Meteor.Error('board-not-found', 'Board not found.'); } @@ -2013,7 +2028,7 @@ if (Meteor.isServer) { }, }); }, - setBoardTeams(boardTeamsArray, membersArray, currBoardId){ + async setBoardTeams(boardTeamsArray, membersArray, currBoardId){ check(boardTeamsArray, Array); check(membersArray, Array); check(currBoardId, String); @@ -2021,7 +2036,7 @@ if (Meteor.isServer) { if (!userId) { throw new Meteor.Error('not-authorized', 'You must be logged in to perform this action.'); } - const board = ReactiveCache.getBoard(currBoardId); + const board = await ReactiveCache.getBoard(currBoardId); if (!board) { throw new Meteor.Error('board-not-found', 'Board not found.'); } @@ -2058,8 +2073,8 @@ if (Meteor.isServer) { } // Insert new board at last position in sort order. -Boards.before.insert((userId, doc) => { - const lastBoard = ReactiveCache.getBoard( +Boards.before.insert(async (userId, doc) => { + const lastBoard = await ReactiveCache.getBoard( { sort: { $exists: true } }, { sort: { sort: -1 } }, ); @@ -2243,7 +2258,7 @@ if (Meteor.isServer) { * @return_type [{_id: string, * title: string}] */ - JsonRoutes.add('GET', '/api/users/:userId/boards', function(req, res) { + JsonRoutes.add('GET', '/api/users/:userId/boards', async function(req, res) { try { Authentication.checkLoggedIn(req.userId); const paramUserId = req.params.userId; @@ -2254,7 +2269,7 @@ if (Meteor.isServer) { req.userId === paramUserId, ); - const data = ReactiveCache.getBoards( + const boards = await ReactiveCache.getBoards( { archived: false, 'members.userId': paramUserId, @@ -2262,7 +2277,8 @@ if (Meteor.isServer) { { sort: { sort: 1 /* boards default sorting */ }, }, - ).map(function(board) { + ); + const data = boards.map(function(board) { return { _id: board._id, title: board.title, @@ -2285,17 +2301,18 @@ if (Meteor.isServer) { * @return_type [{_id: string, title: string}] */ - JsonRoutes.add('GET', '/api/boards', function(req, res) { + JsonRoutes.add('GET', '/api/boards', async function(req, res) { try { Authentication.checkUserId(req.userId); + const boards = await ReactiveCache.getBoards( + { permission: 'public' }, + { + sort: { sort: 1 /* boards default sorting */ }, + }, + ); JsonRoutes.sendResult(res, { code: 200, - data: ReactiveCache.getBoards( - { permission: 'public' }, - { - sort: { sort: 1 /* boards default sorting */ }, - }, - ).map(function(doc) { + data: boards.map(function(doc) { return { _id: doc._id, title: doc.title, @@ -2316,14 +2333,16 @@ if (Meteor.isServer) { * * @return_type {private: integer, public: integer} */ - JsonRoutes.add('GET', '/api/boards_count', function(req, res) { + JsonRoutes.add('GET', '/api/boards_count', async function(req, res) { try { Authentication.checkUserId(req.userId); + const privateBoards = await ReactiveCache.getBoards({ permission: 'private' }); + const publicBoards = await ReactiveCache.getBoards({ permission: 'public' }); JsonRoutes.sendResult(res, { code: 200, data: { - private: ReactiveCache.getBoards({ permission: 'private' }).length, - public: ReactiveCache.getBoards({ permission: 'public' }).length, + private: privateBoards.length, + public: publicBoards.length, }, }); } catch (error) { @@ -2341,14 +2360,15 @@ if (Meteor.isServer) { * @param {string} boardId the ID of the board to retrieve the data * @return_type Boards */ - JsonRoutes.add('GET', '/api/boards/:boardId', function(req, res) { + JsonRoutes.add('GET', '/api/boards/:boardId', async function(req, res) { try { const paramBoardId = req.params.boardId; Authentication.checkBoardAccess(req.userId, paramBoardId); + const board = await ReactiveCache.getBoard(paramBoardId); JsonRoutes.sendResult(res, { code: 200, - data: ReactiveCache.getBoard(paramBoardId), + data: board, }); } catch (error) { JsonRoutes.sendResult(res, { @@ -2493,12 +2513,12 @@ if (Meteor.isServer) { * * @return_type string */ - JsonRoutes.add('PUT', '/api/boards/:boardId/labels', function(req, res) { + JsonRoutes.add('PUT', '/api/boards/:boardId/labels', async function(req, res) { const id = req.params.boardId; Authentication.checkBoardWriteAccess(req.userId, id); try { if (req.body.hasOwnProperty('label')) { - const board = ReactiveCache.getBoard(id); + const board = await ReactiveCache.getBoard(id); const color = req.body.label.color; const name = req.body.label.name; const labelId = Random.id(6); @@ -2536,14 +2556,14 @@ if (Meteor.isServer) { * * @return_type string */ -JsonRoutes.add('POST', '/api/boards/:boardId/copy', function(req, res) { +JsonRoutes.add('POST', '/api/boards/:boardId/copy', async function(req, res) { const id = req.params.boardId; - const board = ReactiveCache.getBoard(id); + const board = await ReactiveCache.getBoard(id); const adminAccess = board.members.some(e => e.userId === req.userId && e.isAdmin); Authentication.checkAdminOrCondition(req.userId, adminAccess); try { - board['title'] = req.body.title || Boards.uniqueTitle(board.title); - ret = board.copy(); + board['title'] = req.body.title || await Boards.uniqueTitle(board.title); + ret = await board.copy(); JsonRoutes.sendResult(res, { code: 200, data: ret, @@ -2580,7 +2600,7 @@ JsonRoutes.add('POST', '/api/boards/:boardId/copy', function(req, res) { const boardId = req.params.boardId; const memberId = req.params.memberId; const { isAdmin, isNoComments, isCommentOnly, isWorker, isNormalAssignedOnly, isCommentAssignedOnly, isReadOnly, isReadAssignedOnly } = req.body; - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); function isTrue(data) { try { return data.toLowerCase() === 'true'; @@ -2630,13 +2650,14 @@ JsonRoutes.add('POST', '/api/boards/:boardId/copy', function(req, res) { * cardId: string * }] */ - JsonRoutes.add('GET', '/api/boards/:boardId/attachments', function(req, res) { + JsonRoutes.add('GET', '/api/boards/:boardId/attachments', async function(req, res) { const paramBoardId = req.params.boardId; Authentication.checkBoardAccess(req.userId, paramBoardId); + const attachments = await ReactiveCache + .getAttachments({'meta.boardId': paramBoardId }, {}, true); JsonRoutes.sendResult(res, { code: 200, - data: ReactiveCache - .getAttachments({'meta.boardId': paramBoardId }, {}, true) + data: attachments .each() .map(function(attachment) { return { diff --git a/models/cardCommentReactions.js b/models/cardCommentReactions.js index 6ddd930fa..3a9a21608 100644 --- a/models/cardCommentReactions.js +++ b/models/cardCommentReactions.js @@ -50,14 +50,14 @@ CardCommentReactions.attachSchema( ); CardCommentReactions.allow({ - insert(userId, doc) { - return allowIsBoardMember(userId, ReactiveCache.getBoard(doc.boardId)); + async insert(userId, doc) { + return allowIsBoardMember(userId, await ReactiveCache.getBoard(doc.boardId)); }, - update(userId, doc) { - return allowIsBoardMember(userId, ReactiveCache.getBoard(doc.boardId)); + async update(userId, doc) { + return allowIsBoardMember(userId, await ReactiveCache.getBoard(doc.boardId)); }, - remove(userId, doc) { - return allowIsBoardMember(userId, ReactiveCache.getBoard(doc.boardId)); + async remove(userId, doc) { + return allowIsBoardMember(userId, await ReactiveCache.getBoard(doc.boardId)); }, fetch: ['boardId'], }); diff --git a/models/cardComments.js b/models/cardComments.js index fd2e8502d..694d2187a 100644 --- a/models/cardComments.js +++ b/models/cardComments.js @@ -81,15 +81,15 @@ CardComments.attachSchema( ); CardComments.allow({ - insert(userId, doc) { + async insert(userId, doc) { // ReadOnly users cannot add comments. Only members who can comment are allowed. - return allowIsBoardMemberCommentOnly(userId, ReactiveCache.getBoard(doc.boardId)); + return allowIsBoardMemberCommentOnly(userId, await ReactiveCache.getBoard(doc.boardId)); }, - update(userId, doc) { - return userId === doc.userId || allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId)); + async update(userId, doc) { + return userId === doc.userId || allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId)); }, - remove(userId, doc) { - return userId === doc.userId || allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId)); + async remove(userId, doc) { + return userId === doc.userId || allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId)); }, fetch: ['userId', 'boardId'], }); @@ -101,21 +101,21 @@ CardComments.helpers({ CardComments.insert(this); }, - user() { - return ReactiveCache.getUser(this.userId); + async user() { + return await ReactiveCache.getUser(this.userId); }, - reactions() { - const cardCommentReactions = ReactiveCache.getCardCommentReaction({cardCommentId: this._id}); + async reactions() { + const cardCommentReactions = await ReactiveCache.getCardCommentReaction({cardCommentId: this._id}); return !!cardCommentReactions ? cardCommentReactions.reactions : []; }, - toggleReaction(reactionCodepoint) { + async toggleReaction(reactionCodepoint) { if (reactionCodepoint !== sanitizeText(reactionCodepoint)) { return false; } else { - const cardCommentReactions = ReactiveCache.getCardCommentReaction({cardCommentId: this._id}); + const cardCommentReactions = await ReactiveCache.getCardCommentReaction({cardCommentId: this._id}); const reactions = !!cardCommentReactions ? cardCommentReactions.reactions : []; const userId = Meteor.userId(); const reaction = reactions.find(r => r.reactionCodepoint === reactionCodepoint); @@ -154,8 +154,8 @@ CardComments.helpers({ CardComments.hookOptions.after.update = { fetchPrevious: false }; -function commentCreation(userId, doc) { - const card = ReactiveCache.getCard(doc.cardId); +async function commentCreation(userId, doc) { + const card = await ReactiveCache.getCard(doc.cardId); Activities.insert({ userId, activityType: 'addComment', @@ -167,9 +167,9 @@ function commentCreation(userId, doc) { }); } -CardComments.textSearch = (userId, textArray) => { +CardComments.textSearch = async (userId, textArray) => { const selector = { - boardId: { $in: Boards.userBoardIds(userId) }, + boardId: { $in: await Boards.userBoardIds(userId) }, $and: [], }; @@ -180,7 +180,7 @@ CardComments.textSearch = (userId, textArray) => { // eslint-disable-next-line no-console // console.log('cardComments selector:', selector); - const comments = ReactiveCache.getCardComments(selector); + const comments = await ReactiveCache.getCardComments(selector); // eslint-disable-next-line no-console // console.log('count:', comments.count()); // eslint-disable-next-line no-console @@ -197,12 +197,12 @@ if (Meteor.isServer) { await CardComments._collection.createIndexAsync({ cardId: 1, createdAt: -1 }); }); - CardComments.after.insert((userId, doc) => { - commentCreation(userId, doc); + CardComments.after.insert(async (userId, doc) => { + await commentCreation(userId, doc); }); - CardComments.after.update((userId, doc) => { - const card = ReactiveCache.getCard(doc.cardId); + CardComments.after.update(async (userId, doc) => { + const card = await ReactiveCache.getCard(doc.cardId); Activities.insert({ userId, activityType: 'editComment', @@ -214,8 +214,8 @@ if (Meteor.isServer) { }); }); - CardComments.before.remove((userId, doc) => { - const card = ReactiveCache.getCard(doc.cardId); + CardComments.before.remove(async (userId, doc) => { + const card = await ReactiveCache.getCard(doc.cardId); Activities.insert({ userId, activityType: 'deleteComment', @@ -225,7 +225,7 @@ if (Meteor.isServer) { listId: card.listId, swimlaneId: card.swimlaneId, }); - const activity = ReactiveCache.getActivity({ commentId: doc._id }); + const activity = await ReactiveCache.getActivity({ commentId: doc._id }); if (activity) { Activities.remove(activity._id); } @@ -244,7 +244,7 @@ if (Meteor.isServer) { * comment: string, * authorId: string}] */ - JsonRoutes.add('GET', '/api/boards/:boardId/cards/:cardId/comments', function ( + JsonRoutes.add('GET', '/api/boards/:boardId/cards/:cardId/comments', async function ( req, res, ) { @@ -254,10 +254,10 @@ if (Meteor.isServer) { Authentication.checkBoardAccess(req.userId, paramBoardId); JsonRoutes.sendResult(res, { code: 200, - data: ReactiveCache.getCardComments({ + data: (await ReactiveCache.getCardComments({ boardId: paramBoardId, cardId: paramCardId, - }).map(function (doc) { + })).map(function (doc) { return { _id: doc._id, comment: doc.text, @@ -285,7 +285,7 @@ if (Meteor.isServer) { JsonRoutes.add( 'GET', '/api/boards/:boardId/cards/:cardId/comments/:commentId', - function (req, res) { + async function (req, res) { try { const paramBoardId = req.params.boardId; const paramCommentId = req.params.commentId; @@ -293,7 +293,7 @@ if (Meteor.isServer) { Authentication.checkBoardAccess(req.userId, paramBoardId); JsonRoutes.sendResult(res, { code: 200, - data: ReactiveCache.getCardComment({ + data: await ReactiveCache.getCardComment({ _id: paramCommentId, cardId: paramCardId, boardId: paramBoardId, @@ -320,7 +320,7 @@ if (Meteor.isServer) { JsonRoutes.add( 'POST', '/api/boards/:boardId/cards/:cardId/comments', - function (req, res) { + async function (req, res) { try { const paramBoardId = req.params.boardId; const paramCardId = req.params.cardId; @@ -339,12 +339,12 @@ if (Meteor.isServer) { }, }); - const cardComment = ReactiveCache.getCardComment({ + const cardComment = await ReactiveCache.getCardComment({ _id: id, cardId: paramCardId, boardId: paramBoardId, }); - commentCreation(req.userId, cardComment); + await commentCreation(req.userId, cardComment); } catch (error) { JsonRoutes.sendResult(res, { code: 200, diff --git a/models/cards.js b/models/cards.js index dbd2f39cb..db715d1d5 100644 --- a/models/cards.js +++ b/models/cards.js @@ -519,7 +519,7 @@ Cards.attachSchema( // Centralized update policy for Cards // Security: deny any direct client updates to 'vote' fields; require write access otherwise -canUpdateCard = function(userId, doc, fields) { +canUpdateCard = async function(userId, doc, fields) { if (!userId) return false; const fieldNames = fields || []; // Block direct updates to voting fields; voting must go through Meteor method 'cards.vote' @@ -531,21 +531,21 @@ canUpdateCard = function(userId, doc, fields) { return false; } // ReadOnly users cannot edit cards - return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId)); + return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(doc.boardId)); }; Cards.allow({ - insert(userId, doc) { + async insert(userId, doc) { // ReadOnly users cannot create cards - return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId)); + return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(doc.boardId)); }, - update(userId, doc, fields) { - return canUpdateCard(userId, doc, fields); + async update(userId, doc, fields) { + return await canUpdateCard(userId, doc, fields); }, - remove(userId, doc) { + async remove(userId, doc) { // ReadOnly users cannot delete cards - return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId)); + return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(doc.boardId)); }, fetch: ['boardId'], }); @@ -576,7 +576,7 @@ Cards.helpers({ // Map custom fields to new board const result = []; for (const cf of this.customFields) { - const oldCf = ReactiveCache.getCustomField(cf._id); + const oldCf = await ReactiveCache.getCustomField(cf._id); // Check if oldCf is undefined or null if (!oldCf) { @@ -585,7 +585,7 @@ Cards.helpers({ continue; } - const newCf = ReactiveCache.getCustomField({ + const newCf = await ReactiveCache.getCustomField({ boardIds: boardId, name: oldCf.name, type: oldCf.type, @@ -603,14 +603,14 @@ Cards.helpers({ }, - copy(boardId, swimlaneId, listId) { + async copy(boardId, swimlaneId, listId) { const oldId = this._id; - const oldCard = ReactiveCache.getCard(oldId); + const oldCard = await ReactiveCache.getCard(oldId); // we must only copy the labels and custom fields if the target board // differs from the source board if (this.boardId !== boardId) { - const oldBoard = ReactiveCache.getBoard(this.boardId); + const oldBoard = await ReactiveCache.getBoard(this.boardId); const oldBoardLabels = oldBoard.labels; // Get old label names @@ -621,7 +621,7 @@ Cards.helpers({ 'name', ); - const newBoard = ReactiveCache.getBoard(boardId); + const newBoard = await ReactiveCache.getBoard(boardId); const newBoardLabels = newBoard.labels; const newCardLabels = _.pluck( _.filter(newBoardLabels, label => { @@ -633,15 +633,16 @@ Cards.helpers({ delete this.labelIds; this.labelIds = newCardLabels; - this.customFields = this.mapCustomFieldsToBoard(newBoard._id); + this.customFields = await this.mapCustomFieldsToBoard(newBoard._id); } delete this._id; this.boardId = boardId; - this.cardNumber = ReactiveCache.getBoard(boardId).getNextCardNumber(); + const board = await ReactiveCache.getBoard(boardId); + this.cardNumber = board.getNextCardNumber(); this.swimlaneId = swimlaneId; this.listId = listId; - const _id = Cards.insert(this); + const _id = await Cards.insertAsync(this); // Copy attachments oldCard.attachments() @@ -650,21 +651,24 @@ Cards.helpers({ }); // copy checklists - ReactiveCache.getChecklists({ cardId: oldId }).forEach(ch => { - ch.copy(_id); - }); + const checklists = await ReactiveCache.getChecklists({ cardId: oldId }); + for (const ch of checklists) { + await ch.copy(_id); + } // copy subtasks - ReactiveCache.getCards({ parentId: oldId }).forEach(subtask => { + const subtasks = await ReactiveCache.getCards({ parentId: oldId }); + for (const subtask of subtasks) { subtask.parentId = _id; subtask._id = null; - Cards.insert(subtask); - }); + await Cards.insertAsync(subtask); + } // copy card comments - ReactiveCache.getCardComments({ cardId: oldId }).forEach(cmt => { - cmt.copy(_id); - }); + const comments = await ReactiveCache.getCardComments({ cardId: oldId }); + for (const cmt of comments) { + await cmt.copy(_id); + } // restore the id, otherwise new copies will fail this._id = oldId; @@ -686,16 +690,16 @@ Cards.helpers({ return Cards.insert(linkCard); }, - list() { - return ReactiveCache.getList(this.listId); + async list() { + return await ReactiveCache.getList(this.listId); }, - swimlane() { - return ReactiveCache.getSwimlane(this.swimlaneId); + async swimlane() { + return await ReactiveCache.getSwimlane(this.swimlaneId); }, - board() { - const ret = ReactiveCache.getBoard(this.boardId); + async board() { + const ret = await ReactiveCache.getBoard(this.boardId); return ret; }, @@ -710,8 +714,8 @@ Cards.helpers({ return this.__id; }, - getList() { - const list = this.list(); + async getList() { + const list = await this.list(); if (!list) { return { _id: this.listId, @@ -723,8 +727,8 @@ Cards.helpers({ return list; }, - getSwimlane() { - const swimlane = this.swimlane(); + async getSwimlane() { + const swimlane = await this.swimlane(); if (!swimlane) { return { _id: this.swimlaneId, @@ -736,8 +740,8 @@ Cards.helpers({ return swimlane; }, - getBoard() { - const board = this.board(); + async getBoard() { + const board = await this.board(); if (!board) { return { _id: this.boardId, @@ -749,8 +753,9 @@ Cards.helpers({ return board; }, - labels() { - const boardLabels = this.board().labels; + async labels() { + const board = await this.board(); + const boardLabels = board.labels; const cardLabels = _.filter(boardLabels, label => { return _.contains(this.labelIds, label._id); }); @@ -766,7 +771,7 @@ Cards.helpers({ * @param swimlaneId a swimlane id * top sorting of the card at the top if true, or from the bottom if false */ - getSort(listId, swimlaneId, top) { + async getSort(listId, swimlaneId, top) { if (!_.isBoolean(top)) { top = true; } @@ -782,7 +787,7 @@ Cards.helpers({ archived: false, }; const sorting = top ? 1 : -1; - const card = ReactiveCache.getCard(selector, { sort: { sort: sorting } }, true); + const card = await ReactiveCache.getCard(selector, { sort: { sort: sorting } }, true); let ret = null if (card) { ret = card.sort; @@ -794,8 +799,8 @@ Cards.helpers({ * @param listId a list id * @param swimlaneId a swimlane id */ - getMinSort(listId, swimlaneId) { - const ret = this.getSort(listId, swimlaneId, true); + async getMinSort(listId, swimlaneId) { + const ret = await this.getSort(listId, swimlaneId, true); return ret; }, @@ -803,40 +808,40 @@ Cards.helpers({ * @param listId a list id * @param swimlaneId a swimlane id */ - getMaxSort(listId, swimlaneId) { - const ret = this.getSort(listId, swimlaneId, false); + async getMaxSort(listId, swimlaneId) { + const ret = await this.getSort(listId, swimlaneId, false); return ret; }, - user() { - return ReactiveCache.getUser(this.userId); + async user() { + return await ReactiveCache.getUser(this.userId); }, - isAssigned(memberId) { - return _.contains(this.getMembers(), memberId); + async isAssigned(memberId) { + return _.contains(await this.getMembers(), memberId); }, - isAssignee(assigneeId) { - return _.contains(this.getAssignees(), assigneeId); + async isAssignee(assigneeId) { + return _.contains(await this.getAssignees(), assigneeId); }, - activities() { + async activities() { let ret; if (this.isLinkedBoard()) { - ret = ReactiveCache.getActivities( + ret = await ReactiveCache.getActivities( { boardId: this.linkedId }, { sort: { createdAt: -1 } }, ); } else { - ret = ReactiveCache.getActivities({ cardId: this.getRealId() }, { sort: { createdAt: -1 } }); + ret = await ReactiveCache.getActivities({ cardId: this.getRealId() }, { sort: { createdAt: -1 } }); } return ret; }, - comments() { + async comments() { let ret if (this.isLinkedBoard()) { - ret = ReactiveCache.getCardComments( + ret = await ReactiveCache.getCardComments( { boardId: this.linkedId }, { sort: { createdAt: -1 } }, ); @@ -850,18 +855,18 @@ Cards.helpers({ return ret; }, - attachments() { - const ret = ReactiveCache.getAttachments( + async attachments() { + const ret = (await ReactiveCache.getAttachments( { 'meta.cardId': this.getRealId() }, { sort: { uploadedAt: -1 } }, true, - ).each(); + )).each(); return ret; }, - cover() { + async cover() { if (!this.coverId) return false; - const cover = ReactiveCache.getAttachment(this.coverId); + const cover = await ReactiveCache.getAttachment(this.coverId); // if we return a cover before it is fully stored, we will get errors when we try to display it // todo XXX we could return a default "upload pending" image in the meantime? return cover && cover.link() && cover; @@ -967,9 +972,9 @@ Cards.helpers({ }, // customFields with definitions - customFieldsWD() { + async customFieldsWD() { // get all definitions - const definitions = ReactiveCache.getCustomFields({ + const definitions = await ReactiveCache.getCustomFields({ boardIds: { $in: [this.boardId] }, }); if (!definitions) { @@ -1020,8 +1025,8 @@ Cards.helpers({ return ''; }, - absoluteUrl() { - const board = this.board(); + async absoluteUrl() { + const board = await this.board(); if (!board) return undefined; return FlowRouter.url('card', { boardId: board._id, @@ -1029,8 +1034,8 @@ Cards.helpers({ cardId: this._id, }); }, - originRelativeUrl() { - const board = this.board(); + async originRelativeUrl() { + const board = await this.board(); if (!board) return undefined; return FlowRouter.path('card', { boardId: board._id, @@ -1039,8 +1044,8 @@ Cards.helpers({ }); }, - canBeRestored() { - const list = ReactiveCache.getList(this.listId); + async canBeRestored() { + const list = await ReactiveCache.getList(this.listId); if ( !list.getWipLimit('soft') && list.getWipLimit('enabled') && @@ -1051,18 +1056,18 @@ Cards.helpers({ return true; }, - parentCard() { + async parentCard() { let ret = null; if (this.parentId) { - ret = ReactiveCache.getCard(this.parentId); + ret = await ReactiveCache.getCard(this.parentId); } return ret; }, - parentCardName() { + async parentCardName() { let result = ''; if (this.parentId) { - const card = ReactiveCache.getCard(this.parentId); + const card = await ReactiveCache.getCard(this.parentId); if (card) { result = card.title; } @@ -1070,11 +1075,11 @@ Cards.helpers({ return result; }, - parentListId() { + async parentListId() { const result = []; let crtParentId = this.parentId; while (crtParentId) { - const crt = ReactiveCache.getCard(crtParentId); + const crt = await ReactiveCache.getCard(crtParentId); if (crt === null || crt === undefined) { // maybe it has been deleted break; @@ -1089,12 +1094,12 @@ Cards.helpers({ return result; }, - parentList() { + async parentList() { const resultId = []; const result = []; let crtParentId = this.parentId; while (crtParentId) { - const crt = ReactiveCache.getCard(crtParentId); + const crt = await ReactiveCache.getCard(crtParentId); if (crt === null || crt === undefined) { // maybe it has been deleted break; @@ -1110,8 +1115,8 @@ Cards.helpers({ return result; }, - parentString(sep) { - return this.parentList() + async parentString(sep) { + return (await this.parentList()) .map(function(elem) { return elem.title; }) @@ -1146,13 +1151,13 @@ Cards.helpers({ } }, - getDescription() { + async getDescription() { if (this.isLinkedCard()) { - const card = ReactiveCache.getCard(this.linkedId); + const card = await ReactiveCache.getCard(this.linkedId); if (card && card.description) return card.description; else return null; } else if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); if (board && board.description) return board.description; else return null; } else if (this.description) { @@ -1162,16 +1167,16 @@ Cards.helpers({ } }, - getMembers() { + async getMembers() { if (this.isLinkedCard()) { - const card = ReactiveCache.getCard(this.linkedId); + const card = await ReactiveCache.getCard(this.linkedId); if (card === undefined) { return null; } else { return card.members; } } else if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); if (board === undefined) { return null; } else { @@ -1184,16 +1189,16 @@ Cards.helpers({ } }, - getAssignees() { + async getAssignees() { if (this.isLinkedCard()) { - const card = ReactiveCache.getCard(this.linkedId); + const card = await ReactiveCache.getCard(this.linkedId); if (card === undefined) { return null; } else { return card.assignees; } } else if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); if (board === undefined) { return null; } else { @@ -1209,10 +1214,10 @@ Cards.helpers({ async assignMember(memberId) { let ret; if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); ret = await board.addMember(memberId); } else { - ret = Cards.update( + ret = await Cards.updateAsync( { _id: this.getRealId() }, { $addToSet: { members: memberId } }, ); @@ -1220,17 +1225,17 @@ Cards.helpers({ return ret; }, - assignAssignee(assigneeId) { + async assignAssignee(assigneeId) { if (this.isLinkedCard()) { - return Cards.update( + return await Cards.updateAsync( { _id: this.linkedId }, { $addToSet: { assignees: assigneeId } }, ); } else if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); return board.addAssignee(assigneeId); } else { - return Cards.update( + return await Cards.updateAsync( { _id: this._id }, { $addToSet: { assignees: assigneeId } }, ); @@ -1239,61 +1244,63 @@ Cards.helpers({ async unassignMember(memberId) { if (this.isLinkedCard()) { - return Cards.update( + return await Cards.updateAsync( { _id: this.linkedId }, { $pull: { members: memberId } }, ); } else if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); return await board.removeMember(memberId); } else { - return Cards.update({ _id: this._id }, { $pull: { members: memberId } }); + return await Cards.updateAsync({ _id: this._id }, { $pull: { members: memberId } }); } }, - unassignAssignee(assigneeId) { + async unassignAssignee(assigneeId) { if (this.isLinkedCard()) { - return Cards.update( + return await Cards.updateAsync( { _id: this.linkedId }, { $pull: { assignees: assigneeId } }, ); } else if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); return board.removeAssignee(assigneeId); } else { - return Cards.update( + return await Cards.updateAsync( { _id: this._id }, { $pull: { assignees: assigneeId } }, ); } }, - toggleMember(memberId) { - if (this.getMembers() && this.getMembers().indexOf(memberId) > -1) { - return this.unassignMember(memberId); + async toggleMember(memberId) { + const members = await this.getMembers(); + if (members && members.indexOf(memberId) > -1) { + return await this.unassignMember(memberId); } else { - return this.assignMember(memberId); + return await this.assignMember(memberId); } }, - toggleAssignee(assigneeId) { - if (this.getAssignees() && this.getAssignees().indexOf(assigneeId) > -1) { - return this.unassignAssignee(assigneeId); + async toggleAssignee(assigneeId) { + const assignees = await this.getAssignees(); + if (assignees && assignees.indexOf(assigneeId) > -1) { + return await this.unassignAssignee(assigneeId); } else { - return this.assignAssignee(assigneeId); + return await this.assignAssignee(assigneeId); } }, - getReceived() { + async getReceived() { if (this.isLinkedCard()) { - const card = ReactiveCache.getCard(this.linkedId); + const card = await ReactiveCache.getCard(this.linkedId); if (card === undefined) { return null; } else { return card.receivedAt; } } else if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); if (board === undefined) { return null; } else { @@ -1312,16 +1319,16 @@ Cards.helpers({ } }, - getStart() { + async getStart() { if (this.isLinkedCard()) { - const card = ReactiveCache.getCard(this.linkedId); + const card = await ReactiveCache.getCard(this.linkedId); if (card === undefined) { return null; } else { return card.startAt; } } else if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); if (board === undefined) { return null; } else { @@ -1340,16 +1347,16 @@ Cards.helpers({ } }, - getDue() { + async getDue() { if (this.isLinkedCard()) { - const card = ReactiveCache.getCard(this.linkedId); + const card = await ReactiveCache.getCard(this.linkedId); if (card === undefined) { return null; } else { return card.dueAt; } } else if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); if (board === undefined) { return null; } else { @@ -1368,16 +1375,16 @@ Cards.helpers({ } }, - getEnd() { + async getEnd() { if (this.isLinkedCard()) { - const card = ReactiveCache.getCard(this.linkedId); + const card = await ReactiveCache.getCard(this.linkedId); if (card === undefined) { return null; } else { return card.endAt; } } else if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); if (board === undefined) { return null; } else { @@ -1396,16 +1403,16 @@ Cards.helpers({ } }, - getIsOvertime() { + async getIsOvertime() { if (this.isLinkedCard()) { - const card = ReactiveCache.getCard(this.linkedId); + const card = await ReactiveCache.getCard(this.linkedId); if (card === undefined) { return null; } else { return card.isOvertime; } } else if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); if (board === undefined) { return null; } else { @@ -1424,16 +1431,16 @@ Cards.helpers({ } }, - getSpentTime() { + async getSpentTime() { if (this.isLinkedCard()) { - const card = ReactiveCache.getCard(this.linkedId); + const card = await ReactiveCache.getCard(this.linkedId); if (card === undefined) { return null; } else { return card.spentTime; } } else if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); if (board === undefined) { return null; } else { @@ -1452,9 +1459,9 @@ Cards.helpers({ } }, - getVoteQuestion() { + async getVoteQuestion() { if (this.isLinkedCard()) { - const card = ReactiveCache.getCard(this.linkedId); + const card = await ReactiveCache.getCard(this.linkedId); if (card === undefined) { return null; } else if (card && card.vote) { @@ -1463,7 +1470,7 @@ Cards.helpers({ return null; } } else if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); if (board === undefined) { return null; } else if (board && board.vote) { @@ -1478,9 +1485,9 @@ Cards.helpers({ } }, - getVotePublic() { + async getVotePublic() { if (this.isLinkedCard()) { - const card = ReactiveCache.getCard(this.linkedId); + const card = await ReactiveCache.getCard(this.linkedId); if (card === undefined) { return null; } else if (card && card.vote) { @@ -1489,7 +1496,7 @@ Cards.helpers({ return null; } } else if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); if (board === undefined) { return null; } else if (board && board.vote) { @@ -1504,9 +1511,9 @@ Cards.helpers({ } }, - getVoteEnd() { + async getVoteEnd() { if (this.isLinkedCard()) { - const card = ReactiveCache.getCard(this.linkedId); + const card = await ReactiveCache.getCard(this.linkedId); if (card === undefined) { return null; } else if (card && card.vote) { @@ -1515,7 +1522,7 @@ Cards.helpers({ return null; } } else if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); if (board === undefined) { return null; } else if (board && board.vote) { @@ -1529,23 +1536,23 @@ Cards.helpers({ return null; } }, - expiredVote() { - let end = this.getVoteEnd(); + async expiredVote() { + let end = await this.getVoteEnd(); if (end) { end = new Date(end); return isBefore(end, new Date()); } return false; }, - voteMemberPositive() { + async voteMemberPositive() { if (this.vote && this.vote.positive) - return ReactiveCache.getUsers({ _id: { $in: this.vote.positive } }); + return await ReactiveCache.getUsers({ _id: { $in: this.vote.positive } }); return []; }, - voteMemberNegative() { + async voteMemberNegative() { if (this.vote && this.vote.negative) - return ReactiveCache.getUsers({ _id: { $in: this.vote.negative } }); + return await ReactiveCache.getUsers({ _id: { $in: this.vote.negative } }); return []; }, voteState() { @@ -1564,9 +1571,9 @@ Cards.helpers({ return null; }, - getPokerQuestion() { + async getPokerQuestion() { if (this.isLinkedCard()) { - const card = ReactiveCache.getCard(this.linkedId); + const card = await ReactiveCache.getCard(this.linkedId); if (card === undefined) { return null; } else if (card && card.poker) { @@ -1575,7 +1582,7 @@ Cards.helpers({ return null; } } else if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); if (board === undefined) { return null; } else if (board && board.poker) { @@ -1598,9 +1605,9 @@ Cards.helpers({ } }, - getPokerEnd() { + async getPokerEnd() { if (this.isLinkedCard()) { - const card = ReactiveCache.getCard(this.linkedId); + const card = await ReactiveCache.getCard(this.linkedId); if (card === undefined) { return null; } else if (card && card.poker) { @@ -1609,7 +1616,7 @@ Cards.helpers({ return null; } } else if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); if (board === undefined) { return null; } else if (board && board.poker) { @@ -1623,62 +1630,62 @@ Cards.helpers({ return null; } }, - expiredPoker() { - let end = this.getPokerEnd(); + async expiredPoker() { + let end = await this.getPokerEnd(); if (end) { end = new Date(end); return isBefore(end, new Date()); } return false; }, - pokerMemberOne() { + async pokerMemberOne() { if (this.poker && this.poker.one) - return ReactiveCache.getUsers({ _id: { $in: this.poker.one } }); + return await ReactiveCache.getUsers({ _id: { $in: this.poker.one } }); return []; }, - pokerMemberTwo() { + async pokerMemberTwo() { if (this.poker && this.poker.two) - return ReactiveCache.getUsers({ _id: { $in: this.poker.two } }); + return await ReactiveCache.getUsers({ _id: { $in: this.poker.two } }); return []; }, - pokerMemberThree() { + async pokerMemberThree() { if (this.poker && this.poker.three) - return ReactiveCache.getUsers({ _id: { $in: this.poker.three } }); + return await ReactiveCache.getUsers({ _id: { $in: this.poker.three } }); return []; }, - pokerMemberFive() { + async pokerMemberFive() { if (this.poker && this.poker.five) - return ReactiveCache.getUsers({ _id: { $in: this.poker.five } }); + return await ReactiveCache.getUsers({ _id: { $in: this.poker.five } }); return []; }, - pokerMemberEight() { + async pokerMemberEight() { if (this.poker && this.poker.eight) - return ReactiveCache.getUsers({ _id: { $in: this.poker.eight } }); + return await ReactiveCache.getUsers({ _id: { $in: this.poker.eight } }); return []; }, - pokerMemberThirteen() { + async pokerMemberThirteen() { if (this.poker && this.poker.thirteen) - return ReactiveCache.getUsers({ _id: { $in: this.poker.thirteen } }); + return await ReactiveCache.getUsers({ _id: { $in: this.poker.thirteen } }); return []; }, - pokerMemberTwenty() { + async pokerMemberTwenty() { if (this.poker && this.poker.twenty) - return ReactiveCache.getUsers({ _id: { $in: this.poker.twenty } }); + return await ReactiveCache.getUsers({ _id: { $in: this.poker.twenty } }); return []; }, - pokerMemberForty() { + async pokerMemberForty() { if (this.poker && this.poker.forty) - return ReactiveCache.getUsers({ _id: { $in: this.poker.forty } }); + return await ReactiveCache.getUsers({ _id: { $in: this.poker.forty } }); return []; }, - pokerMemberOneHundred() { + async pokerMemberOneHundred() { if (this.poker && this.poker.oneHundred) - return ReactiveCache.getUsers({ _id: { $in: this.poker.oneHundred } }); + return await ReactiveCache.getUsers({ _id: { $in: this.poker.oneHundred } }); return []; }, - pokerMemberUnsure() { + async pokerMemberUnsure() { if (this.poker && this.poker.unsure) - return ReactiveCache.getUsers({ _id: { $in: this.poker.unsure } }); + return await ReactiveCache.getUsers({ _id: { $in: this.poker.unsure } }); return []; }, pokerState() { @@ -1749,16 +1756,16 @@ Cards.helpers({ return null; }, - getTitle() { + async getTitle() { if (this.isLinkedCard()) { - const card = ReactiveCache.getCard(this.linkedId); + const card = await ReactiveCache.getCard(this.linkedId); if (card === undefined) { return null; } else { return card.title; } } else if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); if (board === undefined) { return null; } else { @@ -1775,27 +1782,27 @@ Cards.helpers({ return this.cardNumber; }, - getBoardTitle() { + async getBoardTitle() { if (this.isLinkedCard()) { - const card = ReactiveCache.getCard(this.linkedId); + const card = await ReactiveCache.getCard(this.linkedId); if (card === undefined) { return null; } - const board = ReactiveCache.getBoard(card.boardId); + const board = await ReactiveCache.getBoard(card.boardId); if (board === undefined) { return null; } else { return board.title; } } else if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); if (board === undefined) { return null; } else { return board.title; } } else { - const board = ReactiveCache.getBoard(this.boardId); + const board = await ReactiveCache.getBoard(this.boardId); if (board === undefined) { return null; } else { @@ -1822,16 +1829,16 @@ Cards.helpers({ } }, - getArchived() { + async getArchived() { if (this.isLinkedCard()) { - const card = ReactiveCache.getCard(this.linkedId); + const card = await ReactiveCache.getCard(this.linkedId); if (card === undefined) { return null; } else { return card.archived; } } else if (this.isLinkedBoard()) { - const board = ReactiveCache.getBoard(this.linkedId); + const board = await ReactiveCache.getBoard(this.linkedId); if (board === undefined) { return null; } else { @@ -1846,9 +1853,9 @@ Cards.helpers({ return Cards.update({ _id: this.getRealId() }, { $set: { requestedBy } }); }, - getRequestedBy() { + async getRequestedBy() { if (this.isLinkedCard()) { - const card = ReactiveCache.getCard(this.linkedId); + const card = await ReactiveCache.getCard(this.linkedId); if (card === undefined) { return null; } else { @@ -1863,9 +1870,9 @@ Cards.helpers({ return Cards.update({ _id: this.getRealId() }, { $set: { assignedBy } }); }, - getAssignedBy() { + async getAssignedBy() { if (this.isLinkedCard()) { - const card = ReactiveCache.getCard(this.linkedId); + const card = await ReactiveCache.getCard(this.linkedId); if (card === undefined) { return null; } else { @@ -1958,10 +1965,10 @@ Cards.helpers({ this.pokerCountUnsure() ); }, - pokerWinner() { + async pokerWinner() { const pokerListMaps = []; let pokerWinnersListMap = []; - if (this.expiredPoker()) { + if (await this.expiredPoker()) { const one = { count: this.pokerCountOne(), pokerCard: 1 }; const two = { count: this.pokerCountTwo(), pokerCard: 2 }; const three = { count: this.pokerCountThree(), pokerCard: 3 }; @@ -1996,7 +2003,7 @@ Cards.helpers({ }, async applyToChildren(funct) { - const cards = ReactiveCache.getCards({ parentId: this._id }); + const cards = await ReactiveCache.getCards({ parentId: this._id }); for (const card of cards) { await funct(card); } @@ -2026,7 +2033,7 @@ Cards.helpers({ let sortIndex = 0; if (!swimlaneId) { - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); swimlaneId = board.getDefaultSwimline()._id; } let parentElementDom = $(`#swimlane-${swimlaneId}`).get(0); @@ -2049,7 +2056,7 @@ Cards.helpers({ boardId = boardId || this.boardId; swimlaneId = swimlaneId || this.swimlaneId; if (!swimlaneId) { - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); swimlaneId = board.getDefaultSwimline()._id; } listId = listId || this.listId; @@ -2072,7 +2079,7 @@ Cards.helpers({ } if (this.boardId !== boardId) { - const oldBoard = ReactiveCache.getBoard(this.boardId); + const oldBoard = await ReactiveCache.getBoard(this.boardId); const oldBoardLabels = oldBoard.labels; const oldCardLabels = _.pluck( _.filter(oldBoardLabels, label => { @@ -2081,7 +2088,7 @@ Cards.helpers({ 'name', ); - const newBoard = ReactiveCache.getBoard(boardId); + const newBoard = await ReactiveCache.getBoard(boardId); const newBoardLabels = newBoard.labels; const newCardLabelIds = _.pluck( _.filter(newBoardLabels, label => { @@ -2097,7 +2104,7 @@ Cards.helpers({ cardNumber: newCardNumber }); - mutatedFields.customFields = this.mapCustomFieldsToBoard(newBoard._id); + mutatedFields.customFields = await this.mapCustomFieldsToBoard(newBoard._id); } await Cards.updateAsync(this._id, { $set: mutatedFields }); @@ -2404,33 +2411,34 @@ Cards.helpers({ //FUNCTIONS FOR creation of Activities -function updateActivities(doc, fieldNames, modifier) { +async function updateActivities(doc, fieldNames, modifier) { if (_.contains(fieldNames, 'labelIds') && _.contains(fieldNames, 'boardId')) { - ReactiveCache.getActivities({ + const activities = await ReactiveCache.getActivities({ activityType: 'addedLabel', cardId: doc._id, - }).forEach(a => { + }); + for (const a of activities) { const lidx = doc.labelIds.indexOf(a.labelId); if (lidx !== -1 && modifier.$set.labelIds.length > lidx) { - Activities.update(a._id, { + await Activities.updateAsync(a._id, { $set: { labelId: modifier.$set.labelIds[doc.labelIds.indexOf(a.labelId)], boardId: modifier.$set.boardId, }, }); } else { - Activities.remove(a._id); + await Activities.removeAsync(a._id); } - }); + } } else if (_.contains(fieldNames, 'boardId')) { - Activities.remove({ + await Activities.removeAsync({ activityType: 'addedLabel', cardId: doc._id, }); } } -function cardMove( +async function cardMove( userId, doc, fieldNames, @@ -2439,15 +2447,18 @@ function cardMove( oldBoardId, ) { if (_.contains(fieldNames, 'boardId') && doc.boardId !== oldBoardId) { - Activities.insert({ + const newBoard = await ReactiveCache.getBoard(doc.boardId); + const oldBoard = await ReactiveCache.getBoard(oldBoardId); + const swimlane = await ReactiveCache.getSwimlane(doc.swimlaneId); + await Activities.insertAsync({ userId, activityType: 'moveCardBoard', - boardName: ReactiveCache.getBoard(doc.boardId).title, + boardName: newBoard.title, boardId: doc.boardId, oldBoardId, - oldBoardName: ReactiveCache.getBoard(oldBoardId).title, + oldBoardName: oldBoard.title, cardId: doc._id, - swimlaneName: ReactiveCache.getSwimlane(doc.swimlaneId).title, + swimlaneName: swimlane.title, swimlaneId: doc.swimlaneId, oldSwimlaneId, }); @@ -2455,40 +2466,43 @@ function cardMove( (_.contains(fieldNames, 'listId') && doc.listId !== oldListId) || (_.contains(fieldNames, 'swimlaneId') && doc.swimlaneId !== oldSwimlaneId) ) { - Activities.insert({ + const list = await ReactiveCache.getList(doc.listId); + const swimlane = await ReactiveCache.getSwimlane(doc.swimlaneId); + await Activities.insertAsync({ userId, oldListId, activityType: 'moveCard', - listName: ReactiveCache.getList(doc.listId).title, + listName: list.title, listId: doc.listId, boardId: doc.boardId, cardId: doc._id, cardTitle: doc.title, - swimlaneName: ReactiveCache.getSwimlane(doc.swimlaneId).title, + swimlaneName: swimlane.title, swimlaneId: doc.swimlaneId, oldSwimlaneId, }); } } -function cardState(userId, doc, fieldNames) { +async function cardState(userId, doc, fieldNames) { if (_.contains(fieldNames, 'archived')) { + const list = await ReactiveCache.getList(doc.listId); if (doc.archived) { - Activities.insert({ + await Activities.insertAsync({ userId, activityType: 'archivedCard', - listName: ReactiveCache.getList(doc.listId).title, + listName: list.title, boardId: doc.boardId, listId: doc.listId, cardId: doc._id, swimlaneId: doc.swimlaneId, }); } else { - Activities.insert({ + await Activities.insertAsync({ userId, activityType: 'restoredCard', boardId: doc.boardId, - listName: ReactiveCache.getList(doc.listId).title, + listName: list.title, listId: doc.listId, cardId: doc._id, swimlaneId: doc.swimlaneId, @@ -2497,15 +2511,16 @@ function cardState(userId, doc, fieldNames) { } } -function cardMembers(userId, doc, fieldNames, modifier) { +async function cardMembers(userId, doc, fieldNames, modifier) { if (!_.contains(fieldNames, 'members')) return; let memberId; // Say hello to the new member if (modifier.$addToSet && modifier.$addToSet.members) { memberId = modifier.$addToSet.members; - const username = ReactiveCache.getUser(memberId).username; + const user = await ReactiveCache.getUser(memberId); + const username = user.username; if (!_.contains(doc.members, memberId)) { - Activities.insert({ + await Activities.insertAsync({ userId, username, activityType: 'joinMember', @@ -2521,10 +2536,11 @@ function cardMembers(userId, doc, fieldNames, modifier) { // Say goodbye to the former member if (modifier.$pull && modifier.$pull.members) { memberId = modifier.$pull.members; - const username = ReactiveCache.getUser(memberId).username; + const user = await ReactiveCache.getUser(memberId); + const username = user.username; // Check that the former member is member of the card if (_.contains(doc.members, memberId)) { - Activities.insert({ + await Activities.insertAsync({ userId, username, activityType: 'unjoinMember', @@ -2538,15 +2554,16 @@ function cardMembers(userId, doc, fieldNames, modifier) { } } -function cardAssignees(userId, doc, fieldNames, modifier) { +async function cardAssignees(userId, doc, fieldNames, modifier) { if (!_.contains(fieldNames, 'assignees')) return; let assigneeId; // Say hello to the new assignee if (modifier.$addToSet && modifier.$addToSet.assignees) { assigneeId = modifier.$addToSet.assignees; - const username = ReactiveCache.getUser(assigneeId).username; + const user = await ReactiveCache.getUser(assigneeId); + const username = user.username; if (!_.contains(doc.assignees, assigneeId)) { - Activities.insert({ + await Activities.insertAsync({ userId, username, activityType: 'joinAssignee', @@ -2561,10 +2578,11 @@ function cardAssignees(userId, doc, fieldNames, modifier) { // Say goodbye to the former assignee if (modifier.$pull && modifier.$pull.assignees) { assigneeId = modifier.$pull.assignees; - const username = ReactiveCache.getUser(assigneeId).username; + const user = await ReactiveCache.getUser(assigneeId); + const username = user.username; // Check that the former assignee is assignee of the card if (_.contains(doc.assignees, assigneeId)) { - Activities.insert({ + await Activities.insertAsync({ userId, username, activityType: 'unjoinAssignee', @@ -2668,16 +2686,18 @@ function cardCustomFields(userId, doc, fieldNames, modifier) { } } -function cardCreation(userId, doc) { - Activities.insert({ +async function cardCreation(userId, doc) { + const list = await ReactiveCache.getList(doc.listId); + const swimlane = await ReactiveCache.getSwimlane(doc.swimlaneId); + await Activities.insertAsync({ userId, activityType: 'createCard', boardId: doc.boardId, - listName: ReactiveCache.getList(doc.listId).title, + listName: list.title, listId: doc.listId, cardId: doc._id, cardTitle: doc.title, - swimlaneName: ReactiveCache.getSwimlane(doc.swimlaneId).title, + swimlaneName: swimlane.title, swimlaneId: doc.swimlaneId, }); } @@ -2722,13 +2742,15 @@ function cardRemover(userId, doc) { }); } -const findDueCards = days => { - const seekDue = ($from, $to, activityType) => { - ReactiveCache.getCards({ +const findDueCards = async days => { + const seekDue = async ($from, $to, activityType) => { + const cards = await ReactiveCache.getCards({ archived: false, dueAt: { $gte: $from, $lt: $to }, - }).forEach(card => { - const username = ReactiveCache.getUser(card.userId).username; + }); + for (const card of cards) { + const user = await ReactiveCache.getUser(card.userId); + const username = user.username; const activity = { userId: card.userId, username, @@ -2740,15 +2762,15 @@ const findDueCards = days => { timeValue: card.dueAt, swimlaneId: card.swimlaneId, }; - Activities.insert(activity); - }); + await Activities.insertAsync(activity); + } }; const now = new Date(), aday = 3600 * 24 * 1e3, then = day => new Date(now.setHours(0, 0, 0, 0) + day * aday); if (!days) return; if (!days.map) days = [days]; - days.map(day => { + for (const day of days) { let args = []; if (day === 0) { args = [then(0), then(1), 'duenow']; @@ -2757,8 +2779,8 @@ const findDueCards = days => { } else { args = [then(day), now, 'pastdue']; } - seekDue(...args); - }); + await seekDue(...args); + } }; const addCronJob = _.debounce( Meteor.bindEnvironment(function findDueCardsDebounced() { @@ -2801,14 +2823,14 @@ const addCronJob = _.debounce( if (Meteor.isServer) { Meteor.methods({ // Secure poker voting: only the caller's userId is modified - 'cards.pokerVote'(cardId, state) { + async 'cards.pokerVote'(cardId, state) { check(cardId, String); if (state !== undefined && state !== null) check(state, String); if (!this.userId) throw new Meteor.Error('not-authorized'); - const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId); + const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId); if (!card) throw new Meteor.Error('not-found'); - const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId); + const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId); if (!board) throw new Meteor.Error('not-found'); const isMember = allowIsBoardMember(this.userId, board); @@ -2820,19 +2842,19 @@ if (Meteor.isServer) { let mod = card.setPoker(this.userId, state); if (!mod || typeof mod !== 'object') mod = {}; mod.$set = Object.assign({}, mod.$set, { modifiedAt: new Date(), dateLastActivity: new Date() }); - return Cards.update({ _id: cardId }, mod); + return await Cards.updateAsync({ _id: cardId }, mod); }, // Configure planning poker on a card (members only) - 'cards.setPokerQuestion'(cardId, question, allowNonBoardMembers) { + async 'cards.setPokerQuestion'(cardId, question, allowNonBoardMembers) { check(cardId, String); check(question, Boolean); check(allowNonBoardMembers, Boolean); if (!this.userId) throw new Meteor.Error('not-authorized'); - const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId); + const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId); if (!card) throw new Meteor.Error('not-found'); - const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId); + const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId); if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized'); const modifier = { @@ -2846,96 +2868,96 @@ if (Meteor.isServer) { dateLastActivity: new Date(), }, }; - return Cards.update({ _id: cardId }, modifier); + return await Cards.updateAsync({ _id: cardId }, modifier); }, - 'cards.setPokerEnd'(cardId, end) { + async 'cards.setPokerEnd'(cardId, end) { check(cardId, String); check(end, Date); if (!this.userId) throw new Meteor.Error('not-authorized'); - const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId); + const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId); if (!card) throw new Meteor.Error('not-found'); - const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId); + const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId); if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized'); const modifier = { $set: { 'poker.end': end, modifiedAt: new Date(), dateLastActivity: new Date() }, }; - return Cards.update({ _id: cardId }, modifier); + return await Cards.updateAsync({ _id: cardId }, modifier); }, - 'cards.unsetPokerEnd'(cardId) { + async 'cards.unsetPokerEnd'(cardId) { check(cardId, String); if (!this.userId) throw new Meteor.Error('not-authorized'); - const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId); + const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId); if (!card) throw new Meteor.Error('not-found'); - const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId); + const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId); if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized'); const modifier = { $unset: { 'poker.end': '' }, $set: { modifiedAt: new Date(), dateLastActivity: new Date() }, }; - return Cards.update({ _id: cardId }, modifier); + return await Cards.updateAsync({ _id: cardId }, modifier); }, - 'cards.unsetPoker'(cardId) { + async 'cards.unsetPoker'(cardId) { check(cardId, String); if (!this.userId) throw new Meteor.Error('not-authorized'); - const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId); + const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId); if (!card) throw new Meteor.Error('not-found'); - const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId); + const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId); if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized'); const modifier = { $unset: { poker: '' }, $set: { modifiedAt: new Date(), dateLastActivity: new Date() }, }; - return Cards.update({ _id: cardId }, modifier); + return await Cards.updateAsync({ _id: cardId }, modifier); }, - 'cards.setPokerEstimation'(cardId, estimation) { + async 'cards.setPokerEstimation'(cardId, estimation) { check(cardId, String); check(estimation, Number); if (!this.userId) throw new Meteor.Error('not-authorized'); - const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId); + const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId); if (!card) throw new Meteor.Error('not-found'); - const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId); + const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId); if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized'); const modifier = { $set: { 'poker.estimation': estimation, modifiedAt: new Date(), dateLastActivity: new Date() }, }; - return Cards.update({ _id: cardId }, modifier); + return await Cards.updateAsync({ _id: cardId }, modifier); }, - 'cards.unsetPokerEstimation'(cardId) { + async 'cards.unsetPokerEstimation'(cardId) { check(cardId, String); if (!this.userId) throw new Meteor.Error('not-authorized'); - const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId); + const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId); if (!card) throw new Meteor.Error('not-found'); - const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId); + const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId); if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized'); const modifier = { $unset: { 'poker.estimation': '' }, $set: { modifiedAt: new Date(), dateLastActivity: new Date() }, }; - return Cards.update({ _id: cardId }, modifier); + return await Cards.updateAsync({ _id: cardId }, modifier); }, - 'cards.replayPoker'(cardId) { + async 'cards.replayPoker'(cardId) { check(cardId, String); if (!this.userId) throw new Meteor.Error('not-authorized'); - const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId); + const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId); if (!card) throw new Meteor.Error('not-found'); - const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId); + const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId); if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized'); // Reset all poker votes arrays @@ -2947,19 +2969,19 @@ if (Meteor.isServer) { }, $unset: { 'poker.end': '' }, }; - return Cards.update({ _id: cardId }, modifier); + return await Cards.updateAsync({ _id: cardId }, modifier); }, // Configure voting on a card (members only) - 'cards.setVoteQuestion'(cardId, question, publicVote, allowNonBoardMembers) { + async 'cards.setVoteQuestion'(cardId, question, publicVote, allowNonBoardMembers) { check(cardId, String); check(question, String); check(publicVote, Boolean); check(allowNonBoardMembers, Boolean); if (!this.userId) throw new Meteor.Error('not-authorized'); - const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId); + const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId); if (!card) throw new Meteor.Error('not-found'); - const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId); + const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId); if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized'); const modifier = { @@ -2975,66 +2997,66 @@ if (Meteor.isServer) { dateLastActivity: new Date(), }, }; - return Cards.update({ _id: cardId }, modifier); + return await Cards.updateAsync({ _id: cardId }, modifier); }, - 'cards.setVoteEnd'(cardId, end) { + async 'cards.setVoteEnd'(cardId, end) { check(cardId, String); check(end, Date); if (!this.userId) throw new Meteor.Error('not-authorized'); - const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId); + const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId); if (!card) throw new Meteor.Error('not-found'); - const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId); + const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId); if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized'); const modifier = { $set: { 'vote.end': end, modifiedAt: new Date(), dateLastActivity: new Date() }, }; - return Cards.update({ _id: cardId }, modifier); + return await Cards.updateAsync({ _id: cardId }, modifier); }, - 'cards.unsetVoteEnd'(cardId) { + async 'cards.unsetVoteEnd'(cardId) { check(cardId, String); if (!this.userId) throw new Meteor.Error('not-authorized'); - const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId); + const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId); if (!card) throw new Meteor.Error('not-found'); - const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId); + const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId); if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized'); const modifier = { $unset: { 'vote.end': '' }, $set: { modifiedAt: new Date(), dateLastActivity: new Date() }, }; - return Cards.update({ _id: cardId }, modifier); + return await Cards.updateAsync({ _id: cardId }, modifier); }, - 'cards.unsetVote'(cardId) { + async 'cards.unsetVote'(cardId) { check(cardId, String); if (!this.userId) throw new Meteor.Error('not-authorized'); - const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId); + const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId); if (!card) throw new Meteor.Error('not-found'); - const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId); + const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId); if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized'); const modifier = { $unset: { vote: '' }, $set: { modifiedAt: new Date(), dateLastActivity: new Date() }, }; - return Cards.update({ _id: cardId }, modifier); + return await Cards.updateAsync({ _id: cardId }, modifier); }, // Secure voting: only the caller can set/unset their vote; non-members can vote only when allowed - 'cards.vote'(cardId, forIt) { + async 'cards.vote'(cardId, forIt) { check(cardId, String); // forIt may be true (upvote), false (downvote), or null/undefined (clear) if (forIt !== undefined && forIt !== null) check(forIt, Boolean); if (!this.userId) throw new Meteor.Error('not-authorized'); - const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId); + const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId); if (!card) throw new Meteor.Error('not-found'); - const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId); + const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId); if (!board) throw new Meteor.Error('not-found'); const isMember = allowIsBoardMember(this.userId, board); @@ -3065,7 +3087,7 @@ if (Meteor.isServer) { }; } - return Cards.update({ _id: cardId }, modifier); + return await Cards.updateAsync({ _id: cardId }, modifier); }, /** copies a card * <li> this method is needed on the server because attachments can only be copied on the server (access to file system) @@ -3077,7 +3099,7 @@ if (Meteor.isServer) { * @param mergeCardValues this values into the copied card * @return the new card id */ - copyCard(cardId, boardId, swimlaneId, listId, insertAtTop, mergeCardValues) { + async copyCard(cardId, boardId, swimlaneId, listId, insertAtTop, mergeCardValues) { check(cardId, String); check(boardId, String); check(swimlaneId, String); @@ -3085,10 +3107,10 @@ if (Meteor.isServer) { check(insertAtTop, Boolean); check(mergeCardValues, Object); - const card = ReactiveCache.getCard(cardId); + const card = await ReactiveCache.getCard(cardId); Object.assign(card, mergeCardValues); - const sort = card.getSort(listId, swimlaneId, insertAtTop); + const sort = await card.getSort(listId, swimlaneId, insertAtTop); if (insertAtTop) { card.sort = sort - 1; } else @@ -3096,7 +3118,7 @@ if (Meteor.isServer) { card.sort = sort + 1; } - const ret = card.copy(boardId, swimlaneId, listId); + const ret = await card.copy(boardId, swimlaneId, listId); return ret; }, }); @@ -3119,40 +3141,40 @@ if (Meteor.isServer) { }); }); - Cards.after.insert((userId, doc) => { - cardCreation(userId, doc); + Cards.after.insert(async (userId, doc) => { + await cardCreation(userId, doc); // Track original position for new cards - Meteor.setTimeout(() => { - const card = Cards.findOne(doc._id); + Meteor.setTimeout(async () => { + const card = await Cards.findOneAsync(doc._id); if (card) { card.trackOriginalPosition(); } }, 100); }); // New activity for card (un)archivage - Cards.after.update((userId, doc, fieldNames) => { - cardState(userId, doc, fieldNames); + Cards.after.update(async (userId, doc, fieldNames) => { + await cardState(userId, doc, fieldNames); }); //New activity for card moves - Cards.after.update(function(userId, doc, fieldNames) { + Cards.after.update(async function(userId, doc, fieldNames) { const oldListId = this.previous.listId; const oldSwimlaneId = this.previous.swimlaneId; const oldBoardId = this.previous.boardId; - cardMove(userId, doc, fieldNames, oldListId, oldSwimlaneId, oldBoardId); + await cardMove(userId, doc, fieldNames, oldListId, oldSwimlaneId, oldBoardId); }); // Add a new activity if we add or remove a member to the card - Cards.before.update((userId, doc, fieldNames, modifier) => { - cardMembers(userId, doc, fieldNames, modifier); - updateActivities(doc, fieldNames, modifier); + Cards.before.update(async (userId, doc, fieldNames, modifier) => { + await cardMembers(userId, doc, fieldNames, modifier); + await updateActivities(doc, fieldNames, modifier); }); // Add a new activity if we add or remove a assignee to the card - Cards.before.update((userId, doc, fieldNames, modifier) => { - cardAssignees(userId, doc, fieldNames, modifier); - updateActivities(doc, fieldNames, modifier); + Cards.before.update(async (userId, doc, fieldNames, modifier) => { + await cardAssignees(userId, doc, fieldNames, modifier); + await updateActivities(doc, fieldNames, modifier); }); // Add a new activity if we add or remove a label to the card @@ -3166,7 +3188,7 @@ if (Meteor.isServer) { }); // Add a new activity if modify time related field like dueAt startAt etc - Cards.before.update((userId, doc, fieldNames, modifier) => { + Cards.before.update(async (userId, doc, fieldNames, modifier) => { const dla = 'dateLastActivity'; const fields = fieldNames.filter(name => name !== dla); const timingaction = ['receivedAt', 'dueAt', 'startAt', 'endAt']; @@ -3176,15 +3198,15 @@ if (Meteor.isServer) { const value = modifier.$set[action]; const oldvalue = doc[action] || ''; const activityType = `a-${action}`; - const card = ReactiveCache.getCard(doc._id); - const list = card.list(); + const card = await ReactiveCache.getCard(doc._id); + const list = await card.list(); if (list) { // change list modifiedAt, when user modified the key values in // timingaction array, if it's endAt, put the modifiedAt of list // back to one year ago for sorting purpose const modifiedAt = add(now(), -1, 'year').toISOString(); const boardId = list.boardId; - Lists.direct.update( + await Lists.direct.updateAsync( { _id: list._id, }, @@ -3196,7 +3218,8 @@ if (Meteor.isServer) { }, ); } - const username = ReactiveCache.getUser(userId).username; + const user = await ReactiveCache.getUser(userId); + const username = user.username; const activity = { userId, username, @@ -3210,7 +3233,7 @@ if (Meteor.isServer) { listId: card.listId, swimlaneId: card.swimlaneId, }; - Activities.insert(activity); + await Activities.insertAsync(activity); } }); // Remove all activities associated with a card if we remove the card @@ -3235,18 +3258,19 @@ if (Meteor.isServer) { JsonRoutes.add( 'GET', '/api/boards/:boardId/swimlanes/:swimlaneId/cards', - function(req, res) { + async function(req, res) { const paramBoardId = req.params.boardId; const paramSwimlaneId = req.params.swimlaneId; Authentication.checkBoardAccess(req.userId, paramBoardId); + const cards = await ReactiveCache.getCards({ + boardId: paramBoardId, + swimlaneId: paramSwimlaneId, + archived: false, + }, + { sort: ['sort'] }); JsonRoutes.sendResult(res, { code: 200, - data: ReactiveCache.getCards({ - boardId: paramBoardId, - swimlaneId: paramSwimlaneId, - archived: false, - }, - { sort: ['sort'] }).map(function(doc) { + data: cards.map(function(doc) { return { _id: doc._id, title: doc.title, @@ -3276,21 +3300,22 @@ if (Meteor.isServer) { * title: string, * description: string}] */ - JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId/cards', function( + JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId/cards', async function( req, res, ) { const paramBoardId = req.params.boardId; const paramListId = req.params.listId; Authentication.checkBoardAccess(req.userId, paramBoardId); + const cards = await ReactiveCache.getCards({ + boardId: paramBoardId, + listId: paramListId, + archived: false, + }, + { sort: ['sort'] }); JsonRoutes.sendResult(res, { code: 200, - data: ReactiveCache.getCards({ - boardId: paramBoardId, - listId: paramListId, - archived: false, - }, - { sort: ['sort'] }).map(function(doc) { + data: cards.map(function(doc) { return { _id: doc._id, title: doc.title, @@ -3317,9 +3342,9 @@ if (Meteor.isServer) { JsonRoutes.add( 'GET', '/api/cards/:cardId', - function(req, res) { + async function(req, res) { const paramCardId = req.params.cardId; - card = ReactiveCache.getCard(paramCardId) + const card = await ReactiveCache.getCard(paramCardId); Authentication.checkBoardAccess(req.userId, card.boardId); JsonRoutes.sendResult(res, { code: 200, @@ -3340,14 +3365,14 @@ if (Meteor.isServer) { JsonRoutes.add( 'GET', '/api/boards/:boardId/lists/:listId/cards/:cardId', - function(req, res) { + async function(req, res) { const paramBoardId = req.params.boardId; const paramListId = req.params.listId; const paramCardId = req.params.cardId; Authentication.checkBoardAccess(req.userId, paramBoardId); JsonRoutes.sendResult(res, { code: 200, - data: ReactiveCache.getCard({ + data: await ReactiveCache.getCard({ _id: paramCardId, listId: paramListId, boardId: paramBoardId, @@ -3372,7 +3397,7 @@ if (Meteor.isServer) { * @param {string} [assignees] the assignee IDs list of the new card * @return_type {_id: string} */ - JsonRoutes.add('POST', '/api/boards/:boardId/lists/:listId/cards', function( + JsonRoutes.add('POST', '/api/boards/:boardId/lists/:listId/cards', async function( req, res, ) { @@ -3380,7 +3405,7 @@ if (Meteor.isServer) { Authentication.checkLoggedIn(req.userId); const paramBoardId = req.params.boardId; // Check user has permission to add card to the board - const board = ReactiveCache.getBoard(paramBoardId); + const board = await ReactiveCache.getBoard(paramBoardId); const addPermission = allowIsBoardMemberCommentOnly(req.userId, board); Authentication.checkAdminOrCondition(req.userId, addPermission); const paramListId = req.params.listId; @@ -3388,26 +3413,27 @@ if (Meteor.isServer) { const nextCardNumber = board.getNextCardNumber(); let customFieldsArr = []; + const customFields = await ReactiveCache.getCustomFields({'boardIds': paramBoardId}); _.forEach( - ReactiveCache.getCustomFields({'boardIds': paramBoardId}), + customFields, function (field) { if (field.automaticallyOnCard || field.alwaysOnCard) customFieldsArr.push({ _id: field._id, value: null }); }, ); - const currentCards = ReactiveCache.getCards( + const currentCards = await ReactiveCache.getCards( { listId: paramListId, archived: false, }, { sort: ['sort'] }, ); - const check = ReactiveCache.getUser(req.body.authorId); + const checkUser = await ReactiveCache.getUser(req.body.authorId); const members = req.body.members; const assignees = req.body.assignees; - if (typeof check !== 'undefined') { - const id = Cards.direct.insert({ + if (typeof checkUser !== 'undefined') { + const id = await Cards.direct.insertAsync({ title: req.body.title, boardId: paramBoardId, listId: paramListId, @@ -3428,8 +3454,8 @@ if (Meteor.isServer) { }, }); - const card = ReactiveCache.getCard(id); - cardCreation(req.body.authorId, card); + const card = await ReactiveCache.getCard(id); + await cardCreation(req.body.authorId, card); } else { JsonRoutes.sendResult(res, { code: 401, @@ -3444,20 +3470,21 @@ if (Meteor.isServer) { * @param {string} boardId the board ID * @return_type {board_cards_count: integer} */ -JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function( +JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', async function( req, res, ) { try { const paramBoardId = req.params.boardId; Authentication.checkBoardAccess(req.userId, paramBoardId); + const cards = await ReactiveCache.getCards({ + boardId: paramBoardId, + archived: false, + }); JsonRoutes.sendResult(res, { code: 200, data: { - board_cards_count: ReactiveCache.getCards({ - boardId: paramBoardId, - archived: false, - }).length, + board_cards_count: cards.length, } }); } catch (error) { @@ -3476,7 +3503,7 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function( * @param {string} listId the List ID * @return_type {list_cards_count: integer} */ - JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId/cards_count', function( + JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId/cards_count', async function( req, res, ) { @@ -3484,14 +3511,15 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function( const paramBoardId = req.params.boardId; const paramListId = req.params.listId; Authentication.checkBoardAccess(req.userId, paramBoardId); + const cards = await ReactiveCache.getCards({ + boardId: paramBoardId, + listId: paramListId, + archived: false, + }); JsonRoutes.sendResult(res, { code: 200, data: { - list_cards_count: ReactiveCache.getCards({ - boardId: paramBoardId, - listId: paramListId, - archived: false, - }).length, + list_cards_count: cards.length, } }); } catch (error) { @@ -3560,7 +3588,7 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function( JsonRoutes.add( 'PUT', '/api/boards/:boardId/lists/:listId/cards/:cardId', - function(req, res) { + async function(req, res) { const paramBoardId = req.params.boardId; const paramCardId = req.params.cardId; const paramListId = req.params.listId; @@ -3913,8 +3941,8 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function( ); updated = true; - const card = ReactiveCache.getCard(paramCardId); - cardMove( + const card = await ReactiveCache.getCard(paramCardId); + await cardMove( req.body.authorId, card, { @@ -3928,7 +3956,7 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function( Authentication.checkBoardWriteAccess(req.userId, newBoardId); // Validate that the destination list exists and belongs to the destination board - const destList = ReactiveCache.getList({ + const destList = await ReactiveCache.getList({ _id: newListId, boardId: newBoardId, archived: false, @@ -3942,7 +3970,7 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function( } // Validate that the destination swimlane exists and belongs to the destination board - const destSwimlane = ReactiveCache.getSwimlane({ + const destSwimlane = await ReactiveCache.getSwimlane({ _id: newSwimlaneId, boardId: newBoardId, archived: false, @@ -3956,7 +3984,7 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function( } // Move the card to the new board, swimlane, and list - Cards.direct.update( + await Cards.direct.updateAsync( { _id: paramCardId, listId: paramListId, @@ -3973,8 +4001,8 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function( ); updated = true; - const card = ReactiveCache.getCard(paramCardId); - cardMove( + const card = await ReactiveCache.getCard(paramCardId); + await cardMove( req.userId, card, ['boardId', 'swimlaneId', 'listId'], @@ -4037,13 +4065,13 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function( JsonRoutes.add( 'DELETE', '/api/boards/:boardId/lists/:listId/cards/:cardId', - function(req, res) { + async function(req, res) { const paramBoardId = req.params.boardId; const paramListId = req.params.listId; const paramCardId = req.params.cardId; Authentication.checkBoardWriteAccess(req.userId, paramBoardId); - const card = ReactiveCache.getCard(paramCardId); + const card = await ReactiveCache.getCard(paramCardId); Cards.direct.remove({ _id: paramCardId, listId: paramListId, @@ -4075,14 +4103,14 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function( JsonRoutes.add( 'GET', '/api/boards/:boardId/cardsByCustomField/:customFieldId/:customFieldValue', - function(req, res) { + async function(req, res) { const paramBoardId = req.params.boardId; const paramCustomFieldId = req.params.customFieldId; const paramCustomFieldValue = req.params.customFieldValue; Authentication.checkBoardAccess(req.userId, paramBoardId); JsonRoutes.sendResult(res, { code: 200, - data: ReactiveCache.getCards({ + data: await ReactiveCache.getCards({ boardId: paramBoardId, customFields: { $elemMatch: { @@ -4111,14 +4139,14 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function( JsonRoutes.add( 'POST', '/api/boards/:boardId/lists/:listId/cards/:cardId/customFields/:customFieldId', - function(req, res) { + async function(req, res) { const paramBoardId = req.params.boardId; const paramCardId = req.params.cardId; const paramListId = req.params.listId; const paramCustomFieldId = req.params.customFieldId; const paramCustomFieldValue = req.body.value; Authentication.checkBoardWriteAccess(req.userId, paramBoardId); - const card = ReactiveCache.getCard({ + const card = await ReactiveCache.getCard({ _id: paramCardId, listId: paramListId, boardId: paramBoardId, @@ -4174,7 +4202,7 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function( const paramCardId = req.params.cardId; const paramListId = req.params.listId; Authentication.checkBoardWriteAccess(req.userId, paramBoardId); - const card = ReactiveCache.getCard({ + const card = await ReactiveCache.getCard({ _id: paramCardId, listId: paramListId, boardId: paramBoardId, @@ -4213,7 +4241,7 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function( const paramCardId = req.params.cardId; const paramListId = req.params.listId; Authentication.checkBoardWriteAccess(req.userId, paramBoardId); - const card = ReactiveCache.getCard({ + const card = await ReactiveCache.getCard({ _id: paramCardId, listId: paramListId, boardId: paramBoardId, diff --git a/models/checklistItems.js b/models/checklistItems.js index 7b55ac9a7..a1d1f24b7 100644 --- a/models/checklistItems.js +++ b/models/checklistItems.js @@ -69,17 +69,17 @@ ChecklistItems.attachSchema( ); ChecklistItems.allow({ - insert(userId, doc) { + async insert(userId, doc) { // ReadOnly users cannot create checklist items - return allowIsBoardMemberWithWriteAccessByCard(userId, ReactiveCache.getCard(doc.cardId)); + return allowIsBoardMemberWithWriteAccessByCard(userId, await ReactiveCache.getCard(doc.cardId)); }, - update(userId, doc) { + async update(userId, doc) { // ReadOnly users cannot edit checklist items - return allowIsBoardMemberWithWriteAccessByCard(userId, ReactiveCache.getCard(doc.cardId)); + return allowIsBoardMemberWithWriteAccessByCard(userId, await ReactiveCache.getCard(doc.cardId)); }, - remove(userId, doc) { + async remove(userId, doc) { // ReadOnly users cannot delete checklist items - return allowIsBoardMemberWithWriteAccessByCard(userId, ReactiveCache.getCard(doc.cardId)); + return allowIsBoardMemberWithWriteAccessByCard(userId, await ReactiveCache.getCard(doc.cardId)); }, fetch: ['userId', 'cardId'], }); @@ -104,7 +104,8 @@ ChecklistItems.helpers({ return await ChecklistItems.updateAsync(this._id, { $set: { isFinished: !this.isFinished } }); }, async move(checklistId, sortIndex) { - const cardId = ReactiveCache.getChecklist(checklistId).cardId; + const checklist = await ReactiveCache.getChecklist(checklistId); + const cardId = checklist.cardId; return await ChecklistItems.updateAsync(this._id, { $set: { cardId, checklistId, sort: sortIndex }, }); @@ -112,8 +113,8 @@ ChecklistItems.helpers({ }); // Activities helper -function itemCreation(userId, doc) { - const card = ReactiveCache.getCard(doc.cardId); +async function itemCreation(userId, doc) { + const card = await ReactiveCache.getCard(doc.cardId); const boardId = card.boardId; Activities.insert({ userId, @@ -134,8 +135,8 @@ function itemRemover(userId, doc) { }); } -function publishCheckActivity(userId, doc) { - const card = ReactiveCache.getCard(doc.cardId); +async function publishCheckActivity(userId, doc) { + const card = await ReactiveCache.getCard(doc.cardId); const boardId = card.boardId; let activityType; if (doc.isFinished) { @@ -157,11 +158,11 @@ function publishCheckActivity(userId, doc) { Activities.insert(act); } -function publishChekListCompleted(userId, doc) { - const card = ReactiveCache.getCard(doc.cardId); +async function publishChekListCompleted(userId, doc) { + const card = await ReactiveCache.getCard(doc.cardId); const boardId = card.boardId; const checklistId = doc.checklistId; - const checkList = ReactiveCache.getChecklist(checklistId); + const checkList = await ReactiveCache.getChecklist(checklistId); if (checkList.isFinished()) { const act = { userId, @@ -177,11 +178,11 @@ function publishChekListCompleted(userId, doc) { } } -function publishChekListUncompleted(userId, doc) { - const card = ReactiveCache.getCard(doc.cardId); +async function publishChekListUncompleted(userId, doc) { + const card = await ReactiveCache.getCard(doc.cardId); const boardId = card.boardId; const checklistId = doc.checklistId; - const checkList = ReactiveCache.getChecklist(checklistId); + const checkList = await ReactiveCache.getChecklist(checklistId); // BUGS in IFTTT Rules: https://github.com/wekan/wekan/issues/1972 // Currently in checklist all are set as uncompleted/not checked, // IFTTT Rule does not move card to other list. @@ -218,22 +219,22 @@ if (Meteor.isServer) { await ChecklistItems._collection.createIndexAsync({ cardId: 1 }); }); - ChecklistItems.after.update((userId, doc, fieldNames) => { - publishCheckActivity(userId, doc); - publishChekListCompleted(userId, doc, fieldNames); + ChecklistItems.after.update(async (userId, doc, fieldNames) => { + await publishCheckActivity(userId, doc); + await publishChekListCompleted(userId, doc, fieldNames); }); - ChecklistItems.before.update((userId, doc, fieldNames) => { - publishChekListUncompleted(userId, doc, fieldNames); + ChecklistItems.before.update(async (userId, doc, fieldNames) => { + await publishChekListUncompleted(userId, doc, fieldNames); }); - ChecklistItems.after.insert((userId, doc) => { - itemCreation(userId, doc); + ChecklistItems.after.insert(async (userId, doc) => { + await itemCreation(userId, doc); }); - ChecklistItems.before.remove((userId, doc) => { + ChecklistItems.before.remove(async (userId, doc) => { itemRemover(userId, doc); - const card = ReactiveCache.getCard(doc.cardId); + const card = await ReactiveCache.getCard(doc.cardId); const boardId = card.boardId; Activities.insert({ userId, @@ -264,15 +265,15 @@ if (Meteor.isServer) { JsonRoutes.add( 'GET', '/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId', - function(req, res) { + async function(req, res) { const paramBoardId = req.params.boardId; const paramCardId = req.params.cardId; const paramChecklistId = req.params.checklistId; const paramItemId = req.params.itemId; Authentication.checkBoardAccess(req.userId, paramBoardId); - const checklistItem = ReactiveCache.getChecklistItem(paramItemId); + const checklistItem = await ReactiveCache.getChecklistItem(paramItemId); if (checklistItem && checklistItem.cardId === paramCardId && checklistItem.checklistId === paramChecklistId) { - const card = ReactiveCache.getCard(checklistItem.cardId); + const card = await ReactiveCache.getCard(checklistItem.cardId); if (card && card.boardId === paramBoardId) { JsonRoutes.sendResult(res, { code: 200, @@ -305,17 +306,17 @@ if (Meteor.isServer) { JsonRoutes.add( 'POST', '/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items', - function(req, res) { + async function(req, res) { const paramBoardId = req.params.boardId; const paramChecklistId = req.params.checklistId; const paramCardId = req.params.cardId; Authentication.checkBoardAccess(req.userId, paramBoardId); - const checklist = ReactiveCache.getChecklist({ + const checklist = await ReactiveCache.getChecklist({ _id: paramChecklistId, cardId: paramCardId, }); if (checklist) { - const card = ReactiveCache.getCard(paramCardId); + const card = await ReactiveCache.getCard(paramCardId); if (card && card.boardId === paramBoardId) { const id = ChecklistItems.insert({ cardId: paramCardId, @@ -359,21 +360,21 @@ if (Meteor.isServer) { JsonRoutes.add( 'PUT', '/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId', - function(req, res) { + async function(req, res) { const paramBoardId = req.params.boardId; const paramCardId = req.params.cardId; const paramChecklistId = req.params.checklistId; const paramItemId = req.params.itemId; Authentication.checkBoardAccess(req.userId, paramBoardId); - const checklistItem = ReactiveCache.getChecklistItem(paramItemId); + const checklistItem = await ReactiveCache.getChecklistItem(paramItemId); if (!checklistItem || checklistItem.cardId !== paramCardId || checklistItem.checklistId !== paramChecklistId) { JsonRoutes.sendResult(res, { code: 404, }); return; } - const card = ReactiveCache.getCard(checklistItem.cardId); + const card = await ReactiveCache.getCard(checklistItem.cardId); if (!card || card.boardId !== paramBoardId) { JsonRoutes.sendResult(res, { code: 404, @@ -427,21 +428,21 @@ if (Meteor.isServer) { JsonRoutes.add( 'DELETE', '/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId', - function(req, res) { + async function(req, res) { const paramBoardId = req.params.boardId; const paramCardId = req.params.cardId; const paramChecklistId = req.params.checklistId; const paramItemId = req.params.itemId; Authentication.checkBoardAccess(req.userId, paramBoardId); - const checklistItem = ReactiveCache.getChecklistItem(paramItemId); + const checklistItem = await ReactiveCache.getChecklistItem(paramItemId); if (!checklistItem || checklistItem.cardId !== paramCardId || checklistItem.checklistId !== paramChecklistId) { JsonRoutes.sendResult(res, { code: 404, }); return; } - const card = ReactiveCache.getCard(checklistItem.cardId); + const card = await ReactiveCache.getCard(checklistItem.cardId); if (!card || card.boardId !== paramBoardId) { JsonRoutes.sendResult(res, { code: 404, diff --git a/models/checklists.js b/models/checklists.js index 216524f2e..aa0da7b05 100644 --- a/models/checklists.js +++ b/models/checklists.js @@ -88,19 +88,18 @@ Checklists.attachSchema( ); Checklists.helpers({ - copy(newCardId) { + async copy(newCardId) { let copyObj = Object.assign({}, this); delete copyObj._id; copyObj.cardId = newCardId; const newChecklistId = Checklists.insert(copyObj); - ReactiveCache.getChecklistItems({ checklistId: this._id }).forEach(function( - item, - ) { + const items = await ReactiveCache.getChecklistItems({ checklistId: this._id }); + for (const item of items) { item._id = null; item.checklistId = newChecklistId; item.cardId = newCardId; ChecklistItems.insert(item); - }); + } }, itemCount() { @@ -151,19 +150,20 @@ Checklists.helpers({ return ret; }, async checkAllItems() { - const checkItems = ReactiveCache.getChecklistItems({ checklistId: this._id }); + const checkItems = await ReactiveCache.getChecklistItems({ checklistId: this._id }); for (const item of checkItems) { await item.check(); } }, async uncheckAllItems() { - const checkItems = ReactiveCache.getChecklistItems({ checklistId: this._id }); + const checkItems = await ReactiveCache.getChecklistItems({ checklistId: this._id }); for (const item of checkItems) { await item.uncheck(); } }, - itemIndex(itemId) { - const items = ReactiveCache.getChecklist({ _id: this._id }).items; + async itemIndex(itemId) { + const checklist = await ReactiveCache.getChecklist({ _id: this._id }); + const items = checklist.items; return _.pluck(items, '_id').indexOf(itemId); }, @@ -196,17 +196,17 @@ Checklists.helpers({ }); Checklists.allow({ - insert(userId, doc) { + async insert(userId, doc) { // ReadOnly users cannot create checklists - return allowIsBoardMemberWithWriteAccessByCard(userId, ReactiveCache.getCard(doc.cardId)); + return allowIsBoardMemberWithWriteAccessByCard(userId, await ReactiveCache.getCard(doc.cardId)); }, - update(userId, doc) { + async update(userId, doc) { // ReadOnly users cannot edit checklists - return allowIsBoardMemberWithWriteAccessByCard(userId, ReactiveCache.getCard(doc.cardId)); + return allowIsBoardMemberWithWriteAccessByCard(userId, await ReactiveCache.getCard(doc.cardId)); }, - remove(userId, doc) { + async remove(userId, doc) { // ReadOnly users cannot delete checklists - return allowIsBoardMemberWithWriteAccessByCard(userId, ReactiveCache.getCard(doc.cardId)); + return allowIsBoardMemberWithWriteAccessByCard(userId, await ReactiveCache.getCard(doc.cardId)); }, fetch: ['userId', 'cardId'], }); @@ -221,22 +221,22 @@ Checklists.before.insert((userId, doc) => { if (Meteor.isServer) { Meteor.methods({ - moveChecklist(checklistId, newCardId) { + async moveChecklist(checklistId, newCardId) { check(checklistId, String); check(newCardId, String); - const checklist = ReactiveCache.getChecklist(checklistId); + const checklist = await ReactiveCache.getChecklist(checklistId); if (!checklist) { throw new Meteor.Error('checklist-not-found', 'Checklist not found'); } - const newCard = ReactiveCache.getCard(newCardId); + const newCard = await ReactiveCache.getCard(newCardId); if (!newCard) { throw new Meteor.Error('card-not-found', 'Target card not found'); } // Check permissions on both source and target cards - const sourceCard = ReactiveCache.getCard(checklist.cardId); + const sourceCard = await ReactiveCache.getCard(checklist.cardId); if (!allowIsBoardMemberByCard(this.userId, sourceCard)) { throw new Meteor.Error('not-authorized', 'Not authorized to move checklist from source card'); } @@ -245,22 +245,24 @@ if (Meteor.isServer) { } // Update activities - ReactiveCache.getActivities({ checklistId }).forEach(activity => { + const activities = await ReactiveCache.getActivities({ checklistId }); + for (const activity of activities) { Activities.update(activity._id, { $set: { cardId: newCardId, }, }); - }); + } // Update checklist items - ReactiveCache.getChecklistItems({ checklistId }).forEach(checklistItem => { + const checklistItems = await ReactiveCache.getChecklistItems({ checklistId }); + for (const checklistItem of checklistItems) { ChecklistItems.update(checklistItem._id, { $set: { cardId: newCardId, }, }); - }); + } // Update the checklist itself Checklists.update(checklistId, { @@ -278,8 +280,8 @@ if (Meteor.isServer) { await Checklists._collection.createIndexAsync({ cardId: 1, createdAt: 1 }); }); - Checklists.after.insert((userId, doc) => { - const card = ReactiveCache.getCard(doc.cardId); + Checklists.after.insert(async (userId, doc) => { + const card = await ReactiveCache.getCard(doc.cardId); Activities.insert({ userId, activityType: 'addChecklist', @@ -292,19 +294,19 @@ if (Meteor.isServer) { }); }); - Checklists.before.remove((userId, doc) => { - const activities = ReactiveCache.getActivities({ checklistId: doc._id }); - const card = ReactiveCache.getCard(doc.cardId); + Checklists.before.remove(async (userId, doc) => { + const activities = await ReactiveCache.getActivities({ checklistId: doc._id }); + const card = await ReactiveCache.getCard(doc.cardId); if (activities) { - activities.forEach(activity => { + for (const activity of activities) { Activities.remove(activity._id); - }); + } } Activities.insert({ userId, activityType: 'removeChecklist', cardId: doc.cardId, - boardId: ReactiveCache.getCard(doc.cardId).boardId, + boardId: (await ReactiveCache.getCard(doc.cardId)).boardId, checklistId: doc._id, checklistName: doc.title, listId: card.listId, @@ -326,13 +328,13 @@ if (Meteor.isServer) { JsonRoutes.add( 'GET', '/api/boards/:boardId/cards/:cardId/checklists', - function(req, res) { + async function(req, res) { const paramBoardId = req.params.boardId; const paramCardId = req.params.cardId; Authentication.checkBoardAccess(req.userId, paramBoardId); // Verify the card belongs to the board - const card = ReactiveCache.getCard({ + const card = await ReactiveCache.getCard({ _id: paramCardId, boardId: paramBoardId, }); @@ -344,7 +346,7 @@ if (Meteor.isServer) { return; } - const checklists = ReactiveCache.getChecklists({ cardId: paramCardId }).map(function( + const checklists = (await ReactiveCache.getChecklists({ cardId: paramCardId })).map(function( doc, ) { return { @@ -384,14 +386,14 @@ if (Meteor.isServer) { JsonRoutes.add( 'GET', '/api/boards/:boardId/cards/:cardId/checklists/:checklistId', - function(req, res) { + async function(req, res) { const paramBoardId = req.params.boardId; const paramChecklistId = req.params.checklistId; const paramCardId = req.params.cardId; Authentication.checkBoardAccess(req.userId, paramBoardId); // Verify the card belongs to the board - const card = ReactiveCache.getCard({ + const card = await ReactiveCache.getCard({ _id: paramCardId, boardId: paramBoardId, }); @@ -403,14 +405,14 @@ if (Meteor.isServer) { return; } - const checklist = ReactiveCache.getChecklist({ + const checklist = await ReactiveCache.getChecklist({ _id: paramChecklistId, cardId: paramCardId, }); if (checklist) { - checklist.items = ReactiveCache.getChecklistItems({ + checklist.items = (await ReactiveCache.getChecklistItems({ checklistId: checklist._id, - }).map(function(doc) { + })).map(function(doc) { return { _id: doc._id, title: doc.title, @@ -442,19 +444,19 @@ if (Meteor.isServer) { JsonRoutes.add( 'POST', '/api/boards/:boardId/cards/:cardId/checklists', - function(req, res) { + async function(req, res) { // Check user is logged in //Authentication.checkLoggedIn(req.userId); const paramBoardId = req.params.boardId; Authentication.checkBoardAccess(req.userId, paramBoardId); // Check user has permission to add checklist to the card - const board = ReactiveCache.getBoard(paramBoardId); + const board = await ReactiveCache.getBoard(paramBoardId); const addPermission = allowIsBoardMemberCommentOnly(req.userId, board); Authentication.checkAdminOrCondition(req.userId, addPermission); const paramCardId = req.params.cardId; // Verify the card belongs to the board - const card = ReactiveCache.getCard({ + const card = await ReactiveCache.getCard({ _id: paramCardId, boardId: paramBoardId, }); @@ -516,14 +518,14 @@ if (Meteor.isServer) { JsonRoutes.add( 'DELETE', '/api/boards/:boardId/cards/:cardId/checklists/:checklistId', - function(req, res) { + async function(req, res) { const paramBoardId = req.params.boardId; const paramCardId = req.params.cardId; const paramChecklistId = req.params.checklistId; Authentication.checkBoardAccess(req.userId, paramBoardId); // Verify the card belongs to the board - const card = ReactiveCache.getCard({ + const card = await ReactiveCache.getCard({ _id: paramCardId, boardId: paramBoardId, }); @@ -536,7 +538,7 @@ if (Meteor.isServer) { } // Verify the checklist exists and belongs to the card - const checklist = ReactiveCache.getChecklist({ + const checklist = await ReactiveCache.getChecklist({ _id: paramChecklistId, cardId: paramCardId, }); diff --git a/models/csvCreator.js b/models/csvCreator.js index fd3ee399a..85428ab9e 100644 --- a/models/csvCreator.js +++ b/models/csvCreator.js @@ -247,7 +247,7 @@ export class CsvCreator { this.swimlane = swimlaneId; } - createLists(csvData, boardId) { + async createLists(csvData, boardId) { let numOfCreatedLists = 0; for (let i = 1; i < csvData.length; i++) { const listToCreate = { @@ -256,7 +256,7 @@ export class CsvCreator { createdAt: this._now(), }; if (csvData[i][this.fieldIndex.stage]) { - const existingList = ReactiveCache.getLists({ + const existingList = await ReactiveCache.getLists({ title: csvData[i][this.fieldIndex.stage], boardId, }); @@ -279,7 +279,7 @@ export class CsvCreator { } } - createCards(csvData, boardId) { + async createCards(csvData, boardId) { for (let i = 1; i < csvData.length; i++) { const cardToCreate = { archived: false, @@ -321,7 +321,7 @@ export class CsvCreator { } // add the labels if (csvData[i][this.fieldIndex.labels]) { - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); for (const importedLabel of csvData[i][this.fieldIndex.labels].split( ' ', )) { @@ -388,15 +388,15 @@ export class CsvCreator { Meteor.settings.public && Meteor.settings.public.sandstorm; if (isSandstorm && currentBoardId) { - const currentBoard = ReactiveCache.getBoard(currentBoardId); + const currentBoard = await ReactiveCache.getBoard(currentBoardId); await currentBoard.archive(); } this.mapHeadertoCardFieldIndex(board[0]); const boardId = this.createBoard(board); - this.createLists(board, boardId); + await this.createLists(board, boardId); this.createSwimlanes(boardId); this.createCustomFields(boardId); - this.createCards(board, boardId); + await this.createCards(board, boardId); return boardId; } } diff --git a/models/customFields.js b/models/customFields.js index 5d1499e5f..7fa31d9ae 100644 --- a/models/customFields.js +++ b/models/customFields.js @@ -164,26 +164,26 @@ CustomFields.helpers({ }); CustomFields.allow({ - insert(userId, doc) { + async insert(userId, doc) { return allowIsAnyBoardMember( userId, - ReactiveCache.getBoards({ + await ReactiveCache.getBoards({ _id: { $in: doc.boardIds }, }), ); }, - update(userId, doc) { + async update(userId, doc) { return allowIsAnyBoardMember( userId, - ReactiveCache.getBoards({ + await ReactiveCache.getBoards({ _id: { $in: doc.boardIds }, }), ); }, - remove(userId, doc) { + async remove(userId, doc) { return allowIsAnyBoardMember( userId, - ReactiveCache.getBoards({ + await ReactiveCache.getBoards({ _id: { $in: doc.boardIds }, }), ); @@ -214,9 +214,9 @@ function customFieldDeletion(userId, doc) { // This has some bug, it does not show edited customField value at Outgoing Webhook, // instead it shows undefined, and no listId and swimlaneId. -function customFieldEdit(userId, doc) { - const card = ReactiveCache.getCard(doc.cardId); - const customFieldValue = ReactiveCache.getActivity({ customFieldId: doc._id }).value; +async function customFieldEdit(userId, doc) { + const card = await ReactiveCache.getCard(doc.cardId); + const customFieldValue = (await ReactiveCache.getActivity({ customFieldId: doc._id })).value; Activities.insert({ userId, activityType: 'setCustomField', @@ -242,14 +242,14 @@ if (Meteor.isServer) { } }); - CustomFields.before.update((userId, doc, fieldNames, modifier) => { + CustomFields.before.update(async (userId, doc, fieldNames, modifier) => { if (_.contains(fieldNames, 'boardIds') && modifier.$pull) { Cards.update( { boardId: modifier.$pull.boardIds, 'customFields._id': doc._id }, { $pull: { customFields: { _id: doc._id } } }, { multi: true }, ); - customFieldEdit(userId, doc); + await customFieldEdit(userId, doc); Activities.remove({ customFieldId: doc._id, boardId: modifier.$pull.boardIds, @@ -296,7 +296,7 @@ if (Meteor.isServer) { * name: string, * type: string}] */ - JsonRoutes.add('GET', '/api/boards/:boardId/custom-fields', function( + JsonRoutes.add('GET', '/api/boards/:boardId/custom-fields', async function( req, res, ) { @@ -304,7 +304,7 @@ if (Meteor.isServer) { Authentication.checkBoardAccess(req.userId, paramBoardId); JsonRoutes.sendResult(res, { code: 200, - data: ReactiveCache.getCustomFields({ boardIds: { $in: [paramBoardId] } }).map( + data: (await ReactiveCache.getCustomFields({ boardIds: { $in: [paramBoardId] } })).map( function(cf) { return { _id: cf._id, @@ -328,13 +328,13 @@ if (Meteor.isServer) { JsonRoutes.add( 'GET', '/api/boards/:boardId/custom-fields/:customFieldId', - function(req, res) { + async function(req, res) { const paramBoardId = req.params.boardId; const paramCustomFieldId = req.params.customFieldId; Authentication.checkBoardAccess(req.userId, paramBoardId); JsonRoutes.sendResult(res, { code: 200, - data: ReactiveCache.getCustomField({ + data: await ReactiveCache.getCustomField({ _id: paramCustomFieldId, boardIds: { $in: [paramBoardId] }, }), @@ -356,13 +356,13 @@ if (Meteor.isServer) { * @param {boolean} showSumAtTopOfList should the sum of the custom fields be shown at top of list? * @return_type {_id: string} */ - JsonRoutes.add('POST', '/api/boards/:boardId/custom-fields', function( + JsonRoutes.add('POST', '/api/boards/:boardId/custom-fields', async function( req, res, ) { const paramBoardId = req.params.boardId; Authentication.checkBoardAccess(req.userId, paramBoardId); - const board = ReactiveCache.getBoard(paramBoardId); + const board = await ReactiveCache.getBoard(paramBoardId); const id = CustomFields.direct.insert({ name: req.body.name, type: req.body.type, @@ -374,7 +374,7 @@ if (Meteor.isServer) { boardIds: [board._id], }); - const customField = ReactiveCache.getCustomField({ + const customField = await ReactiveCache.getCustomField({ _id: id, boardIds: { $in: [paramBoardId] }, }); diff --git a/models/export.js b/models/export.js index a45f8dc77..c72d2ff42 100644 --- a/models/export.js +++ b/models/export.js @@ -27,14 +27,14 @@ if (Meteor.isServer) { * @param {string} boardId the ID of the board we are exporting * @param {string} authToken the loginToken */ - JsonRoutes.add('get', '/api/boards/:boardId/export', function (req, res) { + JsonRoutes.add('get', '/api/boards/:boardId/export', async function (req, res) { const boardId = req.params.boardId; let user = null; let impersonateDone = false; let adminId = null; // First check if board exists and is public to avoid unnecessary authentication - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) { JsonRoutes.sendResult(res, 404); return; @@ -46,7 +46,7 @@ if (Meteor.isServer) { const exporter = new Exporter(boardId); JsonRoutes.sendResult(res, { code: 200, - data: exporter.build(), + data: await exporter.build(), }); return; } @@ -64,18 +64,18 @@ if (Meteor.isServer) { } const hashToken = Accounts._hashLoginToken(loginToken); - user = ReactiveCache.getUser({ + user = await ReactiveCache.getUser({ 'services.resume.loginTokens.hashedToken': hashToken, }); adminId = user._id.toString(); - impersonateDone = ReactiveCache.getImpersonatedUser({ adminId: adminId }); + impersonateDone = await ReactiveCache.getImpersonatedUser({ adminId: adminId }); } else if (!Meteor.settings.public.sandstorm) { Authentication.checkUserId(req.userId); - user = ReactiveCache.getUser({ _id: req.userId, isAdmin: true }); + user = await ReactiveCache.getUser({ _id: req.userId, isAdmin: true }); } const exporter = new Exporter(boardId); - if (exporter.canExport(user) || impersonateDone) { + if (await exporter.canExport(user) || impersonateDone) { if (impersonateDone) { ImpersonatedUsers.insert({ adminId: adminId, @@ -86,7 +86,7 @@ if (Meteor.isServer) { JsonRoutes.sendResult(res, { code: 200, - data: exporter.build(), + data: await exporter.build(), }); } else { // we could send an explicit error message, but on the other hand the only @@ -118,7 +118,7 @@ if (Meteor.isServer) { JsonRoutes.add( 'get', '/api/boards/:boardId/attachments/:attachmentId/export', - function (req, res) { + async function (req, res) { const boardId = req.params.boardId; const attachmentId = req.params.attachmentId; let user = null; @@ -126,7 +126,7 @@ if (Meteor.isServer) { let adminId = null; // First check if board exists and is public to avoid unnecessary authentication - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) { JsonRoutes.sendResult(res, 404); return; @@ -138,7 +138,7 @@ if (Meteor.isServer) { const exporter = new Exporter(boardId, attachmentId); JsonRoutes.sendResult(res, { code: 200, - data: exporter.build(), + data: await exporter.build(), }); return; } @@ -156,18 +156,18 @@ if (Meteor.isServer) { } const hashToken = Accounts._hashLoginToken(loginToken); - user = ReactiveCache.getUser({ + user = await ReactiveCache.getUser({ 'services.resume.loginTokens.hashedToken': hashToken, }); adminId = user._id.toString(); - impersonateDone = ReactiveCache.getImpersonatedUser({ adminId: adminId }); + impersonateDone = await ReactiveCache.getImpersonatedUser({ adminId: adminId }); } else if (!Meteor.settings.public.sandstorm) { Authentication.checkUserId(req.userId); - user = ReactiveCache.getUser({ _id: req.userId, isAdmin: true }); + user = await ReactiveCache.getUser({ _id: req.userId, isAdmin: true }); } const exporter = new Exporter(boardId, attachmentId); - if (exporter.canExport(user) || impersonateDone) { + if (await exporter.canExport(user) || impersonateDone) { if (impersonateDone) { ImpersonatedUsers.insert({ adminId: adminId, @@ -178,7 +178,7 @@ if (Meteor.isServer) { } JsonRoutes.sendResult(res, { code: 200, - data: exporter.build(), + data: await exporter.build(), }); } else { // we could send an explicit error message, but on the other hand the only @@ -203,14 +203,14 @@ if (Meteor.isServer) { * @param {string} authToken the loginToken * @param {string} delimiter delimiter to use while building export. Default is comma ',' */ - Picker.route('/api/boards/:boardId/export/csv', function (params, req, res) { + Picker.route('/api/boards/:boardId/export/csv', async function (params, req, res) { const boardId = params.boardId; let user = null; let impersonateDone = false; let adminId = null; // First check if board exists and is public to avoid unnecessary authentication - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) { res.writeHead(404); res.end('Board not found'); @@ -237,7 +237,7 @@ if (Meteor.isServer) { // use Uint8Array to prevent from converting bytes to string res.write(new Uint8Array([0xEF, 0xBB, 0xBF])); } - res.write(exporter.buildCsv(params.query.delimiter, 'en')); + res.write(await exporter.buildCsv(params.query.delimiter, 'en')); res.end(); return; } @@ -256,21 +256,21 @@ if (Meteor.isServer) { } const hashToken = Accounts._hashLoginToken(loginToken); - user = ReactiveCache.getUser({ + user = await ReactiveCache.getUser({ 'services.resume.loginTokens.hashedToken': hashToken, }); adminId = user._id.toString(); - impersonateDone = ReactiveCache.getImpersonatedUser({ adminId: adminId }); + impersonateDone = await ReactiveCache.getImpersonatedUser({ adminId: adminId }); } else if (!Meteor.settings.public.sandstorm) { Authentication.checkUserId(req.userId); - user = ReactiveCache.getUser({ + user = await ReactiveCache.getUser({ _id: req.userId, isAdmin: true, }); } const exporter = new Exporter(boardId); - if (exporter.canExport(user) || impersonateDone) { + if (await exporter.canExport(user) || impersonateDone) { if (impersonateDone) { let exportType = 'exportCSV'; if( params.query.delimiter == "\t" ) { @@ -303,7 +303,7 @@ if (Meteor.isServer) { // use Uint8Array to prevent from converting bytes to string res.write(new Uint8Array([0xEF, 0xBB, 0xBF])); } - res.write(exporter.buildCsv(params.query.delimiter, userLanguage)); + res.write(await exporter.buildCsv(params.query.delimiter, userLanguage)); res.end(); } else { res.writeHead(403); diff --git a/models/exportExcel.js b/models/exportExcel.js index 598fb92d6..aac56cd40 100644 --- a/models/exportExcel.js +++ b/models/exportExcel.js @@ -30,14 +30,14 @@ runOnServer(function() { * @param {string} boardId the ID of the board we are exporting * @param {string} authToken the loginToken */ - Picker.route('/api/boards/:boardId/exportExcel', function (params, req, res) { + Picker.route('/api/boards/:boardId/exportExcel', async function (params, req, res) { const boardId = params.boardId; let user = null; let impersonateDone = false; let adminId = null; // First check if board exists and is public to avoid unnecessary authentication - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) { res.end('Board not found'); return; @@ -64,14 +64,14 @@ runOnServer(function() { } const hashToken = Accounts._hashLoginToken(loginToken); - user = ReactiveCache.getUser({ + user = await ReactiveCache.getUser({ 'services.resume.loginTokens.hashedToken': hashToken, }); adminId = user._id.toString(); - impersonateDone = ReactiveCache.getImpersonatedUser({ adminId: adminId }); + impersonateDone = await ReactiveCache.getImpersonatedUser({ adminId: adminId }); } else if (!Meteor.settings.public.sandstorm) { Authentication.checkUserId(req.userId); - user = ReactiveCache.getUser({ + user = await ReactiveCache.getUser({ _id: req.userId, isAdmin: true, }); diff --git a/models/exportPDF.js b/models/exportPDF.js index 272651f98..14173c711 100644 --- a/models/exportPDF.js +++ b/models/exportPDF.js @@ -30,7 +30,7 @@ runOnServer(function() { * @param {string} boardId the ID of the board we are exporting * @param {string} authToken the loginToken */ - Picker.route('/api/boards/:boardId/lists/:listId/cards/:cardId/exportPDF', function (params, req, res) { + Picker.route('/api/boards/:boardId/lists/:listId/cards/:cardId/exportPDF', async function (params, req, res) { const boardId = params.boardId; const paramListId = req.params.listId; const paramCardId = req.params.cardId; @@ -39,7 +39,7 @@ runOnServer(function() { let adminId = null; // First check if board exists and is public to avoid unnecessary authentication - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) { res.end('Board not found'); return; @@ -66,14 +66,14 @@ runOnServer(function() { } const hashToken = Accounts._hashLoginToken(loginToken); - user = ReactiveCache.getUser({ + user = await ReactiveCache.getUser({ 'services.resume.loginTokens.hashedToken': hashToken, }); adminId = user._id.toString(); - impersonateDone = ReactiveCache.getImpersonatedUser({ adminId: adminId }); + impersonateDone = await ReactiveCache.getImpersonatedUser({ adminId: adminId }); } else if (!Meteor.settings.public.sandstorm) { Authentication.checkUserId(req.userId); - user = ReactiveCache.getUser({ + user = await ReactiveCache.getUser({ _id: req.userId, isAdmin: true, }); diff --git a/models/exporter.js b/models/exporter.js index f1f601540..4cef38d1a 100644 --- a/models/exporter.js +++ b/models/exporter.js @@ -34,7 +34,7 @@ export class Exporter { this._attachmentId = attachmentId; } - build() { + async build() { const fs = Npm.require('fs'); const os = Npm.require('os'); const path = Npm.require('path'); @@ -55,7 +55,7 @@ export class Exporter { }; _.extend( result, - ReactiveCache.getBoard(this._boardId, { + await ReactiveCache.getBoard(this._boardId, { fields: { stars: 0, }, @@ -97,7 +97,7 @@ export class Exporter { const byBoardAndAttachment = this._attachmentId ? { boardId: this._boardId, _id: this._attachmentId } : byBoard; - result.attachments = ReactiveCache.getAttachments(byBoardAndAttachment) + result.attachments = (await ReactiveCache.getAttachments(byBoardAndAttachment)) .map((attachment) => { let filebase64 = null; filebase64 = getBase64DataSync(attachment); @@ -116,41 +116,41 @@ export class Exporter { return result.attachments.length > 0 ? result.attachments[0] : {}; } - result.lists = ReactiveCache.getLists(byBoard, noBoardId); - result.cards = ReactiveCache.getCards(byBoardNoLinked, noBoardId); - result.swimlanes = ReactiveCache.getSwimlanes(byBoard, noBoardId); - result.customFields = ReactiveCache.getCustomFields( + result.lists = await ReactiveCache.getLists(byBoard, noBoardId); + result.cards = await ReactiveCache.getCards(byBoardNoLinked, noBoardId); + result.swimlanes = await ReactiveCache.getSwimlanes(byBoard, noBoardId); + result.customFields = await ReactiveCache.getCustomFields( { boardIds: this._boardId }, { fields: { boardIds: 0 } }, ); - result.comments = ReactiveCache.getCardComments(byBoard, noBoardId); - result.activities = ReactiveCache.getActivities(byBoard, noBoardId); - result.rules = ReactiveCache.getRules(byBoard, noBoardId); + result.comments = await ReactiveCache.getCardComments(byBoard, noBoardId); + result.activities = await ReactiveCache.getActivities(byBoard, noBoardId); + result.rules = await ReactiveCache.getRules(byBoard, noBoardId); result.checklists = []; result.checklistItems = []; result.subtaskItems = []; result.triggers = []; result.actions = []; - result.cards.forEach((card) => { + for (const card of result.cards) { result.checklists.push( - ...ReactiveCache.getChecklists({ + ...await ReactiveCache.getChecklists({ cardId: card._id, }), ); result.checklistItems.push( - ...ReactiveCache.getChecklistItems({ + ...await ReactiveCache.getChecklistItems({ cardId: card._id, }), ); result.subtaskItems.push( - ...ReactiveCache.getCards({ + ...await ReactiveCache.getCards({ parentId: card._id, }), ); - }); - result.rules.forEach((rule) => { + } + for (const rule of result.rules) { result.triggers.push( - ...ReactiveCache.getTriggers( + ...await ReactiveCache.getTriggers( { _id: rule.triggerId, }, @@ -158,14 +158,14 @@ export class Exporter { ), ); result.actions.push( - ...ReactiveCache.getActions( + ...await ReactiveCache.getActions( { _id: rule.actionId, }, noBoardId, ), ); - }); + } // we also have to export some user data - as the other elements only // include id but we have to be careful: @@ -211,7 +211,7 @@ export class Exporter { 'profile.avatarUrl': 1, }, }; - result.users = ReactiveCache.getUsers(byUserIds, userFields) + result.users = (await ReactiveCache.getUsers(byUserIds, userFields)) .map((user) => { // user avatar is stored as a relative url, we export absolute if ((user.profile || {}).avatarUrl) { @@ -222,8 +222,8 @@ export class Exporter { return result; } - buildCsv(userDelimiter = ',', userLanguage='en') { - const result = this.build(); + async buildCsv(userDelimiter = ',', userLanguage='en') { + const result = await this.build(); const columnHeaders = []; const cardRows = []; @@ -398,8 +398,8 @@ export class Exporter { return Papa.unparse(cardRows, papaconfig); } - canExport(user) { - const board = ReactiveCache.getBoard(this._boardId); + async canExport(user) { + const board = await ReactiveCache.getBoard(this._boardId); return board && board.isVisibleBy(user); } } diff --git a/models/integrations.js b/models/integrations.js index 3daa3cb8d..fed283f95 100644 --- a/models/integrations.js +++ b/models/integrations.js @@ -101,21 +101,21 @@ Integrations.Const = { }, }; const permissionHelper = { - allow(userId, doc) { - const user = ReactiveCache.getUser(userId); - const isAdmin = user && ReactiveCache.getCurrentUser().isAdmin; - return isAdmin || allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId)); + async allow(userId, doc) { + const user = await ReactiveCache.getUser(userId); + const isAdmin = user && (await ReactiveCache.getCurrentUser()).isAdmin; + return isAdmin || allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId)); }, }; Integrations.allow({ - insert(userId, doc) { - return permissionHelper.allow(userId, doc); + async insert(userId, doc) { + return await permissionHelper.allow(userId, doc); }, - update(userId, doc) { - return permissionHelper.allow(userId, doc); + async update(userId, doc) { + return await permissionHelper.allow(userId, doc); }, - remove(userId, doc) { - return permissionHelper.allow(userId, doc); + async remove(userId, doc) { + return await permissionHelper.allow(userId, doc); }, fetch: ['boardId'], }); @@ -134,7 +134,7 @@ if (Meteor.isServer) { * @param {string} boardId the board ID * @return_type [Integrations] */ - JsonRoutes.add('GET', '/api/boards/:boardId/integrations', function( + JsonRoutes.add('GET', '/api/boards/:boardId/integrations', async function( req, res, ) { @@ -142,10 +142,10 @@ if (Meteor.isServer) { const paramBoardId = req.params.boardId; Authentication.checkBoardAccess(req.userId, paramBoardId); - const data = ReactiveCache.getIntegrations( + const data = (await ReactiveCache.getIntegrations( { boardId: paramBoardId }, { fields: { token: 0 } }, - ).map(function(doc) { + )).map(function(doc) { return doc; }); @@ -166,7 +166,7 @@ if (Meteor.isServer) { * @param {string} intId the integration ID * @return_type Integrations */ - JsonRoutes.add('GET', '/api/boards/:boardId/integrations/:intId', function( + JsonRoutes.add('GET', '/api/boards/:boardId/integrations/:intId', async function( req, res, ) { @@ -177,7 +177,7 @@ if (Meteor.isServer) { JsonRoutes.sendResult(res, { code: 200, - data: ReactiveCache.getIntegration( + data: await ReactiveCache.getIntegration( { _id: paramIntId, boardId: paramBoardId }, { fields: { token: 0 } }, ), @@ -310,7 +310,7 @@ if (Meteor.isServer) { JsonRoutes.add( 'DELETE', '/api/boards/:boardId/integrations/:intId/activities', - function(req, res) { + async function(req, res) { try { const paramBoardId = req.params.boardId; const paramIntId = req.params.intId; @@ -324,7 +324,7 @@ if (Meteor.isServer) { JsonRoutes.sendResult(res, { code: 200, - data: ReactiveCache.getIntegration( + data: await ReactiveCache.getIntegration( { _id: paramIntId, boardId: paramBoardId }, { fields: { _id: 1, activities: 1 } }, ), @@ -350,7 +350,7 @@ if (Meteor.isServer) { JsonRoutes.add( 'POST', '/api/boards/:boardId/integrations/:intId/activities', - function(req, res) { + async function(req, res) { try { const paramBoardId = req.params.boardId; const paramIntId = req.params.intId; @@ -364,7 +364,7 @@ if (Meteor.isServer) { JsonRoutes.sendResult(res, { code: 200, - data: ReactiveCache.getIntegration( + data: await ReactiveCache.getIntegration( { _id: paramIntId, boardId: paramBoardId }, { fields: { _id: 1, activities: 1 } }, ), diff --git a/models/invitationCodes.js b/models/invitationCodes.js index 31e4b4016..1fe84de7a 100644 --- a/models/invitationCodes.js +++ b/models/invitationCodes.js @@ -55,8 +55,8 @@ InvitationCodes.attachSchema( ); InvitationCodes.helpers({ - author() { - return ReactiveCache.getUser(this.authorId); + async author() { + return await ReactiveCache.getUser(this.authorId); }, }); diff --git a/models/lib/fileStoreStrategy.js b/models/lib/fileStoreStrategy.js index 911011526..426e956b5 100644 --- a/models/lib/fileStoreStrategy.js +++ b/models/lib/fileStoreStrategy.js @@ -580,8 +580,8 @@ export const moveToStorage = function(fileObj, storageDestination, fileStoreStra }); }; -export const copyFile = function(fileObj, newCardId, fileStoreStrategyFactory) { - const newCard = ReactiveCache.getCard(newCardId); +export const copyFile = async function(fileObj, newCardId, fileStoreStrategyFactory) { + const newCard = await ReactiveCache.getCard(newCardId); Object.keys(fileObj.versions).forEach(versionName => { const strategyRead = fileStoreStrategyFactory.getFileStrategy(fileObj, versionName); const readStream = strategyRead.getReadStream(); diff --git a/models/lists.js b/models/lists.js index bc5b102a3..415e90fff 100644 --- a/models/lists.js +++ b/models/lists.js @@ -180,30 +180,30 @@ Lists.attachSchema( ); Lists.allow({ - insert(userId, doc) { + async insert(userId, doc) { // ReadOnly and CommentOnly users cannot create lists - return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId)); + return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(doc.boardId)); }, - update(userId, doc) { + async update(userId, doc) { // ReadOnly and CommentOnly users cannot edit lists - return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId)); + return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(doc.boardId)); }, - remove(userId, doc) { + async remove(userId, doc) { // ReadOnly and CommentOnly users cannot delete lists - return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId)); + return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(doc.boardId)); }, fetch: ['boardId'], }); Lists.helpers({ - copy(boardId, swimlaneId) { + async copy(boardId, swimlaneId) { const oldId = this._id; const oldSwimlaneId = this.swimlaneId || null; this.boardId = boardId; this.swimlaneId = swimlaneId; let _id = null; - const existingListWithSameName = ReactiveCache.getList({ + const existingListWithSameName = await ReactiveCache.getList({ boardId, title: this.title, archived: false, @@ -213,21 +213,22 @@ Lists.helpers({ } else { delete this._id; this.swimlaneId = swimlaneId; // Set the target swimlane for the copied list - _id = Lists.insert(this); + _id = await Lists.insertAsync(this); } // Copy all cards in list - ReactiveCache.getCards({ + const cards = await ReactiveCache.getCards({ swimlaneId: oldSwimlaneId, listId: oldId, archived: false, - }).forEach(card => { - card.copy(boardId, swimlaneId, _id); }); + for (const card of cards) { + await card.copy(boardId, swimlaneId, _id); + } }, async move(boardId, swimlaneId) { - const boardList = ReactiveCache.getList({ + const boardList = await ReactiveCache.getList({ boardId, title: this.title, archived: false, @@ -235,13 +236,13 @@ Lists.helpers({ let listId; if (boardList) { listId = boardList._id; - for (const card of this.cards()) { + for (const card of await this.cards()) { await card.move(boardId, this._id, boardList._id); } } else { console.log('list.title:', this.title); console.log('boardList:', boardList); - listId = Lists.insert({ + listId = await Lists.insertAsync({ title: this.title, boardId, type: this.type, @@ -251,42 +252,42 @@ Lists.helpers({ }); } - for (const card of this.cards(swimlaneId)) { + for (const card of await this.cards(swimlaneId)) { await card.move(boardId, swimlaneId, listId); } }, - cards(swimlaneId) { + async cards(swimlaneId) { const selector = { listId: this._id, archived: false, }; if (swimlaneId) selector.swimlaneId = swimlaneId; - const ret = ReactiveCache.getCards(Filter.mongoSelector(selector), { sort: ['sort'] }); + const ret = await ReactiveCache.getCards(Filter.mongoSelector(selector), { sort: ['sort'] }); return ret; }, - cardsUnfiltered(swimlaneId) { + async cardsUnfiltered(swimlaneId) { const selector = { listId: this._id, archived: false, }; if (swimlaneId) selector.swimlaneId = swimlaneId; - const ret = ReactiveCache.getCards(selector, { sort: ['sort'] }); + const ret = await ReactiveCache.getCards(selector, { sort: ['sort'] }); return ret; }, - allCards() { - const ret = ReactiveCache.getCards({ listId: this._id }); + async allCards() { + const ret = await ReactiveCache.getCards({ listId: this._id }); return ret; }, - board() { - return ReactiveCache.getBoard(this.boardId); + async board() { + return await ReactiveCache.getBoard(this.boardId); }, - getWipLimit(option) { - const list = ReactiveCache.getList(this._id); + async getWipLimit(option) { + const list = await ReactiveCache.getList(this._id); if (!list.wipLimit) { // Necessary check to avoid exceptions for the case where the doc doesn't have the wipLimit field yet set return 0; @@ -310,9 +311,9 @@ Lists.helpers({ return this.starred === true; }, - isCollapsed() { + async isCollapsed() { if (Meteor.isClient) { - const user = ReactiveCache.getCurrentUser(); + const user = await ReactiveCache.getCurrentUser(); // Logged-in users: prefer profile/cookie-backed state if (user && user.getCollapsedListFromStorage) { const stored = user.getCollapsedListFromStorage(this.boardId, this._id); @@ -331,13 +332,13 @@ Lists.helpers({ return this.collapsed === true; }, - absoluteUrl() { - const card = ReactiveCache.getCard({ listId: this._id }); - return card && card.absoluteUrl(); + async absoluteUrl() { + const card = await ReactiveCache.getCard({ listId: this._id }); + return card && (await card.absoluteUrl()); }, - originRelativeUrl() { - const card = ReactiveCache.getCard({ listId: this._id }); - return card && card.originRelativeUrl(); + async originRelativeUrl() { + const card = await ReactiveCache.getCard({ listId: this._id }); + return card && (await card.originRelativeUrl()); }, async remove() { return await Lists.removeAsync({ _id: this._id }); @@ -361,7 +362,7 @@ Lists.helpers({ async archive() { if (this.isTemplateList()) { - for (const card of this.cards()) { + for (const card of await this.cards()) { await card.archive(); } } @@ -370,7 +371,7 @@ Lists.helpers({ async restore() { if (this.isTemplateList()) { - for (const card of this.allCards()) { + for (const card of await this.allCards()) { await card.restore(); } } @@ -394,23 +395,25 @@ Lists.helpers({ }, }); -Lists.userArchivedLists = userId => { - return ReactiveCache.getLists({ - boardId: { $in: Boards.userBoardIds(userId, null) }, +Lists.userArchivedLists = async userId => { + return await ReactiveCache.getLists({ + boardId: { $in: await Boards.userBoardIds(userId, null) }, archived: true, }) }; -Lists.userArchivedListIds = () => { - return Lists.userArchivedLists().map(list => { return list._id; }); +Lists.userArchivedListIds = async () => { + const lists = await Lists.userArchivedLists(); + return lists.map(list => { return list._id; }); }; -Lists.archivedLists = () => { - return ReactiveCache.getLists({ archived: true }); +Lists.archivedLists = async () => { + return await ReactiveCache.getLists({ archived: true }); }; -Lists.archivedListIds = () => { - return Lists.archivedLists().map(list => { +Lists.archivedListIds = async () => { + const lists = await Lists.archivedLists(); + return lists.map(list => { return list._id; }); }; @@ -424,12 +427,12 @@ Meteor.methods({ throw new Meteor.Error('not-authorized', 'You must be logged in.'); } - const list = ReactiveCache.getList(listId); + const list = await ReactiveCache.getList(listId); if (!list) { throw new Meteor.Error('list-not-found', 'List not found'); } - const board = ReactiveCache.getBoard(list.boardId); + const board = await ReactiveCache.getBoard(list.boardId); if (!board || !board.hasAdmin(this.userId)) { throw new Meteor.Error('not-authorized', 'You must be a board admin to modify WIP limits.'); } @@ -447,63 +450,62 @@ Meteor.methods({ throw new Meteor.Error('not-authorized', 'You must be logged in.'); } - const list = ReactiveCache.getList(listId); + const list = await ReactiveCache.getList(listId); if (!list) { throw new Meteor.Error('list-not-found', 'List not found'); } - const board = ReactiveCache.getBoard(list.boardId); + const board = await ReactiveCache.getBoard(list.boardId); if (!board || !board.hasAdmin(this.userId)) { throw new Meteor.Error('not-authorized', 'You must be a board admin to modify WIP limits.'); } - if (list.getWipLimit('value') === 0) { + if ((await list.getWipLimit('value')) === 0) { await list.setWipLimit(1); } - list.toggleWipLimit(!list.getWipLimit('enabled')); + await list.toggleWipLimit(!(await list.getWipLimit('enabled'))); }, - enableSoftLimit(listId) { + async enableSoftLimit(listId) { check(listId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in.'); } - - const list = ReactiveCache.getList(listId); + + const list = await ReactiveCache.getList(listId); if (!list) { throw new Meteor.Error('list-not-found', 'List not found'); } - - const board = ReactiveCache.getBoard(list.boardId); + + const board = await ReactiveCache.getBoard(list.boardId); if (!board || !board.hasAdmin(this.userId)) { throw new Meteor.Error('not-authorized', 'You must be a board admin to modify WIP limits.'); } - - list.toggleSoftLimit(!list.getWipLimit('soft')); + + await list.toggleSoftLimit(!(await list.getWipLimit('soft'))); }, - myLists() { + async myLists() { // my lists - return _.uniq( - ReactiveCache.getLists( - { - boardId: { $in: Boards.userBoardIds(this.userId) }, - archived: false, - }, - { - fields: { title: 1 }, - }, - ).map(list => list.title), - ).sort(); + const lists = await ReactiveCache.getLists( + { + boardId: { $in: Boards.userBoardIds(this.userId) }, + archived: false, + }, + { + fields: { title: 1 }, + }, + ); + return _.uniq(lists.map(list => list.title)).sort(); }, - updateListSort(listId, boardId, updateData) { + async updateListSort(listId, boardId, updateData) { check(listId, String); check(boardId, String); check(updateData, Object); - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) { throw new Meteor.Error('board-not-found', 'Board not found'); } @@ -516,7 +518,7 @@ Meteor.methods({ } } - const list = ReactiveCache.getList(listId); + const list = await ReactiveCache.getList(listId); if (!list) { throw new Meteor.Error('list-not-found', 'List not found'); } @@ -529,13 +531,13 @@ Meteor.methods({ }); if (updateData.swimlaneId) { - const swimlane = ReactiveCache.getSwimlane(updateData.swimlaneId); + const swimlane = await ReactiveCache.getSwimlane(updateData.swimlaneId); if (!swimlane || swimlane.boardId !== boardId) { throw new Meteor.Error('invalid-swimlane', 'Invalid swimlane for this board'); } } - Lists.update( + await Lists.updateAsync( { _id: listId, boardId }, { $set: { @@ -583,14 +585,14 @@ Lists.after.insert((userId, doc) => { }, 100); }); -Lists.before.remove((userId, doc) => { - const cards = ReactiveCache.getCards({ listId: doc._id }); +Lists.before.remove(async (userId, doc) => { + const cards = await ReactiveCache.getCards({ listId: doc._id }); if (cards) { - cards.forEach(card => { - Cards.remove(card._id); - }); + for (const card of cards) { + await Cards.removeAsync(card._id); + } } - Activities.insert({ + await Activities.insertAsync({ userId, type: 'list', activityType: 'removeList', @@ -722,12 +724,12 @@ if (Meteor.isServer) { * @param {string} title the title of the List * @return_type {_id: string} */ - JsonRoutes.add('POST', '/api/boards/:boardId/lists', function(req, res) { + JsonRoutes.add('POST', '/api/boards/:boardId/lists', async function(req, res) { try { const paramBoardId = req.params.boardId; Authentication.checkBoardWriteAccess(req.userId, paramBoardId); - const board = ReactiveCache.getBoard(paramBoardId); - const id = Lists.insert({ + const board = await ReactiveCache.getBoard(paramBoardId); + const id = await Lists.insertAsync({ title: req.body.title, boardId: paramBoardId, sort: board.lists().length, @@ -763,7 +765,7 @@ if (Meteor.isServer) { * @param {boolean} [collapsed] whether the list is collapsed * @return_type {_id: string} */ - JsonRoutes.add('PUT', '/api/boards/:boardId/lists/:listId', function( + JsonRoutes.add('PUT', '/api/boards/:boardId/lists/:listId', async function( req, res, ) { @@ -773,7 +775,7 @@ if (Meteor.isServer) { let updated = false; Authentication.checkBoardWriteAccess(req.userId, paramBoardId); - const list = ReactiveCache.getList({ + const list = await ReactiveCache.getList({ _id: paramListId, boardId: paramBoardId, archived: false, diff --git a/models/lockoutSettings.js b/models/lockoutSettings.js index 9020f6227..23e4acda6 100644 --- a/models/lockoutSettings.js +++ b/models/lockoutSettings.js @@ -48,8 +48,8 @@ LockoutSettings.attachSchema( ); LockoutSettings.allow({ - update(userId) { - const user = ReactiveCache.getUser(userId); + async update(userId) { + const user = await ReactiveCache.getUser(userId); return user && user.isAdmin; }, }); diff --git a/models/org.js b/models/org.js index 41bdb47b4..21ac40f76 100644 --- a/models/org.js +++ b/models/org.js @@ -87,8 +87,8 @@ Org.attachSchema( if (Meteor.isServer) { Org.allow({ - insert(userId, doc) { - const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser(); + async insert(userId, doc) { + const user = await ReactiveCache.getUser(userId) || await ReactiveCache.getCurrentUser(); if (user?.isAdmin) return true; if (!user) { @@ -96,8 +96,8 @@ if (Meteor.isServer) { } return doc._id === userId; }, - update(userId, doc) { - const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser(); + async update(userId, doc) { + const user = await ReactiveCache.getUser(userId) || await ReactiveCache.getCurrentUser(); if (user?.isAdmin) return true; if (!user) { @@ -105,8 +105,8 @@ if (Meteor.isServer) { } return doc._id === userId; }, - remove(userId, doc) { - const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser(); + async remove(userId, doc) { + const user = await ReactiveCache.getUser(userId) || await ReactiveCache.getCurrentUser(); if (user?.isAdmin) return true; if (!user) { @@ -119,7 +119,7 @@ if (Meteor.isServer) { Meteor.methods({ - setCreateOrg( + async setCreateOrg( orgDisplayName, orgDesc, orgShortName, @@ -127,7 +127,7 @@ if (Meteor.isServer) { orgWebsite, orgIsActive, ) { - if (ReactiveCache.getCurrentUser()?.isAdmin) { + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { check(orgDisplayName, String); check(orgDesc, String); check(orgShortName, String); @@ -135,7 +135,7 @@ if (Meteor.isServer) { check(orgWebsite, String); check(orgIsActive, Boolean); - const nOrgNames = ReactiveCache.getOrgs({ orgShortName }).length; + const nOrgNames = (await ReactiveCache.getOrgs({ orgShortName })).length; if (nOrgNames > 0) { throw new Meteor.Error('orgname-already-taken'); } else { @@ -150,7 +150,7 @@ if (Meteor.isServer) { } } }, - setCreateOrgFromOidc( + async setCreateOrgFromOidc( orgDisplayName, orgDesc, orgShortName, @@ -165,7 +165,7 @@ if (Meteor.isServer) { check(orgWebsite, String); check(orgIsActive, Boolean); - const nOrgNames = ReactiveCache.getOrgs({ orgShortName }).length; + const nOrgNames = (await ReactiveCache.getOrgs({ orgShortName })).length; if (nOrgNames > 0) { throw new Meteor.Error('orgname-already-taken'); } else { @@ -179,19 +179,19 @@ if (Meteor.isServer) { }); } }, - setOrgDisplayName(org, orgDisplayName) { - if (ReactiveCache.getCurrentUser()?.isAdmin) { + async setOrgDisplayName(org, orgDisplayName) { + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { check(org, Object); check(orgDisplayName, String); Org.update(org, { $set: { orgDisplayName: orgDisplayName }, }); - Meteor.call('setUsersOrgsOrgDisplayName', org._id, orgDisplayName); + await Meteor.callAsync('setUsersOrgsOrgDisplayName', org._id, orgDisplayName); } }, - setOrgDesc(org, orgDesc) { - if (ReactiveCache.getCurrentUser()?.isAdmin) { + async setOrgDesc(org, orgDesc) { + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { check(org, Object); check(orgDesc, String); Org.update(org, { @@ -200,8 +200,8 @@ if (Meteor.isServer) { } }, - setOrgShortName(org, orgShortName) { - if (ReactiveCache.getCurrentUser()?.isAdmin) { + async setOrgShortName(org, orgShortName) { + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { check(org, Object); check(orgShortName, String); Org.update(org, { @@ -210,8 +210,8 @@ if (Meteor.isServer) { } }, - setAutoAddUsersWithDomainName(org, orgAutoAddUsersWithDomainName) { - if (ReactiveCache.getCurrentUser()?.isAdmin) { + async setAutoAddUsersWithDomainName(org, orgAutoAddUsersWithDomainName) { + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { check(org, Object); check(orgAutoAddUsersWithDomainName, String); Org.update(org, { @@ -220,8 +220,8 @@ if (Meteor.isServer) { } }, - setOrgIsActive(org, orgIsActive) { - if (ReactiveCache.getCurrentUser()?.isAdmin) { + async setOrgIsActive(org, orgIsActive) { + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { check(org, Object); check(orgIsActive, Boolean); Org.update(org, { @@ -229,7 +229,7 @@ if (Meteor.isServer) { }); } }, - setOrgAllFieldsFromOidc( + async setOrgAllFieldsFromOidc( org, orgDisplayName, orgDesc, @@ -255,9 +255,9 @@ if (Meteor.isServer) { orgIsActive: orgIsActive, }, }); - Meteor.call('setUsersOrgsOrgDisplayName', org._id, orgDisplayName); + await Meteor.callAsync('setUsersOrgsOrgDisplayName', org._id, orgDisplayName); }, - setOrgAllFields( + async setOrgAllFields( org, orgDisplayName, orgDesc, @@ -266,7 +266,7 @@ if (Meteor.isServer) { orgWebsite, orgIsActive, ) { - if (ReactiveCache.getCurrentUser()?.isAdmin) { + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { check(org, Object); check(orgDisplayName, String); check(orgDesc, String); @@ -284,7 +284,7 @@ if (Meteor.isServer) { orgIsActive: orgIsActive, }, }); - Meteor.call('setUsersOrgsOrgDisplayName', org._id, orgDisplayName); + await Meteor.callAsync('setUsersOrgsOrgDisplayName', org._id, orgDisplayName); } }, }); diff --git a/models/rules.js b/models/rules.js index ea1ba571f..127e8fb3c 100644 --- a/models/rules.js +++ b/models/rules.js @@ -54,32 +54,32 @@ Rules.helpers({ async rename(description) { return await Rules.updateAsync(this._id, { $set: { description } }); }, - getAction() { - return ReactiveCache.getAction(this.actionId); + async getAction() { + return await ReactiveCache.getAction(this.actionId); }, - getTrigger() { - return ReactiveCache.getTrigger(this.triggerId); + async getTrigger() { + return await ReactiveCache.getTrigger(this.triggerId); }, - board() { - return ReactiveCache.getBoard(this.boardId); + async board() { + return await ReactiveCache.getBoard(this.boardId); }, - trigger() { - return ReactiveCache.getTrigger(this.triggerId); + async trigger() { + return await ReactiveCache.getTrigger(this.triggerId); }, - action() { - return ReactiveCache.getAction(this.actionId); + async action() { + return await ReactiveCache.getAction(this.actionId); }, }); Rules.allow({ - insert(userId, doc) { - return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId)); + async insert(userId, doc) { + return allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId)); }, - update(userId, doc) { - return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId)); + async update(userId, doc) { + return allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId)); }, - remove(userId, doc) { - return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId)); + async remove(userId, doc) { + return allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId)); }, }); diff --git a/models/server/ExporterCardPDF.js b/models/server/ExporterCardPDF.js index 37551e6cb..454c17f4c 100644 --- a/models/server/ExporterCardPDF.js +++ b/models/server/ExporterCardPDF.js @@ -640,8 +640,8 @@ class ExporterCardPDF { } - canExport(user) { - const board = ReactiveCache.getBoard(this._boardId); + async canExport(user) { + const board = await ReactiveCache.getBoard(this._boardId); return board && board.isVisibleBy(user); } } diff --git a/models/server/ExporterExcel.js b/models/server/ExporterExcel.js index 66684917e..55d27011e 100644 --- a/models/server/ExporterExcel.js +++ b/models/server/ExporterExcel.js @@ -31,7 +31,7 @@ class ExporterExcel { this.userLanguage = userLanguage; } - build(res) { + async build(res) { const fs = Npm.require('fs'); const os = Npm.require('os'); const path = Npm.require('path'); @@ -56,16 +56,16 @@ class ExporterExcel { }; _.extend( result, - ReactiveCache.getBoard(this._boardId, { + await ReactiveCache.getBoard(this._boardId, { fields: { stars: 0, }, }), ); - result.lists = ReactiveCache.getLists(byBoard, noBoardId); - result.cards = ReactiveCache.getCards(byBoardNoLinked, noBoardId); - result.swimlanes = ReactiveCache.getSwimlanes(byBoard, noBoardId); - result.customFields = ReactiveCache.getCustomFields( + result.lists = await ReactiveCache.getLists(byBoard, noBoardId); + result.cards = await ReactiveCache.getCards(byBoardNoLinked, noBoardId); + result.swimlanes = await ReactiveCache.getSwimlanes(byBoard, noBoardId); + result.customFields = await ReactiveCache.getCustomFields( { boardIds: { $in: [this.boardId], @@ -77,34 +77,34 @@ class ExporterExcel { }, }, ); - result.comments = ReactiveCache.getCardComments(byBoard, noBoardId); - result.activities = ReactiveCache.getActivities(byBoard, noBoardId); - result.rules = ReactiveCache.getRules(byBoard, noBoardId); + result.comments = await ReactiveCache.getCardComments(byBoard, noBoardId); + result.activities = await ReactiveCache.getActivities(byBoard, noBoardId); + result.rules = await ReactiveCache.getRules(byBoard, noBoardId); result.checklists = []; result.checklistItems = []; result.subtaskItems = []; result.triggers = []; result.actions = []; - result.cards.forEach((card) => { + for (const card of result.cards) { result.checklists.push( - ...ReactiveCache.getChecklists({ + ...await ReactiveCache.getChecklists({ cardId: card._id, }), ); result.checklistItems.push( - ...ReactiveCache.getChecklistItems({ + ...await ReactiveCache.getChecklistItems({ cardId: card._id, }), ); result.subtaskItems.push( - ...ReactiveCache.getCards({ + ...await ReactiveCache.getCards({ parentId: card._id, }), ); - }); - result.rules.forEach((rule) => { + } + for (const rule of result.rules) { result.triggers.push( - ...ReactiveCache.getTriggers( + ...await ReactiveCache.getTriggers( { _id: rule.triggerId, }, @@ -112,14 +112,14 @@ class ExporterExcel { ), ); result.actions.push( - ...ReactiveCache.getActions( + ...await ReactiveCache.getActions( { _id: rule.actionId, }, noBoardId, ), ); - }); + } // we also have to export some user data - as the other elements only // include id but we have to be careful: @@ -169,7 +169,7 @@ class ExporterExcel { 'profile.avatarUrl': 1, }, }; - result.users = ReactiveCache.getUsers(byUserIds, userFields) + result.users = (await ReactiveCache.getUsers(byUserIds, userFields)) .map((user) => { // user avatar is stored as a relative url, we export absolute if ((user.profile || {}).avatarUrl) { @@ -905,8 +905,8 @@ class ExporterExcel { workbook.xlsx.write(res).then(function () {}); } - canExport(user) { - const board = ReactiveCache.getBoard(this._boardId); + async canExport(user) { + const board = await ReactiveCache.getBoard(this._boardId); return board && board.isVisibleBy(user); } } diff --git a/models/server/metrics.js b/models/server/metrics.js index 669bbbf92..af4c9eca0 100644 --- a/models/server/metrics.js +++ b/models/server/metrics.js @@ -57,12 +57,12 @@ const getBoardTitleWithMostActivities = (dateWithXdaysAgo, nbLimit) => { ); }; -const getBoards = (boardIds) => { - const ret = ReactiveCache.getBoards({ _id: { $in: boardIds } }); +const getBoards = async (boardIds) => { + const ret = await ReactiveCache.getBoards({ _id: { $in: boardIds } }); return ret; }; Meteor.startup(() => { - WebApp.connectHandlers.use('/metrics', (req, res, next) => { + WebApp.connectHandlers.use('/metrics', async (req, res, next) => { try { const ipAddress = req.headers['x-forwarded-for'] || req.socket.remoteAddress; @@ -95,7 +95,7 @@ Meteor.startup(() => { metricsRes += '# Number of registered users\n'; // Get number of registered user - resCount = ReactiveCache.getUsers({}).length; // KPI 2 + resCount = (await ReactiveCache.getUsers({})).length; // KPI 2 metricsRes += 'wekan_registeredUsers ' + resCount + '\n'; resCount = 0; @@ -103,7 +103,7 @@ Meteor.startup(() => { metricsRes += '# Number of registered boards\n'; // Get number of registered boards - resCount = ReactiveCache.getBoards({ archived: false, type: 'board' }).length; // KPI 3 + resCount = (await ReactiveCache.getBoards({ archived: false, type: 'board' })).length; // KPI 3 metricsRes += 'wekan_registeredboards ' + resCount + '\n'; resCount = 0; @@ -112,8 +112,8 @@ Meteor.startup(() => { // Get number of registered boards by registered users resCount = - ReactiveCache.getBoards({ archived: false, type: 'board' }).length / - ReactiveCache.getUsers({}).length; // KPI 4 + (await ReactiveCache.getBoards({ archived: false, type: 'board' })).length / + (await ReactiveCache.getUsers({})).length; // KPI 4 metricsRes += 'wekan_registeredboardsBysRegisteredUsers ' + resCount + '\n'; resCount = 0; @@ -122,11 +122,11 @@ Meteor.startup(() => { metricsRes += '# Number of registered boards\n'; // Get board numbers with only one member - resCount = ReactiveCache.getBoards({ + resCount = (await ReactiveCache.getBoards({ archived: false, type: 'board', members: { $size: 1 }, - }).length; // KPI 5 + })).length; // KPI 5 metricsRes += 'wekan_registeredboardsWithOnlyOneMember ' + resCount + '\n'; resCount = 0; @@ -144,9 +144,9 @@ Meteor.startup(() => { let dateWithXdaysAgo = new Date( new Date() - xdays * 24 * 60 * 60 * 1000, ); - resCount = ReactiveCache.getUsers({ + resCount = (await ReactiveCache.getUsers({ lastConnectionDate: { $gte: dateWithXdaysAgo }, - }).length; // KPI 5 + })).length; // KPI 5 metricsRes += 'wekan_usersWithLastConnectionDated5DaysAgo ' + resCount + '\n'; resCount = 0; @@ -157,9 +157,9 @@ Meteor.startup(() => { // Get number of users with last connection dated 10 days ago xdays = 10; dateWithXdaysAgo = new Date(new Date() - xdays * 24 * 60 * 60 * 1000); - resCount = ReactiveCache.getUsers({ + resCount = (await ReactiveCache.getUsers({ lastConnectionDate: { $gte: dateWithXdaysAgo }, - }).length; // KPI 5 + })).length; // KPI 5 metricsRes += 'wekan_usersWithLastConnectionDated10DaysAgo ' + resCount + '\n'; resCount = 0; @@ -170,9 +170,9 @@ Meteor.startup(() => { // Get number of users with last connection dated 20 days ago xdays = 20; dateWithXdaysAgo = new Date(new Date() - xdays * 24 * 60 * 60 * 1000); - resCount = ReactiveCache.getUsers({ + resCount = (await ReactiveCache.getUsers({ lastConnectionDate: { $gte: dateWithXdaysAgo }, - }).length; // KPI 5 + })).length; // KPI 5 metricsRes += 'wekan_usersWithLastConnectionDated20DaysAgo ' + resCount + '\n'; resCount = 0; @@ -183,9 +183,9 @@ Meteor.startup(() => { // Get number of users with last connection dated 20 days ago xdays = 30; dateWithXdaysAgo = new Date(new Date() - xdays * 24 * 60 * 60 * 1000); - resCount = ReactiveCache.getUsers({ + resCount = (await ReactiveCache.getUsers({ lastConnectionDate: { $gte: dateWithXdaysAgo }, - }).length; // KPI 5 + })).length; // KPI 5 metricsRes += 'wekan_usersWithLastConnectionDated30DaysAgo ' + resCount + '\n'; resCount = 0; diff --git a/models/settings.js b/models/settings.js index 00107b8e4..df0f71af9 100644 --- a/models/settings.js +++ b/models/settings.js @@ -195,8 +195,8 @@ Settings.helpers({ }, }); Settings.allow({ - update(userId) { - const user = ReactiveCache.getUser(userId); + async update(userId) { + const user = await ReactiveCache.getUser(userId); return user && user.isAdmin; }, }); @@ -204,7 +204,7 @@ Settings.allow({ if (Meteor.isServer) { Meteor.startup(async () => { await Settings._collection.createIndexAsync({ modifiedAt: -1 }); - const setting = ReactiveCache.getCurrentSetting(); + const setting = await ReactiveCache.getCurrentSetting(); if (!setting) { const now = new Date(); const domain = process.env.ROOT_URL.match( @@ -230,7 +230,7 @@ if (Meteor.isServer) { } if (isSandstorm) { // At Sandstorm, Admin Panel has SMTP settings - const newSetting = ReactiveCache.getCurrentSetting(); + const newSetting = await ReactiveCache.getCurrentSetting(); if (!process.env.MAIL_URL && newSetting.mailUrl()) process.env.MAIL_URL = newSetting.mailUrl(); Accounts.emailTemplates.from = process.env.MAIL_FROM @@ -284,15 +284,16 @@ if (Meteor.isServer) { return config; } - function sendInvitationEmail(_id) { - const icode = ReactiveCache.getInvitationCode(_id); - const author = ReactiveCache.getCurrentUser(); + async function sendInvitationEmail(_id) { + const icode = await ReactiveCache.getInvitationCode(_id); + const author = await ReactiveCache.getCurrentUser(); try { - const fullName = ReactiveCache.getUser(icode.authorId)?.profile?.fullname || ""; + const authorUser = await ReactiveCache.getUser(icode.authorId); + const fullName = authorUser?.profile?.fullname || ""; const params = { email: icode.email, - inviter: fullName != "" ? fullName + " (" + ReactiveCache.getUser(icode.authorId).username + " )" : ReactiveCache.getUser(icode.authorId).username, + inviter: fullName != "" ? fullName + " (" + authorUser.username + " )" : authorUser.username, user: icode.email.split('@')[0], icode: icode.code, url: FlowRouter.url('sign-up'), @@ -323,8 +324,8 @@ if (Meteor.isServer) { } } - function isNonAdminAllowedToSendMail(currentUser){ - const currSett = ReactiveCache.getCurrentSetting(); + async function isNonAdminAllowedToSendMail(currentUser){ + const currSett = await ReactiveCache.getCurrentSetting(); let isAllowed = false; if(currSett && currSett != undefined && currSett.disableRegistration && currSett.mailDomainName !== undefined && currSett.mailDomainName != ""){ for(let i = 0; i < currentUser.emails.length; i++) { @@ -361,20 +362,20 @@ if (Meteor.isServer) { } Meteor.methods({ - sendInvitation(emails, boards) { + async sendInvitation(emails, boards) { let rc = 0; check(emails, [String]); check(boards, [String]); - const user = ReactiveCache.getCurrentUser(); - if (!user.isAdmin && !isNonAdminAllowedToSendMail(user)) { + const user = await ReactiveCache.getCurrentUser(); + if (!user.isAdmin && !(await isNonAdminAllowedToSendMail(user))) { rc = -1; throw new Meteor.Error('not-allowed'); } - emails.forEach(email => { + for (const email of emails) { if (email && SimpleSchema.RegEx.Email.test(email)) { // Checks if the email is already link to an account. - const userExist = ReactiveCache.getUser({ email }); + const userExist = await ReactiveCache.getUser({ email }); if (userExist) { rc = -1; throw new Meteor.Error( @@ -383,12 +384,12 @@ if (Meteor.isServer) { ); } // Checks if the email is already link to an invitation. - const invitation = ReactiveCache.getInvitationCode({ email }); + const invitation = await ReactiveCache.getInvitationCode({ email }); if (invitation) { InvitationCodes.update(invitation, { $set: { boardsToBeInvited: boards }, }); - sendInvitationEmail(invitation._id); + await sendInvitationEmail(invitation._id); } else { const code = getRandomNum(100000, 999999); InvitationCodes.insert( @@ -399,9 +400,9 @@ if (Meteor.isServer) { createdAt: new Date(), authorId: Meteor.userId(), }, - function(err, _id) { + async function(err, _id) { if (!err && _id) { - sendInvitationEmail(_id); + await sendInvitationEmail(_id); } else { rc = -1; throw new Meteor.Error( @@ -413,15 +414,15 @@ if (Meteor.isServer) { ); } } - }); + } return rc; }, - sendSMTPTestEmail() { + async sendSMTPTestEmail() { if (!Meteor.userId()) { throw new Meteor.Error('invalid-user'); } - const user = ReactiveCache.getCurrentUser(); + const user = await ReactiveCache.getCurrentUser(); if (!user.emails || !user.emails[0] || !user.emails[0].address) { throw new Meteor.Error('email-invalid'); } @@ -471,8 +472,8 @@ if (Meteor.isServer) { }; }, - getCustomUI() { - const setting = ReactiveCache.getCurrentSetting(); + async getCustomUI() { + const setting = await ReactiveCache.getCurrentSetting(); if (!setting.productName) { return { productName: '', @@ -484,8 +485,8 @@ if (Meteor.isServer) { } }, - isDisableRegistration() { - const setting = ReactiveCache.getCurrentSetting(); + async isDisableRegistration() { + const setting = await ReactiveCache.getCurrentSetting(); if (setting.disableRegistration === true) { return true; } else { @@ -493,8 +494,8 @@ if (Meteor.isServer) { } }, - isDisableForgotPassword() { - const setting = ReactiveCache.getCurrentSetting(); + async isDisableForgotPassword() { + const setting = await ReactiveCache.getCurrentSetting(); if (setting.disableForgotPassword === true) { return true; } else { diff --git a/models/swimlanes.js b/models/swimlanes.js index 6eda75982..0bf1bd99e 100644 --- a/models/swimlanes.js +++ b/models/swimlanes.js @@ -131,28 +131,28 @@ Swimlanes.attachSchema( ); Swimlanes.allow({ - insert(userId, doc) { + async insert(userId, doc) { // ReadOnly and CommentOnly users cannot create swimlanes - return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId)); + return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(doc.boardId)); }, - update(userId, doc) { + async update(userId, doc) { // ReadOnly and CommentOnly users cannot edit swimlanes - return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId)); + return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(doc.boardId)); }, - remove(userId, doc) { + async remove(userId, doc) { // ReadOnly and CommentOnly users cannot delete swimlanes - return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId)); + return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(doc.boardId)); }, fetch: ['boardId'], }); Swimlanes.helpers({ - copy(boardId) { + async copy(boardId) { const oldId = this._id; const oldBoardId = this.boardId; this.boardId = boardId; delete this._id; - const _id = Swimlanes.insert(this); + const _id = await Swimlanes.insertAsync(this); const query = { swimlaneId: { $in: [oldId, ''] }, @@ -163,17 +163,18 @@ Swimlanes.helpers({ } // Copy all lists in swimlane - ReactiveCache.getLists(query).forEach(list => { + const lists = await ReactiveCache.getLists(query); + for (const list of lists) { list.type = 'list'; list.swimlaneId = oldId; list.boardId = boardId; - list.copy(boardId, _id); - }); + await list.copy(boardId, _id); + } }, async move(toBoardId) { - for (const list of this.lists()) { - const toList = ReactiveCache.getList({ + for (const list of await this.lists()) { + const toList = await ReactiveCache.getList({ boardId: toBoardId, title: list.title, archived: false, @@ -193,7 +194,7 @@ Swimlanes.helpers({ }); } - const cards = ReactiveCache.getCards({ + const cards = await ReactiveCache.getCards({ listId: list._id, swimlaneId: this._id, }); @@ -209,11 +210,11 @@ Swimlanes.helpers({ }); // make sure there is a default swimlane - this.board().getDefaultSwimline(); + (await this.board()).getDefaultSwimline(); }, - cards() { - const ret = ReactiveCache.getCards( + async cards() { + const ret = await ReactiveCache.getCards( Filter.mongoSelector({ swimlaneId: this._id, archived: false, @@ -223,12 +224,12 @@ Swimlanes.helpers({ return ret; }, - lists() { - return this.draggableLists(); + async lists() { + return await this.draggableLists(); }, - newestLists() { + async newestLists() { // Revert to shared lists across swimlanes: filter by board only - return ReactiveCache.getLists( + return await ReactiveCache.getLists( { boardId: this.boardId, archived: false, @@ -236,9 +237,9 @@ Swimlanes.helpers({ { sort: { modifiedAt: -1 } }, ); }, - draggableLists() { + async draggableLists() { // Revert to shared lists across swimlanes: filter by board only - return ReactiveCache.getLists( + return await ReactiveCache.getLists( { boardId: this.boardId, //archived: false, @@ -247,10 +248,10 @@ Swimlanes.helpers({ ); }, - myLists() { + async myLists() { // Return per-swimlane lists: provide lists specific to this swimlane - return ReactiveCache.getLists( - { + return await ReactiveCache.getLists( + { boardId: this.boardId, swimlaneId: this._id, archived: false @@ -259,14 +260,14 @@ Swimlanes.helpers({ ); }, - allCards() { - const ret = ReactiveCache.getCards({ swimlaneId: this._id }); + async allCards() { + const ret = await ReactiveCache.getCards({ swimlaneId: this._id }); return ret; }, - isCollapsed() { + async isCollapsed() { if (Meteor.isClient) { - const user = ReactiveCache.getCurrentUser(); + const user = await ReactiveCache.getCurrentUser(); if (user && user.getCollapsedSwimlaneFromStorage) { const stored = user.getCollapsedSwimlaneFromStorage(this.boardId, this._id); if (typeof stored === 'boolean') { @@ -283,8 +284,8 @@ Swimlanes.helpers({ return this.collapsed === true; }, - board() { - return ReactiveCache.getBoard(this.boardId); + async board() { + return await ReactiveCache.getBoard(this.boardId); }, colorClass() { @@ -300,18 +301,18 @@ Swimlanes.helpers({ return this.type === 'template-container'; }, - isListTemplatesSwimlane() { - const user = ReactiveCache.getCurrentUser(); + async isListTemplatesSwimlane() { + const user = await ReactiveCache.getCurrentUser(); return (user.profile || {}).listTemplatesSwimlaneId === this._id; }, - isCardTemplatesSwimlane() { - const user = ReactiveCache.getCurrentUser(); + async isCardTemplatesSwimlane() { + const user = await ReactiveCache.getCurrentUser(); return (user.profile || {}).cardTemplatesSwimlaneId === this._id; }, - isBoardTemplatesSwimlane() { - const user = ReactiveCache.getCurrentUser(); + async isBoardTemplatesSwimlane() { + const user = await ReactiveCache.getCurrentUser(); return (user.profile || {}).boardTemplatesSwimlaneId === this._id; }, @@ -328,7 +329,7 @@ Swimlanes.helpers({ async archive() { if (this.isTemplateSwimlane()) { - for (const list of this.myLists()) { + for (const list of await this.myLists()) { await list.archive(); } } @@ -337,7 +338,7 @@ Swimlanes.helpers({ async restore() { if (this.isTemplateSwimlane()) { - for (const list of this.myLists()) { + for (const list of await this.myLists()) { await list.restore(); } } @@ -349,23 +350,25 @@ Swimlanes.helpers({ }, }); -Swimlanes.userArchivedSwimlanes = userId => { - return ReactiveCache.getSwimlanes({ - boardId: { $in: Boards.userBoardIds(userId, null) }, +Swimlanes.userArchivedSwimlanes = async userId => { + return await ReactiveCache.getSwimlanes({ + boardId: { $in: await Boards.userBoardIds(userId, null) }, archived: true, }) }; -Swimlanes.userArchivedSwimlaneIds = () => { - return Swimlanes.userArchivedSwimlanes().map(swim => { return swim._id; }); +Swimlanes.userArchivedSwimlaneIds = async () => { + const swimlanes = await Swimlanes.userArchivedSwimlanes(); + return swimlanes.map(swim => { return swim._id; }); }; -Swimlanes.archivedSwimlanes = () => { - return ReactiveCache.getSwimlanes({ archived: true }); +Swimlanes.archivedSwimlanes = async () => { + return await ReactiveCache.getSwimlanes({ archived: true }); }; -Swimlanes.archivedSwimlaneIds = () => { - return Swimlanes.archivedSwimlanes().map(swim => { +Swimlanes.archivedSwimlaneIds = async () => { + const swimlanes = await Swimlanes.archivedSwimlanes(); + return swimlanes.map(swim => { return swim._id; }); }; @@ -396,8 +399,8 @@ if (Meteor.isServer) { }, 100); }); - Swimlanes.before.remove(function(userId, doc) { - const lists = ReactiveCache.getLists( + Swimlanes.before.remove(async function(userId, doc) { + const lists = await ReactiveCache.getLists( { boardId: doc.boardId, swimlaneId: { $in: [doc._id, ''] }, @@ -407,14 +410,14 @@ if (Meteor.isServer) { ); if (lists.length < 2) { - lists.forEach(list => { - list.remove(); - }); + for (const list of lists) { + await list.remove(); + } } else { - Cards.remove({ swimlaneId: doc._id }); + await Cards.removeAsync({ swimlaneId: doc._id }); } - Activities.insert({ + await Activities.insertAsync({ userId, type: 'swimlane', activityType: 'removeSwimlane', @@ -473,14 +476,15 @@ if (Meteor.isServer) { * @return_type [{_id: string, * title: string}] */ - JsonRoutes.add('GET', '/api/boards/:boardId/swimlanes', function(req, res) { + JsonRoutes.add('GET', '/api/boards/:boardId/swimlanes', async function(req, res) { try { const paramBoardId = req.params.boardId; Authentication.checkBoardAccess(req.userId, paramBoardId); + const swimlanes = await ReactiveCache.getSwimlanes({ boardId: paramBoardId, archived: false }); JsonRoutes.sendResult(res, { code: 200, - data: ReactiveCache.getSwimlanes({ boardId: paramBoardId, archived: false }).map( + data: swimlanes.map( function(doc) { return { _id: doc._id, @@ -506,7 +510,7 @@ if (Meteor.isServer) { * @param {string} swimlaneId the ID of the swimlane * @return_type Swimlanes */ - JsonRoutes.add('GET', '/api/boards/:boardId/swimlanes/:swimlaneId', function( + JsonRoutes.add('GET', '/api/boards/:boardId/swimlanes/:swimlaneId', async function( req, res, ) { @@ -517,7 +521,7 @@ if (Meteor.isServer) { JsonRoutes.sendResult(res, { code: 200, - data: ReactiveCache.getSwimlane({ + data: await ReactiveCache.getSwimlane({ _id: paramSwimlaneId, boardId: paramBoardId, archived: false, @@ -540,13 +544,13 @@ if (Meteor.isServer) { * @param {string} title the new title of the swimlane * @return_type {_id: string} */ - JsonRoutes.add('POST', '/api/boards/:boardId/swimlanes', function(req, res) { + JsonRoutes.add('POST', '/api/boards/:boardId/swimlanes', async function(req, res) { try { const paramBoardId = req.params.boardId; Authentication.checkBoardWriteAccess(req.userId, paramBoardId); - const board = ReactiveCache.getBoard(paramBoardId); - const id = Swimlanes.insert({ + const board = await ReactiveCache.getBoard(paramBoardId); + const id = await Swimlanes.insertAsync({ title: req.body.title, boardId: paramBoardId, sort: board.swimlanes().length, @@ -575,13 +579,13 @@ if (Meteor.isServer) { * @param {string} title the new title of the swimlane * @return_type {_id: string} */ - JsonRoutes.add('PUT', '/api/boards/:boardId/swimlanes/:swimlaneId', function(req, res) { + JsonRoutes.add('PUT', '/api/boards/:boardId/swimlanes/:swimlaneId', async function(req, res) { try { const paramBoardId = req.params.boardId; const paramSwimlaneId = req.params.swimlaneId; Authentication.checkBoardWriteAccess(req.userId, paramBoardId); - const board = ReactiveCache.getBoard(paramBoardId); - const swimlane = ReactiveCache.getSwimlane({ + const board = await ReactiveCache.getBoard(paramBoardId); + const swimlane = await ReactiveCache.getSwimlane({ _id: paramSwimlaneId, boardId: paramBoardId, }); diff --git a/models/tableVisibilityModeSettings.js b/models/tableVisibilityModeSettings.js index b68cd971b..e6b10566e 100644 --- a/models/tableVisibilityModeSettings.js +++ b/models/tableVisibilityModeSettings.js @@ -45,8 +45,8 @@ TableVisibilityModeSettings.attachSchema( ); TableVisibilityModeSettings.allow({ - update(userId) { - const user = ReactiveCache.getUser(userId); + async update(userId) { + const user = await ReactiveCache.getUser(userId); return user && user.isAdmin; }, }); diff --git a/models/team.js b/models/team.js index 57fbf315a..a38f703cd 100644 --- a/models/team.js +++ b/models/team.js @@ -78,8 +78,8 @@ Team.attachSchema( if (Meteor.isServer) { Team.allow({ - insert(userId, doc) { - const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser(); + async insert(userId, doc) { + const user = await ReactiveCache.getUser(userId) || await ReactiveCache.getCurrentUser(); if (user?.isAdmin) return true; if (!user) { @@ -87,8 +87,8 @@ if (Meteor.isServer) { } return doc._id === userId; }, - update(userId, doc) { - const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser(); + async update(userId, doc) { + const user = await ReactiveCache.getUser(userId) || await ReactiveCache.getCurrentUser(); if (user?.isAdmin) return true; if (!user) { @@ -96,8 +96,8 @@ if (Meteor.isServer) { } return doc._id === userId; }, - remove(userId, doc) { - const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser(); + async remove(userId, doc) { + const user = await ReactiveCache.getUser(userId) || await ReactiveCache.getCurrentUser(); if (user?.isAdmin) return true; if (!user) { @@ -109,21 +109,21 @@ if (Meteor.isServer) { }); Meteor.methods({ - setCreateTeam( + async setCreateTeam( teamDisplayName, teamDesc, teamShortName, teamWebsite, teamIsActive, ) { - if (ReactiveCache.getCurrentUser()?.isAdmin) { + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { check(teamDisplayName, String); check(teamDesc, String); check(teamShortName, String); check(teamWebsite, String); check(teamIsActive, Boolean); - const nTeamNames = ReactiveCache.getTeams({ teamShortName }).length; + const nTeamNames = (await ReactiveCache.getTeams({ teamShortName })).length; if (nTeamNames > 0) { throw new Meteor.Error('teamname-already-taken'); } else { @@ -137,7 +137,7 @@ if (Meteor.isServer) { } } }, - setCreateTeamFromOidc( + async setCreateTeamFromOidc( teamDisplayName, teamDesc, teamShortName, @@ -149,7 +149,7 @@ if (Meteor.isServer) { check(teamShortName, String); check(teamWebsite, String); check(teamIsActive, Boolean); - const nTeamNames = ReactiveCache.getTeams({ teamShortName }).length; + const nTeamNames = (await ReactiveCache.getTeams({ teamShortName })).length; if (nTeamNames > 0) { throw new Meteor.Error('teamname-already-taken'); } else { @@ -162,19 +162,19 @@ if (Meteor.isServer) { }); } }, - setTeamDisplayName(team, teamDisplayName) { - if (ReactiveCache.getCurrentUser()?.isAdmin) { + async setTeamDisplayName(team, teamDisplayName) { + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { check(team, Object); check(teamDisplayName, String); Team.update(team, { $set: { teamDisplayName: teamDisplayName }, }); - Meteor.call('setUsersTeamsTeamDisplayName', team._id, teamDisplayName); + await Meteor.callAsync('setUsersTeamsTeamDisplayName', team._id, teamDisplayName); } }, - setTeamDesc(team, teamDesc) { - if (ReactiveCache.getCurrentUser()?.isAdmin) { + async setTeamDesc(team, teamDesc) { + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { check(team, Object); check(teamDesc, String); Team.update(team, { @@ -183,8 +183,8 @@ if (Meteor.isServer) { } }, - setTeamShortName(team, teamShortName) { - if (ReactiveCache.getCurrentUser()?.isAdmin) { + async setTeamShortName(team, teamShortName) { + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { check(team, Object); check(teamShortName, String); Team.update(team, { @@ -193,8 +193,8 @@ if (Meteor.isServer) { } }, - setTeamIsActive(team, teamIsActive) { - if (ReactiveCache.getCurrentUser()?.isAdmin) { + async setTeamIsActive(team, teamIsActive) { + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { check(team, Object); check(teamIsActive, Boolean); Team.update(team, { @@ -202,7 +202,7 @@ if (Meteor.isServer) { }); } }, - setTeamAllFieldsFromOidc( + async setTeamAllFieldsFromOidc( team, teamDisplayName, teamDesc, @@ -225,9 +225,9 @@ if (Meteor.isServer) { teamIsActive: teamIsActive, }, }); - Meteor.call('setUsersTeamsTeamDisplayName', team._id, teamDisplayName); + await Meteor.callAsync('setUsersTeamsTeamDisplayName', team._id, teamDisplayName); }, - setTeamAllFields( + async setTeamAllFields( team, teamDisplayName, teamDesc, @@ -235,7 +235,7 @@ if (Meteor.isServer) { teamWebsite, teamIsActive, ) { - if (ReactiveCache.getCurrentUser()?.isAdmin) { + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { check(team, Object); check(teamDisplayName, String); check(teamDesc, String); @@ -251,7 +251,7 @@ if (Meteor.isServer) { teamIsActive: teamIsActive, }, }); - Meteor.call('setUsersTeamsTeamDisplayName', team._id, teamDisplayName); + await Meteor.callAsync('setUsersTeamsTeamDisplayName', team._id, teamDisplayName); } }, }); diff --git a/models/translation.js b/models/translation.js index a901c3eeb..b71099628 100644 --- a/models/translation.js +++ b/models/translation.js @@ -58,8 +58,8 @@ Translation.attachSchema( if (Meteor.isServer) { Translation.allow({ - insert(userId, doc) { - const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser(); + async insert(userId, doc) { + const user = await ReactiveCache.getUser(userId) || await ReactiveCache.getCurrentUser(); if (user?.isAdmin) return true; if (!user) { @@ -67,8 +67,8 @@ if (Meteor.isServer) { } return doc._id === userId; }, - update(userId, doc) { - const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser(); + async update(userId, doc) { + const user = await ReactiveCache.getUser(userId) || await ReactiveCache.getCurrentUser(); if (user?.isAdmin) return true; if (!user) { @@ -76,8 +76,8 @@ if (Meteor.isServer) { } return doc._id === userId; }, - remove(userId, doc) { - const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser(); + async remove(userId, doc) { + const user = await ReactiveCache.getUser(userId) || await ReactiveCache.getCurrentUser(); if (user?.isAdmin) return true; if (!user) { @@ -89,7 +89,7 @@ if (Meteor.isServer) { }); Meteor.methods({ - setCreateTranslation( + async setCreateTranslation( language, text, translationText, @@ -98,11 +98,11 @@ if (Meteor.isServer) { check(text, String); check(translationText, String); - if (!ReactiveCache.getCurrentUser()?.isAdmin) { + if (!(await ReactiveCache.getCurrentUser())?.isAdmin) { throw new Meteor.Error('not-authorized'); } - const nTexts = ReactiveCache.getTranslations({ language, text }).length; + const nTexts = (await ReactiveCache.getTranslations({ language, text })).length; if (nTexts > 0) { throw new Meteor.Error('text-already-taken'); } else { @@ -113,11 +113,11 @@ if (Meteor.isServer) { }); } }, - setTranslationText(translation, translationText) { + async setTranslationText(translation, translationText) { check(translation, Object); check(translationText, String); - if (!ReactiveCache.getCurrentUser()?.isAdmin) { + if (!(await ReactiveCache.getCurrentUser())?.isAdmin) { throw new Meteor.Error('not-authorized'); } @@ -125,10 +125,10 @@ if (Meteor.isServer) { $set: { translationText: translationText }, }); }, - deleteTranslation(translationId) { + async deleteTranslation(translationId) { check(translationId, String); - if (!ReactiveCache.getCurrentUser()?.isAdmin) { + if (!(await ReactiveCache.getCurrentUser())?.isAdmin) { throw new Meteor.Error('not-authorized'); } diff --git a/models/trelloCreator.js b/models/trelloCreator.js index a16a7d491..1ee3cae9c 100644 --- a/models/trelloCreator.js +++ b/models/trelloCreator.js @@ -775,7 +775,7 @@ export class TrelloCreator { Meteor.settings.public && Meteor.settings.public.sandstorm; if (isSandstorm && currentBoardId) { - const currentBoard = ReactiveCache.getBoard(currentBoardId); + const currentBoard = await ReactiveCache.getBoard(currentBoardId); await currentBoard.archive(); } this.parseActions(board.actions); diff --git a/models/triggers.js b/models/triggers.js index 58da64ee9..4173ce5f8 100644 --- a/models/triggers.js +++ b/models/triggers.js @@ -14,14 +14,14 @@ Triggers.before.update((userId, doc, fieldNames, modifier) => { }); Triggers.allow({ - insert(userId, doc) { - return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId)); + async insert(userId, doc) { + return allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId)); }, - update(userId, doc) { - return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId)); + async update(userId, doc) { + return allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId)); }, - remove(userId, doc) { - return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId)); + async remove(userId, doc) { + return allowIsBoardAdmin(userId, await ReactiveCache.getBoard(doc.boardId)); }, }); @@ -36,20 +36,20 @@ Triggers.helpers({ return this.desc; }, - getRule() { - return ReactiveCache.getRule({ triggerId: this._id }); + async getRule() { + return await ReactiveCache.getRule({ triggerId: this._id }); }, - fromList() { - return ReactiveCache.getList(this.fromId); + async fromList() { + return await ReactiveCache.getList(this.fromId); }, - toList() { - return ReactiveCache.getList(this.toId); + async toList() { + return await ReactiveCache.getList(this.toId); }, - findList(title) { - return ReactiveCache.getList({ + async findList(title) { + return await ReactiveCache.getList({ title, }); }, diff --git a/models/userPositionHistory.js b/models/userPositionHistory.js index 8dba36e3e..b961b30c5 100644 --- a/models/userPositionHistory.js +++ b/models/userPositionHistory.js @@ -174,17 +174,17 @@ UserPositionHistory.helpers({ /** * Can this change be undone? */ - canUndo() { + async canUndo() { // Can undo if the entity still exists switch (this.entityType) { case 'card': - return !!ReactiveCache.getCard(this.entityId); + return !!(await ReactiveCache.getCard(this.entityId)); case 'list': - return !!ReactiveCache.getList(this.entityId); + return !!(await ReactiveCache.getList(this.entityId)); case 'swimlane': - return !!ReactiveCache.getSwimlane(this.entityId); + return !!(await ReactiveCache.getSwimlane(this.entityId)); case 'checklist': - return !!ReactiveCache.getChecklist(this.entityId); + return !!(await ReactiveCache.getChecklist(this.entityId)); case 'checklistItem': return !!ChecklistItems.findOne(this.entityId); default: @@ -195,23 +195,23 @@ UserPositionHistory.helpers({ /** * Undo this change */ - undo() { - if (!this.canUndo()) { + async undo() { + if (!(await this.canUndo())) { throw new Meteor.Error('cannot-undo', 'Entity no longer exists'); } const userId = this.userId; - + switch (this.entityType) { case 'card': { - const card = ReactiveCache.getCard(this.entityId); + const card = await ReactiveCache.getCard(this.entityId); if (card) { // Restore previous position const boardId = this.previousBoardId || card.boardId; const swimlaneId = this.previousSwimlaneId || card.swimlaneId; const listId = this.previousListId || card.listId; const sort = this.previousSort !== undefined ? this.previousSort : card.sort; - + Cards.update(card._id, { $set: { boardId, @@ -224,11 +224,11 @@ UserPositionHistory.helpers({ break; } case 'list': { - const list = ReactiveCache.getList(this.entityId); + const list = await ReactiveCache.getList(this.entityId); if (list) { const sort = this.previousSort !== undefined ? this.previousSort : list.sort; const swimlaneId = this.previousSwimlaneId || list.swimlaneId; - + Lists.update(list._id, { $set: { sort, @@ -239,10 +239,10 @@ UserPositionHistory.helpers({ break; } case 'swimlane': { - const swimlane = ReactiveCache.getSwimlane(this.entityId); + const swimlane = await ReactiveCache.getSwimlane(this.entityId); if (swimlane) { const sort = this.previousSort !== undefined ? this.previousSort : swimlane.sort; - + Swimlanes.update(swimlane._id, { $set: { sort, @@ -252,10 +252,10 @@ UserPositionHistory.helpers({ break; } case 'checklist': { - const checklist = ReactiveCache.getChecklist(this.entityId); + const checklist = await ReactiveCache.getChecklist(this.entityId); if (checklist) { const sort = this.previousSort !== undefined ? this.previousSort : checklist.sort; - + Checklists.update(checklist._id, { $set: { sort, @@ -270,7 +270,7 @@ UserPositionHistory.helpers({ if (item) { const sort = this.previousSort !== undefined ? this.previousSort : item.sort; const checklistId = this.previousState?.checklistId || item.checklistId; - + ChecklistItems.update(item._id, { $set: { sort, @@ -411,19 +411,19 @@ Meteor.methods({ }); }, - 'userPositionHistory.undo'(historyId) { + async 'userPositionHistory.undo'(historyId) { check(historyId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - + const history = UserPositionHistory.findOne({ _id: historyId, userId: this.userId }); if (!history) { throw new Meteor.Error('not-found', 'History entry not found'); } - - return history.undo(); + + return await history.undo(); }, 'userPositionHistory.getRecent'(boardId, limit = 50) { @@ -453,23 +453,23 @@ Meteor.methods({ ).fetch(); }, - 'userPositionHistory.restoreToCheckpoint'(checkpointId) { + async 'userPositionHistory.restoreToCheckpoint'(checkpointId) { check(checkpointId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - - const checkpoint = UserPositionHistory.findOne({ - _id: checkpointId, + + const checkpoint = UserPositionHistory.findOne({ + _id: checkpointId, userId: this.userId, isCheckpoint: true, }); - + if (!checkpoint) { throw new Meteor.Error('not-found', 'Checkpoint not found'); } - + // Find all changes after this checkpoint and undo them in reverse order const changesToUndo = UserPositionHistory.find( { @@ -480,19 +480,19 @@ Meteor.methods({ }, { sort: { createdAt: -1 } } ).fetch(); - + let undoneCount = 0; - changesToUndo.forEach(change => { + for (const change of changesToUndo) { try { - if (change.canUndo()) { - change.undo(); + if (await change.canUndo()) { + await change.undo(); undoneCount++; } } catch (e) { console.warn('Failed to undo change:', change._id, e); } - }); - + } + return { undoneCount, totalChanges: changesToUndo.length }; }, }); diff --git a/models/users.js b/models/users.js index 732fa5bd0..ade8039ef 100644 --- a/models/users.js +++ b/models/users.js @@ -902,10 +902,10 @@ if (Meteor.isClient) { return board && board.hasWorker(this._id); }, - isBoardAdmin(boardId) { + async isBoardAdmin(boardId) { let board; if (boardId) { - board = ReactiveCache.getBoard(boardId); + board = await ReactiveCache.getBoard(boardId); } else { board = Utils.getCurrentBoard(); } @@ -1798,7 +1798,7 @@ Users.helpers({ Meteor.methods({ // Secure user removal method with proper authorization checks - removeUser(targetUserId) { + async removeUser(targetUserId) { check(targetUserId, String); const currentUserId = Meteor.userId(); @@ -1806,12 +1806,12 @@ Meteor.methods({ throw new Meteor.Error('not-authorized', 'User must be logged in'); } - const currentUser = ReactiveCache.getUser(currentUserId); + const currentUser = await ReactiveCache.getUser(currentUserId); if (!currentUser) { throw new Meteor.Error('not-authorized', 'Current user not found'); } - const targetUser = ReactiveCache.getUser(targetUserId); + const targetUser = await ReactiveCache.getUser(targetUserId); if (!targetUser) { throw new Meteor.Error('user-not-found', 'Target user not found'); } @@ -1829,9 +1829,9 @@ Meteor.methods({ } // Check if target user is the last admin - const adminsNumber = ReactiveCache.getUsers({ + const adminsNumber = (await ReactiveCache.getUsers({ isAdmin: true, - }).length; + })).length; if (adminsNumber === 1 && targetUser.isAdmin) { throw new Meteor.Error('not-authorized', 'Cannot delete the last administrator'); @@ -1841,7 +1841,7 @@ Meteor.methods({ Users.remove(targetUserId); return { success: true, message: 'User deleted successfully' }; }, - editUser(targetUserId, updateData) { + async editUser(targetUserId, updateData) { check(targetUserId, String); check(updateData, Object); @@ -1850,7 +1850,7 @@ Meteor.methods({ throw new Meteor.Error('not-authorized', 'User must be logged in'); } - const currentUser = ReactiveCache.getUser(currentUserId); + const currentUser = await ReactiveCache.getUser(currentUserId); if (!currentUser) { throw new Meteor.Error('not-authorized', 'Current user not found'); } @@ -1860,7 +1860,7 @@ Meteor.methods({ throw new Meteor.Error('not-authorized', 'Only administrators can edit other users'); } - const targetUser = ReactiveCache.getUser(targetUserId); + const targetUser = await ReactiveCache.getUser(targetUserId); if (!targetUser) { throw new Meteor.Error('user-not-found', 'Target user not found'); } @@ -1894,9 +1894,9 @@ Meteor.methods({ Users.update(targetUserId, { $set: updateObject }); }, - setListSortBy(value) { + async setListSortBy(value) { check(value, String); - ReactiveCache.getCurrentUser().setListSortBy(value); + (await ReactiveCache.getCurrentUser()).setListSortBy(value); }, setAvatarUrl(avatarUrl) { check(avatarUrl, String); @@ -1943,8 +1943,8 @@ Meteor.methods({ Users.update(this.userId, { $set: { 'profile.GreyIcons': newValue } }); return newValue; }, - toggleDesktopDragHandles() { - const user = ReactiveCache.getCurrentUser(); + async toggleDesktopDragHandles() { + const user = await ReactiveCache.getCurrentUser(); user.toggleDesktopHandles(user.hasShowDesktopDragHandles()); }, // Spaces: create a new space under parentId (or root when null) @@ -2015,16 +2015,16 @@ Meteor.methods({ }); return true; }, - toggleHideCheckedItems() { - const user = ReactiveCache.getCurrentUser(); + async toggleHideCheckedItems() { + const user = await ReactiveCache.getCurrentUser(); user.toggleHideCheckedItems(); }, - toggleCustomFieldsGrid() { - const user = ReactiveCache.getCurrentUser(); + async toggleCustomFieldsGrid() { + const user = await ReactiveCache.getCurrentUser(); user.toggleFieldsGrid(user.hasCustomFieldsGrid()); }, - toggleCardMaximized() { - const user = ReactiveCache.getCurrentUser(); + async toggleCardMaximized() { + const user = await ReactiveCache.getCurrentUser(); user.toggleCardMaximized(user.hasCardMaximized()); }, setCardCollapsed(value) { @@ -2032,32 +2032,32 @@ Meteor.methods({ if (!this.userId) throw new Meteor.Error('not-logged-in'); Users.update(this.userId, { $set: { 'profile.cardCollapsed': value } }); }, - toggleMinicardLabelText() { - const user = ReactiveCache.getCurrentUser(); + async toggleMinicardLabelText() { + const user = await ReactiveCache.getCurrentUser(); user.toggleLabelText(user.hasHiddenMinicardLabelText()); }, - toggleRescueCardDescription() { - const user = ReactiveCache.getCurrentUser(); + async toggleRescueCardDescription() { + const user = await ReactiveCache.getCurrentUser(); user.toggleRescueCardDescription(user.hasRescuedCardDescription()); }, - changeLimitToShowCardsCount(limit) { + async changeLimitToShowCardsCount(limit) { check(limit, Number); - ReactiveCache.getCurrentUser().setShowCardsCountAt(limit); + (await ReactiveCache.getCurrentUser()).setShowCardsCountAt(limit); }, - changeStartDayOfWeek(startDay) { + async changeStartDayOfWeek(startDay) { check(startDay, Number); - ReactiveCache.getCurrentUser().setStartDayOfWeek(startDay); + (await ReactiveCache.getCurrentUser()).setStartDayOfWeek(startDay); }, - changeDateFormat(dateFormat) { + async changeDateFormat(dateFormat) { check(dateFormat, String); - ReactiveCache.getCurrentUser().setDateFormat(dateFormat); + (await ReactiveCache.getCurrentUser()).setDateFormat(dateFormat); }, - applyListWidth(boardId, listId, width, constraint) { + async applyListWidth(boardId, listId, width, constraint) { check(boardId, String); check(listId, String); check(width, Number); check(constraint, Number); - const user = ReactiveCache.getCurrentUser(); + const user = await ReactiveCache.getCurrentUser(); user.setListWidth(boardId, listId, width); user.setListConstraint(boardId, listId, constraint); }, @@ -2081,11 +2081,11 @@ Meteor.methods({ }, }); }, - applySwimlaneHeight(boardId, swimlaneId, height) { + async applySwimlaneHeight(boardId, swimlaneId, height) { check(boardId, String); check(swimlaneId, String); check(height, Number); - const user = ReactiveCache.getCurrentUser(); + const user = await ReactiveCache.getCurrentUser(); user.setSwimlaneHeight(boardId, swimlaneId, height); }, @@ -2110,42 +2110,42 @@ Meteor.methods({ }); }, - applySwimlaneHeightToStorage(boardId, swimlaneId, height) { + async applySwimlaneHeightToStorage(boardId, swimlaneId, height) { check(boardId, String); check(swimlaneId, String); check(height, Number); - const user = ReactiveCache.getCurrentUser(); + const user = await ReactiveCache.getCurrentUser(); if (user) { user.setSwimlaneHeightToStorage(boardId, swimlaneId, height); } // For non-logged-in users, the client-side code will handle localStorage }, - applyListWidthToStorage(boardId, listId, width, constraint) { + async applyListWidthToStorage(boardId, listId, width, constraint) { check(boardId, String); check(listId, String); check(width, Number); check(constraint, Number); - const user = ReactiveCache.getCurrentUser(); + const user = await ReactiveCache.getCurrentUser(); if (user) { user.setListWidthToStorage(boardId, listId, width); user.setListConstraintToStorage(boardId, listId, constraint); } // For non-logged-in users, the client-side code will handle localStorage }, - setZoomLevel(level) { + async setZoomLevel(level) { check(level, Number); - const user = ReactiveCache.getCurrentUser(); + const user = await ReactiveCache.getCurrentUser(); user.setZoomLevel(level); }, - setMobileMode(enabled) { + async setMobileMode(enabled) { check(enabled, Boolean); - const user = ReactiveCache.getCurrentUser(); + const user = await ReactiveCache.getCurrentUser(); user.setMobileMode(enabled); }, - setBoardView(view) { + async setBoardView(view) { check(view, String); - const user = ReactiveCache.getCurrentUser(); + const user = await ReactiveCache.getCurrentUser(); if (!user) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } @@ -2155,7 +2155,7 @@ Meteor.methods({ if (Meteor.isServer) { Meteor.methods({ - setCreateUser( + async setCreateUser( fullname, username, initials, @@ -2185,13 +2185,13 @@ if (Meteor.isServer) { initials.includes('/')) { return false; } - if (ReactiveCache.getCurrentUser()?.isAdmin) { - const nUsersWithUsername = ReactiveCache.getUsers({ + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { + const nUsersWithUsername = (await ReactiveCache.getUsers({ username, - }).length; - const nUsersWithEmail = ReactiveCache.getUsers({ + })).length; + const nUsersWithEmail = (await ReactiveCache.getUsers({ email, - }).length; + })).length; if (nUsersWithUsername > 0) { throw new Meteor.Error('username-already-taken'); } else if (nUsersWithEmail > 0) { @@ -2206,8 +2206,8 @@ if (Meteor.isServer) { from: 'admin', }); const user = - ReactiveCache.getUser(username) || - ReactiveCache.getUser({ username }); + await ReactiveCache.getUser(username) || + await ReactiveCache.getUser({ username }); if (user) { Users.update(user._id, { $set: { @@ -2222,7 +2222,7 @@ if (Meteor.isServer) { } } }, - setUsername(username, userId) { + async setUsername(username, userId) { check(username, String); check(userId, String); // Prevent Hyperlink Injection https://github.com/wekan/wekan/issues/5176 @@ -2231,10 +2231,10 @@ if (Meteor.isServer) { userId.includes('/')) { return false; } - if (ReactiveCache.getCurrentUser()?.isAdmin) { - const nUsersWithUsername = ReactiveCache.getUsers({ + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { + const nUsersWithUsername = (await ReactiveCache.getUsers({ username, - }).length; + })).length; if (nUsersWithUsername > 0) { throw new Meteor.Error('username-already-taken'); } else { @@ -2246,7 +2246,7 @@ if (Meteor.isServer) { } } }, - setEmail(email, userId) { + async setEmail(email, userId) { check(email, String); check(username, String); // Prevent Hyperlink Injection https://github.com/wekan/wekan/issues/5176 @@ -2255,11 +2255,11 @@ if (Meteor.isServer) { email.includes('/')) { return false; } - if (ReactiveCache.getCurrentUser()?.isAdmin) { + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { if (Array.isArray(email)) { email = email.shift(); } - const existingUser = ReactiveCache.getUser( + const existingUser = await ReactiveCache.getUser( { 'emails.address': email, }, @@ -2285,7 +2285,7 @@ if (Meteor.isServer) { } } }, - setUsernameAndEmail(username, email, userId) { + async setUsernameAndEmail(username, email, userId) { check(username, String); check(email, String); check(userId, String); @@ -2296,22 +2296,22 @@ if (Meteor.isServer) { userId.includes('/')) { return false; } - if (ReactiveCache.getCurrentUser()?.isAdmin) { + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { if (Array.isArray(email)) { email = email.shift(); } - Meteor.call('setUsername', username, userId); - Meteor.call('setEmail', email, userId); + await Meteor.callAsync('setUsername', username, userId); + await Meteor.callAsync('setEmail', email, userId); } }, - setPassword(newPassword, userId) { + async setPassword(newPassword, userId) { check(userId, String); check(newPassword, String); - if (ReactiveCache.getCurrentUser()?.isAdmin) { + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { Accounts.setPassword(userId, newPassword); } }, - setEmailVerified(email, verified, userId) { + async setEmailVerified(email, verified, userId) { check(email, String); check(verified, Boolean); check(userId, String); @@ -2321,7 +2321,7 @@ if (Meteor.isServer) { userId.includes('/')) { return false; } - if (ReactiveCache.getCurrentUser()?.isAdmin) { + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { Users.update(userId, { $set: { emails: [ @@ -2334,7 +2334,7 @@ if (Meteor.isServer) { }); } }, - setInitials(initials, userId) { + async setInitials(initials, userId) { check(initials, String); check(userId, String); // Prevent Hyperlink Injection https://github.com/wekan/wekan/issues/5176 @@ -2343,7 +2343,7 @@ if (Meteor.isServer) { userId.includes('/')) { return false; } - if (ReactiveCache.getCurrentUser()?.isAdmin) { + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { Users.update(userId, { $set: { 'profile.initials': initials, @@ -2352,7 +2352,7 @@ if (Meteor.isServer) { } }, // we accept userId, username, email - inviteUserToBoard(username, boardId) { + async inviteUserToBoard(username, boardId) { check(username, String); check(boardId, String); // Prevent Hyperlink Injection https://github.com/wekan/wekan/issues/5176 @@ -2361,8 +2361,8 @@ if (Meteor.isServer) { boardId.includes('/')) { return false; } - const inviter = ReactiveCache.getCurrentUser(); - const board = ReactiveCache.getBoard(boardId); + const inviter = await ReactiveCache.getCurrentUser(); + const board = await ReactiveCache.getBoard(boardId); const member = _.find(board.members, function(member) { return member.userId === inviter._id; }); if (!member) throw new Meteor.Error('error-board-notAMember'); const allowInvite = member.isActive; @@ -2375,7 +2375,7 @@ if (Meteor.isServer) { const posAt = username.indexOf('@'); let user = null; if (posAt >= 0) { - user = ReactiveCache.getUser({ + user = await ReactiveCache.getUser({ emails: { $elemMatch: { address: username, @@ -2384,15 +2384,15 @@ if (Meteor.isServer) { }); } else { user = - ReactiveCache.getUser(username) || - ReactiveCache.getUser({ username }); + await ReactiveCache.getUser(username) || + await ReactiveCache.getUser({ username }); } if (user) { if (user._id === inviter._id) throw new Meteor.Error('error-user-notAllowSelf'); } else { if (posAt <= 0) throw new Meteor.Error('error-user-doesNotExist'); - if (ReactiveCache.getCurrentSetting().disableRegistration) { + if ((await ReactiveCache.getCurrentSetting()).disableRegistration) { throw new Meteor.Error('error-user-notCreated'); } // Set in lowercase email before creating account @@ -2418,7 +2418,7 @@ if (Meteor.isServer) { }); } Accounts.sendEnrollmentEmail(newUserId); - user = ReactiveCache.getUser(newUserId); + user = await ReactiveCache.getUser(newUserId); } const memberIndex = board.members.findIndex(m => m.userId === user._id); @@ -2431,7 +2431,7 @@ if (Meteor.isServer) { //Check if there is a subtasks board if (board.subtasksDefaultBoardId) { - const subBoard = ReactiveCache.getBoard(board.subtasksDefaultBoardId); + const subBoard = await ReactiveCache.getBoard(board.subtasksDefaultBoardId); //If there is, also add user to that board if (subBoard) { const subMemberIndex = subBoard.members.findIndex(m => m.userId === user._id); @@ -2495,35 +2495,35 @@ if (Meteor.isServer) { email: user.emails[0].address, }; }, - impersonate(userId) { + async impersonate(userId) { check(userId, String); - if (!ReactiveCache.getUser(userId)) + if (!(await ReactiveCache.getUser(userId))) throw new Meteor.Error(404, 'User not found'); - if (!ReactiveCache.getCurrentUser().isAdmin) + if (!(await ReactiveCache.getCurrentUser()).isAdmin) throw new Meteor.Error(403, 'Permission denied'); ImpersonatedUsers.insert({ - adminId: ReactiveCache.getCurrentUser()._id, + adminId: (await ReactiveCache.getCurrentUser())._id, userId: userId, reason: 'clickedImpersonate', }); this.setUserId(userId); }, - isImpersonated(userId) { + async isImpersonated(userId) { check(userId, String); - const isImpersonated = ReactiveCache.getImpersonatedUser({ userId: userId }); + const isImpersonated = await ReactiveCache.getImpersonatedUser({ userId: userId }); return isImpersonated; }, - setUsersTeamsTeamDisplayName(teamId, teamDisplayName) { + async setUsersTeamsTeamDisplayName(teamId, teamDisplayName) { check(teamId, String); check(teamDisplayName, String); - if (ReactiveCache.getCurrentUser()?.isAdmin) { - ReactiveCache.getUsers({ + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { + for (const user of await ReactiveCache.getUsers({ teams: { $elemMatch: { teamId: teamId }, }, - }).forEach((user) => { + })) { Users.update( { _id: user._id, @@ -2537,18 +2537,18 @@ if (Meteor.isServer) { }, }, ); - }); + } } }, - setUsersOrgsOrgDisplayName(orgId, orgDisplayName) { + async setUsersOrgsOrgDisplayName(orgId, orgDisplayName) { check(orgId, String); check(orgDisplayName, String); - if (ReactiveCache.getCurrentUser()?.isAdmin) { - ReactiveCache.getUsers({ + if ((await ReactiveCache.getCurrentUser())?.isAdmin) { + for (const user of await ReactiveCache.getUsers({ orgs: { $elemMatch: { orgId: orgId }, }, - }).forEach((user) => { + })) { Users.update( { _id: user._id, @@ -2562,12 +2562,12 @@ if (Meteor.isServer) { }, }, ); - }); + } } }, }); - Accounts.onCreateUser((options, user) => { - const userCount = ReactiveCache.getUsers({}, {}, true).count(); + Accounts.onCreateUser(async (options, user) => { + const userCount = (await ReactiveCache.getUsers({}, {}, true)).count(); user.isAdmin = userCount === 0; if (user.services.oidc) { @@ -2607,7 +2607,7 @@ if (Meteor.isServer) { user.authenticationMethod = 'oauth2'; // see if any existing user has this email address or username, otherwise create new - const existingUser = ReactiveCache.getUser({ + const existingUser = await ReactiveCache.getUser({ $or: [ { 'emails.address': email, @@ -2641,7 +2641,7 @@ if (Meteor.isServer) { return user; } - const disableRegistration = ReactiveCache.getCurrentSetting().disableRegistration; + const disableRegistration = (await ReactiveCache.getCurrentSetting()).disableRegistration; // If this is the first Authentication by the ldap and self registration disabled if (disableRegistration && options && options.ldap) { user.authenticationMethod = 'ldap'; @@ -2659,7 +2659,7 @@ if (Meteor.isServer) { 'The invitation code is required', ); } - const invitationCode = ReactiveCache.getInvitationCode({ + const invitationCode = await ReactiveCache.getInvitationCode({ code: options.profile.invitationcode, email: options.email, valid: true, @@ -2702,8 +2702,8 @@ const addCronJob = _.debounce( SyncedCron.add({ name: 'notification_cleanup', schedule: (parser) => parser.text('every 1 days'), - job: () => { - for (const user of ReactiveCache.getUsers()) { + job: async () => { + for (const user of await ReactiveCache.getUsers()) { if (!user.profile || !user.profile.notifications) continue; for (const notification of user.profile.notifications) { if (notification.read) { @@ -2938,9 +2938,9 @@ if (Meteor.isServer) { }); } - Users.after.insert((userId, doc) => { + Users.after.insert(async (userId, doc) => { // HACK - doc = ReactiveCache.getUser(doc._id); + doc = await ReactiveCache.getUser(doc._id); if (doc.createdThroughApi) { // The admin user should be able to create a user despite disabling registration because // it is two different things (registration and creation). @@ -2957,19 +2957,19 @@ if (Meteor.isServer) { } //invite user to corresponding boards - const disableRegistration = ReactiveCache.getCurrentSetting().disableRegistration; + const disableRegistration = (await ReactiveCache.getCurrentSetting()).disableRegistration; // If ldap, bypass the inviation code if the self registration isn't allowed. // TODO : pay attention if ldap field in the user model change to another content ex : ldap field to connection_type if (doc.authenticationMethod !== 'ldap' && disableRegistration) { let invitationCode = null; if (doc.authenticationMethod.toLowerCase() == 'oauth2') { // OIDC authentication mode - invitationCode = ReactiveCache.getInvitationCode({ + invitationCode = await ReactiveCache.getInvitationCode({ email: doc.emails[0].address.toLowerCase(), valid: true, }); } else { - invitationCode = ReactiveCache.getInvitationCode({ + invitationCode = await ReactiveCache.getInvitationCode({ code: doc.profile.icode, valid: true, }); @@ -2977,15 +2977,15 @@ if (Meteor.isServer) { if (!invitationCode) { throw new Meteor.Error('error-invitation-code-not-exist'); } else { - invitationCode.boardsToBeInvited.forEach((boardId) => { - const board = ReactiveCache.getBoard(boardId); + for (const boardId of invitationCode.boardsToBeInvited) { + const board = await ReactiveCache.getBoard(boardId); const memberIndex = board.members.findIndex(m => m.userId === doc._id); if (memberIndex >= 0) { Boards.update(boardId, { $set: { [`members.${memberIndex}.isActive`]: true } }); } else { Boards.update(boardId, { $push: { members: { userId: doc._id, isAdmin: false, isActive: true, isNoComments: false, isCommentOnly: false, isWorker: false, isNormalAssignedOnly: false, isCommentAssignedOnly: false, isReadOnly: false, isReadAssignedOnly: false } } }); } - }); + } if (!doc.profile) { doc.profile = {}; } @@ -3026,16 +3026,16 @@ if (Meteor.isServer) { * @summary returns the current user * @return_type Users */ - JsonRoutes.add('GET', '/api/user', function (req, res) { + JsonRoutes.add('GET', '/api/user', async function (req, res) { try { Authentication.checkLoggedIn(req.userId); - const data = ReactiveCache.getUser({ + const data = await ReactiveCache.getUser({ _id: req.userId, }); delete data.services; // get all boards where the user is member of - let boards = ReactiveCache.getBoards( + let boards = await ReactiveCache.getBoards( { type: 'board', 'members.userId': req.userId, @@ -3106,22 +3106,22 @@ if (Meteor.isServer) { * @param {string} userId the user ID or username * @return_type Users */ - JsonRoutes.add('GET', '/api/users/:userId', function (req, res) { + JsonRoutes.add('GET', '/api/users/:userId', async function (req, res) { try { Authentication.checkUserId(req.userId); let id = req.params.userId; - let user = ReactiveCache.getUser({ + let user = await ReactiveCache.getUser({ _id: id, }); if (!user) { - user = ReactiveCache.getUser({ + user = await ReactiveCache.getUser({ username: id, }); id = user._id; } // get all boards where the user is member of - let boards = ReactiveCache.getBoards( + let boards = await ReactiveCache.getBoards( { type: 'board', 'members.userId': id, @@ -3175,12 +3175,12 @@ if (Meteor.isServer) { Authentication.checkUserId(req.userId); const id = req.params.userId; const action = req.body.action; - let data = ReactiveCache.getUser({ + let data = await ReactiveCache.getUser({ _id: id, }); if (data !== undefined) { if (action === 'takeOwnership') { - const boards = ReactiveCache.getBoards( + const boards = await ReactiveCache.getBoards( { 'members.userId': id, 'members.isAdmin': true, @@ -3227,7 +3227,7 @@ if (Meteor.isServer) { }, ); } - data = ReactiveCache.getUser(id); + data = await ReactiveCache.getUser(id); } } JsonRoutes.sendResult(res, { @@ -3270,19 +3270,19 @@ if (Meteor.isServer) { JsonRoutes.add( 'POST', '/api/boards/:boardId/members/:userId/add', - function (req, res) { + async function (req, res) { try { Authentication.checkUserId(req.userId); const userId = req.params.userId; const boardId = req.params.boardId; const action = req.body.action; const { isAdmin, isNoComments, isCommentOnly, isWorker, isNormalAssignedOnly, isCommentAssignedOnly, isReadOnly, isReadAssignedOnly } = req.body; - let data = ReactiveCache.getUser(userId); + let data = await ReactiveCache.getUser(userId); if (data !== undefined) { if (action === 'add') { - data = ReactiveCache.getBoards({ + data = (await ReactiveCache.getBoards({ _id: boardId, - }).map(function (board) { + })).map(function (board) { const hasMember = board.members.some(m => m.userId === userId && m.isActive); if (!hasMember) { const memberIndex = board.members.findIndex(m => m.userId === userId); @@ -3345,18 +3345,18 @@ if (Meteor.isServer) { JsonRoutes.add( 'POST', '/api/boards/:boardId/members/:userId/remove', - function (req, res) { + async function (req, res) { try { Authentication.checkUserId(req.userId); const userId = req.params.userId; const boardId = req.params.boardId; const action = req.body.action; - let data = ReactiveCache.getUser(userId); + let data = await ReactiveCache.getUser(userId); if (data !== undefined) { if (action === 'remove') { - data = ReactiveCache.getBoards({ + data = (await ReactiveCache.getBoards({ _id: boardId, - }).map(function (board) { + })).map(function (board) { const hasMember = board.members.some(m => m.userId === userId && m.isActive); if (hasMember) { const memberIndex = board.members.findIndex(m => m.userId === userId); @@ -3591,7 +3591,7 @@ if (Meteor.isServer) { check(userData, Object); return sanitizeUserForSearch(userData); }, - searchUsers(query, boardId) { + async searchUsers(query, boardId) { check(query, String); check(boardId, String); @@ -3599,8 +3599,8 @@ if (Meteor.isServer) { throw new Meteor.Error('not-logged-in', 'User must be logged in'); } - const currentUser = ReactiveCache.getCurrentUser(); - const board = ReactiveCache.getBoard(boardId); + const currentUser = await ReactiveCache.getCurrentUser(); + const board = await ReactiveCache.getBoard(boardId); // Check if current user is a member of the board const member = _.find(board.members, function(member) { return member.userId === currentUser._id; }); @@ -3613,7 +3613,7 @@ if (Meteor.isServer) { } const searchRegex = new RegExp(query, 'i'); - const users = ReactiveCache.getUsers({ + const users = await ReactiveCache.getUsers({ $or: [ { username: searchRegex }, { 'profile.fullname': searchRegex }, diff --git a/models/wekanCreator.js b/models/wekanCreator.js index 0580ca139..a37ceb0c8 100644 --- a/models/wekanCreator.js +++ b/models/wekanCreator.js @@ -244,14 +244,14 @@ export class WekanCreator { ]); } - getMembersToMap(data) { + async getMembersToMap(data) { // we will work on the list itself (an ordered array of objects) when a // mapping is done, we add a 'wekan' field to the object representing the // imported member const membersToMap = data.members; const users = data.users; // auto-map based on username - membersToMap.forEach(importedMember => { + for (const importedMember of membersToMap) { importedMember.id = importedMember.userId; delete importedMember.userId; const user = users.filter(user => { @@ -261,11 +261,11 @@ export class WekanCreator { importedMember.fullName = user.profile.fullname; } importedMember.username = user.username; - const wekanUser = ReactiveCache.getUser({ username: importedMember.username }); + const wekanUser = await ReactiveCache.getUser({ username: importedMember.username }); if (wekanUser) { importedMember.wekanId = wekanUser._id; } - }); + } return membersToMap; } @@ -665,8 +665,8 @@ export class WekanCreator { }); } - createSubtasks(wekanCards) { - wekanCards.forEach(card => { + async createSubtasks(wekanCards) { + for (const card of wekanCards) { // get new id of card (in created / new board) const cardIdInNewBoard = this.cards[card._id]; @@ -683,7 +683,7 @@ export class WekanCreator { : card.parentId; //if the parent card exists, proceed - if (ReactiveCache.getCard(parentIdInNewBoard)) { + if (await ReactiveCache.getCard(parentIdInNewBoard)) { //set parent id of the card in the new board to the new id of the parent Cards.direct.update(cardIdInNewBoard, { $set: { @@ -691,7 +691,7 @@ export class WekanCreator { }, }); } - }); + } } createChecklists(wekanChecklists) { @@ -978,7 +978,7 @@ export class WekanCreator { Meteor.settings.public && Meteor.settings.public.sandstorm; if (isSandstorm && currentBoardId) { - const currentBoard = ReactiveCache.getBoard(currentBoardId); + const currentBoard = await ReactiveCache.getBoard(currentBoardId); await currentBoard.archive(); } this.parseActivities(board); @@ -987,7 +987,7 @@ export class WekanCreator { this.createSwimlanes(board.swimlanes, boardId); this.createCustomFields(board.customFields, boardId); this.createCards(board.cards, boardId); - this.createSubtasks(board.cards); + await this.createSubtasks(board.cards); this.createChecklists(board.checklists); this.createChecklistItems(board.checklistItems); this.importActivities(board.activities, boardId); diff --git a/models/wekanmapper.js b/models/wekanmapper.js index 3dc449c7a..be849af7c 100644 --- a/models/wekanmapper.js +++ b/models/wekanmapper.js @@ -1,13 +1,13 @@ import { ReactiveCache } from '/imports/reactiveCache'; -export function getMembersToMap(data) { +export async function getMembersToMap(data) { // we will work on the list itself (an ordered array of objects) when a // mapping is done, we add a 'wekan' field to the object representing the // imported member const membersToMap = data.members; const users = data.users; // auto-map based on username - membersToMap.forEach(importedMember => { + for (const importedMember of membersToMap) { importedMember.id = importedMember.userId; delete importedMember.userId; const user = users.filter(user => { @@ -17,10 +17,10 @@ export function getMembersToMap(data) { importedMember.fullName = user.profile.fullname; } importedMember.username = user.username; - const wekanUser = ReactiveCache.getUser({ username: importedMember.username }); + const wekanUser = await ReactiveCache.getUser({ username: importedMember.username }); if (wekanUser) { importedMember.wekanId = wekanUser._id; } - }); + } return membersToMap; } diff --git a/server/attachmentApi.js b/server/attachmentApi.js index 148753548..a0cafd198 100644 --- a/server/attachmentApi.js +++ b/server/attachmentApi.js @@ -12,7 +12,7 @@ import { ObjectID } from 'bson'; if (Meteor.isServer) { Meteor.methods({ // Upload attachment via API - 'api.attachment.upload'(boardId, swimlaneId, listId, cardId, fileData, fileName, fileType, storageBackend) { + async 'api.attachment.upload'(boardId, swimlaneId, listId, cardId, fileData, fileName, fileType, storageBackend) { if (!this.userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } @@ -23,12 +23,12 @@ if (Meteor.isServer) { } // Check if user has permission to modify the card - const card = ReactiveCache.getCard(cardId); + const card = await ReactiveCache.getCard(cardId); if (!card) { throw new Meteor.Error('card-not-found', 'Card not found'); } - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) { throw new Meteor.Error('board-not-found', 'Board not found'); } @@ -114,19 +114,19 @@ if (Meteor.isServer) { }, // Download attachment via API - 'api.attachment.download'(attachmentId) { + async 'api.attachment.download'(attachmentId) { if (!this.userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } // Get attachment - const attachment = ReactiveCache.getAttachment(attachmentId); + const attachment = await ReactiveCache.getAttachment(attachmentId); if (!attachment) { throw new Meteor.Error('attachment-not-found', 'Attachment not found'); } // Check permissions - const board = ReactiveCache.getBoard(attachment.meta.boardId); + const board = await ReactiveCache.getBoard(attachment.meta.boardId); if (!board || !board.isBoardMember(this.userId)) { throw new Meteor.Error('not-authorized', 'You do not have permission to access this attachment'); } @@ -173,13 +173,13 @@ if (Meteor.isServer) { }, // List attachments for board, swimlane, list, or card - 'api.attachment.list'(boardId, swimlaneId, listId, cardId) { + async 'api.attachment.list'(boardId, swimlaneId, listId, cardId) { if (!this.userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } // Check permissions - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board || !board.isBoardMember(this.userId)) { throw new Meteor.Error('not-authorized', 'You do not have permission to access this board'); } @@ -199,7 +199,7 @@ if (Meteor.isServer) { query['meta.cardId'] = cardId; } - const attachments = ReactiveCache.getAttachments(query); + const attachments = await ReactiveCache.getAttachments(query); const attachmentList = attachments.map(attachment => { const strategy = fileStoreStrategyFactory.getFileStrategy(attachment, 'original'); @@ -230,25 +230,25 @@ if (Meteor.isServer) { }, // Copy attachment to another card - 'api.attachment.copy'(attachmentId, targetBoardId, targetSwimlaneId, targetListId, targetCardId) { + async 'api.attachment.copy'(attachmentId, targetBoardId, targetSwimlaneId, targetListId, targetCardId) { if (!this.userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } // Get source attachment - const sourceAttachment = ReactiveCache.getAttachment(attachmentId); + const sourceAttachment = await ReactiveCache.getAttachment(attachmentId); if (!sourceAttachment) { throw new Meteor.Error('attachment-not-found', 'Source attachment not found'); } // Check source permissions - const sourceBoard = ReactiveCache.getBoard(sourceAttachment.meta.boardId); + const sourceBoard = await ReactiveCache.getBoard(sourceAttachment.meta.boardId); if (!sourceBoard || !sourceBoard.isBoardMember(this.userId)) { throw new Meteor.Error('not-authorized', 'You do not have permission to access the source attachment'); } // Check target permissions - const targetBoard = ReactiveCache.getBoard(targetBoardId); + const targetBoard = await ReactiveCache.getBoard(targetBoardId); if (!targetBoard || !targetBoard.isBoardMember(this.userId)) { throw new Meteor.Error('not-authorized', 'You do not have permission to modify the target card'); } @@ -328,25 +328,25 @@ if (Meteor.isServer) { }, // Move attachment to another card - 'api.attachment.move'(attachmentId, targetBoardId, targetSwimlaneId, targetListId, targetCardId) { + async 'api.attachment.move'(attachmentId, targetBoardId, targetSwimlaneId, targetListId, targetCardId) { if (!this.userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } // Get source attachment - const sourceAttachment = ReactiveCache.getAttachment(attachmentId); + const sourceAttachment = await ReactiveCache.getAttachment(attachmentId); if (!sourceAttachment) { throw new Meteor.Error('attachment-not-found', 'Source attachment not found'); } // Check source permissions - const sourceBoard = ReactiveCache.getBoard(sourceAttachment.meta.boardId); + const sourceBoard = await ReactiveCache.getBoard(sourceAttachment.meta.boardId); if (!sourceBoard || !sourceBoard.isBoardMember(this.userId)) { throw new Meteor.Error('not-authorized', 'You do not have permission to access the source attachment'); } // Check target permissions - const targetBoard = ReactiveCache.getBoard(targetBoardId); + const targetBoard = await ReactiveCache.getBoard(targetBoardId); if (!targetBoard || !targetBoard.isBoardMember(this.userId)) { throw new Meteor.Error('not-authorized', 'You do not have permission to modify the target card'); } @@ -385,19 +385,19 @@ if (Meteor.isServer) { }, // Delete attachment via API - 'api.attachment.delete'(attachmentId) { + async 'api.attachment.delete'(attachmentId) { if (!this.userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } // Get attachment - const attachment = ReactiveCache.getAttachment(attachmentId); + const attachment = await ReactiveCache.getAttachment(attachmentId); if (!attachment) { throw new Meteor.Error('attachment-not-found', 'Attachment not found'); } // Check permissions - const board = ReactiveCache.getBoard(attachment.meta.boardId); + const board = await ReactiveCache.getBoard(attachment.meta.boardId); if (!board || !board.isBoardMember(this.userId)) { throw new Meteor.Error('not-authorized', 'You do not have permission to delete this attachment'); } @@ -419,19 +419,19 @@ if (Meteor.isServer) { }, // Get attachment info via API - 'api.attachment.info'(attachmentId) { + async 'api.attachment.info'(attachmentId) { if (!this.userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } // Get attachment - const attachment = ReactiveCache.getAttachment(attachmentId); + const attachment = await ReactiveCache.getAttachment(attachmentId); if (!attachment) { throw new Meteor.Error('attachment-not-found', 'Attachment not found'); } // Check permissions - const board = ReactiveCache.getBoard(attachment.meta.boardId); + const board = await ReactiveCache.getBoard(attachment.meta.boardId); if (!board || !board.isBoardMember(this.userId)) { throw new Meteor.Error('not-authorized', 'You do not have permission to access this attachment'); } diff --git a/server/attachmentMigration.js b/server/attachmentMigration.js index 318893067..f9dc87b4f 100644 --- a/server/attachmentMigration.js +++ b/server/attachmentMigration.js @@ -119,13 +119,13 @@ class AttachmentMigrationService { async migrateAttachment(attachment) { try { // Get the card to find board and list information - const card = ReactiveCache.getCard(attachment.cardId); + const card = await ReactiveCache.getCard(attachment.cardId); if (!card) { console.warn(`Card not found for attachment ${attachment._id}`); return; } - const list = ReactiveCache.getList(card.listId); + const list = await ReactiveCache.getList(card.listId); if (!list) { console.warn(`List not found for attachment ${attachment._id}`); return; @@ -203,17 +203,17 @@ const attachmentMigrationService = new AttachmentMigrationService(); Meteor.methods({ async 'attachmentMigration.migrateBoardAttachments'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized'); } - - const board = ReactiveCache.getBoard(boardId); + + const board = await ReactiveCache.getBoard(boardId); if (!board) { throw new Meteor.Error('board-not-found'); } - - const user = ReactiveCache.getUser(this.userId); + + const user = await ReactiveCache.getUser(this.userId); const isBoardAdmin = board.hasAdmin(this.userId); const isInstanceAdmin = user && user.isAdmin; @@ -224,14 +224,14 @@ Meteor.methods({ return await attachmentMigrationService.migrateBoardAttachments(boardId); }, - 'attachmentMigration.getProgress'(boardId) { + async 'attachmentMigration.getProgress'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized'); } - - const board = ReactiveCache.getBoard(boardId); + + const board = await ReactiveCache.getBoard(boardId); if (!board || !board.isVisibleBy({ _id: this.userId })) { throw new Meteor.Error('not-authorized', 'You do not have access to this board.'); } @@ -239,14 +239,14 @@ Meteor.methods({ return attachmentMigrationService.getMigrationProgress(boardId); }, - 'attachmentMigration.getUnconvertedAttachments'(boardId) { + async 'attachmentMigration.getUnconvertedAttachments'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized'); } - - const board = ReactiveCache.getBoard(boardId); + + const board = await ReactiveCache.getBoard(boardId); if (!board || !board.isVisibleBy({ _id: this.userId })) { throw new Meteor.Error('not-authorized', 'You do not have access to this board.'); } @@ -254,14 +254,14 @@ Meteor.methods({ return attachmentMigrationService.getUnconvertedAttachments(boardId); }, - 'attachmentMigration.isBoardMigrated'(boardId) { + async 'attachmentMigration.isBoardMigrated'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized'); } - - const board = ReactiveCache.getBoard(boardId); + + const board = await ReactiveCache.getBoard(boardId); if (!board || !board.isVisibleBy({ _id: this.userId })) { throw new Meteor.Error('not-authorized', 'You do not have access to this board.'); } diff --git a/server/authentication.js b/server/authentication.js index 5b7ad2ee6..77aac7f9f 100644 --- a/server/authentication.js +++ b/server/authentication.js @@ -15,13 +15,13 @@ Meteor.startup(() => { Authentication = {}; - Authentication.checkUserId = function(userId) { + Authentication.checkUserId = async function(userId) { if (userId === undefined) { const error = new Meteor.Error('Unauthorized', 'Unauthorized'); error.statusCode = 401; throw error; } - const admin = ReactiveCache.getUser({ _id: userId, isAdmin: true }); + const admin = await ReactiveCache.getUser({ _id: userId, isAdmin: true }); if (admin === undefined) { const error = new Meteor.Error('Forbidden', 'Forbidden'); @@ -42,9 +42,9 @@ Meteor.startup(() => { // An admin should be authorized to access everything, so we use a separate check for admins // This throws an error if otherReq is false and the user is not an admin - Authentication.checkAdminOrCondition = function(userId, otherReq) { + Authentication.checkAdminOrCondition = async function(userId, otherReq) { if (otherReq) return; - const admin = ReactiveCache.getUser({ _id: userId, isAdmin: true }); + const admin = await ReactiveCache.getUser({ _id: userId, isAdmin: true }); if (admin === undefined) { const error = new Meteor.Error('Forbidden', 'Forbidden'); error.statusCode = 403; @@ -53,19 +53,19 @@ Meteor.startup(() => { }; // Helper function. Will throw an error if the user is not active BoardAdmin or active Normal user of the board. - Authentication.checkBoardAccess = function(userId, boardId) { + Authentication.checkBoardAccess = async function(userId, boardId) { Authentication.checkLoggedIn(userId); - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); const normalAccess = board.members.some(e => e.userId === userId && e.isActive && !e.isNoComments && !e.isCommentOnly && !e.isWorker); - Authentication.checkAdminOrCondition(userId, normalAccess); + await Authentication.checkAdminOrCondition(userId, normalAccess); }; // Helper function. Will throw an error if the user does not have write access to the board (excludes read-only users). - Authentication.checkBoardWriteAccess = function(userId, boardId) { + Authentication.checkBoardWriteAccess = async function(userId, boardId) { Authentication.checkLoggedIn(userId); - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); const writeAccess = board.members.some(e => e.userId === userId && e.isActive && !e.isNoComments && !e.isCommentOnly && !e.isWorker && !e.isReadOnly && !e.isReadAssignedOnly); - Authentication.checkAdminOrCondition(userId, writeAccess); + await Authentication.checkAdminOrCondition(userId, writeAccess); }; if (Meteor.isServer) { diff --git a/server/cronMigrationManager.js b/server/cronMigrationManager.js index ffb5801bc..580d1d0d6 100644 --- a/server/cronMigrationManager.js +++ b/server/cronMigrationManager.js @@ -1782,134 +1782,134 @@ Meteor.startup(() => { // Meteor methods for client-server communication Meteor.methods({ - 'cron.startAllMigrations'() { + async 'cron.startAllMigrations'() { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronMigrationManager.startAllMigrations(); }, - 'cron.startSpecificMigration'(migrationIndex) { + async 'cron.startSpecificMigration'(migrationIndex) { check(migrationIndex, Number); const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronMigrationManager.startSpecificMigration(migrationIndex); }, - 'cron.startJob'(cronName) { + async 'cron.startJob'(cronName) { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronMigrationManager.startCronJob(cronName); }, - 'cron.stopJob'(cronName) { + async 'cron.stopJob'(cronName) { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronMigrationManager.stopCronJob(cronName); }, - 'cron.pauseJob'(cronName) { + async 'cron.pauseJob'(cronName) { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronMigrationManager.pauseCronJob(cronName); }, - 'cron.resumeJob'(cronName) { + async 'cron.resumeJob'(cronName) { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronMigrationManager.resumeCronJob(cronName); }, - 'cron.removeJob'(cronName) { + async 'cron.removeJob'(cronName) { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronMigrationManager.removeCronJob(cronName); }, - 'cron.addJob'(jobData) { + async 'cron.addJob'(jobData) { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronMigrationManager.addCronJob(jobData); }, - 'cron.getJobs'() { + async 'cron.getJobs'() { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronMigrationManager.getAllCronJobs(); }, - 'cron.getMigrationProgress'() { + async 'cron.getMigrationProgress'() { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return { progress: cronMigrationProgress.get(), status: cronMigrationStatus.get(), @@ -1921,337 +1921,337 @@ Meteor.methods({ }; }, - 'cron.pauseAllMigrations'() { + async 'cron.pauseAllMigrations'() { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronMigrationManager.pauseAllMigrations(); }, - 'cron.resumeAllMigrations'() { + async 'cron.resumeAllMigrations'() { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronMigrationManager.resumeAllMigrations(); }, - 'cron.retryFailedMigrations'() { + async 'cron.retryFailedMigrations'() { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronMigrationManager.retryFailedMigrations(); }, - 'cron.getAllMigrationErrors'(limit = 50) { + async 'cron.getAllMigrationErrors'(limit = 50) { check(limit, Match.Optional(Number)); - + const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronMigrationManager.getAllMigrationErrors(limit); }, - 'cron.getJobErrors'(jobId, options = {}) { + async 'cron.getJobErrors'(jobId, options = {}) { check(jobId, String); check(options, Match.Optional(Object)); - + const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronMigrationManager.getJobErrors(jobId, options); }, - 'cron.getMigrationStats'() { + async 'cron.getMigrationStats'() { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronMigrationManager.getMigrationStats(); }, - 'cron.startBoardOperation'(boardId, operationType, operationData) { + async 'cron.startBoardOperation'(boardId, operationType, operationData) { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - + // Check if user is global admin OR board admin - const user = ReactiveCache.getUser(userId); - const board = ReactiveCache.getBoard(boardId); - + const user = await ReactiveCache.getUser(userId); + const board = await ReactiveCache.getBoard(boardId); + if (!user) { throw new Meteor.Error('not-authorized', 'User not found'); } - + if (!board) { throw new Meteor.Error('not-found', 'Board not found'); } - + // Check global admin or board admin const isGlobalAdmin = user.isAdmin; - const isBoardAdmin = board.members && board.members.some(member => + const isBoardAdmin = board.members && board.members.some(member => member.userId === userId && member.isAdmin ); - + if (!isGlobalAdmin && !isBoardAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required for this board'); } - + return cronMigrationManager.startBoardOperation(boardId, operationType, operationData); }, - 'cron.getBoardOperations'(boardId) { + async 'cron.getBoardOperations'(boardId) { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - + // Check if user is global admin OR board admin - const user = ReactiveCache.getUser(userId); - const board = ReactiveCache.getBoard(boardId); - + const user = await ReactiveCache.getUser(userId); + const board = await ReactiveCache.getBoard(boardId); + if (!user) { throw new Meteor.Error('not-authorized', 'User not found'); } - + if (!board) { throw new Meteor.Error('not-found', 'Board not found'); } - + // Check global admin or board admin const isGlobalAdmin = user.isAdmin; - const isBoardAdmin = board.members && board.members.some(member => + const isBoardAdmin = board.members && board.members.some(member => member.userId === userId && member.isAdmin ); - + if (!isGlobalAdmin && !isBoardAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required for this board'); } - + return cronMigrationManager.getBoardOperations(boardId); }, - 'cron.getAllBoardOperations'(page, limit, searchTerm) { + async 'cron.getAllBoardOperations'(page, limit, searchTerm) { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronMigrationManager.getAllBoardOperations(page, limit, searchTerm); }, - 'cron.getBoardOperationStats'() { + async 'cron.getBoardOperationStats'() { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronMigrationManager.getBoardOperationStats(); }, - 'cron.getJobDetails'(jobId) { + async 'cron.getJobDetails'(jobId) { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronJobStorage.getJobDetails(jobId); }, - 'cron.getQueueStats'() { + async 'cron.getQueueStats'() { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronJobStorage.getQueueStats(); }, - 'cron.getSystemResources'() { + async 'cron.getSystemResources'() { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronJobStorage.getSystemResources(); }, - 'cron.clearAllJobs'() { + async 'cron.clearAllJobs'() { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronMigrationManager.clearAllCronJobs(); }, - 'cron.pauseJob'(jobId) { + async 'cron.pauseJob'(jobId) { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + cronJobStorage.updateQueueStatus(jobId, 'paused'); cronJobStorage.saveJobStatus(jobId, { status: 'paused' }); return { success: true }; }, - 'cron.resumeJob'(jobId) { + async 'cron.resumeJob'(jobId) { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + cronJobStorage.updateQueueStatus(jobId, 'pending'); cronJobStorage.saveJobStatus(jobId, { status: 'pending' }); return { success: true }; }, - 'cron.stopJob'(jobId) { + async 'cron.stopJob'(jobId) { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + cronJobStorage.updateQueueStatus(jobId, 'stopped'); - cronJobStorage.saveJobStatus(jobId, { + cronJobStorage.saveJobStatus(jobId, { status: 'stopped', stoppedAt: new Date() }); return { success: true }; }, - 'cron.cleanupOldJobs'(daysOld) { + async 'cron.cleanupOldJobs'(daysOld) { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + return cronJobStorage.cleanupOldJobs(daysOld); }, - 'cron.pauseAllMigrations'() { + async 'cron.pauseAllMigrations'() { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + // Pause all running jobs in the queue const runningJobs = cronJobStorage.getIncompleteJobs().filter(job => job.status === 'running'); runningJobs.forEach(job => { cronJobStorage.updateQueueStatus(job.jobId, 'paused'); cronJobStorage.saveJobStatus(job.jobId, { status: 'paused' }); }); - + cronMigrationStatus.set('All migrations paused'); return { success: true, message: 'All migrations paused' }; }, - 'cron.stopAllMigrations'() { + async 'cron.stopAllMigrations'() { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + // Clear monitor interval first to prevent status override if (cronMigrationManager.monitorInterval) { Meteor.clearInterval(cronMigrationManager.monitorInterval); cronMigrationManager.monitorInterval = null; } - + // Stop all running and pending jobs const incompleteJobs = cronJobStorage.getIncompleteJobs(); incompleteJobs.forEach(job => { cronJobStorage.updateQueueStatus(job.jobId, 'stopped', { stoppedAt: new Date() }); - cronJobStorage.saveJobStatus(job.jobId, { + cronJobStorage.saveJobStatus(job.jobId, { status: 'stopped', stoppedAt: new Date() }); }); - + // Reset migration state immediately cronMigrationManager.isRunning = false; cronIsMigrating.set(false); @@ -2260,40 +2260,40 @@ Meteor.methods({ cronMigrationCurrentStepNum.set(0); cronMigrationTotalSteps.set(0); cronMigrationStatus.set('All migrations stopped'); - + // Clear status message after delay setTimeout(() => { cronMigrationStatus.set(''); }, 3000); - + return { success: true, message: 'All migrations stopped' }; }, - 'cron.getBoardMigrationStats'() { + async 'cron.getBoardMigrationStats'() { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + // Import the board migration detector const { boardMigrationDetector } = require('./boardMigrationDetector'); return boardMigrationDetector.getMigrationStats(); }, - 'cron.forceBoardMigrationScan'() { + async 'cron.forceBoardMigrationScan'() { const userId = this.userId; if (!userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Admin access required'); } - + // Import the board migration detector const { boardMigrationDetector } = require('./boardMigrationDetector'); return boardMigrationDetector.forceScan(); diff --git a/server/lib/emailLocalization.js b/server/lib/emailLocalization.js index 4c8bd0b17..19f0e8992 100644 --- a/server/lib/emailLocalization.js +++ b/server/lib/emailLocalization.js @@ -17,13 +17,13 @@ EmailLocalization = { * @param {String} options.language - Language code to use (if not provided, will try to detect) * @param {String} options.userId - User ID to determine language (if not provided with language) */ - sendEmail(options) { + async sendEmail(options) { // Determine the language to use let lang = options.language; // If no language is specified but we have a userId, try to get the user's language if (!lang && options.userId) { - const user = ReactiveCache.getUser(options.userId); + const user = await ReactiveCache.getUser(options.userId); if (user) { lang = user.getLanguage(); } diff --git a/server/methods/fixDuplicateLists.js b/server/methods/fixDuplicateLists.js index 8f2cb9e77..1fbc1b2c9 100644 --- a/server/methods/fixDuplicateLists.js +++ b/server/methods/fixDuplicateLists.js @@ -11,12 +11,12 @@ import { ReactiveCache } from '/imports/reactiveCache'; * This method identifies and removes duplicate lists while preserving cards */ Meteor.methods({ - 'fixDuplicateLists.fixAllBoards'() { + async 'fixDuplicateLists.fixAllBoards'() { if (!this.userId) { throw new Meteor.Error('not-authorized'); } - if (!ReactiveCache.getUser(this.userId).isAdmin) { + if (!(await ReactiveCache.getUser(this.userId)).isAdmin) { throw new Meteor.Error('not-authorized', 'Admin required'); } @@ -53,14 +53,14 @@ Meteor.methods({ }; }, - 'fixDuplicateLists.fixBoard'(boardId) { + async 'fixDuplicateLists.fixBoard'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized'); } - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board || !board.hasAdmin(this.userId)) { throw new Meteor.Error('not-authorized'); } @@ -208,12 +208,12 @@ function fixDuplicateLists(boardId) { } Meteor.methods({ - 'fixDuplicateLists.getReport'() { + async 'fixDuplicateLists.getReport'() { if (!this.userId) { throw new Meteor.Error('not-authorized'); } - if (!ReactiveCache.getUser(this.userId).isAdmin) { + if (!(await ReactiveCache.getUser(this.userId)).isAdmin) { throw new Meteor.Error('not-authorized', 'Admin required'); } diff --git a/server/methods/lockedUsers.js b/server/methods/lockedUsers.js index a5de5075b..7458b832e 100644 --- a/server/methods/lockedUsers.js +++ b/server/methods/lockedUsers.js @@ -8,7 +8,7 @@ Meteor.methods({ if (!userId) { throw new Meteor.Error('error-invalid-user', 'Invalid user'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('error-not-allowed', 'Not allowed'); } @@ -56,7 +56,7 @@ Meteor.methods({ if (!adminId) { throw new Meteor.Error('error-invalid-user', 'Invalid user'); } - const admin = ReactiveCache.getUser(adminId); + const admin = await ReactiveCache.getUser(adminId); if (!admin || !admin.isAdmin) { throw new Meteor.Error('error-not-allowed', 'Not allowed'); } @@ -86,7 +86,7 @@ Meteor.methods({ if (!adminId) { throw new Meteor.Error('error-invalid-user', 'Invalid user'); } - const admin = ReactiveCache.getUser(adminId); + const admin = await ReactiveCache.getUser(adminId); if (!admin || !admin.isAdmin) { throw new Meteor.Error('error-not-allowed', 'Not allowed'); } diff --git a/server/methods/lockoutSettings.js b/server/methods/lockoutSettings.js index cf12b083a..70b8efead 100644 --- a/server/methods/lockoutSettings.js +++ b/server/methods/lockoutSettings.js @@ -9,7 +9,7 @@ Meteor.methods({ if (!userId) { throw new Meteor.Error('error-invalid-user', 'Invalid user'); } - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); if (!user || !user.isAdmin) { throw new Meteor.Error('error-not-allowed', 'Not allowed'); } diff --git a/server/methods/positionHistory.js b/server/methods/positionHistory.js index 704b3b9d6..63369cfca 100644 --- a/server/methods/positionHistory.js +++ b/server/methods/positionHistory.js @@ -13,294 +13,294 @@ Meteor.methods({ /** * Track original position for a swimlane */ - 'positionHistory.trackSwimlane'(swimlaneId) { + async 'positionHistory.trackSwimlane'(swimlaneId) { check(swimlaneId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in.'); } - + const swimlane = Swimlanes.findOne(swimlaneId); if (!swimlane) { throw new Meteor.Error('swimlane-not-found', 'Swimlane not found'); } - - const board = ReactiveCache.getBoard(swimlane.boardId); + + const board = await ReactiveCache.getBoard(swimlane.boardId); if (!board || !board.isVisibleBy({ _id: this.userId })) { throw new Meteor.Error('not-authorized', 'You do not have access to this board.'); } - + return swimlane.trackOriginalPosition(); }, /** * Track original position for a list */ - 'positionHistory.trackList'(listId) { + async 'positionHistory.trackList'(listId) { check(listId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in.'); } - + const list = Lists.findOne(listId); if (!list) { throw new Meteor.Error('list-not-found', 'List not found'); } - - const board = ReactiveCache.getBoard(list.boardId); + + const board = await ReactiveCache.getBoard(list.boardId); if (!board || !board.isVisibleBy({ _id: this.userId })) { throw new Meteor.Error('not-authorized', 'You do not have access to this board.'); } - + return list.trackOriginalPosition(); }, /** * Track original position for a card */ - 'positionHistory.trackCard'(cardId) { + async 'positionHistory.trackCard'(cardId) { check(cardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in.'); } - + const card = Cards.findOne(cardId); if (!card) { throw new Meteor.Error('card-not-found', 'Card not found'); } - - const board = ReactiveCache.getBoard(card.boardId); + + const board = await ReactiveCache.getBoard(card.boardId); if (!board || !board.isVisibleBy({ _id: this.userId })) { throw new Meteor.Error('not-authorized', 'You do not have access to this board.'); } - + return card.trackOriginalPosition(); }, /** * Get original position for a swimlane */ - 'positionHistory.getSwimlaneOriginalPosition'(swimlaneId) { + async 'positionHistory.getSwimlaneOriginalPosition'(swimlaneId) { check(swimlaneId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in.'); } - + const swimlane = Swimlanes.findOne(swimlaneId); if (!swimlane) { throw new Meteor.Error('swimlane-not-found', 'Swimlane not found'); } - - const board = ReactiveCache.getBoard(swimlane.boardId); + + const board = await ReactiveCache.getBoard(swimlane.boardId); if (!board || !board.isVisibleBy({ _id: this.userId })) { throw new Meteor.Error('not-authorized', 'You do not have access to this board.'); } - + return swimlane.getOriginalPosition(); }, /** * Get original position for a list */ - 'positionHistory.getListOriginalPosition'(listId) { + async 'positionHistory.getListOriginalPosition'(listId) { check(listId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in.'); } - + const list = Lists.findOne(listId); if (!list) { throw new Meteor.Error('list-not-found', 'List not found'); } - - const board = ReactiveCache.getBoard(list.boardId); + + const board = await ReactiveCache.getBoard(list.boardId); if (!board || !board.isVisibleBy({ _id: this.userId })) { throw new Meteor.Error('not-authorized', 'You do not have access to this board.'); } - + return list.getOriginalPosition(); }, /** * Get original position for a card */ - 'positionHistory.getCardOriginalPosition'(cardId) { + async 'positionHistory.getCardOriginalPosition'(cardId) { check(cardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in.'); } - + const card = Cards.findOne(cardId); if (!card) { throw new Meteor.Error('card-not-found', 'Card not found'); } - - const board = ReactiveCache.getBoard(card.boardId); + + const board = await ReactiveCache.getBoard(card.boardId); if (!board || !board.isVisibleBy({ _id: this.userId })) { throw new Meteor.Error('not-authorized', 'You do not have access to this board.'); } - + return card.getOriginalPosition(); }, /** * Check if a swimlane has moved from its original position */ - 'positionHistory.hasSwimlaneMoved'(swimlaneId) { + async 'positionHistory.hasSwimlaneMoved'(swimlaneId) { check(swimlaneId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in.'); } - + const swimlane = Swimlanes.findOne(swimlaneId); if (!swimlane) { throw new Meteor.Error('swimlane-not-found', 'Swimlane not found'); } - - const board = ReactiveCache.getBoard(swimlane.boardId); + + const board = await ReactiveCache.getBoard(swimlane.boardId); if (!board || !board.isVisibleBy({ _id: this.userId })) { throw new Meteor.Error('not-authorized', 'You do not have access to this board.'); } - + return swimlane.hasMovedFromOriginalPosition(); }, /** * Check if a list has moved from its original position */ - 'positionHistory.hasListMoved'(listId) { + async 'positionHistory.hasListMoved'(listId) { check(listId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in.'); } - + const list = Lists.findOne(listId); if (!list) { throw new Meteor.Error('list-not-found', 'List not found'); } - - const board = ReactiveCache.getBoard(list.boardId); + + const board = await ReactiveCache.getBoard(list.boardId); if (!board || !board.isVisibleBy({ _id: this.userId })) { throw new Meteor.Error('not-authorized', 'You do not have access to this board.'); } - + return list.hasMovedFromOriginalPosition(); }, /** * Check if a card has moved from its original position */ - 'positionHistory.hasCardMoved'(cardId) { + async 'positionHistory.hasCardMoved'(cardId) { check(cardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in.'); } - + const card = Cards.findOne(cardId); if (!card) { throw new Meteor.Error('card-not-found', 'Card not found'); } - - const board = ReactiveCache.getBoard(card.boardId); + + const board = await ReactiveCache.getBoard(card.boardId); if (!board || !board.isVisibleBy({ _id: this.userId })) { throw new Meteor.Error('not-authorized', 'You do not have access to this board.'); } - + return card.hasMovedFromOriginalPosition(); }, /** * Get original position description for a swimlane */ - 'positionHistory.getSwimlaneDescription'(swimlaneId) { + async 'positionHistory.getSwimlaneDescription'(swimlaneId) { check(swimlaneId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in.'); } - + const swimlane = Swimlanes.findOne(swimlaneId); if (!swimlane) { throw new Meteor.Error('swimlane-not-found', 'Swimlane not found'); } - - const board = ReactiveCache.getBoard(swimlane.boardId); + + const board = await ReactiveCache.getBoard(swimlane.boardId); if (!board || !board.isVisibleBy({ _id: this.userId })) { throw new Meteor.Error('not-authorized', 'You do not have access to this board.'); } - + return swimlane.getOriginalPositionDescription(); }, /** * Get original position description for a list */ - 'positionHistory.getListDescription'(listId) { + async 'positionHistory.getListDescription'(listId) { check(listId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in.'); } - + const list = Lists.findOne(listId); if (!list) { throw new Meteor.Error('list-not-found', 'List not found'); } - - const board = ReactiveCache.getBoard(list.boardId); + + const board = await ReactiveCache.getBoard(list.boardId); if (!board || !board.isVisibleBy({ _id: this.userId })) { throw new Meteor.Error('not-authorized', 'You do not have access to this board.'); } - + return list.getOriginalPositionDescription(); }, /** * Get original position description for a card */ - 'positionHistory.getCardDescription'(cardId) { + async 'positionHistory.getCardDescription'(cardId) { check(cardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in.'); } - + const card = Cards.findOne(cardId); if (!card) { throw new Meteor.Error('card-not-found', 'Card not found'); } - - const board = ReactiveCache.getBoard(card.boardId); + + const board = await ReactiveCache.getBoard(card.boardId); if (!board || !board.isVisibleBy({ _id: this.userId })) { throw new Meteor.Error('not-authorized', 'You do not have access to this board.'); } - + return card.getOriginalPositionDescription(); }, /** * Get all position history for a board */ - 'positionHistory.getBoardHistory'(boardId) { + async 'positionHistory.getBoardHistory'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in.'); } - - const board = ReactiveCache.getBoard(boardId); + + const board = await ReactiveCache.getBoard(boardId); if (!board || !board.isVisibleBy({ _id: this.userId })) { throw new Meteor.Error('not-authorized', 'You do not have access to this board.'); } - + return PositionHistory.find({ boardId: boardId, }, { @@ -311,23 +311,23 @@ Meteor.methods({ /** * Get position history by entity type for a board */ - 'positionHistory.getBoardHistoryByType'(boardId, entityType) { + async 'positionHistory.getBoardHistoryByType'(boardId, entityType) { check(boardId, String); check(entityType, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in.'); } - - const board = ReactiveCache.getBoard(boardId); + + const board = await ReactiveCache.getBoard(boardId); if (!board || !board.isVisibleBy({ _id: this.userId })) { throw new Meteor.Error('not-authorized', 'You do not have access to this board.'); } - + if (!['swimlane', 'list', 'card'].includes(entityType)) { throw new Meteor.Error('invalid-entity-type', 'Entity type must be swimlane, list, or card'); } - + return PositionHistory.find({ boardId: boardId, entityType: entityType, diff --git a/server/migrations/comprehensiveBoardMigration.js b/server/migrations/comprehensiveBoardMigration.js index 23ecd2f2e..aad2ca7e1 100644 --- a/server/migrations/comprehensiveBoardMigration.js +++ b/server/migrations/comprehensiveBoardMigration.js @@ -41,9 +41,9 @@ class ComprehensiveBoardMigration { /** * Check if migration is needed for a board */ - needsMigration(boardId) { + async needsMigration(boardId) { try { - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) return false; // Check if board has already been processed @@ -52,7 +52,7 @@ class ComprehensiveBoardMigration { } // Check for various issues that need migration - const issues = this.detectMigrationIssues(boardId); + const issues = await this.detectMigrationIssues(boardId); return issues.length > 0; } catch (error) { @@ -64,13 +64,13 @@ class ComprehensiveBoardMigration { /** * Detect all migration issues in a board */ - detectMigrationIssues(boardId) { + async detectMigrationIssues(boardId) { const issues = []; - + try { - const cards = ReactiveCache.getCards({ boardId }); - const lists = ReactiveCache.getLists({ boardId }); - const swimlanes = ReactiveCache.getSwimlanes({ boardId }); + const cards = await ReactiveCache.getCards({ boardId }); + const lists = await ReactiveCache.getLists({ boardId }); + const swimlanes = await ReactiveCache.getSwimlanes({ boardId }); // Issue 1: Cards with missing swimlaneId const cardsWithoutSwimlane = cards.filter(card => !card.swimlaneId); @@ -157,7 +157,7 @@ class ComprehensiveBoardMigration { console.log(`Starting comprehensive board migration for board ${boardId}`); } - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) { throw new Error(`Board ${boardId} not found`); } @@ -288,7 +288,7 @@ class ComprehensiveBoardMigration { * Step 1: Analyze board structure */ async analyzeBoardStructure(boardId) { - const issues = this.detectMigrationIssues(boardId); + const issues = await this.detectMigrationIssues(boardId); return { issues, issueCount: issues.length, @@ -300,9 +300,9 @@ class ComprehensiveBoardMigration { * Step 2: Fix orphaned cards (cards with missing swimlaneId or listId) */ async fixOrphanedCards(boardId, progressCallback = null) { - const cards = ReactiveCache.getCards({ boardId }); - const swimlanes = ReactiveCache.getSwimlanes({ boardId }); - const lists = ReactiveCache.getLists({ boardId }); + const cards = await ReactiveCache.getCards({ boardId }); + const swimlanes = await ReactiveCache.getSwimlanes({ boardId }); + const lists = await ReactiveCache.getLists({ boardId }); let cardsFixed = 0; const defaultSwimlane = swimlanes.find(s => s.title === 'Default') || swimlanes[0]; @@ -370,9 +370,9 @@ class ComprehensiveBoardMigration { * Step 3: Convert shared lists to per-swimlane lists */ async convertSharedListsToPerSwimlane(boardId, progressCallback = null) { - const cards = ReactiveCache.getCards({ boardId }); - const lists = ReactiveCache.getLists({ boardId }); - const swimlanes = ReactiveCache.getSwimlanes({ boardId }); + const cards = await ReactiveCache.getCards({ boardId }); + const lists = await ReactiveCache.getLists({ boardId }); + const swimlanes = await ReactiveCache.getSwimlanes({ boardId }); let listsProcessed = 0; let listsCreated = 0; @@ -475,8 +475,8 @@ class ComprehensiveBoardMigration { * Step 4: Ensure all lists are per-swimlane */ async ensurePerSwimlaneLists(boardId) { - const lists = ReactiveCache.getLists({ boardId }); - const swimlanes = ReactiveCache.getSwimlanes({ boardId }); + const lists = await ReactiveCache.getLists({ boardId }); + const swimlanes = await ReactiveCache.getSwimlanes({ boardId }); const defaultSwimlane = swimlanes.find(s => s.title === 'Default') || swimlanes[0]; let listsProcessed = 0; @@ -501,8 +501,8 @@ class ComprehensiveBoardMigration { * Step 5: Cleanup empty lists (lists with no cards) */ async cleanupEmptyLists(boardId) { - const lists = ReactiveCache.getLists({ boardId }); - const cards = ReactiveCache.getCards({ boardId }); + const lists = await ReactiveCache.getLists({ boardId }); + const cards = await ReactiveCache.getCards({ boardId }); let listsRemoved = 0; @@ -527,9 +527,9 @@ class ComprehensiveBoardMigration { * Step 6: Validate migration */ async validateMigration(boardId) { - const issues = this.detectMigrationIssues(boardId); - const cards = ReactiveCache.getCards({ boardId }); - const lists = ReactiveCache.getLists({ boardId }); + const issues = await this.detectMigrationIssues(boardId); + const cards = await ReactiveCache.getCards({ boardId }); + const lists = await ReactiveCache.getLists({ boardId }); // Check that all cards have valid swimlaneId and listId const validCards = cards.filter(card => card.swimlaneId && card.listId); @@ -555,7 +555,7 @@ class ComprehensiveBoardMigration { * Step 7: Fix avatar URLs (remove problematic auth parameters and fix URL formats) */ async fixAvatarUrls() { - const users = ReactiveCache.getUsers({}); + const users = await ReactiveCache.getUsers({}); let avatarsFixed = 0; for (const user of users) { @@ -610,7 +610,7 @@ class ComprehensiveBoardMigration { * Step 8: Fix attachment URLs (remove problematic auth parameters and fix URL formats) */ async fixAttachmentUrls() { - const attachments = ReactiveCache.getAttachments({}); + const attachments = await ReactiveCache.getAttachments({}); let attachmentsFixed = 0; for (const attachment of attachments) { @@ -669,24 +669,24 @@ class ComprehensiveBoardMigration { /** * Get migration status for a board */ - getMigrationStatus(boardId) { + async getMigrationStatus(boardId) { try { - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) { return { status: 'board_not_found' }; } if (board.comprehensiveMigrationCompleted) { - return { + return { status: 'completed', completedAt: board.comprehensiveMigrationCompletedAt, results: board.comprehensiveMigrationResults }; } - const needsMigration = this.needsMigration(boardId); - const issues = this.detectMigrationIssues(boardId); - + const needsMigration = await this.needsMigration(boardId); + const issues = await this.detectMigrationIssues(boardId); + return { status: needsMigration ? 'needed' : 'not_needed', issues, @@ -705,82 +705,82 @@ export const comprehensiveBoardMigration = new ComprehensiveBoardMigration(); // Meteor methods Meteor.methods({ - 'comprehensiveBoardMigration.check'(boardId) { + async 'comprehensiveBoardMigration.check'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized'); } - - return comprehensiveBoardMigration.getMigrationStatus(boardId); + + return await comprehensiveBoardMigration.getMigrationStatus(boardId); }, - 'comprehensiveBoardMigration.execute'(boardId) { + async 'comprehensiveBoardMigration.execute'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized'); } - - const user = ReactiveCache.getUser(this.userId); - const board = ReactiveCache.getBoard(boardId); + + const user = await ReactiveCache.getUser(this.userId); + const board = await ReactiveCache.getBoard(boardId); if (!board) { throw new Meteor.Error('board-not-found'); } - + const isBoardAdmin = board.hasAdmin(this.userId); const isInstanceAdmin = user && user.isAdmin; - + if (!isBoardAdmin && !isInstanceAdmin) { throw new Meteor.Error('not-authorized', 'You must be a board admin or instance admin to perform this action.'); } - - return comprehensiveBoardMigration.executeMigration(boardId); + + return await comprehensiveBoardMigration.executeMigration(boardId); }, - 'comprehensiveBoardMigration.needsMigration'(boardId) { + async 'comprehensiveBoardMigration.needsMigration'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized'); } - - return comprehensiveBoardMigration.needsMigration(boardId); + + return await comprehensiveBoardMigration.needsMigration(boardId); }, - 'comprehensiveBoardMigration.detectIssues'(boardId) { + async 'comprehensiveBoardMigration.detectIssues'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized'); } - - return comprehensiveBoardMigration.detectMigrationIssues(boardId); + + return await comprehensiveBoardMigration.detectMigrationIssues(boardId); }, - 'comprehensiveBoardMigration.fixAvatarUrls'() { + async 'comprehensiveBoardMigration.fixAvatarUrls'() { if (!this.userId) { throw new Meteor.Error('not-authorized'); } - - const user = ReactiveCache.getUser(this.userId); + + const user = await ReactiveCache.getUser(this.userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Only instance admins can perform this action.'); } - - return comprehensiveBoardMigration.fixAvatarUrls(); + + return await comprehensiveBoardMigration.fixAvatarUrls(); }, - 'comprehensiveBoardMigration.fixAttachmentUrls'() { + async 'comprehensiveBoardMigration.fixAttachmentUrls'() { if (!this.userId) { throw new Meteor.Error('not-authorized'); } - - const user = ReactiveCache.getUser(this.userId); + + const user = await ReactiveCache.getUser(this.userId); if (!user || !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Only instance admins can perform this action.'); } - - return comprehensiveBoardMigration.fixAttachmentUrls(); + + return await comprehensiveBoardMigration.fixAttachmentUrls(); } }); diff --git a/server/migrations/deleteDuplicateEmptyLists.js b/server/migrations/deleteDuplicateEmptyLists.js index dadbd5391..f0eff66bf 100644 --- a/server/migrations/deleteDuplicateEmptyLists.js +++ b/server/migrations/deleteDuplicateEmptyLists.js @@ -26,10 +26,10 @@ class DeleteDuplicateEmptyListsMigration { /** * Check if migration is needed for a board */ - needsMigration(boardId) { + async needsMigration(boardId) { try { - const lists = ReactiveCache.getLists({ boardId }); - const cards = ReactiveCache.getCards({ boardId }); + const lists = await ReactiveCache.getLists({ boardId }); + const cards = await ReactiveCache.getCards({ boardId }); // Check if there are any empty lists that have a duplicate with the same title containing cards for (const list of lists) { @@ -104,9 +104,9 @@ class DeleteDuplicateEmptyListsMigration { * Convert shared lists (lists without swimlaneId) to per-swimlane lists */ async convertSharedListsToPerSwimlane(boardId) { - const lists = ReactiveCache.getLists({ boardId }); - const swimlanes = ReactiveCache.getSwimlanes({ boardId, archived: false }); - const cards = ReactiveCache.getCards({ boardId }); + const lists = await ReactiveCache.getLists({ boardId }); + const swimlanes = await ReactiveCache.getSwimlanes({ boardId, archived: false }); + const cards = await ReactiveCache.getCards({ boardId }); let listsConverted = 0; @@ -206,8 +206,8 @@ class DeleteDuplicateEmptyListsMigration { * 3. Have a duplicate list with the same title on the same board that contains cards */ async deleteEmptyPerSwimlaneLists(boardId) { - const lists = ReactiveCache.getLists({ boardId }); - const cards = ReactiveCache.getCards({ boardId }); + const lists = await ReactiveCache.getLists({ boardId }); + const cards = await ReactiveCache.getCards({ boardId }); let listsDeleted = 0; @@ -268,8 +268,8 @@ class DeleteDuplicateEmptyListsMigration { * Get detailed status of empty lists */ async getStatus(boardId) { - const lists = ReactiveCache.getLists({ boardId }); - const cards = ReactiveCache.getCards({ boardId }); + const lists = await ReactiveCache.getLists({ boardId }); + const cards = await ReactiveCache.getCards({ boardId }); const sharedLists = []; const emptyPerSwimlaneLists = []; @@ -319,30 +319,30 @@ const deleteDuplicateEmptyListsMigration = new DeleteDuplicateEmptyListsMigratio // Register Meteor methods Meteor.methods({ - 'deleteDuplicateEmptyLists.needsMigration'(boardId) { + async 'deleteDuplicateEmptyLists.needsMigration'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in'); } - return deleteDuplicateEmptyListsMigration.needsMigration(boardId); + return await deleteDuplicateEmptyListsMigration.needsMigration(boardId); }, - 'deleteDuplicateEmptyLists.execute'(boardId) { + async 'deleteDuplicateEmptyLists.execute'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in'); } // Check if user is board admin - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) { throw new Meteor.Error('board-not-found', 'Board not found'); } - const user = ReactiveCache.getUser(this.userId); + const user = await ReactiveCache.getUser(this.userId); if (!user) { throw new Meteor.Error('user-not-found', 'User not found'); } @@ -356,17 +356,17 @@ Meteor.methods({ throw new Meteor.Error('not-authorized', 'Only board administrators can run migrations'); } - return deleteDuplicateEmptyListsMigration.executeMigration(boardId); + return await deleteDuplicateEmptyListsMigration.executeMigration(boardId); }, - 'deleteDuplicateEmptyLists.getStatus'(boardId) { + async 'deleteDuplicateEmptyLists.getStatus'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in'); } - return deleteDuplicateEmptyListsMigration.getStatus(boardId); + return await deleteDuplicateEmptyListsMigration.getStatus(boardId); } }); diff --git a/server/migrations/fixAllFileUrls.js b/server/migrations/fixAllFileUrls.js index f713ac8ae..f1b3d77db 100644 --- a/server/migrations/fixAllFileUrls.js +++ b/server/migrations/fixAllFileUrls.js @@ -22,17 +22,17 @@ class FixAllFileUrlsMigration { /** * Check if migration is needed for a board */ - needsMigration(boardId) { + async needsMigration(boardId) { // Get all users who are members of this board - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board || !board.members) { return false; } - + const memberIds = board.members.map(m => m.userId); - + // Check for problematic avatar URLs for board members - const users = ReactiveCache.getUsers({ _id: { $in: memberIds } }); + const users = await ReactiveCache.getUsers({ _id: { $in: memberIds } }); for (const user of users) { if (user.profile && user.profile.avatarUrl) { const avatarUrl = user.profile.avatarUrl; @@ -43,9 +43,9 @@ class FixAllFileUrlsMigration { } // Check for problematic attachment URLs on this board - const cards = ReactiveCache.getCards({ boardId }); + const cards = await ReactiveCache.getCards({ boardId }); const cardIds = cards.map(c => c._id); - const attachments = ReactiveCache.getAttachments({ cardId: { $in: cardIds } }); + const attachments = await ReactiveCache.getAttachments({ cardId: { $in: cardIds } }); for (const attachment of attachments) { if (attachment.url && this.hasProblematicUrl(attachment.url)) { @@ -133,13 +133,13 @@ class FixAllFileUrlsMigration { * Fix avatar URLs in user profiles for board members */ async fixAvatarUrls(boardId) { - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board || !board.members) { return 0; } - + const memberIds = board.members.map(m => m.userId); - const users = ReactiveCache.getUsers({ _id: { $in: memberIds } }); + const users = await ReactiveCache.getUsers({ _id: { $in: memberIds } }); let avatarsFixed = 0; for (const user of users) { @@ -189,9 +189,9 @@ class FixAllFileUrlsMigration { * Fix attachment URLs in attachment records for this board */ async fixAttachmentUrls(boardId) { - const cards = ReactiveCache.getCards({ boardId }); + const cards = await ReactiveCache.getCards({ boardId }); const cardIds = cards.map(c => c._id); - const attachments = ReactiveCache.getAttachments({ cardId: { $in: cardIds } }); + const attachments = await ReactiveCache.getAttachments({ cardId: { $in: cardIds } }); let attachmentsFixed = 0; for (const attachment of attachments) { @@ -229,9 +229,9 @@ class FixAllFileUrlsMigration { * Fix attachment URLs in the Attachments collection for this board */ async fixCardAttachmentUrls(boardId) { - const cards = ReactiveCache.getCards({ boardId }); + const cards = await ReactiveCache.getCards({ boardId }); const cardIds = cards.map(c => c._id); - const attachments = ReactiveCache.getAttachments({ cardId: { $in: cardIds } }); + const attachments = await ReactiveCache.getAttachments({ cardId: { $in: cardIds } }); let attachmentsFixed = 0; for (const attachment of attachments) { @@ -270,20 +270,20 @@ export const fixAllFileUrlsMigration = new FixAllFileUrlsMigration(); // Meteor methods Meteor.methods({ - 'fixAllFileUrls.execute'(boardId) { + async 'fixAllFileUrls.execute'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in'); } // Check if user is board admin - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) { throw new Meteor.Error('board-not-found', 'Board not found'); } - const user = ReactiveCache.getUser(this.userId); + const user = await ReactiveCache.getUser(this.userId); if (!user) { throw new Meteor.Error('user-not-found', 'User not found'); } @@ -296,17 +296,17 @@ Meteor.methods({ if (!isBoardAdmin && !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Only board administrators can run migrations'); } - - return fixAllFileUrlsMigration.execute(boardId); + + return await fixAllFileUrlsMigration.execute(boardId); }, - 'fixAllFileUrls.needsMigration'(boardId) { + async 'fixAllFileUrls.needsMigration'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in'); } - - return fixAllFileUrlsMigration.needsMigration(boardId); + + return await fixAllFileUrlsMigration.needsMigration(boardId); } }); diff --git a/server/migrations/fixAvatarUrls.js b/server/migrations/fixAvatarUrls.js index 82677eb48..0b6c27f7b 100644 --- a/server/migrations/fixAvatarUrls.js +++ b/server/migrations/fixAvatarUrls.js @@ -19,15 +19,15 @@ class FixAvatarUrlsMigration { /** * Check if migration is needed for a board */ - needsMigration(boardId) { + async needsMigration(boardId) { // Get all users who are members of this board - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board || !board.members) { return false; } - + const memberIds = board.members.map(m => m.userId); - const users = ReactiveCache.getUsers({ _id: { $in: memberIds } }); + const users = await ReactiveCache.getUsers({ _id: { $in: memberIds } }); for (const user of users) { if (user.profile && user.profile.avatarUrl) { @@ -46,16 +46,16 @@ class FixAvatarUrlsMigration { */ async execute(boardId) { // Get all users who are members of this board - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board || !board.members) { return { success: false, error: 'Board not found or has no members' }; } - + const memberIds = board.members.map(m => m.userId); - const users = ReactiveCache.getUsers({ _id: { $in: memberIds } }); + const users = await ReactiveCache.getUsers({ _id: { $in: memberIds } }); let avatarsFixed = 0; console.log(`Starting avatar URL fix migration for board ${boardId}...`); @@ -131,20 +131,20 @@ export const fixAvatarUrlsMigration = new FixAvatarUrlsMigration(); // Meteor method Meteor.methods({ - 'fixAvatarUrls.execute'(boardId) { + async 'fixAvatarUrls.execute'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in'); } // Check if user is board admin - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) { throw new Meteor.Error('board-not-found', 'Board not found'); } - const user = ReactiveCache.getUser(this.userId); + const user = await ReactiveCache.getUser(this.userId); if (!user) { throw new Meteor.Error('user-not-found', 'User not found'); } @@ -157,17 +157,17 @@ Meteor.methods({ if (!isBoardAdmin && !user.isAdmin) { throw new Meteor.Error('not-authorized', 'Only board administrators can run migrations'); } - - return fixAvatarUrlsMigration.execute(boardId); + + return await fixAvatarUrlsMigration.execute(boardId); }, - 'fixAvatarUrls.needsMigration'(boardId) { + async 'fixAvatarUrls.needsMigration'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in'); } - - return fixAvatarUrlsMigration.needsMigration(boardId); + + return await fixAvatarUrlsMigration.needsMigration(boardId); } }); diff --git a/server/migrations/fixMissingListsMigration.js b/server/migrations/fixMissingListsMigration.js index 22e5b16de..05de463d6 100644 --- a/server/migrations/fixMissingListsMigration.js +++ b/server/migrations/fixMissingListsMigration.js @@ -31,9 +31,9 @@ class FixMissingListsMigration { /** * Check if migration is needed for a board */ - needsMigration(boardId) { + async needsMigration(boardId) { try { - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) return false; // Check if board has already been processed @@ -42,8 +42,8 @@ class FixMissingListsMigration { } // Check if there are cards with mismatched listId/swimlaneId - const cards = ReactiveCache.getCards({ boardId }); - const lists = ReactiveCache.getLists({ boardId }); + const cards = await ReactiveCache.getCards({ boardId }); + const lists = await ReactiveCache.getLists({ boardId }); // Create a map of listId -> swimlaneId for existing lists const listSwimlaneMap = new Map(); @@ -77,15 +77,15 @@ class FixMissingListsMigration { if (process.env.DEBUG === 'true') { console.log(`Starting fix missing lists migration for board ${boardId}`); } - - const board = ReactiveCache.getBoard(boardId); + + const board = await ReactiveCache.getBoard(boardId); if (!board) { throw new Error(`Board ${boardId} not found`); } - const cards = ReactiveCache.getCards({ boardId }); - const lists = ReactiveCache.getLists({ boardId }); - const swimlanes = ReactiveCache.getSwimlanes({ boardId }); + const cards = await ReactiveCache.getCards({ boardId }); + const lists = await ReactiveCache.getLists({ boardId }); + const swimlanes = await ReactiveCache.getSwimlanes({ boardId }); // Create maps for efficient lookup const listSwimlaneMap = new Map(); @@ -214,21 +214,21 @@ class FixMissingListsMigration { /** * Get migration status for a board */ - getMigrationStatus(boardId) { + async getMigrationStatus(boardId) { try { - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) { return { status: 'board_not_found' }; } if (board.fixMissingListsCompleted) { - return { + return { status: 'completed', completedAt: board.fixMissingListsCompletedAt }; } - const needsMigration = this.needsMigration(boardId); + const needsMigration = await this.needsMigration(boardId); return { status: needsMigration ? 'needed' : 'not_needed' }; @@ -245,33 +245,33 @@ export const fixMissingListsMigration = new FixMissingListsMigration(); // Meteor methods Meteor.methods({ - 'fixMissingListsMigration.check'(boardId) { + async 'fixMissingListsMigration.check'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized'); } - - return fixMissingListsMigration.getMigrationStatus(boardId); + + return await fixMissingListsMigration.getMigrationStatus(boardId); }, - 'fixMissingListsMigration.execute'(boardId) { + async 'fixMissingListsMigration.execute'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized'); } - - return fixMissingListsMigration.executeMigration(boardId); + + return await fixMissingListsMigration.executeMigration(boardId); }, - 'fixMissingListsMigration.needsMigration'(boardId) { + async 'fixMissingListsMigration.needsMigration'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized'); } - - return fixMissingListsMigration.needsMigration(boardId); + + return await fixMissingListsMigration.needsMigration(boardId); } }); diff --git a/server/migrations/migrateAttachments.js b/server/migrations/migrateAttachments.js index 55ffdb0c7..5ff8dcce5 100644 --- a/server/migrations/migrateAttachments.js +++ b/server/migrations/migrateAttachments.js @@ -14,7 +14,7 @@ if (Meteor.isServer) { * @param {string} attachmentId - The old attachment ID * @returns {Object} - Migration result */ - migrateAttachment(attachmentId) { + async migrateAttachment(attachmentId) { if (!this.userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } @@ -27,7 +27,7 @@ if (Meteor.isServer) { } // Check if already migrated - const existingAttachment = ReactiveCache.getAttachment(attachmentId); + const existingAttachment = await ReactiveCache.getAttachment(attachmentId); if (existingAttachment) { return { success: true, message: 'Already migrated', attachmentId }; } @@ -72,7 +72,7 @@ if (Meteor.isServer) { * @param {string} cardId - The card ID * @returns {Object} - Migration results */ - migrateCardAttachments(cardId) { + async migrateCardAttachments(cardId) { if (!this.userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } @@ -85,7 +85,7 @@ if (Meteor.isServer) { try { // Get all old attachments for this card - const oldAttachments = ReactiveCache.getAttachments({ 'meta.cardId': cardId }); + const oldAttachments = await ReactiveCache.getAttachments({ 'meta.cardId': cardId }); for (const attachment of oldAttachments) { const result = Meteor.call('migrateAttachment', attachment._id); @@ -113,14 +113,14 @@ if (Meteor.isServer) { * @param {string} cardId - The card ID (optional) * @returns {Object} - Migration status */ - getAttachmentMigrationStatus(cardId) { + async getAttachmentMigrationStatus(cardId) { if (!this.userId) { throw new Meteor.Error('not-authorized', 'Must be logged in'); } try { const selector = cardId ? { 'meta.cardId': cardId } : {}; - const allAttachments = ReactiveCache.getAttachments(selector); + const allAttachments = await ReactiveCache.getAttachments(selector); const status = { total: allAttachments.length, diff --git a/server/migrations/restoreAllArchived.js b/server/migrations/restoreAllArchived.js index 825f9a2f4..92f9b41fe 100644 --- a/server/migrations/restoreAllArchived.js +++ b/server/migrations/restoreAllArchived.js @@ -24,11 +24,11 @@ class RestoreAllArchivedMigration { /** * Check if migration is needed for a board */ - needsMigration(boardId) { + async needsMigration(boardId) { try { - const archivedSwimlanes = ReactiveCache.getSwimlanes({ boardId, archived: true }); - const archivedLists = ReactiveCache.getLists({ boardId, archived: true }); - const archivedCards = ReactiveCache.getCards({ boardId, archived: true }); + const archivedSwimlanes = await ReactiveCache.getSwimlanes({ boardId, archived: true }); + const archivedLists = await ReactiveCache.getLists({ boardId, archived: true }); + const archivedCards = await ReactiveCache.getCards({ boardId, archived: true }); return archivedSwimlanes.length > 0 || archivedLists.length > 0 || archivedCards.length > 0; } catch (error) { @@ -50,19 +50,19 @@ class RestoreAllArchivedMigration { errors: [] }; - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) { throw new Error('Board not found'); } // Get archived items - const archivedSwimlanes = ReactiveCache.getSwimlanes({ boardId, archived: true }); - const archivedLists = ReactiveCache.getLists({ boardId, archived: true }); - const archivedCards = ReactiveCache.getCards({ boardId, archived: true }); + const archivedSwimlanes = await ReactiveCache.getSwimlanes({ boardId, archived: true }); + const archivedLists = await ReactiveCache.getLists({ boardId, archived: true }); + const archivedCards = await ReactiveCache.getCards({ boardId, archived: true }); // Get active items for reference - const activeSwimlanes = ReactiveCache.getSwimlanes({ boardId, archived: false }); - const activeLists = ReactiveCache.getLists({ boardId, archived: false }); + const activeSwimlanes = await ReactiveCache.getSwimlanes({ boardId, archived: false }); + const activeLists = await ReactiveCache.getLists({ boardId, archived: false }); // Restore all archived swimlanes for (const swimlane of archivedSwimlanes) { @@ -101,7 +101,7 @@ class RestoreAllArchivedMigration { updatedAt: new Date(), archived: false }); - targetSwimlane = ReactiveCache.getSwimlane(swimlaneId); + targetSwimlane = await ReactiveCache.getSwimlane(swimlaneId); } updateFields.swimlaneId = targetSwimlane._id; @@ -123,8 +123,8 @@ class RestoreAllArchivedMigration { } // Refresh lists after restoration - const allLists = ReactiveCache.getLists({ boardId, archived: false }); - const allSwimlanes = ReactiveCache.getSwimlanes({ boardId, archived: false }); + const allLists = await ReactiveCache.getLists({ boardId, archived: false }); + const allSwimlanes = await ReactiveCache.getSwimlanes({ boardId, archived: false }); // Restore all archived cards and fix missing IDs for (const card of archivedCards) { @@ -153,7 +153,7 @@ class RestoreAllArchivedMigration { updatedAt: new Date(), archived: false }); - targetList = ReactiveCache.getList(listId); + targetList = await ReactiveCache.getList(listId); } updateFields.listId = targetList._id; @@ -222,30 +222,30 @@ const restoreAllArchivedMigration = new RestoreAllArchivedMigration(); // Register Meteor methods Meteor.methods({ - 'restoreAllArchived.needsMigration'(boardId) { + async 'restoreAllArchived.needsMigration'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in'); } - return restoreAllArchivedMigration.needsMigration(boardId); + return await restoreAllArchivedMigration.needsMigration(boardId); }, - 'restoreAllArchived.execute'(boardId) { + async 'restoreAllArchived.execute'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in'); } // Check if user is board admin - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) { throw new Meteor.Error('board-not-found', 'Board not found'); } - const user = ReactiveCache.getUser(this.userId); + const user = await ReactiveCache.getUser(this.userId); if (!user) { throw new Meteor.Error('user-not-found', 'User not found'); } @@ -259,7 +259,7 @@ Meteor.methods({ throw new Meteor.Error('not-authorized', 'Only board administrators can run migrations'); } - return restoreAllArchivedMigration.executeMigration(boardId); + return await restoreAllArchivedMigration.executeMigration(boardId); } }); diff --git a/server/migrations/restoreLostCards.js b/server/migrations/restoreLostCards.js index 781caa0fb..a844f2866 100644 --- a/server/migrations/restoreLostCards.js +++ b/server/migrations/restoreLostCards.js @@ -24,10 +24,10 @@ class RestoreLostCardsMigration { /** * Check if migration is needed for a board */ - needsMigration(boardId) { + async needsMigration(boardId) { try { - const cards = ReactiveCache.getCards({ boardId, archived: false }); - const lists = ReactiveCache.getLists({ boardId, archived: false }); + const cards = await ReactiveCache.getCards({ boardId, archived: false }); + const lists = await ReactiveCache.getLists({ boardId, archived: false }); // Check for cards missing swimlaneId or listId const lostCards = cards.filter(card => !card.swimlaneId || !card.listId); @@ -70,15 +70,15 @@ class RestoreLostCardsMigration { errors: [] }; - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) { throw new Error('Board not found'); } // Get all non-archived items - const cards = ReactiveCache.getCards({ boardId, archived: false }); - const lists = ReactiveCache.getLists({ boardId, archived: false }); - const swimlanes = ReactiveCache.getSwimlanes({ boardId, archived: false }); + const cards = await ReactiveCache.getCards({ boardId, archived: false }); + const lists = await ReactiveCache.getLists({ boardId, archived: false }); + const swimlanes = await ReactiveCache.getSwimlanes({ boardId, archived: false }); // Detect items to restore BEFORE creating anything const lostLists = lists.filter(list => !list.swimlaneId); @@ -116,7 +116,7 @@ class RestoreLostCardsMigration { updatedAt: new Date(), archived: false }); - lostCardsSwimlane = ReactiveCache.getSwimlane(swimlaneId); + lostCardsSwimlane = await ReactiveCache.getSwimlane(swimlaneId); results.lostCardsSwimlaneCreated = true; if (process.env.DEBUG === 'true') { console.log(`Created "Lost Cards" swimlane for board ${boardId}`); @@ -156,7 +156,7 @@ class RestoreLostCardsMigration { updatedAt: new Date(), archived: false }); - defaultList = ReactiveCache.getList(listId); + defaultList = await ReactiveCache.getList(listId); if (process.env.DEBUG === 'true') { console.log(`Created default list in Lost Cards swimlane`); } @@ -215,30 +215,30 @@ const restoreLostCardsMigration = new RestoreLostCardsMigration(); // Register Meteor methods Meteor.methods({ - 'restoreLostCards.needsMigration'(boardId) { + async 'restoreLostCards.needsMigration'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in'); } - return restoreLostCardsMigration.needsMigration(boardId); + return await restoreLostCardsMigration.needsMigration(boardId); }, - 'restoreLostCards.execute'(boardId) { + async 'restoreLostCards.execute'(boardId) { check(boardId, String); - + if (!this.userId) { throw new Meteor.Error('not-authorized', 'You must be logged in'); } // Check if user is board admin - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) { throw new Meteor.Error('board-not-found', 'Board not found'); } - const user = ReactiveCache.getUser(this.userId); + const user = await ReactiveCache.getUser(this.userId); if (!user) { throw new Meteor.Error('user-not-found', 'User not found'); } @@ -252,7 +252,7 @@ Meteor.methods({ throw new Meteor.Error('not-authorized', 'Only board administrators can run migrations'); } - return restoreLostCardsMigration.executeMigration(boardId); + return await restoreLostCardsMigration.executeMigration(boardId); } }); diff --git a/server/notifications/email.js b/server/notifications/email.js index 7b1bf9a42..3b9706402 100644 --- a/server/notifications/email.js +++ b/server/notifications/email.js @@ -33,8 +33,8 @@ Meteor.startup(() => { // Meteor.setTimeout(func, delay) does not accept args :-( // so we pass userId with closure const userId = user._id; - Meteor.setTimeout(() => { - const user = ReactiveCache.getUser(userId); + Meteor.setTimeout(async () => { + const user = await ReactiveCache.getUser(userId); // for each user, in the timed period, only the first call will get the cached content, // other calls will get nothing diff --git a/server/notifications/notifications.js b/server/notifications/notifications.js index 0d9b5259b..000603560 100644 --- a/server/notifications/notifications.js +++ b/server/notifications/notifications.js @@ -19,12 +19,12 @@ Notifications = { delete notifyServices[serviceName]; }, - getUsers: watchers => { + getUsers: async watchers => { const users = []; - watchers.forEach(userId => { - const user = ReactiveCache.getUser(userId); + for (const userId of watchers) { + const user = await ReactiveCache.getUser(userId); if (user && user._id) users.push(user); - }); + } return users; }, diff --git a/server/notifications/outgoing.js b/server/notifications/outgoing.js index ff5123946..1703b1ead 100644 --- a/server/notifications/outgoing.js +++ b/server/notifications/outgoing.js @@ -70,20 +70,20 @@ if (Meteor.isServer) { 'label', 'attachmentId', ]; - const responseFunc = data => { + const responseFunc = async data => { const paramCommentId = data.commentId; const paramCardId = data.cardId; const paramBoardId = data.boardId; const newComment = data.comment; if (paramCardId && paramBoardId && newComment) { // only process data with the cardid, boardid and comment text, TODO can expand other functions here to react on returned data - const comment = ReactiveCache.getCardComment({ + const comment = await ReactiveCache.getCardComment({ _id: paramCommentId, cardId: paramCardId, boardId: paramBoardId, }); - const board = ReactiveCache.getBoard(paramBoardId); - const card = ReactiveCache.getCard(paramCardId); + const board = await ReactiveCache.getBoard(paramBoardId); + const card = await ReactiveCache.getCard(paramCardId); if (board && card) { if (comment) { Lock.set(comment._id, newComment); @@ -108,8 +108,8 @@ if (Meteor.isServer) { } }; Meteor.methods({ - outgoingWebhooks(integration, description, params) { - if (ReactiveCache.getCurrentUser()) { + async outgoingWebhooks(integration, description, params) { + if (await ReactiveCache.getCurrentUser()) { check(integration, Object); check(description, String); check(params, Object); @@ -137,7 +137,7 @@ if (Meteor.isServer) { }); const userId = params.userId ? params.userId : integrations[0].userId; - const user = ReactiveCache.getUser(userId); + const user = await ReactiveCache.getUser(userId); const descriptionText = TAPi18n.__( description, quoteParams, @@ -171,7 +171,7 @@ if (Meteor.isServer) { data: is2way ? { description, ...clonedParams } : value, }; - if (!ReactiveCache.getIntegration({ url: integration.url })) return; + if (!(await ReactiveCache.getIntegration({ url: integration.url }))) return; const url = integration.url; @@ -198,7 +198,7 @@ if (Meteor.isServer) { const data = response.data; // only an JSON encoded response will be actioned if (data) { try { - responseFunc(data); + await responseFunc(data); } catch (e) { throw new Meteor.Error('error-process-data'); } diff --git a/server/notifications/watch.js b/server/notifications/watch.js index 3b4907220..cb9cf5029 100644 --- a/server/notifications/watch.js +++ b/server/notifications/watch.js @@ -11,17 +11,17 @@ Meteor.methods({ let watchableObj = null; let board = null; if (watchableType === 'board') { - watchableObj = ReactiveCache.getBoard(id); + watchableObj = await ReactiveCache.getBoard(id); if (!watchableObj) throw new Meteor.Error('error-board-doesNotExist'); board = watchableObj; } else if (watchableType === 'list') { - watchableObj = ReactiveCache.getList(id); + watchableObj = await ReactiveCache.getList(id); if (!watchableObj) throw new Meteor.Error('error-list-doesNotExist'); - board = watchableObj.board(); + board = await watchableObj.board(); } else if (watchableType === 'card') { - watchableObj = ReactiveCache.getCard(id); + watchableObj = await ReactiveCache.getCard(id); if (!watchableObj) throw new Meteor.Error('error-card-doesNotExist'); - board = watchableObj.board(); + board = await watchableObj.board(); } else { throw new Meteor.Error('error-json-schema'); } diff --git a/server/publications/activities.js b/server/publications/activities.js index 8c3e17d69..65a931631 100644 --- a/server/publications/activities.js +++ b/server/publications/activities.js @@ -5,7 +5,7 @@ import { ReactiveCache } from '/imports/reactiveCache'; // 2. The card activity tab // We use this publication to paginate for these two publications. -Meteor.publish('activities', function(kind, id, limit, showActivities) { +Meteor.publish('activities', async function(kind, id, limit, showActivities) { check( kind, Match.Where(x => { @@ -29,27 +29,27 @@ Meteor.publish('activities', function(kind, id, limit, showActivities) { let board; if (kind === 'board') { - board = ReactiveCache.getBoard(id); + board = await ReactiveCache.getBoard(id); if (!board || !board.isVisibleBy(this.userId)) { return this.ready(); } // Get linked boards, but only those visible to the user - ReactiveCache.getCards({ + (await ReactiveCache.getCards({ "type": "cardType-linkedBoard", "boardId": id - }).forEach(card => { - const linkedBoard = ReactiveCache.getBoard(card.linkedId); + })).forEach(async card => { + const linkedBoard = await ReactiveCache.getBoard(card.linkedId); if (linkedBoard && linkedBoard.isVisibleBy(this.userId)) { linkedElmtId.push(card.linkedId); } }); } else if (kind === 'card') { - const card = ReactiveCache.getCard(id); + const card = await ReactiveCache.getCard(id); if (!card) { return this.ready(); } - board = ReactiveCache.getBoard(card.boardId); + board = await ReactiveCache.getBoard(card.boardId); if (!board || !board.isVisibleBy(this.userId)) { return this.ready(); } @@ -58,7 +58,7 @@ Meteor.publish('activities', function(kind, id, limit, showActivities) { const selector = showActivities ? { [`${kind}Id`]: { $in: linkedElmtId } } : { $and: [{ activityType: 'addComment' }, { [`${kind}Id`]: { $in: linkedElmtId } }] }; - const ret = ReactiveCache.getActivities(selector, + const ret = await ReactiveCache.getActivities(selector, { limit, sort: { createdAt: -1 }, diff --git a/server/publications/attachments.js b/server/publications/attachments.js index d618012f8..a25c82487 100644 --- a/server/publications/attachments.js +++ b/server/publications/attachments.js @@ -1,16 +1,16 @@ import Attachments from '/models/attachments'; import { ObjectID } from 'bson'; -Meteor.publish('attachmentsList', function(limit) { +Meteor.publish('attachmentsList', async function(limit) { const userId = this.userId; // Get boards the user has access to - const userBoards = ReactiveCache.getBoards({ + const userBoards = (await ReactiveCache.getBoards({ $or: [ { permission: 'public' }, { members: { $elemMatch: { userId, isActive: true } } } ] - }).map(board => board._id); + })).map(board => board._id); if (userBoards.length === 0) { // User has no access to any boards, return empty cursor @@ -18,10 +18,10 @@ Meteor.publish('attachmentsList', function(limit) { } // Get cards from those boards - const userCards = ReactiveCache.getCards({ + const userCards = (await ReactiveCache.getCards({ boardId: { $in: userBoards }, archived: false - }).map(card => card._id); + })).map(card => card._id); if (userCards.length === 0) { // No cards found, return empty cursor @@ -29,7 +29,7 @@ Meteor.publish('attachmentsList', function(limit) { } // Only return attachments for cards the user has access to - const ret = ReactiveCache.getAttachments( + const ret = (await ReactiveCache.getAttachments( { 'meta.cardId': { $in: userCards } }, { fields: { @@ -47,6 +47,6 @@ Meteor.publish('attachmentsList', function(limit) { limit: limit, }, true, - ).cursor; + )).cursor; return ret; }); diff --git a/server/publications/avatars.js b/server/publications/avatars.js index 47f9f0bdd..d7ca862f4 100644 --- a/server/publications/avatars.js +++ b/server/publications/avatars.js @@ -1,5 +1,5 @@ import Avatars from '../../models/avatars'; -Meteor.publish('my-avatars', function() { - const ret = ReactiveCache.getAvatars({ userId: this.userId }, {}, true).cursor; +Meteor.publish('my-avatars', async function() { + const ret = (await ReactiveCache.getAvatars({ userId: this.userId }, {}, true)).cursor; return ret; }); diff --git a/server/publications/boards.js b/server/publications/boards.js index c070949ba..a202bf67d 100644 --- a/server/publications/boards.js +++ b/server/publications/boards.js @@ -18,8 +18,8 @@ publishComposite('boards', function() { } return { - find() { - return ReactiveCache.getBoards( + async find() { + return await ReactiveCache.getBoards( { archived: false, _id: { $in: Boards.userBoardIds(userId, false) }, @@ -32,10 +32,10 @@ publishComposite('boards', function() { }, children: [ { - find(board) { + async find(board) { // Publish lists with extended fields for proper sync // Including swimlaneId, modifiedAt, and _updatedAt for list order changes - return ReactiveCache.getLists( + return await ReactiveCache.getLists( { boardId: board._id, archived: false }, { fields: { @@ -54,8 +54,8 @@ publishComposite('boards', function() { } }, { - find(board) { - return ReactiveCache.getCards( + async find(board) { + return await ReactiveCache.getCards( { boardId: board._id, archived: false }, { fields: { @@ -74,13 +74,13 @@ publishComposite('boards', function() { }; }); -Meteor.publish('boardsReport', function() { +Meteor.publish('boardsReport', async function() { const userId = this.userId; // Ensure that the user is connected. If it is not, we need to return an empty // array to tell the client to remove the previously published docs. if (!Match.test(userId, String) || !userId) return []; - const boards = ReactiveCache.getBoards( + const boards = await ReactiveCache.getBoards( { _id: { $in: Boards.userBoardIds(userId, null) }, }, @@ -129,18 +129,18 @@ Meteor.publish('boardsReport', function() { const ret = [ boards, - ReactiveCache.getUsers({ _id: { $in: userIds } }, { fields: Users.safeFields }, true), - ReactiveCache.getTeams({ _id: { $in: teamIds } }, {}, true), - ReactiveCache.getOrgs({ _id: { $in: orgIds } }, {}, true), + await ReactiveCache.getUsers({ _id: { $in: userIds } }, { fields: Users.safeFields }, true), + await ReactiveCache.getTeams({ _id: { $in: teamIds } }, {}, true), + await ReactiveCache.getOrgs({ _id: { $in: orgIds } }, {}, true), ] return ret; }); -Meteor.publish('archivedBoards', function() { +Meteor.publish('archivedBoards', async function() { const userId = this.userId; if (!Match.test(userId, String)) return []; - const ret = ReactiveCache.getBoards( + const ret = await ReactiveCache.getBoards( { _id: { $in: Boards.userBoardIds(userId, true)}, archived: true, @@ -170,14 +170,14 @@ Meteor.publish('archivedBoards', function() { // If isArchived = false, this will only return board elements which are not archived. // If isArchived = true, this will only return board elements which are archived. -publishComposite('board', function(boardId, isArchived) { +publishComposite('board', async function(boardId, isArchived) { check(boardId, String); check(isArchived, Boolean); const thisUserId = this.userId; const $or = [{ permission: 'public' }]; - let currUser = (!Match.test(thisUserId, String) || !thisUserId) ? 'undefined' : ReactiveCache.getUser(thisUserId); + let currUser = (!Match.test(thisUserId, String) || !thisUserId) ? 'undefined' : await ReactiveCache.getUser(thisUserId); let orgIdsUserBelongs = currUser !== 'undefined' && currUser.teams !== 'undefined' ? currUser.orgIdsUserBelongs() : ''; let teamIdsUserBelongs = currUser !== 'undefined' && currUser.teams !== 'undefined' ? currUser.teamIdsUserBelongs() : ''; let orgsIds = []; @@ -197,8 +197,8 @@ publishComposite('board', function(boardId, isArchived) { } return { - find() { - return ReactiveCache.getBoards( + async find() { + return await ReactiveCache.getBoards( { _id: boardId, archived: false, @@ -212,32 +212,32 @@ publishComposite('board', function(boardId, isArchived) { children: [ // Lists { - find(board) { - return ReactiveCache.getLists({ boardId: board._id, archived: isArchived }, {}, true); + async find(board) { + return await ReactiveCache.getLists({ boardId: board._id, archived: isArchived }, {}, true); } }, // Swimlanes { - find(board) { - return ReactiveCache.getSwimlanes({ boardId: board._id, archived: isArchived }, {}, true); + async find(board) { + return await ReactiveCache.getSwimlanes({ boardId: board._id, archived: isArchived }, {}, true); } }, // Integrations { - find(board) { - return ReactiveCache.getIntegrations({ boardId: board._id }, {}, true); + async find(board) { + return await ReactiveCache.getIntegrations({ boardId: board._id }, {}, true); } }, // CardCommentReactions at board level { - find(board) { - return ReactiveCache.getCardCommentReactions({ boardId: board._id }, {}, true); + async find(board) { + return await ReactiveCache.getCardCommentReactions({ boardId: board._id }, {}, true); } }, // CustomFields { - find(board) { - return ReactiveCache.getCustomFields( + async find(board) { + return await ReactiveCache.getCustomFields( { boardIds: { $in: [board._id] } }, { sort: { name: 1 } }, true, @@ -246,7 +246,7 @@ publishComposite('board', function(boardId, isArchived) { }, // Cards and their related data { - find(board) { + async find(board) { const cardSelector = { boardId: { $in: [board._id, board.subtasksDefaultBoardId] }, archived: isArchived, @@ -261,7 +261,7 @@ publishComposite('board', function(boardId, isArchived) { } } - return ReactiveCache.getCards(cardSelector, {}, true); + return await ReactiveCache.getCards(cardSelector, {}, true); }, children: [ // CardComments for each card @@ -366,7 +366,7 @@ publishComposite('board', function(boardId, isArchived) { }, // Board members/Users { - find(board) { + async find(board) { if (board.members) { // Board members. This publication also includes former board members that // aren't members anymore but may have some activities attached to them in @@ -376,7 +376,7 @@ publishComposite('board', function(boardId, isArchived) { // We omit the current user because the client should already have that data, // and sending it triggers a subtle bug: // https://github.com/wefork/wekan/issues/15 - return ReactiveCache.getUsers( + return await ReactiveCache.getUsers( { _id: { $in: _.without(memberIds, thisUserId) }, }, @@ -399,12 +399,12 @@ publishComposite('board', function(boardId, isArchived) { }); Meteor.methods({ - copyBoard(boardId, properties) { + async copyBoard(boardId, properties) { check(boardId, String); check(properties, Object); let ret = null; - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (board) { for (const key in properties) { board[key] = properties[key]; diff --git a/server/publications/cards.js b/server/publications/cards.js index e9d8fcf6e..b5c576936 100644 --- a/server/publications/cards.js +++ b/server/publications/cards.js @@ -74,21 +74,21 @@ import { CARD_TYPES } from '../../config/const'; import Org from "../../models/org"; import Team from "../../models/team"; -Meteor.publish('card', cardId => { +Meteor.publish('card', async cardId => { check(cardId, String); - + const userId = Meteor.userId(); - const card = ReactiveCache.getCard({ _id: cardId }); - + const card = await ReactiveCache.getCard({ _id: cardId }); + if (!card || !card.boardId) { return []; } - - const board = ReactiveCache.getBoard({ _id: card.boardId }); + + const board = await ReactiveCache.getBoard({ _id: card.boardId }); if (!board || !board.isVisibleBy(userId)) { return []; } - + // If user has assigned-only permissions, check if they're assigned to this card if (userId && board.members) { const member = _.findWhere(board.members, { userId: userId, isActive: true }); @@ -99,8 +99,8 @@ Meteor.publish('card', cardId => { } } } - - const ret = ReactiveCache.getCards( + + const ret = await ReactiveCache.getCards( { _id: cardId }, {}, true, @@ -111,17 +111,17 @@ Meteor.publish('card', cardId => { /** publish all data which is necessary to display card details as popup * @returns array of cursors */ -publishComposite('popupCardData', function(cardId) { +publishComposite('popupCardData', async function(cardId) { check(cardId, String); const userId = this.userId; - const card = ReactiveCache.getCard({ _id: cardId }); + const card = await ReactiveCache.getCard({ _id: cardId }); if (!card || !card.boardId) { return []; } - const board = ReactiveCache.getBoard({ _id: card.boardId }); + const board = await ReactiveCache.getBoard({ _id: card.boardId }); if (!board || !board.isVisibleBy(userId)) { return []; } @@ -138,29 +138,29 @@ publishComposite('popupCardData', function(cardId) { } return { - find() { - return ReactiveCache.getCards({ _id: cardId }, {}, true); + async find() { + return await ReactiveCache.getCards({ _id: cardId }, {}, true); }, children: [ { - find(card) { - return ReactiveCache.getBoards({ _id: card.boardId }, {}, true); + async find(card) { + return await ReactiveCache.getBoards({ _id: card.boardId }, {}, true); } }, { - find(card) { - return ReactiveCache.getLists({ boardId: card.boardId }, {}, true); + async find(card) { + return await ReactiveCache.getLists({ boardId: card.boardId }, {}, true); } } ] }; }); -Meteor.publish('myCards', function(sessionId) { +Meteor.publish('myCards', async function(sessionId) { check(sessionId, String); const queryParams = new QueryParams(); - queryParams.addPredicate(OPERATOR_USER, ReactiveCache.getCurrentUser().username); + queryParams.addPredicate(OPERATOR_USER, (await ReactiveCache.getCurrentUser()).username); queryParams.setPredicate(OPERATOR_LIMIT, 200); const query = buildQuery(queryParams); @@ -175,9 +175,9 @@ Meteor.publish('myCards', function(sessionId) { }); // Optimized due cards publication for better performance -Meteor.publish('dueCards', function(allUsers = false) { +Meteor.publish('dueCards', async function(allUsers = false) { check(allUsers, Boolean); - + const userId = this.userId; if (!userId) { return this.ready(); @@ -188,12 +188,12 @@ Meteor.publish('dueCards', function(allUsers = false) { } // Get user's board memberships for efficient filtering - const userBoards = ReactiveCache.getBoards({ + const userBoards = (await ReactiveCache.getBoards({ $or: [ { permission: 'public' }, { members: { $elemMatch: { userId, isActive: true } } } ] - }).map(board => board._id); + })).map(board => board._id); if (process.env.DEBUG === 'true') { console.log('dueCards userBoards:', userBoards); @@ -273,7 +273,7 @@ Meteor.publish('dueCards', function(allUsers = false) { return result; }); -Meteor.publish('globalSearch', function(sessionId, params, text) { +Meteor.publish('globalSearch', async function(sessionId, params, text) { check(sessionId, String); check(params, Object); check(text, String); @@ -282,7 +282,7 @@ Meteor.publish('globalSearch', function(sessionId, params, text) { console.log('globalSearch publication called with:', { sessionId, params, text }); } - const ret = findCards(sessionId, buildQuery(new QueryParams(params, text))); + const ret = findCards(sessionId, await buildQuery(new QueryParams(params, text))); if (process.env.DEBUG === 'true') { console.log('globalSearch publication returning:', ret); } @@ -303,7 +303,7 @@ Meteor.publish('sessionData', function(sessionId) { return cursor; }); -function buildSelector(queryParams) { +async function buildSelector(queryParams) { const userId = Meteor.userId(); const errors = new QueryErrors(); @@ -336,8 +336,8 @@ function buildSelector(queryParams) { if (queryParams.hasOperator(OPERATOR_ORG)) { const orgs = []; - queryParams.getPredicates(OPERATOR_ORG).forEach(name => { - const org = ReactiveCache.getOrg({ + for (const name of queryParams.getPredicates(OPERATOR_ORG)) { + const org = await ReactiveCache.getOrg({ $or: [ { orgDisplayName: name }, { orgShortName: name } @@ -348,7 +348,7 @@ function buildSelector(queryParams) { } else { errors.addNotFound(OPERATOR_ORG, name); } - }); + } if (orgs.length) { boardsSelector.orgs = { $elemMatch: { orgId: { $in: orgs }, isActive: true } @@ -358,8 +358,8 @@ function buildSelector(queryParams) { if (queryParams.hasOperator(OPERATOR_TEAM)) { const teams = []; - queryParams.getPredicates(OPERATOR_TEAM).forEach(name => { - const team = ReactiveCache.getTeam({ + for (const name of queryParams.getPredicates(OPERATOR_TEAM)) { + const team = await ReactiveCache.getTeam({ $or: [ { teamDisplayName: name }, { teamShortName: name } @@ -370,7 +370,7 @@ function buildSelector(queryParams) { } else { errors.addNotFound(OPERATOR_TEAM, name); } - }); + } if (teams.length) { boardsSelector.teams = { $elemMatch: { teamId: { $in: teams }, isActive: true } @@ -442,8 +442,8 @@ function buildSelector(queryParams) { if (queryParams.hasOperator(OPERATOR_SWIMLANE)) { const querySwimlanes = []; - queryParams.getPredicates(OPERATOR_SWIMLANE).forEach(query => { - const swimlanes = ReactiveCache.getSwimlanes({ + for (const query of queryParams.getPredicates(OPERATOR_SWIMLANE)) { + const swimlanes = await ReactiveCache.getSwimlanes({ title: new RegExp(escapeForRegex(query), 'i'), }); if (swimlanes.length) { @@ -453,7 +453,7 @@ function buildSelector(queryParams) { } else { errors.addNotFound(OPERATOR_SWIMLANE, query); } - }); + } // eslint-disable-next-line no-prototype-builtins if (!selector.swimlaneId.hasOwnProperty('swimlaneId')) { @@ -464,8 +464,8 @@ function buildSelector(queryParams) { if (queryParams.hasOperator(OPERATOR_LIST)) { const queryLists = []; - queryParams.getPredicates(OPERATOR_LIST).forEach(query => { - const lists = ReactiveCache.getLists({ + for (const query of queryParams.getPredicates(OPERATOR_LIST)) { + const lists = await ReactiveCache.getLists({ title: new RegExp(escapeForRegex(query), 'i'), }); if (lists.length) { @@ -475,7 +475,7 @@ function buildSelector(queryParams) { } else { errors.addNotFound(OPERATOR_LIST, query); } - }); + } // eslint-disable-next-line no-prototype-builtins if (!selector.hasOwnProperty('listId')) { @@ -516,14 +516,14 @@ function buildSelector(queryParams) { if (queryParams.hasOperator(OPERATOR_USER)) { const users = []; - queryParams.getPredicates(OPERATOR_USER).forEach(username => { - const user = ReactiveCache.getUser({ username }); + for (const username of queryParams.getPredicates(OPERATOR_USER)) { + const user = await ReactiveCache.getUser({ username }); if (user) { users.push(user._id); } else { errors.addNotFound(OPERATOR_USER, username); } - }); + } if (users.length) { selector.$and.push({ $or: [{ members: { $in: users } }, { assignees: { $in: users } }], @@ -531,22 +531,22 @@ function buildSelector(queryParams) { } } - [OPERATOR_MEMBER, OPERATOR_ASSIGNEE, OPERATOR_CREATOR].forEach(key => { + for (const key of [OPERATOR_MEMBER, OPERATOR_ASSIGNEE, OPERATOR_CREATOR]) { if (queryParams.hasOperator(key)) { const users = []; - queryParams.getPredicates(key).forEach(username => { - const user = ReactiveCache.getUser({ username }); + for (const username of queryParams.getPredicates(key)) { + const user = await ReactiveCache.getUser({ username }); if (user) { users.push(user._id); } else { errors.addNotFound(key, username); } - }); + } if (users.length) { selector[key] = { $in: users }; } } - }); + } if (queryParams.hasOperator(OPERATOR_LABEL)) { const queryLabels = []; @@ -605,12 +605,12 @@ function buildSelector(queryParams) { } if (queryParams.hasOperator(OPERATOR_HAS)) { - queryParams.getPredicates(OPERATOR_HAS).forEach(has => { + for (const has of queryParams.getPredicates(OPERATOR_HAS)) { switch (has.field) { case PREDICATE_ATTACHMENT: selector.$and.push({ _id: { - $in: ReactiveCache.getAttachments({}, { fields: { cardId: 1 } }).map( + $in: (await ReactiveCache.getAttachments({}, { fields: { cardId: 1 } })).map( a => a.cardId, ), }, @@ -619,7 +619,7 @@ function buildSelector(queryParams) { case PREDICATE_CHECKLIST: selector.$and.push({ _id: { - $in: ReactiveCache.getChecklists({}, { fields: { cardId: 1 } }).map( + $in: (await ReactiveCache.getChecklists({}, { fields: { cardId: 1 } })).map( a => a.cardId, ), }, @@ -644,17 +644,17 @@ function buildSelector(queryParams) { } break; } - }); + } } if (queryParams.text) { const regex = new RegExp(escapeForRegex(queryParams.text), 'i'); - const items = ReactiveCache.getChecklistItems( + const items = await ReactiveCache.getChecklistItems( { title: regex }, { fields: { cardId: 1, checklistId: 1 } }, ); - const checklists = ReactiveCache.getChecklists( + const checklists = await ReactiveCache.getChecklists( { $or: [ { title: regex }, @@ -664,9 +664,9 @@ function buildSelector(queryParams) { { fields: { cardId: 1 } }, ); - const attachments = ReactiveCache.getAttachments({ 'original.name': regex }); + const attachments = await ReactiveCache.getAttachments({ 'original.name': regex }); - const comments = ReactiveCache.getCardComments( + const comments = await ReactiveCache.getCardComments( { text: regex }, { fields: { cardId: 1 } }, ); @@ -806,18 +806,18 @@ function buildProjection(query) { return query; } -function buildQuery(queryParams) { - const query = buildSelector(queryParams); +async function buildQuery(queryParams) { + const query = await buildSelector(queryParams); return buildProjection(query); } -Meteor.publish('brokenCards', function(sessionId) { +Meteor.publish('brokenCards', async function(sessionId) { check(sessionId, String); const params = new QueryParams(); params.addPredicate(OPERATOR_STATUS, PREDICATE_ALL); - const query = buildQuery(params); + const query = await buildQuery(params); query.selector.$or = [ { boardId: { $in: [null, ''] } }, { swimlaneId: { $in: [null, ''] } }, @@ -830,10 +830,10 @@ Meteor.publish('brokenCards', function(sessionId) { return ret; }); -Meteor.publish('nextPage', function(sessionId) { +Meteor.publish('nextPage', async function(sessionId) { check(sessionId, String); - const session = ReactiveCache.getSessionData({ sessionId }); + const session = await ReactiveCache.getSessionData({ sessionId }); const projection = session.getProjection(); projection.skip = session.lastHit; @@ -841,10 +841,10 @@ Meteor.publish('nextPage', function(sessionId) { return ret; }); -Meteor.publish('previousPage', function(sessionId) { +Meteor.publish('previousPage', async function(sessionId) { check(sessionId, String); - const session = ReactiveCache.getSessionData({ sessionId }); + const session = await ReactiveCache.getSessionData({ sessionId }); const projection = session.getProjection(); projection.skip = session.lastHit - session.resultsCount - projection.limit; @@ -852,7 +852,7 @@ Meteor.publish('previousPage', function(sessionId) { return ret; }); -function findCards(sessionId, query) { +async function findCards(sessionId, query) { const userId = Meteor.userId(); // eslint-disable-next-line no-console @@ -863,7 +863,7 @@ function findCards(sessionId, query) { console.log('findCards - projection:', query.projection); } - const cards = ReactiveCache.getCards(query.selector, query.projection, true); + const cards = await ReactiveCache.getCards(query.selector, query.projection, true); if (process.env.DEBUG === 'true') { console.log('findCards - cards count:', cards ? cards.count() : 0); } @@ -977,23 +977,23 @@ function findCards(sessionId, query) { return [ cards, - ReactiveCache.getBoards( + await ReactiveCache.getBoards( { _id: { $in: boards } }, { fields: { ...fields, labels: 1, color: 1 } }, true, ), - ReactiveCache.getSwimlanes( + await ReactiveCache.getSwimlanes( { _id: { $in: swimlanes } }, { fields: { ...fields, color: 1 } }, true, ), - ReactiveCache.getLists({ _id: { $in: lists } }, { fields }, true), - ReactiveCache.getCustomFields({ _id: { $in: customFieldIds } }, {}, true), - ReactiveCache.getUsers({ _id: { $in: users } }, { fields: Users.safeFields }, true), - ReactiveCache.getChecklists({ cardId: { $in: cards.map(c => c._id) } }, {}, true), - ReactiveCache.getChecklistItems({ cardId: { $in: cards.map(c => c._id) } }, {}, true), - ReactiveCache.getAttachments({ 'meta.cardId': { $in: cards.map(c => c._id) } }, {}, true).cursor, - ReactiveCache.getCardComments({ cardId: { $in: cards.map(c => c._id) } }, {}, true), + await ReactiveCache.getLists({ _id: { $in: lists } }, { fields }, true), + await ReactiveCache.getCustomFields({ _id: { $in: customFieldIds } }, {}, true), + await ReactiveCache.getUsers({ _id: { $in: users } }, { fields: Users.safeFields }, true), + await ReactiveCache.getChecklists({ cardId: { $in: cards.map(c => c._id) } }, {}, true), + await ReactiveCache.getChecklistItems({ cardId: { $in: cards.map(c => c._id) } }, {}, true), + (await ReactiveCache.getAttachments({ 'meta.cardId': { $in: cards.map(c => c._id) } }, {}, true)).cursor, + await ReactiveCache.getCardComments({ cardId: { $in: cards.map(c => c._id) } }, {}, true), sessionDataCursor, ]; } diff --git a/server/publications/notifications.js b/server/publications/notifications.js index 1d9db198e..bc6998a4c 100644 --- a/server/publications/notifications.js +++ b/server/publications/notifications.js @@ -3,33 +3,33 @@ import { ReactiveCache } from '/imports/reactiveCache'; // We use these when displaying notifications in the notificationsDrawer // gets all activities associated with the current user -Meteor.publish('notificationActivities', () => { - const ret = activities(); +Meteor.publish('notificationActivities', async () => { + const ret = await activities(); return ret; }); // gets all attachments associated with activities associated with the current user -Meteor.publish('notificationAttachments', function() { - const ret = ReactiveCache.getAttachments( +Meteor.publish('notificationAttachments', async function() { + const ret = (await ReactiveCache.getAttachments( { _id: { - $in: activities() + $in: (await activities()) .map(v => v.attachmentId) .filter(v => !!v), }, }, {}, true, - ).cursor; + )).cursor; return ret; }); // gets all cards associated with activities associated with the current user -Meteor.publish('notificationCards', function() { - const ret = ReactiveCache.getCards( +Meteor.publish('notificationCards', async function() { + const ret = await ReactiveCache.getCards( { _id: { - $in: activities() + $in: (await activities()) .map(v => v.cardId) .filter(v => !!v), }, @@ -41,11 +41,11 @@ Meteor.publish('notificationCards', function() { }); // gets all checklistItems associated with activities associated with the current user -Meteor.publish('notificationChecklistItems', function() { - const ret = ReactiveCache.getChecklistItems( +Meteor.publish('notificationChecklistItems', async function() { + const ret = await ReactiveCache.getChecklistItems( { _id: { - $in: activities() + $in: (await activities()) .map(v => v.checklistItemId) .filter(v => !!v), }, @@ -57,11 +57,11 @@ Meteor.publish('notificationChecklistItems', function() { }); // gets all checklists associated with activities associated with the current user -Meteor.publish('notificationChecklists', function() { - const ret = ReactiveCache.getChecklists( +Meteor.publish('notificationChecklists', async function() { + const ret = await ReactiveCache.getChecklists( { _id: { - $in: activities() + $in: (await activities()) .map(v => v.checklistId) .filter(v => !!v), }, @@ -73,11 +73,11 @@ Meteor.publish('notificationChecklists', function() { }); // gets all comments associated with activities associated with the current user -Meteor.publish('notificationComments', function() { - const ret = ReactiveCache.getCardComments( +Meteor.publish('notificationComments', async function() { + const ret = await ReactiveCache.getCardComments( { _id: { - $in: activities() + $in: (await activities()) .map(v => v.commentId) .filter(v => !!v), }, @@ -89,11 +89,11 @@ Meteor.publish('notificationComments', function() { }); // gets all lists associated with activities associated with the current user -Meteor.publish('notificationLists', function() { - const ret = ReactiveCache.getLists( +Meteor.publish('notificationLists', async function() { + const ret = await ReactiveCache.getLists( { _id: { - $in: activities() + $in: (await activities()) .map(v => v.listId) .filter(v => !!v), }, @@ -105,11 +105,11 @@ Meteor.publish('notificationLists', function() { }); // gets all swimlanes associated with activities associated with the current user -Meteor.publish('notificationSwimlanes', function() { - const ret = ReactiveCache.getSwimlanes( +Meteor.publish('notificationSwimlanes', async function() { + const ret = await ReactiveCache.getSwimlanes( { _id: { - $in: activities() + $in: (await activities()) .map(v => v.swimlaneId) .filter(v => !!v), }, @@ -121,11 +121,11 @@ Meteor.publish('notificationSwimlanes', function() { }); // gets all users associated with activities associated with the current user -Meteor.publish('notificationUsers', function() { - const ret = ReactiveCache.getUsers( +Meteor.publish('notificationUsers', async function() { + const ret = await ReactiveCache.getUsers( { _id: { - $in: activities() + $in: (await activities()) .map(v => v.userId) .filter(v => !!v), }, @@ -136,11 +136,11 @@ Meteor.publish('notificationUsers', function() { return ret; }); -function activities() { - const activityIds = ReactiveCache.getCurrentUser()?.profile?.notifications?.map(v => v.activity) || []; +async function activities() { + const activityIds = (await ReactiveCache.getCurrentUser())?.profile?.notifications?.map(v => v.activity) || []; let ret = []; if (activityIds.length > 0) { - ret = ReactiveCache.getActivities( + ret = await ReactiveCache.getActivities( { _id: { $in: activityIds }, }, diff --git a/server/publications/org.js b/server/publications/org.js index adf7f33d0..e7569a396 100644 --- a/server/publications/org.js +++ b/server/publications/org.js @@ -1,14 +1,14 @@ import { ReactiveCache } from '/imports/reactiveCache'; -Meteor.publish('org', function(query, limit) { +Meteor.publish('org', async function(query, limit) { check(query, Match.OneOf(Object, null)); check(limit, Number); let ret = []; - const user = ReactiveCache.getCurrentUser(); + const user = await ReactiveCache.getCurrentUser(); if (user && user.isAdmin) { - ret = ReactiveCache.getOrgs(query, + ret = await ReactiveCache.getOrgs(query, { limit, sort: { createdAt: -1 }, diff --git a/server/publications/people.js b/server/publications/people.js index 7135cb820..57602757f 100644 --- a/server/publications/people.js +++ b/server/publications/people.js @@ -1,14 +1,14 @@ import { ReactiveCache } from '/imports/reactiveCache'; -Meteor.publish('people', function(query, limit) { +Meteor.publish('people', async function(query, limit) { check(query, Match.OneOf(Object, null)); check(limit, Number); let ret = []; - const user = ReactiveCache.getCurrentUser(); + const user = await ReactiveCache.getCurrentUser(); if (user && user.isAdmin) { - ret = ReactiveCache.getUsers(query, { + ret = await ReactiveCache.getUsers(query, { limit, sort: { createdAt: -1 }, fields: { diff --git a/server/publications/rules.js b/server/publications/rules.js index 45d949e7b..78c2f5896 100644 --- a/server/publications/rules.js +++ b/server/publications/rules.js @@ -4,24 +4,24 @@ import Triggers from '/models/triggers'; import Rules from '/models/rules'; import { ReactiveCache } from '/imports/reactiveCache'; -Meteor.publish('rules', function(ruleId) { +Meteor.publish('rules', async function(ruleId) { check(ruleId, String); if (!this.userId) { return this.ready(); } - const rule = ReactiveCache.getRule(ruleId); + const rule = await ReactiveCache.getRule(ruleId); if (!rule) { return this.ready(); } - const board = ReactiveCache.getBoard(rule.boardId); + const board = await ReactiveCache.getBoard(rule.boardId); if (!board || !board.isVisibleBy(this.userId)) { return this.ready(); } - const ret = ReactiveCache.getRules( + const ret = await ReactiveCache.getRules( { _id: ruleId, }, @@ -31,39 +31,39 @@ Meteor.publish('rules', function(ruleId) { return ret; }); -Meteor.publish('allRules', function() { - if (!this.userId || !ReactiveCache.getUser(this.userId).isAdmin) { +Meteor.publish('allRules', async function() { + if (!this.userId || !(await ReactiveCache.getUser(this.userId)).isAdmin) { return this.ready(); } - const ret = ReactiveCache.getRules({}, {}, true); + const ret = await ReactiveCache.getRules({}, {}, true); return ret; }); -Meteor.publish('allTriggers', function() { - if (!this.userId || !ReactiveCache.getUser(this.userId).isAdmin) { +Meteor.publish('allTriggers', async function() { + if (!this.userId || !(await ReactiveCache.getUser(this.userId)).isAdmin) { return this.ready(); } - const ret = ReactiveCache.getTriggers({}, {}, true); + const ret = await ReactiveCache.getTriggers({}, {}, true); return ret; }); -Meteor.publish('allActions', function() { - if (!this.userId || !ReactiveCache.getUser(this.userId).isAdmin) { +Meteor.publish('allActions', async function() { + if (!this.userId || !(await ReactiveCache.getUser(this.userId)).isAdmin) { return this.ready(); } - const ret = ReactiveCache.getActions({}, {}, true); + const ret = await ReactiveCache.getActions({}, {}, true); return ret; }); -Meteor.publish('rulesReport', function() { - if (!this.userId || !ReactiveCache.getUser(this.userId).isAdmin) { +Meteor.publish('rulesReport', async function() { + if (!this.userId || !(await ReactiveCache.getUser(this.userId)).isAdmin) { return this.ready(); } - const rules = ReactiveCache.getRules({}, {}, true); + const rules = await ReactiveCache.getRules({}, {}, true); const actionIds = []; const triggerIds = []; const boardIds = []; @@ -76,9 +76,9 @@ Meteor.publish('rulesReport', function() { const ret = [ rules, - ReactiveCache.getActions({ _id: { $in: actionIds } }, {}, true), - ReactiveCache.getTriggers({ _id: { $in: triggerIds } }, {}, true), - ReactiveCache.getBoards({ _id: { $in: boardIds } }, { fields: { title: 1 } }, true), + await ReactiveCache.getActions({ _id: { $in: actionIds } }, {}, true), + await ReactiveCache.getTriggers({ _id: { $in: triggerIds } }, {}, true), + await ReactiveCache.getBoards({ _id: { $in: boardIds } }, { fields: { title: 1 } }, true), ]; return ret; }); diff --git a/server/publications/settings.js b/server/publications/settings.js index e2365d523..6a06f5b7a 100644 --- a/server/publications/settings.js +++ b/server/publications/settings.js @@ -1,8 +1,8 @@ import { ReactiveCache } from '/imports/reactiveCache'; -Meteor.publish('globalwebhooks', () => { +Meteor.publish('globalwebhooks', async () => { const boardId = Integrations.Const.GLOBAL_WEBHOOK_ID; - const ret = ReactiveCache.getIntegrations( + const ret = await ReactiveCache.getIntegrations( { boardId, }, @@ -47,8 +47,8 @@ Meteor.publish('setting', () => { return ret; }); -Meteor.publish('mailServer', function() { - const user = ReactiveCache.getCurrentUser(); +Meteor.publish('mailServer', async function() { + const user = await ReactiveCache.getCurrentUser(); let ret = [] if (user && user.isAdmin) { diff --git a/server/publications/swimlanes.js b/server/publications/swimlanes.js index fec6958c0..dc8f295bf 100644 --- a/server/publications/swimlanes.js +++ b/server/publications/swimlanes.js @@ -1,12 +1,12 @@ import { ReactiveCache } from '/imports/reactiveCache'; Meteor.methods({ - copySwimlane(swimlaneId, toBoardId) { + async copySwimlane(swimlaneId, toBoardId) { check(swimlaneId, String); check(toBoardId, String); - const swimlane = ReactiveCache.getSwimlane(swimlaneId); - const toBoard = ReactiveCache.getBoard(toBoardId); + const swimlane = await ReactiveCache.getSwimlane(swimlaneId); + const toBoard = await ReactiveCache.getBoard(toBoardId); let ret = false; if (swimlane && toBoard) { @@ -21,8 +21,8 @@ Meteor.methods({ check(swimlaneId, String); check(toBoardId, String); - const swimlane = ReactiveCache.getSwimlane(swimlaneId); - const toBoard = ReactiveCache.getBoard(toBoardId); + const swimlane = await ReactiveCache.getSwimlane(swimlaneId); + const toBoard = await ReactiveCache.getBoard(toBoardId); let ret = false; if (swimlane && toBoard) { diff --git a/server/publications/team.js b/server/publications/team.js index 37a161793..6a8437c02 100644 --- a/server/publications/team.js +++ b/server/publications/team.js @@ -1,14 +1,14 @@ import { ReactiveCache } from '/imports/reactiveCache'; -Meteor.publish('team', function(query, limit) { +Meteor.publish('team', async function(query, limit) { check(query, Match.OneOf(Object, null)); check(limit, Number); - const user = ReactiveCache.getCurrentUser(); + const user = await ReactiveCache.getCurrentUser(); let ret = []; if (user && user.isAdmin) { - ret = ReactiveCache.getTeams(query, + ret = await ReactiveCache.getTeams(query, { limit, sort: { createdAt: -1 }, diff --git a/server/publications/translation.js b/server/publications/translation.js index 2868329f0..09b79fb5d 100644 --- a/server/publications/translation.js +++ b/server/publications/translation.js @@ -1,14 +1,14 @@ import { ReactiveCache } from '/imports/reactiveCache'; -Meteor.publish('translation', function(query, limit) { +Meteor.publish('translation', async function(query, limit) { check(query, Match.OneOf(Object, null)); check(limit, Number); let ret = []; - const user = ReactiveCache.getCurrentUser(); + const user = await ReactiveCache.getCurrentUser(); if (user && user.isAdmin) { - ret = ReactiveCache.getTranslations(query, + ret = await ReactiveCache.getTranslations(query, { limit, sort: { modifiedAt: -1 }, diff --git a/server/publications/users.js b/server/publications/users.js index 4730a3d35..f929c5db1 100644 --- a/server/publications/users.js +++ b/server/publications/users.js @@ -1,9 +1,9 @@ -Meteor.publish('user-miniprofile', function (usernames) { +Meteor.publish('user-miniprofile', async function (usernames) { check(usernames, Array); // eslint-disable-next-line no-console // console.log('usernames:', usernames); - const ret = ReactiveCache.getUsers( + const ret = await ReactiveCache.getUsers( { $or: [ { username: { $in: usernames } }, @@ -33,9 +33,9 @@ Meteor.publish('user-admin', function () { return ret; }); -Meteor.publish('user-authenticationMethod', function (match) { +Meteor.publish('user-authenticationMethod', async function (match) { check(match, String); - const ret = ReactiveCache.getUsers( + const ret = await ReactiveCache.getUsers( { $or: [{ _id: match }, { email: match }, { username: match }] }, { fields: { @@ -50,7 +50,7 @@ Meteor.publish('user-authenticationMethod', function (match) { }); // Secure user search publication for board sharing -Meteor.publish('user-search', function (searchTerm) { +Meteor.publish('user-search', async function (searchTerm) { check(searchTerm, String); // Only allow logged-in users to search for other users @@ -62,7 +62,7 @@ Meteor.publish('user-search', function (searchTerm) { const searchRegex = new RegExp(searchTerm, 'i'); // Search for users by username, fullname, or email - const ret = ReactiveCache.getUsers( + const ret = await ReactiveCache.getUsers( { $or: [ { username: searchRegex }, diff --git a/server/routes/attachmentApi.js b/server/routes/attachmentApi.js index 490c54f7f..568b930ea 100644 --- a/server/routes/attachmentApi.js +++ b/server/routes/attachmentApi.js @@ -48,7 +48,7 @@ if (Meteor.isServer) { } // Upload attachment endpoint - WebApp.connectHandlers.use('/api/attachment/upload', (req, res, next) => { + WebApp.connectHandlers.use('/api/attachment/upload', async (req, res, next) => { if (req.method !== 'POST') { return next(); } @@ -75,11 +75,11 @@ if (Meteor.isServer) { } }); - req.on('end', () => { + req.on('end', async () => { if (bodyComplete) return; // Already processed bodyComplete = true; clearTimeout(timeout); - + try { const data = JSON.parse(body); const { boardId, swimlaneId, listId, cardId, fileData, fileName, fileType, storageBackend } = data; @@ -90,12 +90,12 @@ if (Meteor.isServer) { } // Check if user has permission to modify the card - const card = ReactiveCache.getCard(cardId); + const card = await ReactiveCache.getCard(cardId); if (!card) { return sendErrorResponse(res, 404, 'Card not found'); } - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board) { return sendErrorResponse(res, 404, 'Board not found'); } @@ -207,7 +207,7 @@ if (Meteor.isServer) { }); // Download attachment endpoint - WebApp.connectHandlers.use('/api/attachment/download/([^/]+)', (req, res, next) => { + WebApp.connectHandlers.use('/api/attachment/download/([^/]+)', async (req, res, next) => { if (req.method !== 'GET') { return next(); } @@ -217,13 +217,13 @@ if (Meteor.isServer) { const attachmentId = req.params[0]; // Get attachment - const attachment = ReactiveCache.getAttachment(attachmentId); + const attachment = await ReactiveCache.getAttachment(attachmentId); if (!attachment) { return sendErrorResponse(res, 404, 'Attachment not found'); } // Check permissions - const board = ReactiveCache.getBoard(attachment.meta.boardId); + const board = await ReactiveCache.getBoard(attachment.meta.boardId); if (!board || !board.isBoardMember(userId)) { return sendErrorResponse(res, 403, 'You do not have permission to access this attachment'); } @@ -267,7 +267,7 @@ if (Meteor.isServer) { }); // List attachments endpoint - WebApp.connectHandlers.use('/api/attachment/list/([^/]+)/([^/]+)/([^/]+)/([^/]+)', (req, res, next) => { + WebApp.connectHandlers.use('/api/attachment/list/([^/]+)/([^/]+)/([^/]+)/([^/]+)', async (req, res, next) => { if (req.method !== 'GET') { return next(); } @@ -280,14 +280,14 @@ if (Meteor.isServer) { const cardId = req.params[3]; // Check permissions - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (!board || !board.isBoardMember(userId)) { return sendErrorResponse(res, 403, 'You do not have permission to access this board'); } // If cardId is provided, verify it belongs to the board if (cardId && cardId !== 'null') { - const card = ReactiveCache.getCard(cardId); + const card = await ReactiveCache.getCard(cardId); if (!card || card.boardId !== boardId) { return sendErrorResponse(res, 404, 'Card not found or does not belong to the specified board'); } @@ -307,7 +307,7 @@ if (Meteor.isServer) { query['meta.cardId'] = cardId; } - const attachments = ReactiveCache.getAttachments(query); + const attachments = await ReactiveCache.getAttachments(query); const attachmentList = attachments.map(attachment => { const strategy = fileStoreStrategyFactory.getFileStrategy(attachment, 'original'); @@ -337,7 +337,7 @@ if (Meteor.isServer) { }); // Copy attachment endpoint - WebApp.connectHandlers.use('/api/attachment/copy', (req, res, next) => { + WebApp.connectHandlers.use('/api/attachment/copy', async (req, res, next) => { if (req.method !== 'POST') { return next(); } @@ -350,10 +350,10 @@ if (Meteor.isServer) { try { const userId = authenticateApiRequest(req); - + let body = ''; let bodyComplete = false; - + req.on('data', chunk => { body += chunk.toString(); if (body.length > 10 * 1024 * 1024) { // 10MB limit for metadata @@ -362,35 +362,35 @@ if (Meteor.isServer) { } }); - req.on('end', () => { + req.on('end', async () => { if (bodyComplete) return; bodyComplete = true; clearTimeout(timeout); - + try { const data = JSON.parse(body); const { attachmentId, targetBoardId, targetSwimlaneId, targetListId, targetCardId } = data; // Get source attachment - const sourceAttachment = ReactiveCache.getAttachment(attachmentId); + const sourceAttachment = await ReactiveCache.getAttachment(attachmentId); if (!sourceAttachment) { return sendErrorResponse(res, 404, 'Source attachment not found'); } // Check source permissions - const sourceBoard = ReactiveCache.getBoard(sourceAttachment.meta.boardId); + const sourceBoard = await ReactiveCache.getBoard(sourceAttachment.meta.boardId); if (!sourceBoard || !sourceBoard.isBoardMember(userId)) { return sendErrorResponse(res, 403, 'You do not have permission to access the source attachment'); } // Check target permissions - const targetBoard = ReactiveCache.getBoard(targetBoardId); + const targetBoard = await ReactiveCache.getBoard(targetBoardId); if (!targetBoard || !targetBoard.isBoardMember(userId)) { return sendErrorResponse(res, 403, 'You do not have permission to modify the target card'); } // Verify that the target card belongs to the target board - const targetCard = ReactiveCache.getCard(targetCardId); + const targetCard = await ReactiveCache.getCard(targetCardId); if (!targetCard) { return sendErrorResponse(res, 404, 'Target card not found'); } @@ -493,7 +493,7 @@ if (Meteor.isServer) { }); // Move attachment endpoint - WebApp.connectHandlers.use('/api/attachment/move', (req, res, next) => { + WebApp.connectHandlers.use('/api/attachment/move', async (req, res, next) => { if (req.method !== 'POST') { return next(); } @@ -506,10 +506,10 @@ if (Meteor.isServer) { try { const userId = authenticateApiRequest(req); - + let body = ''; let bodyComplete = false; - + req.on('data', chunk => { body += chunk.toString(); if (body.length > 10 * 1024 * 1024) { @@ -518,35 +518,35 @@ if (Meteor.isServer) { } }); - req.on('end', () => { + req.on('end', async () => { if (bodyComplete) return; bodyComplete = true; clearTimeout(timeout); - + try { const data = JSON.parse(body); const { attachmentId, targetBoardId, targetSwimlaneId, targetListId, targetCardId } = data; // Get source attachment - const sourceAttachment = ReactiveCache.getAttachment(attachmentId); + const sourceAttachment = await ReactiveCache.getAttachment(attachmentId); if (!sourceAttachment) { return sendErrorResponse(res, 404, 'Source attachment not found'); } // Check source permissions - const sourceBoard = ReactiveCache.getBoard(sourceAttachment.meta.boardId); + const sourceBoard = await ReactiveCache.getBoard(sourceAttachment.meta.boardId); if (!sourceBoard || !sourceBoard.isBoardMember(userId)) { return sendErrorResponse(res, 403, 'You do not have permission to access the source attachment'); } // Check target permissions - const targetBoard = ReactiveCache.getBoard(targetBoardId); + const targetBoard = await ReactiveCache.getBoard(targetBoardId); if (!targetBoard || !targetBoard.isBoardMember(userId)) { return sendErrorResponse(res, 403, 'You do not have permission to modify the target card'); } // Verify that the target card belongs to the target board - const targetCard = ReactiveCache.getCard(targetCardId); + const targetCard = await ReactiveCache.getCard(targetCardId); if (!targetCard) { return sendErrorResponse(res, 404, 'Target card not found'); } @@ -610,7 +610,7 @@ if (Meteor.isServer) { }); // Delete attachment endpoint - WebApp.connectHandlers.use('/api/attachment/delete/([^/]+)', (req, res, next) => { + WebApp.connectHandlers.use('/api/attachment/delete/([^/]+)', async (req, res, next) => { if (req.method !== 'DELETE') { return next(); } @@ -620,13 +620,13 @@ if (Meteor.isServer) { const attachmentId = req.params[0]; // Get attachment - const attachment = ReactiveCache.getAttachment(attachmentId); + const attachment = await ReactiveCache.getAttachment(attachmentId); if (!attachment) { return sendErrorResponse(res, 404, 'Attachment not found'); } // Check permissions - const board = ReactiveCache.getBoard(attachment.meta.boardId); + const board = await ReactiveCache.getBoard(attachment.meta.boardId); if (!board || !board.isBoardMember(userId)) { return sendErrorResponse(res, 403, 'You do not have permission to delete this attachment'); } @@ -646,7 +646,7 @@ if (Meteor.isServer) { }); // Get attachment info endpoint - WebApp.connectHandlers.use('/api/attachment/info/([^/]+)', (req, res, next) => { + WebApp.connectHandlers.use('/api/attachment/info/([^/]+)', async (req, res, next) => { if (req.method !== 'GET') { return next(); } @@ -656,13 +656,13 @@ if (Meteor.isServer) { const attachmentId = req.params[0]; // Get attachment - const attachment = ReactiveCache.getAttachment(attachmentId); + const attachment = await ReactiveCache.getAttachment(attachmentId); if (!attachment) { return sendErrorResponse(res, 404, 'Attachment not found'); } // Check permissions - const board = ReactiveCache.getBoard(attachment.meta.boardId); + const board = await ReactiveCache.getBoard(attachment.meta.boardId); if (!board || !board.isBoardMember(userId)) { return sendErrorResponse(res, 403, 'You do not have permission to access this attachment'); } diff --git a/server/routes/avatarServer.js b/server/routes/avatarServer.js index 008ea573a..9463e6130 100644 --- a/server/routes/avatarServer.js +++ b/server/routes/avatarServer.js @@ -13,14 +13,14 @@ import path from 'path'; if (Meteor.isServer) { // Handle avatar file downloads - WebApp.connectHandlers.use('/cdn/storage/avatars/([^/]+)', (req, res, next) => { + WebApp.connectHandlers.use('/cdn/storage/avatars/([^/]+)', async (req, res, next) => { if (req.method !== 'GET') { return next(); } try { const fileName = req.params[0]; - + if (!fileName) { res.writeHead(400); res.end('Invalid avatar file name'); @@ -29,7 +29,7 @@ if (Meteor.isServer) { // Extract file ID from filename (format: fileId-original-filename) const fileId = fileName.split('-original-')[0]; - + if (!fileId) { res.writeHead(400); res.end('Invalid avatar file format'); @@ -37,7 +37,7 @@ if (Meteor.isServer) { } // Get avatar file from database - const avatar = ReactiveCache.getAvatar(fileId); + const avatar = await ReactiveCache.getAvatar(fileId); if (!avatar) { res.writeHead(404); res.end('Avatar not found'); diff --git a/server/routes/legacyAttachments.js b/server/routes/legacyAttachments.js index e36986a7a..2925babd9 100644 --- a/server/routes/legacyAttachments.js +++ b/server/routes/legacyAttachments.js @@ -58,7 +58,7 @@ function buildContentDispositionHeader(disposition, sanitizedFilename) { if (Meteor.isServer) { // Handle legacy attachment downloads - WebApp.connectHandlers.use('/cfs/files/attachments', (req, res, next) => { + WebApp.connectHandlers.use('/cfs/files/attachments', async (req, res, next) => { const attachmentId = req.url.split('/').pop(); if (!attachmentId) { @@ -78,7 +78,7 @@ if (Meteor.isServer) { } // Check permissions - const board = ReactiveCache.getBoard(attachment.meta.boardId); + const board = await ReactiveCache.getBoard(attachment.meta.boardId); if (!board) { res.writeHead(404); res.end('Board not found'); diff --git a/server/routes/universalFileServer.js b/server/routes/universalFileServer.js index 5d4f05051..1c797bb2f 100644 --- a/server/routes/universalFileServer.js +++ b/server/routes/universalFileServer.js @@ -172,7 +172,7 @@ if (Meteor.isServer) { * - Else if avatar's owner belongs to at least one public board -> allow * - Otherwise -> deny */ - function isAuthorizedForAvatar(req, avatar) { + async function isAuthorizedForAvatar(req, avatar) { try { if (!avatar) return false; @@ -180,7 +180,7 @@ if (Meteor.isServer) { const q = parseQuery(req); const boardId = q.boardId || q.board || q.b; if (boardId) { - const board = ReactiveCache.getBoard(boardId); + const board = await ReactiveCache.getBoard(boardId); if (board && board.isPublic && board.isPublic()) return true; // If private board is specified, require membership of requester @@ -297,7 +297,7 @@ if (Meteor.isServer) { * - Public boards: allow * - Private boards: require valid user who is a member */ - function isAuthorizedForBoard(req, board) { + async function isAuthorizedForBoard(req, board) { try { if (!board) return false; if (board.isPublic && board.isPublic()) return true; @@ -389,14 +389,14 @@ if (Meteor.isServer) { * Serve attachments from new Meteor-Files structure * Route: /cdn/storage/attachments/{fileId} or /cdn/storage/attachments/{fileId}/original/{filename} */ - WebApp.connectHandlers.use('/cdn/storage/attachments', (req, res, next) => { + WebApp.connectHandlers.use('/cdn/storage/attachments', async (req, res, next) => { if (req.method !== 'GET') { return next(); } try { const fileId = extractFirstIdFromUrl(req, '/cdn/storage/attachments'); - + if (!fileId) { res.writeHead(400); res.end('Invalid attachment file ID'); @@ -412,7 +412,7 @@ if (Meteor.isServer) { } // Check permissions - const board = ReactiveCache.getBoard(attachment.meta.boardId); + const board = await ReactiveCache.getBoard(attachment.meta.boardId); if (!board) { res.writeHead(404); res.end('Board not found'); @@ -420,7 +420,7 @@ if (Meteor.isServer) { } // Enforce cookie/header/query-based auth for private boards - if (!isAuthorizedForBoard(req, board)) { + if (!(await isAuthorizedForBoard(req, board))) { res.writeHead(403); res.end('Access denied'); return; @@ -476,14 +476,14 @@ if (Meteor.isServer) { * Serve avatars from new Meteor-Files structure * Route: /cdn/storage/avatars/{fileId} or /cdn/storage/avatars/{fileId}/original/{filename} */ - WebApp.connectHandlers.use('/cdn/storage/avatars', (req, res, next) => { + WebApp.connectHandlers.use('/cdn/storage/avatars', async (req, res, next) => { if (req.method !== 'GET') { return next(); } try { const fileId = extractFirstIdFromUrl(req, '/cdn/storage/avatars'); - + if (!fileId) { res.writeHead(400); res.end('Invalid avatar file ID'); @@ -491,7 +491,7 @@ if (Meteor.isServer) { } // Get avatar from database - const avatar = ReactiveCache.getAvatar(fileId); + const avatar = await ReactiveCache.getAvatar(fileId); if (!avatar) { res.writeHead(404); res.end('Avatar not found'); @@ -499,7 +499,7 @@ if (Meteor.isServer) { } // Enforce visibility: avatars are public only in the context of public boards - if (!isAuthorizedForAvatar(req, avatar)) { + if (!(await isAuthorizedForAvatar(req, avatar))) { res.writeHead(403); res.end('Access denied'); return; @@ -541,14 +541,14 @@ if (Meteor.isServer) { * Serve legacy attachments from CollectionFS structure * Route: /cfs/files/attachments/{attachmentId} */ - WebApp.connectHandlers.use('/cfs/files/attachments', (req, res, next) => { + WebApp.connectHandlers.use('/cfs/files/attachments', async (req, res, next) => { if (req.method !== 'GET') { return next(); } try { const attachmentId = extractFirstIdFromUrl(req, '/cfs/files/attachments'); - + if (!attachmentId) { res.writeHead(400); res.end('Invalid attachment ID'); @@ -564,7 +564,7 @@ if (Meteor.isServer) { } // Check permissions - const board = ReactiveCache.getBoard(attachment.meta.boardId); + const board = await ReactiveCache.getBoard(attachment.meta.boardId); if (!board) { res.writeHead(404); res.end('Board not found'); @@ -572,7 +572,7 @@ if (Meteor.isServer) { } // Enforce cookie/header/query-based auth for private boards - if (!isAuthorizedForBoard(req, board)) { + if (!(await isAuthorizedForBoard(req, board))) { res.writeHead(403); res.end('Access denied'); return; @@ -617,14 +617,14 @@ if (Meteor.isServer) { * Serve legacy avatars from CollectionFS structure * Route: /cfs/files/avatars/{avatarId} */ - WebApp.connectHandlers.use('/cfs/files/avatars', (req, res, next) => { + WebApp.connectHandlers.use('/cfs/files/avatars', async (req, res, next) => { if (req.method !== 'GET') { return next(); } try { const avatarId = extractFirstIdFromUrl(req, '/cfs/files/avatars'); - + if (!avatarId) { res.writeHead(400); res.end('Invalid avatar ID'); @@ -632,8 +632,8 @@ if (Meteor.isServer) { } // Try to get avatar from database (new structure first) - let avatar = ReactiveCache.getAvatar(avatarId); - + let avatar = await ReactiveCache.getAvatar(avatarId); + // If not found in new structure, try to handle legacy format if (!avatar) { // For legacy avatars, we might need to handle different ID formats @@ -644,7 +644,7 @@ if (Meteor.isServer) { } // Enforce visibility for legacy avatars as well - if (!isAuthorizedForAvatar(req, avatar)) { + if (!(await isAuthorizedForAvatar(req, avatar))) { res.writeHead(403); res.end('Access denied'); return; diff --git a/server/rulesHelper.js b/server/rulesHelper.js index b783b407d..3d5317c3a 100644 --- a/server/rulesHelper.js +++ b/server/rulesHelper.js @@ -10,14 +10,14 @@ RulesHelper = { } } }, - findMatchingRules(activity) { + async findMatchingRules(activity) { const activityType = activity.activityType; if (TriggersDef[activityType] === undefined) { return []; } const matchingFields = TriggersDef[activityType].matchingFields; - const matchingMap = this.buildMatchingFieldsMap(activity, matchingFields); - const matchingTriggers = ReactiveCache.getTriggers(matchingMap); + const matchingMap = await this.buildMatchingFieldsMap(activity, matchingFields); + const matchingTriggers = await ReactiveCache.getTriggers(matchingMap); const matchingRules = []; matchingTriggers.forEach(function(trigger) { const rule = trigger.getRule(); @@ -29,20 +29,20 @@ RulesHelper = { }); return matchingRules; }, - buildMatchingFieldsMap(activity, matchingFields) { + async buildMatchingFieldsMap(activity, matchingFields) { const matchingMap = { activityType: activity.activityType }; - matchingFields.forEach(field => { + for (const field of matchingFields) { // Creating a matching map with the actual field of the activity // and with the wildcard (for example: trigger when a card is added // in any [*] board let value = activity[field]; if (field === 'oldListName') { - const oldList = ReactiveCache.getList(activity.oldListId); + const oldList = await ReactiveCache.getList(activity.oldListId); if (oldList) { value = oldList.title; } } else if (field === 'oldSwimlaneName') { - const oldSwimlane = ReactiveCache.getSwimlane(activity.oldSwimlaneId); + const oldSwimlane = await ReactiveCache.getSwimlane(activity.oldSwimlaneId); if (oldSwimlane) { value = oldSwimlane.title; } @@ -54,11 +54,11 @@ RulesHelper = { matchingMap[field] = { $in: matchesList, }; - }); + } return matchingMap; }, async performAction(activity, action) { - const card = ReactiveCache.getCard(activity.cardId); + const card = await ReactiveCache.getCard(activity.cardId); const boardId = activity.boardId; if ( action.actionType === 'moveCardToTop' || @@ -69,10 +69,10 @@ RulesHelper = { if (action.listName === '*') { list = card.list(); if (boardId !== action.boardId) { - list = ReactiveCache.getList({ title: list.title, boardId: action.boardId }); + list = await ReactiveCache.getList({ title: list.title, boardId: action.boardId }); } } else { - list = ReactiveCache.getList({ + list = await ReactiveCache.getList({ title: action.listName, boardId: action.boardId, }); @@ -86,24 +86,24 @@ RulesHelper = { let swimlane; let swimlaneId; if (action.swimlaneName === '*') { - swimlane = ReactiveCache.getSwimlane(card.swimlaneId); + swimlane = await ReactiveCache.getSwimlane(card.swimlaneId); if (boardId !== action.boardId) { - swimlane = ReactiveCache.getSwimlane({ + swimlane = await ReactiveCache.getSwimlane({ title: swimlane.title, boardId: action.boardId, }); } } else { - swimlane = ReactiveCache.getSwimlane({ + swimlane = await ReactiveCache.getSwimlane({ title: action.swimlaneName, boardId: action.boardId, }); } if (swimlane === undefined) { - swimlaneId = ReactiveCache.getSwimlane({ + swimlaneId = (await ReactiveCache.getSwimlane({ title: 'Default', boardId: action.boardId, - })._id; + }))._id; } else { swimlaneId = swimlane._id; } @@ -132,7 +132,7 @@ RulesHelper = { // Check if recipient is a Wekan user to get their language if (to && to.includes('@')) { - recipientUser = ReactiveCache.getUser({ 'emails.address': to.toLowerCase() }); + recipientUser = await ReactiveCache.getUser({ 'emails.address': to.toLowerCase() }); if (recipientUser && typeof recipientUser.getLanguage === 'function') { recipientLang = recipientUser.getLanguage(); } @@ -262,7 +262,7 @@ RulesHelper = { card.removeLabel(action.labelId); } if (action.actionType === 'addMember') { - const memberId = ReactiveCache.getUser({ username: action.username })._id; + const memberId = (await ReactiveCache.getUser({ username: action.username }))._id; card.assignMember(memberId); } if (action.actionType === 'removeMember') { @@ -272,41 +272,41 @@ RulesHelper = { card.unassignMember(members[i]); } } else { - const memberId = ReactiveCache.getUser({ username: action.username })._id; + const memberId = (await ReactiveCache.getUser({ username: action.username }))._id; card.unassignMember(memberId); } } if (action.actionType === 'checkAll') { - const checkList = ReactiveCache.getChecklist({ + const checkList = await ReactiveCache.getChecklist({ title: action.checklistName, cardId: card._id, }); await checkList.checkAllItems(); } if (action.actionType === 'uncheckAll') { - const checkList = ReactiveCache.getChecklist({ + const checkList = await ReactiveCache.getChecklist({ title: action.checklistName, cardId: card._id, }); await checkList.uncheckAllItems(); } if (action.actionType === 'checkItem') { - const checkList = ReactiveCache.getChecklist({ + const checkList = await ReactiveCache.getChecklist({ title: action.checklistName, cardId: card._id, }); - const checkItem = ReactiveCache.getChecklistItem({ + const checkItem = await ReactiveCache.getChecklistItem({ title: action.checkItemName, checkListId: checkList._id, }); await checkItem.check(); } if (action.actionType === 'uncheckItem') { - const checkList = ReactiveCache.getChecklist({ + const checkList = await ReactiveCache.getChecklist({ title: action.checklistName, cardId: card._id, }); - const checkItem = ReactiveCache.getChecklistItem({ + const checkItem = await ReactiveCache.getChecklistItem({ title: action.checkItemName, checkListId: checkList._id, }); @@ -340,7 +340,7 @@ RulesHelper = { sort: 0, }); const itemsArray = action.checklistItems.split(','); - const checkList = ReactiveCache.getChecklist(checkListId); + const checkList = await ReactiveCache.getChecklist(checkListId); for (let i = 0; i < itemsArray.length; i++) { ChecklistItems.insert({ title: itemsArray[i], @@ -351,10 +351,10 @@ RulesHelper = { } } if (action.actionType === 'createCard') { - const list = ReactiveCache.getList({ title: action.listName, boardId }); + const list = await ReactiveCache.getList({ title: action.listName, boardId }); let listId = ''; let swimlaneId = ''; - const swimlane = ReactiveCache.getSwimlane({ + const swimlane = await ReactiveCache.getSwimlane({ title: action.swimlaneName, boardId, }); @@ -364,7 +364,7 @@ RulesHelper = { listId = list._id; } if (swimlane === undefined) { - swimlaneId = ReactiveCache.getSwimlane({ title: 'Default', boardId })._id; + swimlaneId = (await ReactiveCache.getSwimlane({ title: 'Default', boardId }))._id; } else { swimlaneId = swimlane._id; } @@ -377,11 +377,11 @@ RulesHelper = { }); } if (action.actionType === 'linkCard') { - const list = ReactiveCache.getList({ title: action.listName, boardId: action.boardId }); - const card = ReactiveCache.getCard(activity.cardId); + const list = await ReactiveCache.getList({ title: action.listName, boardId: action.boardId }); + const card = await ReactiveCache.getCard(activity.cardId); let listId = ''; let swimlaneId = ''; - const swimlane = ReactiveCache.getSwimlane({ + const swimlane = await ReactiveCache.getSwimlane({ title: action.swimlaneName, boardId: action.boardId, }); @@ -391,7 +391,7 @@ RulesHelper = { listId = list._id; } if (swimlane === undefined) { - swimlaneId = ReactiveCache.getSwimlane({ title: 'Default', boardId: action.boardId })._id; + swimlaneId = (await ReactiveCache.getSwimlane({ title: 'Default', boardId: action.boardId }))._id; } else { swimlaneId = swimlane._id; }