Fixed migrations.

Thanks to xet7 !
This commit is contained in:
Lauri Ojansivu 2025-10-14 01:30:59 +03:00
parent e8453783da
commit 63c314ca18
5 changed files with 314 additions and 187 deletions

View file

@ -5,7 +5,6 @@ import { boardConverter } from '/client/lib/boardConverter';
import { migrationManager } from '/client/lib/migrationManager';
import { attachmentMigrationManager } from '/client/lib/attachmentMigrationManager';
import { Swimlanes } from '/models/swimlanes';
import { Lists } from '/models/lists';
const subManager = new SubsManager();
const { calculateIndex } = Utils;
@ -88,21 +87,26 @@ BlazeComponent.extendComponent({
}
// Check if board needs conversion (for old structure)
const needsConversion = boardConverter.needsConversion(boardId);
if (needsConversion) {
this.isConverting.set(true);
const success = await boardConverter.convertBoard(boardId);
this.isConverting.set(false);
if (success) {
this.isBoardReady.set(true);
} else {
console.error('Board conversion failed, setting ready to true anyway');
this.isBoardReady.set(true); // Still show board even if conversion failed
}
} else {
if (boardConverter.isBoardConverted(boardId)) {
console.log(`Board ${boardId} has already been converted, skipping conversion`);
this.isBoardReady.set(true);
} else {
const needsConversion = boardConverter.needsConversion(boardId);
if (needsConversion) {
this.isConverting.set(true);
const success = await boardConverter.convertBoard(boardId);
this.isConverting.set(false);
if (success) {
this.isBoardReady.set(true);
} else {
console.error('Board conversion failed, setting ready to true anyway');
this.isBoardReady.set(true); // Still show board even if conversion failed
}
} else {
this.isBoardReady.set(true);
}
}
// Start attachment migration in background if needed
@ -132,12 +136,22 @@ BlazeComponent.extendComponent({
async startAttachmentMigrationIfNeeded(boardId) {
try {
// Check if board has already been migrated
if (attachmentMigrationManager.isBoardMigrated(boardId)) {
console.log(`Board ${boardId} has already been migrated, skipping`);
return;
}
// Check if there are unconverted attachments
const unconvertedAttachments = attachmentMigrationManager.getUnconvertedAttachments(boardId);
if (unconvertedAttachments.length > 0) {
console.log(`Starting attachment migration for ${unconvertedAttachments.length} attachments in board ${boardId}`);
await attachmentMigrationManager.startAttachmentMigration(boardId);
} else {
// No attachments to migrate, mark board as migrated
// This will be handled by the migration manager itself
console.log(`Board ${boardId} has no attachments to migrate`);
}
} catch (error) {
console.error('Error starting attachment migration:', error);

View file

@ -13,9 +13,13 @@ export const attachmentMigrationStatus = new ReactiveVar('');
export const isMigratingAttachments = new ReactiveVar(false);
export const unconvertedAttachments = new ReactiveVar([]);
// Global tracking of migrated boards (persistent across component reinitializations)
const globalMigratedBoards = new Set();
class AttachmentMigrationManager {
constructor() {
this.migrationCache = new Map(); // Cache migrated attachment IDs
this.migratedBoards = new Set(); // Track boards that have been migrated
}
/**
@ -43,6 +47,33 @@ class AttachmentMigrationManager {
}
}
/**
* Check if a board has been migrated
* @param {string} boardId - The board ID
* @returns {boolean} - True if board has been migrated
*/
isBoardMigrated(boardId) {
return globalMigratedBoards.has(boardId);
}
/**
* Check if a board has been migrated (server-side check)
* @param {string} boardId - The board ID
* @returns {Promise<boolean>} - True if board has been migrated
*/
async isBoardMigratedServer(boardId) {
return new Promise((resolve) => {
Meteor.call('attachmentMigration.isBoardMigrated', boardId, (error, result) => {
if (error) {
console.error('Error checking board migration status:', error);
resolve(false);
} else {
resolve(result);
}
});
});
}
/**
* Get all unconverted attachments for a board
* @param {string} boardId - The board ID
@ -70,6 +101,20 @@ class AttachmentMigrationManager {
return; // Already migrating
}
// Check if this board has already been migrated (client-side check first)
if (globalMigratedBoards.has(boardId)) {
console.log(`Board ${boardId} has already been migrated (client-side), skipping`);
return;
}
// Double-check with server-side check
const serverMigrated = await this.isBoardMigratedServer(boardId);
if (serverMigrated) {
console.log(`Board ${boardId} has already been migrated (server-side), skipping`);
globalMigratedBoards.add(boardId); // Sync client-side tracking
return;
}
isMigratingAttachments.set(true);
attachmentMigrationStatus.set('Starting attachment migration...');
attachmentMigrationProgress.set(0);
@ -82,6 +127,8 @@ class AttachmentMigrationManager {
attachmentMigrationStatus.set('All attachments are already migrated');
attachmentMigrationProgress.set(100);
isMigratingAttachments.set(false);
globalMigratedBoards.add(boardId); // Mark board as migrated
console.log(`Board ${boardId} has no attachments to migrate, marked as migrated`);
return;
}
@ -89,7 +136,8 @@ class AttachmentMigrationManager {
Meteor.call('attachmentMigration.migrateBoardAttachments', boardId, (error, result) => {
if (error) {
console.error('Failed to start attachment migration:', error);
attachmentMigrationStatus.set(`Migration failed: ${error.message}`);
const errorMessage = error.message || error.reason || error.toString();
attachmentMigrationStatus.set(`Migration failed: ${errorMessage}`);
isMigratingAttachments.set(false);
} else {
console.log('Attachment migration started for board:', boardId);
@ -128,6 +176,8 @@ class AttachmentMigrationManager {
clearInterval(pollInterval);
isMigratingAttachments.set(false);
this.migrationCache.clear(); // Clear cache to refresh data
globalMigratedBoards.add(boardId); // Mark board as migrated
console.log(`Board ${boardId} migration completed and marked as migrated`);
}
}
});

View file

@ -13,11 +13,23 @@ export const conversionStatus = new ReactiveVar('');
export const conversionEstimatedTime = new ReactiveVar('');
export const isConverting = new ReactiveVar(false);
// Global tracking of converted boards (persistent across component reinitializations)
const globalConvertedBoards = new Set();
class BoardConverter {
constructor() {
this.conversionCache = new Map(); // Cache converted board IDs
}
/**
* Check if a board has been converted
* @param {string} boardId - The board ID
* @returns {boolean} - True if board has been converted
*/
isBoardConverted(boardId) {
return globalConvertedBoards.has(boardId);
}
/**
* Check if a board needs conversion
* @param {string} boardId - The board ID to check
@ -55,6 +67,12 @@ class BoardConverter {
* @returns {Promise<boolean>} - True if conversion was successful
*/
async convertBoard(boardId) {
// Check if board has already been converted
if (this.isBoardConverted(boardId)) {
console.log(`Board ${boardId} has already been converted, skipping`);
return true;
}
if (this.conversionCache.has(boardId)) {
return true; // Already converted
}
@ -87,7 +105,9 @@ class BoardConverter {
if (listsToConvert.length === 0) {
this.conversionCache.set(boardId, true);
globalConvertedBoards.add(boardId); // Mark board as converted
isConverting.set(false);
console.log(`Board ${boardId} has no lists to convert, marked as converted`);
return true;
}
@ -124,9 +144,11 @@ class BoardConverter {
// Mark as converted
this.conversionCache.set(boardId, true);
globalConvertedBoards.add(boardId); // Mark board as converted
conversionStatus.set('Board conversion completed!');
conversionProgress.set(100);
console.log(`Board ${boardId} conversion completed and marked as converted`);
// Clear status after a delay
setTimeout(() => {
@ -159,7 +181,7 @@ class BoardConverter {
// Update lists in batch
updates.forEach(update => {
ReactiveCache.getCollection('lists').update(update._id, {
Lists.update(update._id, {
$set: { swimlaneId: update.swimlaneId }
});
});