2025-10-25 21:09:07 +03:00
|
|
|
/**
|
|
|
|
|
* Fix Avatar URLs Migration
|
|
|
|
|
* Removes problematic auth parameters from existing avatar URLs
|
|
|
|
|
*/
|
|
|
|
|
|
2025-11-05 20:22:56 +02:00
|
|
|
import { Meteor } from 'meteor/meteor';
|
|
|
|
|
import { check } from 'meteor/check';
|
2025-10-25 21:09:07 +03:00
|
|
|
import { ReactiveCache } from '/imports/reactiveCache';
|
2025-11-05 20:22:56 +02:00
|
|
|
import Boards from '/models/boards';
|
2025-10-25 21:09:07 +03:00
|
|
|
import Users from '/models/users';
|
|
|
|
|
import { generateUniversalAvatarUrl, cleanFileUrl, extractFileIdFromUrl, isUniversalFileUrl } from '/models/lib/universalUrlGenerator';
|
|
|
|
|
|
|
|
|
|
class FixAvatarUrlsMigration {
|
|
|
|
|
constructor() {
|
|
|
|
|
this.name = 'fixAvatarUrls';
|
|
|
|
|
this.version = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-11-05 20:22:56 +02:00
|
|
|
* Check if migration is needed for a board
|
2025-10-25 21:09:07 +03: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
2026-02-01 00:54:38 +02:00
|
|
|
async needsMigration(boardId) {
|
2025-11-05 20:22:56 +02:00
|
|
|
// Get all users who are members of this board
|
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
2026-02-01 00:54:38 +02:00
|
|
|
const board = await ReactiveCache.getBoard(boardId);
|
2025-11-05 20:22:56 +02:00
|
|
|
if (!board || !board.members) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
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
2026-02-01 00:54:38 +02:00
|
|
|
|
2025-11-05 20:22:56 +02:00
|
|
|
const memberIds = board.members.map(m => m.userId);
|
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
2026-02-01 00:54:38 +02:00
|
|
|
const users = await ReactiveCache.getUsers({ _id: { $in: memberIds } });
|
2025-10-25 21:09:07 +03:00
|
|
|
|
|
|
|
|
for (const user of users) {
|
|
|
|
|
if (user.profile && user.profile.avatarUrl) {
|
|
|
|
|
const avatarUrl = user.profile.avatarUrl;
|
|
|
|
|
if (avatarUrl.includes('auth=false') || avatarUrl.includes('brokenIsFine=true')) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-02-08 00:48:39 +02:00
|
|
|
|
2025-10-25 21:09:07 +03:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-11-05 20:22:56 +02:00
|
|
|
* Execute the migration for a board
|
2025-10-25 21:09:07 +03:00
|
|
|
*/
|
2025-11-05 20:22:56 +02:00
|
|
|
async execute(boardId) {
|
|
|
|
|
// Get all users who are members of this board
|
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
2026-02-01 00:54:38 +02:00
|
|
|
const board = await ReactiveCache.getBoard(boardId);
|
2025-11-05 20:22:56 +02:00
|
|
|
if (!board || !board.members) {
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
error: 'Board not found or has no members'
|
|
|
|
|
};
|
|
|
|
|
}
|
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
2026-02-01 00:54:38 +02:00
|
|
|
|
2025-11-05 20:22:56 +02:00
|
|
|
const memberIds = board.members.map(m => m.userId);
|
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
2026-02-01 00:54:38 +02:00
|
|
|
const users = await ReactiveCache.getUsers({ _id: { $in: memberIds } });
|
2025-10-25 21:09:07 +03:00
|
|
|
let avatarsFixed = 0;
|
|
|
|
|
|
2025-11-05 20:22:56 +02:00
|
|
|
console.log(`Starting avatar URL fix migration for board ${boardId}...`);
|
2025-10-25 21:09:07 +03:00
|
|
|
|
|
|
|
|
for (const user of users) {
|
|
|
|
|
if (user.profile && user.profile.avatarUrl) {
|
|
|
|
|
const avatarUrl = user.profile.avatarUrl;
|
|
|
|
|
let needsUpdate = false;
|
|
|
|
|
let cleanUrl = avatarUrl;
|
2026-02-08 00:48:39 +02:00
|
|
|
|
2025-10-25 21:09:07 +03:00
|
|
|
// Check if URL has problematic parameters
|
|
|
|
|
if (avatarUrl.includes('auth=false') || avatarUrl.includes('brokenIsFine=true')) {
|
|
|
|
|
// Remove problematic parameters
|
|
|
|
|
cleanUrl = cleanUrl.replace(/[?&]auth=false/g, '');
|
|
|
|
|
cleanUrl = cleanUrl.replace(/[?&]brokenIsFine=true/g, '');
|
|
|
|
|
cleanUrl = cleanUrl.replace(/\?&/g, '?');
|
|
|
|
|
cleanUrl = cleanUrl.replace(/\?$/g, '');
|
|
|
|
|
needsUpdate = true;
|
|
|
|
|
}
|
2026-02-08 00:48:39 +02:00
|
|
|
|
2025-10-25 21:09:07 +03:00
|
|
|
// Check if URL is using old CollectionFS format
|
|
|
|
|
if (avatarUrl.includes('/cfs/files/avatars/')) {
|
|
|
|
|
cleanUrl = cleanUrl.replace('/cfs/files/avatars/', '/cdn/storage/avatars/');
|
|
|
|
|
needsUpdate = true;
|
|
|
|
|
}
|
2026-02-08 00:48:39 +02:00
|
|
|
|
2025-10-25 21:09:07 +03:00
|
|
|
// Check if URL is missing the /cdn/storage/avatars/ prefix
|
|
|
|
|
if (avatarUrl.includes('avatars/') && !avatarUrl.includes('/cdn/storage/avatars/') && !avatarUrl.includes('/cfs/files/avatars/')) {
|
|
|
|
|
// This might be a relative URL, make it absolute
|
|
|
|
|
if (!avatarUrl.startsWith('http') && !avatarUrl.startsWith('/')) {
|
|
|
|
|
cleanUrl = `/cdn/storage/avatars/${avatarUrl}`;
|
|
|
|
|
needsUpdate = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-02-08 00:48:39 +02:00
|
|
|
|
2025-10-25 21:09:07 +03:00
|
|
|
// If we have a file ID, generate a universal URL
|
|
|
|
|
const fileId = extractFileIdFromUrl(avatarUrl, 'avatar');
|
|
|
|
|
if (fileId && !isUniversalFileUrl(cleanUrl, 'avatar')) {
|
|
|
|
|
cleanUrl = generateUniversalAvatarUrl(fileId);
|
|
|
|
|
needsUpdate = true;
|
|
|
|
|
}
|
2026-02-08 00:48:39 +02:00
|
|
|
|
2025-10-25 21:09:07 +03:00
|
|
|
if (needsUpdate) {
|
|
|
|
|
// Update user's avatar URL
|
|
|
|
|
Users.update(user._id, {
|
|
|
|
|
$set: {
|
|
|
|
|
'profile.avatarUrl': cleanUrl,
|
|
|
|
|
modifiedAt: new Date()
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-02-08 00:48:39 +02:00
|
|
|
|
2025-10-25 21:09:07 +03:00
|
|
|
avatarsFixed++;
|
2026-02-08 00:48:39 +02:00
|
|
|
|
2025-10-25 21:09:07 +03:00
|
|
|
if (process.env.DEBUG === 'true') {
|
|
|
|
|
console.log(`Fixed avatar URL for user ${user.username}: ${avatarUrl} -> ${cleanUrl}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-05 20:22:56 +02:00
|
|
|
console.log(`Avatar URL fix migration completed for board ${boardId}. Fixed ${avatarsFixed} avatar URLs.`);
|
2026-02-08 00:48:39 +02:00
|
|
|
|
2025-10-25 21:09:07 +03:00
|
|
|
return {
|
|
|
|
|
success: true,
|
2025-11-05 20:22:56 +02:00
|
|
|
avatarsFixed,
|
|
|
|
|
changes: [`Fixed ${avatarsFixed} avatar URLs for board members`]
|
2025-10-25 21:09:07 +03:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Export singleton instance
|
|
|
|
|
export const fixAvatarUrlsMigration = new FixAvatarUrlsMigration();
|
|
|
|
|
|
|
|
|
|
// Meteor method
|
|
|
|
|
Meteor.methods({
|
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
2026-02-01 00:54:38 +02:00
|
|
|
async 'fixAvatarUrls.execute'(boardId) {
|
2025-11-05 20:22:56 +02:00
|
|
|
check(boardId, String);
|
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
2026-02-01 00:54:38 +02:00
|
|
|
|
2025-10-25 21:09:07 +03:00
|
|
|
if (!this.userId) {
|
2025-11-05 20:22:56 +02:00
|
|
|
throw new Meteor.Error('not-authorized', 'You must be logged in');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if user is board admin
|
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
2026-02-01 00:54:38 +02:00
|
|
|
const board = await ReactiveCache.getBoard(boardId);
|
2025-11-05 20:22:56 +02:00
|
|
|
if (!board) {
|
|
|
|
|
throw new Meteor.Error('board-not-found', 'Board not found');
|
|
|
|
|
}
|
|
|
|
|
|
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
2026-02-01 00:54:38 +02:00
|
|
|
const user = await ReactiveCache.getUser(this.userId);
|
2025-11-05 20:22:56 +02:00
|
|
|
if (!user) {
|
|
|
|
|
throw new Meteor.Error('user-not-found', 'User not found');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Only board admins can run migrations
|
|
|
|
|
const isBoardAdmin = board.members && board.members.some(
|
|
|
|
|
member => member.userId === this.userId && member.isAdmin
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!isBoardAdmin && !user.isAdmin) {
|
|
|
|
|
throw new Meteor.Error('not-authorized', 'Only board administrators can run migrations');
|
2025-10-25 21:09:07 +03: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
2026-02-01 00:54:38 +02:00
|
|
|
|
|
|
|
|
return await fixAvatarUrlsMigration.execute(boardId);
|
2025-10-25 21:09:07 +03: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
2026-02-01 00:54:38 +02:00
|
|
|
async 'fixAvatarUrls.needsMigration'(boardId) {
|
2025-11-05 20:22:56 +02:00
|
|
|
check(boardId, String);
|
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
2026-02-01 00:54:38 +02:00
|
|
|
|
2025-10-25 21:09:07 +03:00
|
|
|
if (!this.userId) {
|
2025-11-05 20:22:56 +02:00
|
|
|
throw new Meteor.Error('not-authorized', 'You must be logged in');
|
2025-10-25 21:09:07 +03: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
2026-02-01 00:54:38 +02:00
|
|
|
|
|
|
|
|
return await fixAvatarUrlsMigration.needsMigration(boardId);
|
2025-10-25 21:09:07 +03:00
|
|
|
}
|
|
|
|
|
});
|