| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- /**
- * Fix Avatar URLs Migration
- * Removes problematic auth parameters from existing avatar URLs
- */
- import { Meteor } from 'meteor/meteor';
- import { check } from 'meteor/check';
- import { ReactiveCache } from '/imports/reactiveCache';
- import Boards from '/models/boards';
- import Users from '/models/users';
- import { generateUniversalAvatarUrl, cleanFileUrl, extractFileIdFromUrl, isUniversalFileUrl } from '/models/lib/universalUrlGenerator';
- class FixAvatarUrlsMigration {
- constructor() {
- this.name = 'fixAvatarUrls';
- this.version = 1;
- }
- /**
- * Check if migration is needed for a board
- */
- needsMigration(boardId) {
- // Get all users who are members of this board
- const board = ReactiveCache.getBoard(boardId);
- if (!board || !board.members) {
- return false;
- }
-
- const memberIds = board.members.map(m => m.userId);
- const users = ReactiveCache.getUsers({ _id: { $in: memberIds } });
-
- 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;
- }
- }
- }
-
- return false;
- }
- /**
- * Execute the migration for a board
- */
- async execute(boardId) {
- // Get all users who are members of this board
- const board = 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 } });
- let avatarsFixed = 0;
- console.log(`Starting avatar URL fix migration for board ${boardId}...`);
- for (const user of users) {
- if (user.profile && user.profile.avatarUrl) {
- const avatarUrl = user.profile.avatarUrl;
- let needsUpdate = false;
- let cleanUrl = avatarUrl;
-
- // 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;
- }
-
- // 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;
- }
-
- // 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;
- }
- }
-
- // 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;
- }
-
- if (needsUpdate) {
- // Update user's avatar URL
- Users.update(user._id, {
- $set: {
- 'profile.avatarUrl': cleanUrl,
- modifiedAt: new Date()
- }
- });
-
- avatarsFixed++;
-
- if (process.env.DEBUG === 'true') {
- console.log(`Fixed avatar URL for user ${user.username}: ${avatarUrl} -> ${cleanUrl}`);
- }
- }
- }
- }
- console.log(`Avatar URL fix migration completed for board ${boardId}. Fixed ${avatarsFixed} avatar URLs.`);
-
- return {
- success: true,
- avatarsFixed,
- changes: [`Fixed ${avatarsFixed} avatar URLs for board members`]
- };
- }
- }
- // Export singleton instance
- export const fixAvatarUrlsMigration = new FixAvatarUrlsMigration();
- // Meteor method
- Meteor.methods({
- '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);
- if (!board) {
- throw new Meteor.Error('board-not-found', 'Board not found');
- }
- const user = ReactiveCache.getUser(this.userId);
- 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');
- }
-
- return fixAvatarUrlsMigration.execute(boardId);
- },
- 'fixAvatarUrls.needsMigration'(boardId) {
- check(boardId, String);
-
- if (!this.userId) {
- throw new Meteor.Error('not-authorized', 'You must be logged in');
- }
-
- return fixAvatarUrlsMigration.needsMigration(boardId);
- }
- });
|