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:
Harry Adel 2026-02-01 00:54:38 +02:00
parent 2f6e34c5f5
commit 71eb01e233
81 changed files with 2218 additions and 2148 deletions

View file

@ -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');
}

View file

@ -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');

View file

@ -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');

View file

@ -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;