From 63c314ca185aeda650c01b4a67fcde1067320d22 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Tue, 14 Oct 2025 01:30:59 +0300 Subject: [PATCH] Fixed migrations. Thanks to xet7 ! --- client/components/boards/boardBody.js | 44 ++- client/lib/attachmentMigrationManager.js | 52 +++- client/lib/boardConverter.js | 24 +- package-lock.json | 336 +++++++++++------------ server/attachmentMigration.js | 45 ++- 5 files changed, 314 insertions(+), 187 deletions(-) diff --git a/client/components/boards/boardBody.js b/client/components/boards/boardBody.js index cf99e9150..64c4854b4 100644 --- a/client/components/boards/boardBody.js +++ b/client/components/boards/boardBody.js @@ -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); diff --git a/client/lib/attachmentMigrationManager.js b/client/lib/attachmentMigrationManager.js index cc2c7bc0d..9dc6f1d2f 100644 --- a/client/lib/attachmentMigrationManager.js +++ b/client/lib/attachmentMigrationManager.js @@ -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} - 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`); } } }); diff --git a/client/lib/boardConverter.js b/client/lib/boardConverter.js index 0f19d31fb..93f2ccc9a 100644 --- a/client/lib/boardConverter.js +++ b/client/lib/boardConverter.js @@ -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} - 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 } }); }); diff --git a/package-lock.json b/package-lock.json index 2f9ca93bd..bea1e08d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -667,79 +667,79 @@ "dev": true }, "@smithy/abort-controller": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.0.tgz", - "integrity": "sha512-PLUYa+SUKOEZtXFURBu/CNxlsxfaFGxSBPcStL13KpVeVWIfdezWyDqkz7iDLmwnxojXD0s5KzuB5HGHvt4Aeg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.1.tgz", + "integrity": "sha512-OvVe992TXYHR7QpYebmtw+/MF5AP9vU0fjfyfW1VmNYeA/dfibLhN13xrzIj+EO0HYMPur5lUIB9hRZ7IhjLDQ==", "optional": true, "requires": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.7.0", "tslib": "^2.6.2" } }, "@smithy/config-resolver": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.3.0.tgz", - "integrity": "sha512-9oH+n8AVNiLPK/iK/agOsoWfrKZ3FGP3502tkksd6SRsKMYiu7AFX0YXo6YBADdsAj7C+G/aLKdsafIJHxuCkQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.3.1.tgz", + "integrity": "sha512-tWDwrWy37CDVGeaP8AIGZPFL2RoFtmd5Y+nTzLw5qroXNedT2S66EY2d+XzB1zxulCd6nfDXnAQu4auq90aj5Q==", "optional": true, "requires": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/types": "^4.6.0", + "@smithy/node-config-provider": "^4.3.1", + "@smithy/types": "^4.7.0", "@smithy/util-config-provider": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", + "@smithy/util-middleware": "^4.2.1", "tslib": "^2.6.2" } }, "@smithy/core": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.15.0.tgz", - "integrity": "sha512-VJWncXgt+ExNn0U2+Y7UywuATtRYaodGQKFo9mDyh70q+fJGedfrqi2XuKU1BhiLeXgg6RZrW7VEKfeqFhHAJA==", + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.16.0.tgz", + "integrity": "sha512-T6eJ+yhnCP5plm6aEaenUpxkHTd5zVCKpyWAbP4ekJ7R5wSmKQjmvQIA58CXB1sgrwaYZJXOJMeRtpghxP7n1g==", "optional": true, "requires": { - "@smithy/middleware-serde": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", + "@smithy/middleware-serde": "^4.2.1", + "@smithy/protocol-http": "^5.3.1", + "@smithy/types": "^4.7.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", - "@smithy/util-stream": "^4.5.0", + "@smithy/util-middleware": "^4.2.1", + "@smithy/util-stream": "^4.5.1", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "@smithy/credential-provider-imds": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.0.tgz", - "integrity": "sha512-SOhFVvFH4D5HJZytb0bLKxCrSnwcqPiNlrw+S4ZXjMnsC+o9JcUQzbZOEQcA8yv9wJFNhfsUiIUKiEnYL68Big==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.1.tgz", + "integrity": "sha512-Y7Gq6xZvAUJOf60prfpknyKIJoIU89q/t6Cr4AWLYZBaaIhEdWJRIWvLqiqL5Hb6iK8btorKHI8jT6ZuQB+BVg==", "optional": true, "requires": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/types": "^4.6.0", - "@smithy/url-parser": "^4.2.0", + "@smithy/node-config-provider": "^4.3.1", + "@smithy/property-provider": "^4.2.1", + "@smithy/types": "^4.7.0", + "@smithy/url-parser": "^4.2.1", "tslib": "^2.6.2" } }, "@smithy/fetch-http-handler": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.1.tgz", - "integrity": "sha512-3AvYYbB+Dv5EPLqnJIAgYw/9+WzeBiUYS8B+rU0pHq5NMQMvrZmevUROS4V2GAt0jEOn9viBzPLrZE+riTNd5Q==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.2.tgz", + "integrity": "sha512-3CXDhyjl6nz0na+te37f+aGqmDwJeyeo9GK7ThPStoa/ruZcUm17UPRC4xJvbm8Z4JCvbnh54mRCFtiR/IzXjw==", "optional": true, "requires": { - "@smithy/protocol-http": "^5.3.0", - "@smithy/querystring-builder": "^4.2.0", - "@smithy/types": "^4.6.0", + "@smithy/protocol-http": "^5.3.1", + "@smithy/querystring-builder": "^4.2.1", + "@smithy/types": "^4.7.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "@smithy/hash-node": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.0.tgz", - "integrity": "sha512-ugv93gOhZGysTctZh9qdgng8B+xO0cj+zN0qAZ+Sgh7qTQGPOJbMdIuyP89KNfUyfAqFSNh5tMvC+h2uCpmTtA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.1.tgz", + "integrity": "sha512-eqyR+zua9LI8K0NhYMUEh8HDy7zaT1gRuB3d1kNIKeSG9nc2JxNbKXYNRdmIvAWG3wJyl9uUWPs+H3k8uDes1Q==", "optional": true, "requires": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.7.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" @@ -767,12 +767,12 @@ } }, "@smithy/invalid-dependency": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.0.tgz", - "integrity": "sha512-ZmK5X5fUPAbtvRcUPtk28aqIClVhbfcmfoS4M7UQBTnDdrNxhsrxYVv0ZEl5NaPSyExsPWqL4GsPlRvtlwg+2A==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.1.tgz", + "integrity": "sha512-mGH4fyQwVun9jtAbNQjU5Dt2pItOM1ULQrceaISyyu8pEjreBjyC0T5BN+zU2ltqKF3NefjQ+ApfoAk1w1UplQ==", "optional": true, "requires": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.7.0", "tslib": "^2.6.2" } }, @@ -786,166 +786,166 @@ } }, "@smithy/middleware-content-length": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.0.tgz", - "integrity": "sha512-6ZAnwrXFecrA4kIDOcz6aLBhU5ih2is2NdcZtobBDSdSHtE9a+MThB5uqyK4XXesdOCvOcbCm2IGB95birTSOQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.1.tgz", + "integrity": "sha512-+V6TdTAcS/dGILfe4hZP5lVnCuUvcX05yj+GihbOpy/ylGzUYhE/oYmv4vU33vMj5rfpdcfuyuESHkJTTRDXGw==", "optional": true, "requires": { - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", + "@smithy/protocol-http": "^5.3.1", + "@smithy/types": "^4.7.0", "tslib": "^2.6.2" } }, "@smithy/middleware-endpoint": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.1.tgz", - "integrity": "sha512-JtM4SjEgImLEJVXdsbvWHYiJ9dtuKE8bqLlvkvGi96LbejDL6qnVpVxEFUximFodoQbg0Gnkyff9EKUhFhVJFw==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.2.tgz", + "integrity": "sha512-3UP7E5SD0rF6cQEWVMxfbMvpC0fv9fTbusMQfKAXlff5g7L2tn2kspiiGX+nqyK78FV2kP/O2WS7rbIvhfw6/Q==", "optional": true, "requires": { - "@smithy/core": "^3.15.0", - "@smithy/middleware-serde": "^4.2.0", - "@smithy/node-config-provider": "^4.3.0", - "@smithy/shared-ini-file-loader": "^4.3.0", - "@smithy/types": "^4.6.0", - "@smithy/url-parser": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", + "@smithy/core": "^3.16.0", + "@smithy/middleware-serde": "^4.2.1", + "@smithy/node-config-provider": "^4.3.1", + "@smithy/shared-ini-file-loader": "^4.3.1", + "@smithy/types": "^4.7.0", + "@smithy/url-parser": "^4.2.1", + "@smithy/util-middleware": "^4.2.1", "tslib": "^2.6.2" } }, "@smithy/middleware-retry": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.1.tgz", - "integrity": "sha512-wXxS4ex8cJJteL0PPQmWYkNi9QKDWZIpsndr0wZI2EL+pSSvA/qqxXU60gBOJoIc2YgtZSWY/PE86qhKCCKP1w==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.2.tgz", + "integrity": "sha512-cuPmDJi7AE7PkdfeqJaHKBR33mXCl1MPxrboQDR/zZUo9u947m0gnYRd25NTSRER5LZpNDCvVTSedeAC9dHckA==", "optional": true, "requires": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/service-error-classification": "^4.2.0", - "@smithy/smithy-client": "^4.7.1", - "@smithy/types": "^4.6.0", - "@smithy/util-middleware": "^4.2.0", - "@smithy/util-retry": "^4.2.0", + "@smithy/node-config-provider": "^4.3.1", + "@smithy/protocol-http": "^5.3.1", + "@smithy/service-error-classification": "^4.2.1", + "@smithy/smithy-client": "^4.8.0", + "@smithy/types": "^4.7.0", + "@smithy/util-middleware": "^4.2.1", + "@smithy/util-retry": "^4.2.1", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "@smithy/middleware-serde": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.0.tgz", - "integrity": "sha512-rpTQ7D65/EAbC6VydXlxjvbifTf4IH+sADKg6JmAvhkflJO2NvDeyU9qsWUNBelJiQFcXKejUHWRSdmpJmEmiw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.1.tgz", + "integrity": "sha512-0J1EDeGGBNz0h0R/UGKudF7gBMS+UMJEWuNPY1hDV/RTyyKgBfsKH87nKCeCSB81EgjnBDFsnfXD2ZMRCfIPWA==", "optional": true, "requires": { - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", + "@smithy/protocol-http": "^5.3.1", + "@smithy/types": "^4.7.0", "tslib": "^2.6.2" } }, "@smithy/middleware-stack": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.0.tgz", - "integrity": "sha512-G5CJ//eqRd9OARrQu9MK1H8fNm2sMtqFh6j8/rPozhEL+Dokpvi1Og+aCixTuwDAGZUkJPk6hJT5jchbk/WCyg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.1.tgz", + "integrity": "sha512-gWKgBqYYrcdtkEMzN8hEtypab7zgU4VVZHSwURAR5YGrvGJxbBh5mC9RPmVWS7TZxr/vB4yMKfxEQTrYRKRQ3Q==", "optional": true, "requires": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.7.0", "tslib": "^2.6.2" } }, "@smithy/node-config-provider": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.0.tgz", - "integrity": "sha512-5QgHNuWdT9j9GwMPPJCKxy2KDxZ3E5l4M3/5TatSZrqYVoEiqQrDfAq8I6KWZw7RZOHtVtCzEPdYz7rHZixwcA==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.1.tgz", + "integrity": "sha512-Ap8Wd95HCrWRktMAZNc0AVzdPdUSPHsG59+DMe+4aH74FLDnVTo/7XDcRhSkSZCHeDjaDtzAh5OvnHOE0VHwUg==", "optional": true, "requires": { - "@smithy/property-provider": "^4.2.0", - "@smithy/shared-ini-file-loader": "^4.3.0", - "@smithy/types": "^4.6.0", + "@smithy/property-provider": "^4.2.1", + "@smithy/shared-ini-file-loader": "^4.3.1", + "@smithy/types": "^4.7.0", "tslib": "^2.6.2" } }, "@smithy/node-http-handler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.3.0.tgz", - "integrity": "sha512-RHZ/uWCmSNZ8cneoWEVsVwMZBKy/8123hEpm57vgGXA3Irf/Ja4v9TVshHK2ML5/IqzAZn0WhINHOP9xl+Qy6Q==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.0.tgz", + "integrity": "sha512-E00fuesARqnmdc1vR4qurQjQH+QWcsKjmM6kYoJBWjxgqNfp1WHc1SwfC18EdVaYamgctxyXV6kWhHmanhYgCg==", "optional": true, "requires": { - "@smithy/abort-controller": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/querystring-builder": "^4.2.0", - "@smithy/types": "^4.6.0", + "@smithy/abort-controller": "^4.2.1", + "@smithy/protocol-http": "^5.3.1", + "@smithy/querystring-builder": "^4.2.1", + "@smithy/types": "^4.7.0", "tslib": "^2.6.2" } }, "@smithy/property-provider": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.0.tgz", - "integrity": "sha512-rV6wFre0BU6n/tx2Ztn5LdvEdNZ2FasQbPQmDOPfV9QQyDmsCkOAB0osQjotRCQg+nSKFmINhyda0D3AnjSBJw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.1.tgz", + "integrity": "sha512-2zthf6j/u4XV3nRvulJgQsZdAs9xNf7dJPE5+Wvrx4yAsNrmtchadydASqRLXEw67ovl8c+HFa58QEXD/jUMSg==", "optional": true, "requires": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.7.0", "tslib": "^2.6.2" } }, "@smithy/protocol-http": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.0.tgz", - "integrity": "sha512-6POSYlmDnsLKb7r1D3SVm7RaYW6H1vcNcTWGWrF7s9+2noNYvUsm7E4tz5ZQ9HXPmKn6Hb67pBDRIjrT4w/d7Q==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.1.tgz", + "integrity": "sha512-DqbfSgeZC0qo3/3fLgr5UEdOE7/o/VlVOt6LtpShwVcw3PIoqQMRCUTzMpJ0keAVb86Cl1w5YtW7uDUzeNMMLA==", "optional": true, "requires": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.7.0", "tslib": "^2.6.2" } }, "@smithy/querystring-builder": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.0.tgz", - "integrity": "sha512-Q4oFD0ZmI8yJkiPPeGUITZj++4HHYCW3pYBYfIobUCkYpI6mbkzmG1MAQQ3lJYYWj3iNqfzOenUZu+jqdPQ16A==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.1.tgz", + "integrity": "sha512-2Qf5x7Afn6ofV3XLYL9+oaOwWK2FUC/LLTarex0SaXEKctVdzCdOOzEfaAZJSwSSiYqFWF6e2r0m7PFDzA44fA==", "optional": true, "requires": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.7.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "@smithy/querystring-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.0.tgz", - "integrity": "sha512-BjATSNNyvVbQxOOlKse0b0pSezTWGMvA87SvoFoFlkRsKXVsN3bEtjCxvsNXJXfnAzlWFPaT9DmhWy1vn0sNEA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.1.tgz", + "integrity": "sha512-y1DmifEgOF5J1MmrLP2arzI17tEaVqD+NUnfE+sVcpPcEHmAUL0TF9gQzAi5s6GGHUyDurO+zHvZQOeo7LuJnQ==", "optional": true, "requires": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.7.0", "tslib": "^2.6.2" } }, "@smithy/service-error-classification": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.0.tgz", - "integrity": "sha512-Ylv1ttUeKatpR0wEOMnHf1hXMktPUMObDClSWl2TpCVT4DwtJhCeighLzSLbgH3jr5pBNM0LDXT5yYxUvZ9WpA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.1.tgz", + "integrity": "sha512-NEcg3bGL9MddDd0GtH1+6bLg+e9SpbNEAVV8vEM4uWgqixECItz6wf0sYcq+N0lQjeRljdwaG3wxd2YgJ7JfbQ==", "optional": true, "requires": { - "@smithy/types": "^4.6.0" + "@smithy/types": "^4.7.0" } }, "@smithy/shared-ini-file-loader": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.3.0.tgz", - "integrity": "sha512-VCUPPtNs+rKWlqqntX0CbVvWyjhmX30JCtzO+s5dlzzxrvSfRh5SY0yxnkirvc1c80vdKQttahL71a9EsdolSQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.3.1.tgz", + "integrity": "sha512-V4XVUUCsuVeSNkjeXLR4Y5doyNkTx29Cp8NfKoklgpSsWawyxmJbVvJ1kFHRulOmdBlLuHoqDrAirN8ZoduUCA==", "optional": true, "requires": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.7.0", "tslib": "^2.6.2" } }, "@smithy/signature-v4": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.0.tgz", - "integrity": "sha512-MKNyhXEs99xAZaFhm88h+3/V+tCRDQ+PrDzRqL0xdDpq4gjxcMmf5rBA3YXgqZqMZ/XwemZEurCBQMfxZOWq/g==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.1.tgz", + "integrity": "sha512-7jimpk6X2jzV3UmesOFFV675N/4D8QqNg6NdZFNa/RmWAco+jyX/TbX2mHFImNm+DoafpwEfcDNsPxDSYF0Pxw==", "optional": true, "requires": { "@smithy/is-array-buffer": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", + "@smithy/protocol-http": "^5.3.1", + "@smithy/types": "^4.7.0", "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", + "@smithy/util-middleware": "^4.2.1", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" @@ -963,37 +963,37 @@ } }, "@smithy/smithy-client": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.7.1.tgz", - "integrity": "sha512-WXVbiyNf/WOS/RHUoFMkJ6leEVpln5ojCjNBnzoZeMsnCg3A0BRhLK3WYc4V7PmYcYPZh9IYzzAg9XcNSzYxYQ==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.8.0.tgz", + "integrity": "sha512-gbpNLnuDnguDcXQvbeIAd05F9EDK4HasFtiRzJoM5NbsvXGnW2dGd4mHaShR+ZNveoP9KaWlwF8Hj4ZtipaM3Q==", "optional": true, "requires": { - "@smithy/core": "^3.15.0", - "@smithy/middleware-endpoint": "^4.3.1", - "@smithy/middleware-stack": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", - "@smithy/util-stream": "^4.5.0", + "@smithy/core": "^3.16.0", + "@smithy/middleware-endpoint": "^4.3.2", + "@smithy/middleware-stack": "^4.2.1", + "@smithy/protocol-http": "^5.3.1", + "@smithy/types": "^4.7.0", + "@smithy/util-stream": "^4.5.1", "tslib": "^2.6.2" } }, "@smithy/types": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.6.0.tgz", - "integrity": "sha512-4lI9C8NzRPOv66FaY1LL1O/0v0aLVrq/mXP/keUa9mJOApEeae43LsLd2kZRUJw91gxOQfLIrV3OvqPgWz1YsA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.7.0.tgz", + "integrity": "sha512-KM8Or+jCDCrUI3wYYhj7ehrC7aATB1NdJ1aFEE/YLKNLVH257k9RNeOqKdg0JOxjyEpVD7KKsmmob9mRy1Ho2g==", "optional": true, "requires": { "tslib": "^2.6.2" } }, "@smithy/url-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.0.tgz", - "integrity": "sha512-AlBmD6Idav2ugmoAL6UtR6ItS7jU5h5RNqLMZC7QrLCoITA9NzIN3nx9GWi8g4z1pfWh2r9r96SX/jHiNwPJ9A==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.1.tgz", + "integrity": "sha512-dHm6hDcl79Ededl0oKgpSq3mM5b7Xdw+jic8bq1G7Z2spVpm7HpHJuLCV9PUJLjMbDbZfRUf5GEOnnOIvgfYgQ==", "optional": true, "requires": { - "@smithy/querystring-parser": "^4.2.0", - "@smithy/types": "^4.6.0", + "@smithy/querystring-parser": "^4.2.1", + "@smithy/types": "^4.7.0", "tslib": "^2.6.2" } }, @@ -1067,40 +1067,40 @@ } }, "@smithy/util-defaults-mode-browser": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.0.tgz", - "integrity": "sha512-H4MAj8j8Yp19Mr7vVtGgi7noJjvjJbsKQJkvNnLlrIFduRFT5jq5Eri1k838YW7rN2g5FTnXpz5ktKVr1KVgPQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.1.tgz", + "integrity": "sha512-B3kaaqtc11rIc7SN3g6TYGdUrQfCkoHvpqbhd9kdfRUQZG7M7dcc0oLcCjMuBhCSUdtorkK7OA5uGq9BB+isaA==", "optional": true, "requires": { - "@smithy/property-provider": "^4.2.0", - "@smithy/smithy-client": "^4.7.1", - "@smithy/types": "^4.6.0", + "@smithy/property-provider": "^4.2.1", + "@smithy/smithy-client": "^4.8.0", + "@smithy/types": "^4.7.0", "tslib": "^2.6.2" } }, "@smithy/util-defaults-mode-node": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.1.tgz", - "integrity": "sha512-PuDcgx7/qKEMzV1QFHJ7E4/MMeEjaA7+zS5UNcHCLPvvn59AeZQ0DSDGMpqC2xecfa/1cNGm4l8Ec/VxCuY7Ug==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.2.tgz", + "integrity": "sha512-cneOHBPi/DGbjz65oV8wID+uUbtzrFAQ8w3a7uS3C1jjrInSrinAitup8SouDpmi8jr5GVOAck1/hsR3n/WvaQ==", "optional": true, "requires": { - "@smithy/config-resolver": "^4.3.0", - "@smithy/credential-provider-imds": "^4.2.0", - "@smithy/node-config-provider": "^4.3.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/smithy-client": "^4.7.1", - "@smithy/types": "^4.6.0", + "@smithy/config-resolver": "^4.3.1", + "@smithy/credential-provider-imds": "^4.2.1", + "@smithy/node-config-provider": "^4.3.1", + "@smithy/property-provider": "^4.2.1", + "@smithy/smithy-client": "^4.8.0", + "@smithy/types": "^4.7.0", "tslib": "^2.6.2" } }, "@smithy/util-endpoints": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.0.tgz", - "integrity": "sha512-TXeCn22D56vvWr/5xPqALc9oO+LN+QpFjrSM7peG/ckqEPoI3zaKZFp+bFwfmiHhn5MGWPaLCqDOJPPIixk9Wg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.1.tgz", + "integrity": "sha512-lJudabG/ll+BD22i8IgxZgxS+1hEdUfFqtC1tNubC9vlGwInUktcXodTe5CvM+xDiqGZfqYLY7mKFdabCIrkYw==", "optional": true, "requires": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/types": "^4.6.0", + "@smithy/node-config-provider": "^4.3.1", + "@smithy/types": "^4.7.0", "tslib": "^2.6.2" } }, @@ -1114,35 +1114,35 @@ } }, "@smithy/util-middleware": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.0.tgz", - "integrity": "sha512-u9OOfDa43MjagtJZ8AapJcmimP+K2Z7szXn8xbty4aza+7P1wjFmy2ewjSbhEiYQoW1unTlOAIV165weYAaowA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.1.tgz", + "integrity": "sha512-4rf5Ma0e0uuKmtzMihsvs3jnb9iGMRDWrUe6mfdZBWm52PW1xVHdEeP4+swhheF+YAXhVH/O+taKJuqOrVsG3w==", "optional": true, "requires": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.7.0", "tslib": "^2.6.2" } }, "@smithy/util-retry": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.0.tgz", - "integrity": "sha512-BWSiuGbwRnEE2SFfaAZEX0TqaxtvtSYPM/J73PFVm+A29Fg1HTPiYFb8TmX1DXp4hgcdyJcNQmprfd5foeORsg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.1.tgz", + "integrity": "sha512-0DQqQtZ9brT/QCMts9ssPnsU6CmQAgzkAvTIGcTHoMbntQa7v5VPxxpiyyiTK/BIl8y0vCZSXcOS+kOMXAYRpg==", "optional": true, "requires": { - "@smithy/service-error-classification": "^4.2.0", - "@smithy/types": "^4.6.0", + "@smithy/service-error-classification": "^4.2.1", + "@smithy/types": "^4.7.0", "tslib": "^2.6.2" } }, "@smithy/util-stream": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.0.tgz", - "integrity": "sha512-0TD5M5HCGu5diEvZ/O/WquSjhJPasqv7trjoqHyWjNh/FBeBl7a0ztl9uFMOsauYtRfd8jvpzIAQhDHbx+nvZw==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.1.tgz", + "integrity": "sha512-kVnOiYDDb84ZUGwpQBiVQROWR7epNXikxMGw971Mww3+eufKl2NHYyao2Gg4Wd3iG+D9hF/d9VrmMBxBcVprXw==", "optional": true, "requires": { - "@smithy/fetch-http-handler": "^5.3.1", - "@smithy/node-http-handler": "^4.3.0", - "@smithy/types": "^4.6.0", + "@smithy/fetch-http-handler": "^5.3.2", + "@smithy/node-http-handler": "^4.4.0", + "@smithy/types": "^4.7.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", diff --git a/server/attachmentMigration.js b/server/attachmentMigration.js index 2d2d80bc7..d769dde92 100644 --- a/server/attachmentMigration.js +++ b/server/attachmentMigration.js @@ -5,24 +5,44 @@ import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; +import { check } from 'meteor/check'; import { ReactiveCache } from '/imports/reactiveCache'; +import Attachments from '/models/attachments'; // Reactive variables for tracking migration progress const migrationProgress = new ReactiveVar(0); const migrationStatus = new ReactiveVar(''); const unconvertedAttachments = new ReactiveVar([]); +// Track migrated boards on server side +const migratedBoards = new Set(); + class AttachmentMigrationService { constructor() { this.migrationCache = new Map(); } + /** + * Check if a board has been migrated + * @param {string} boardId - The board ID + * @returns {boolean} - True if board has been migrated + */ + isBoardMigrated(boardId) { + return migratedBoards.has(boardId); + } + /** * Migrate all attachments for a board * @param {string} boardId - The board ID */ async migrateBoardAttachments(boardId) { try { + // Check if board has already been migrated + if (this.isBoardMigrated(boardId)) { + console.log(`Board ${boardId} has already been migrated, skipping`); + return { success: true, message: 'Board already migrated' }; + } + console.log(`Starting attachment migration for board: ${boardId}`); // Get all attachments for the board @@ -61,7 +81,12 @@ class AttachmentMigrationService { migrationStatus.set('Attachment migration completed'); migrationProgress.set(100); + // Mark board as migrated + migratedBoards.add(boardId); console.log(`Attachment migration completed for board: ${boardId}`); + console.log(`Marked board ${boardId} as migrated`); + + return { success: true, message: 'Migration completed' }; } catch (error) { console.error(`Error migrating attachments for board ${boardId}:`, error); @@ -176,15 +201,19 @@ const attachmentMigrationService = new AttachmentMigrationService(); // Meteor methods Meteor.methods({ - 'attachmentMigration.migrateBoardAttachments'(boardId) { + async 'attachmentMigration.migrateBoardAttachments'(boardId) { + check(boardId, String); + if (!this.userId) { throw new Meteor.Error('not-authorized'); } - return attachmentMigrationService.migrateBoardAttachments(boardId); + return await attachmentMigrationService.migrateBoardAttachments(boardId); }, 'attachmentMigration.getProgress'(boardId) { + check(boardId, String); + if (!this.userId) { throw new Meteor.Error('not-authorized'); } @@ -193,11 +222,23 @@ Meteor.methods({ }, 'attachmentMigration.getUnconvertedAttachments'(boardId) { + check(boardId, String); + if (!this.userId) { throw new Meteor.Error('not-authorized'); } return attachmentMigrationService.getUnconvertedAttachments(boardId); + }, + + 'attachmentMigration.isBoardMigrated'(boardId) { + check(boardId, String); + + if (!this.userId) { + throw new Meteor.Error('not-authorized'); + } + + return attachmentMigrationService.isBoardMigrated(boardId); } });