wekan/server/migrations/fixAvatarUrls.js
Lauri Ojansivu 30620d0ca4
Some checks failed
Docker / build (push) Has been cancelled
Docker Image CI / build (push) Has been cancelled
Release Charts / release (push) Has been cancelled
Test suite / Meteor tests (push) Has been cancelled
Test suite / Coverage report (push) Has been cancelled
Some migrations and mobile fixes.
Thanks to xet7 !
2025-10-25 21:09:07 +03:00

128 lines
3.7 KiB
JavaScript

/**
* Fix Avatar URLs Migration
* Removes problematic auth parameters from existing avatar URLs
*/
import { ReactiveCache } from '/imports/reactiveCache';
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
*/
needsMigration() {
const users = ReactiveCache.getUsers({});
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
*/
async execute() {
const users = ReactiveCache.getUsers({});
let avatarsFixed = 0;
console.log(`Starting avatar URL fix migration...`);
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. Fixed ${avatarsFixed} avatar URLs.`);
return {
success: true,
avatarsFixed
};
}
}
// Export singleton instance
export const fixAvatarUrlsMigration = new FixAvatarUrlsMigration();
// Meteor method
Meteor.methods({
'fixAvatarUrls.execute'() {
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
return fixAvatarUrlsMigration.execute();
},
'fixAvatarUrls.needsMigration'() {
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
return fixAvatarUrlsMigration.needsMigration();
}
});