mirror of
https://github.com/wekan/wekan.git
synced 2026-02-20 23:14:07 +01:00
Update ReactiveCache call sites to use async/await for Meteor 3.0
Part 3 of ReactiveCache async migration: - Add await before all ReactiveCache.getX() calls - Make functions containing ReactiveCache calls async - Convert forEach/map/filter loops with async callbacks to for...of - Update model helpers, Meteor methods, JsonRoutes handlers - Update collection hooks (.before/.after insert/update/remove) - Update .allow() callbacks to async Files updated across models/ and server/ directories: - Model files: cards, boards, lists, swimlanes, activities, users, checklists, checklistItems, customFields, attachments, integrations, cardComments, settings files, creators, exporters, and more - Server files: publications, methods, notifications, routes, migrations
This commit is contained in:
parent
2f6e34c5f5
commit
71eb01e233
81 changed files with 2218 additions and 2148 deletions
|
|
@ -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');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue