wekan/server/attachmentMigration.js
Lauri Ojansivu bd8c565415 Fixes to make board showing correctly.
Thanks to xet7 !
2025-10-12 03:48:21 +03:00

204 lines
No EOL
5.8 KiB
JavaScript

/**
* Server-side Attachment Migration System
* Handles migration of attachments from old structure to new structure
*/
import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
import { ReactiveCache } from '/imports/reactiveCache';
// Reactive variables for tracking migration progress
const migrationProgress = new ReactiveVar(0);
const migrationStatus = new ReactiveVar('');
const unconvertedAttachments = new ReactiveVar([]);
class AttachmentMigrationService {
constructor() {
this.migrationCache = new Map();
}
/**
* Migrate all attachments for a board
* @param {string} boardId - The board ID
*/
async migrateBoardAttachments(boardId) {
try {
console.log(`Starting attachment migration for board: ${boardId}`);
// Get all attachments for the board
const attachments = Attachments.find({
'meta.boardId': boardId
}).fetch();
const totalAttachments = attachments.length;
let migratedCount = 0;
migrationStatus.set(`Migrating ${totalAttachments} attachments...`);
migrationProgress.set(0);
for (const attachment of attachments) {
try {
// Check if attachment needs migration
if (this.needsMigration(attachment)) {
await this.migrateAttachment(attachment);
this.migrationCache.set(attachment._id, true);
}
migratedCount++;
const progress = Math.round((migratedCount / totalAttachments) * 100);
migrationProgress.set(progress);
migrationStatus.set(`Migrated ${migratedCount}/${totalAttachments} attachments...`);
} catch (error) {
console.error(`Error migrating attachment ${attachment._id}:`, error);
}
}
// Update unconverted attachments list
const remainingUnconverted = this.getUnconvertedAttachments(boardId);
unconvertedAttachments.set(remainingUnconverted);
migrationStatus.set('Attachment migration completed');
migrationProgress.set(100);
console.log(`Attachment migration completed for board: ${boardId}`);
} catch (error) {
console.error(`Error migrating attachments for board ${boardId}:`, error);
migrationStatus.set(`Migration failed: ${error.message}`);
throw error;
}
}
/**
* Check if an attachment needs migration
* @param {Object} attachment - The attachment object
* @returns {boolean} - True if attachment needs migration
*/
needsMigration(attachment) {
if (this.migrationCache.has(attachment._id)) {
return false; // Already migrated
}
// Check if attachment has old structure
return !attachment.meta ||
!attachment.meta.cardId ||
!attachment.meta.boardId ||
!attachment.meta.listId;
}
/**
* Migrate a single attachment
* @param {Object} attachment - The attachment object
*/
async migrateAttachment(attachment) {
try {
// Get the card to find board and list information
const card = ReactiveCache.getCard(attachment.cardId);
if (!card) {
console.warn(`Card not found for attachment ${attachment._id}`);
return;
}
const list = ReactiveCache.getList(card.listId);
if (!list) {
console.warn(`List not found for attachment ${attachment._id}`);
return;
}
// Update attachment with new structure
const updateData = {
meta: {
cardId: attachment.cardId,
boardId: list.boardId,
listId: card.listId,
userId: attachment.userId,
createdAt: attachment.createdAt || new Date(),
migratedAt: new Date()
}
};
// Preserve existing meta data if it exists
if (attachment.meta) {
updateData.meta = {
...attachment.meta,
...updateData.meta
};
}
Attachments.update(attachment._id, { $set: updateData });
console.log(`Migrated attachment ${attachment._id}`);
} catch (error) {
console.error(`Error migrating attachment ${attachment._id}:`, error);
throw error;
}
}
/**
* Get unconverted attachments for a board
* @param {string} boardId - The board ID
* @returns {Array} - Array of unconverted attachments
*/
getUnconvertedAttachments(boardId) {
try {
const attachments = Attachments.find({
'meta.boardId': boardId
}).fetch();
return attachments.filter(attachment => this.needsMigration(attachment));
} catch (error) {
console.error('Error getting unconverted attachments:', error);
return [];
}
}
/**
* Get migration progress
* @param {string} boardId - The board ID
* @returns {Object} - Migration progress data
*/
getMigrationProgress(boardId) {
const progress = migrationProgress.get();
const status = migrationStatus.get();
const unconverted = this.getUnconvertedAttachments(boardId);
return {
progress,
status,
unconvertedAttachments: unconverted
};
}
}
const attachmentMigrationService = new AttachmentMigrationService();
// Meteor methods
Meteor.methods({
'attachmentMigration.migrateBoardAttachments'(boardId) {
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
return attachmentMigrationService.migrateBoardAttachments(boardId);
},
'attachmentMigration.getProgress'(boardId) {
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
return attachmentMigrationService.getMigrationProgress(boardId);
},
'attachmentMigration.getUnconvertedAttachments'(boardId) {
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
return attachmentMigrationService.getUnconvertedAttachments(boardId);
}
});
export { attachmentMigrationService };