| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 | 
							- /**
 
-  * Board Migration Detector
 
-  * Detects boards that need migration and manages automatic migration scheduling
 
-  */
 
- import { Meteor } from 'meteor/meteor';
 
- import { ReactiveVar } from 'meteor/reactive-var';
 
- import { check, Match } from 'meteor/check';
 
- import { cronJobStorage } from './cronJobStorage';
 
- import Boards from '/models/boards';
 
- // Reactive variables for board migration tracking
 
- export const unmigratedBoards = new ReactiveVar([]);
 
- export const migrationScanInProgress = new ReactiveVar(false);
 
- export const lastMigrationScan = new ReactiveVar(null);
 
- class BoardMigrationDetector {
 
-   constructor() {
 
-     this.scanInterval = null;
 
-     this.isScanning = false;
 
-     this.migrationCheckInterval = 30000; // Check every 30 seconds
 
-     this.scanInterval = 60000; // Full scan every minute
 
-   }
 
-   /**
 
-    * Start the automatic migration detector
 
-    */
 
-   start() {
 
-     if (this.scanInterval) {
 
-       return; // Already running
 
-     }
 
-     // Check for idle migration opportunities
 
-     this.scanInterval = Meteor.setInterval(() => {
 
-       this.checkForIdleMigration();
 
-     }, this.migrationCheckInterval);
 
-     // Full board scan every minute
 
-     this.fullScanInterval = Meteor.setInterval(() => {
 
-       this.scanUnmigratedBoards();
 
-     }, this.scanInterval);
 
-     // Board migration detector started
 
-   }
 
-   /**
 
-    * Stop the automatic migration detector
 
-    */
 
-   stop() {
 
-     if (this.scanInterval) {
 
-       Meteor.clearInterval(this.scanInterval);
 
-       this.scanInterval = null;
 
-     }
 
-     if (this.fullScanInterval) {
 
-       Meteor.clearInterval(this.fullScanInterval);
 
-       this.fullScanInterval = null;
 
-     }
 
-   }
 
-   /**
 
-    * Check if system is idle and can run migrations
 
-    */
 
-   isSystemIdle() {
 
-     const resources = cronJobStorage.getSystemResources();
 
-     const queueStats = cronJobStorage.getQueueStats();
 
-     
 
-     // Check if no jobs are running
 
-     if (queueStats.running > 0) {
 
-       return false;
 
-     }
 
-     // Check if CPU usage is low
 
-     if (resources.cpuUsage > 30) { // Lower threshold for idle migration
 
-       return false;
 
-     }
 
-     // Check if memory usage is reasonable
 
-     if (resources.memoryUsage > 85) {
 
-       return false;
 
-     }
 
-     return true;
 
-   }
 
-   /**
 
-    * Check for idle migration opportunities
 
-    */
 
-   async checkForIdleMigration() {
 
-     if (!this.isSystemIdle()) {
 
-       return;
 
-     }
 
-     // Get unmigrated boards
 
-     const unmigrated = unmigratedBoards.get();
 
-     if (unmigrated.length === 0) {
 
-       return; // No boards to migrate
 
-     }
 
-     // Check if we can start a new job
 
-     const canStart = cronJobStorage.canStartNewJob();
 
-     if (!canStart.canStart) {
 
-       return;
 
-     }
 
-     // Start migrating the next board
 
-     const boardToMigrate = unmigrated[0];
 
-     await this.startBoardMigration(boardToMigrate);
 
-   }
 
-   /**
 
-    * Scan for unmigrated boards
 
-    */
 
-   async scanUnmigratedBoards() {
 
-     if (this.isScanning) {
 
-       return; // Already scanning
 
-     }
 
-     this.isScanning = true;
 
-     migrationScanInProgress.set(true);
 
-     try {
 
-       // Scanning for unmigrated boards
 
-       
 
-       // Get all boards from the database
 
-       const boards = this.getAllBoards();
 
-       const unmigrated = [];
 
-       for (const board of boards) {
 
-         if (await this.needsMigration(board)) {
 
-           unmigrated.push(board);
 
-         }
 
-       }
 
-       unmigratedBoards.set(unmigrated);
 
-       lastMigrationScan.set(new Date());
 
-       // Found unmigrated boards
 
-     } catch (error) {
 
-       console.error('Error scanning for unmigrated boards:', error);
 
-     } finally {
 
-       this.isScanning = false;
 
-       migrationScanInProgress.set(false);
 
-     }
 
-   }
 
-   /**
 
-    * Get all boards from the database
 
-    */
 
-   getAllBoards() {
 
-     // This would need to be implemented based on your board model
 
-     // For now, we'll simulate getting boards
 
-     try {
 
-       // Assuming you have a Boards collection
 
-       if (typeof Boards !== 'undefined') {
 
-         return Boards.find({}, { fields: { _id: 1, title: 1, createdAt: 1, modifiedAt: 1 } }).fetch();
 
-       }
 
-       
 
-       // Fallback: return empty array if Boards collection not available
 
-       return [];
 
-     } catch (error) {
 
-       console.error('Error getting boards:', error);
 
-       return [];
 
-     }
 
-   }
 
-   /**
 
-    * Check if a board needs migration
 
-    */
 
-   async needsMigration(board) {
 
-     try {
 
-       // Check if board has been migrated by looking for migration markers
 
-       const migrationMarkers = this.getMigrationMarkers(board._id);
 
-       
 
-       // Check for specific migration indicators
 
-       const needsListMigration = !migrationMarkers.listsMigrated;
 
-       const needsAttachmentMigration = !migrationMarkers.attachmentsMigrated;
 
-       const needsSwimlaneMigration = !migrationMarkers.swimlanesMigrated;
 
-       
 
-       return needsListMigration || needsAttachmentMigration || needsSwimlaneMigration;
 
-       
 
-     } catch (error) {
 
-       console.error(`Error checking migration status for board ${board._id}:`, error);
 
-       return false;
 
-     }
 
-   }
 
-   /**
 
-    * Get migration markers for a board
 
-    */
 
-   getMigrationMarkers(boardId) {
 
-     try {
 
-       // Check if board has migration metadata
 
-       const board = Boards.findOne(boardId, { fields: { migrationMarkers: 1 } });
 
-       
 
-       if (!board || !board.migrationMarkers) {
 
-         return {
 
-           listsMigrated: false,
 
-           attachmentsMigrated: false,
 
-           swimlanesMigrated: false
 
-         };
 
-       }
 
-       return board.migrationMarkers;
 
-     } catch (error) {
 
-       console.error(`Error getting migration markers for board ${boardId}:`, error);
 
-       return {
 
-         listsMigrated: false,
 
-         attachmentsMigrated: false,
 
-         swimlanesMigrated: false
 
-       };
 
-     }
 
-   }
 
-   /**
 
-    * Start migration for a specific board
 
-    */
 
-   async startBoardMigration(boardId) {
 
-     try {
 
-       const board = Boards.findOne(boardId);
 
-       if (!board) {
 
-         throw new Error(`Board ${boardId} not found`);
 
-       }
 
-       // Check if board already has latest migration version
 
-       if (board.migrationVersion && board.migrationVersion >= 1) {
 
-         console.log(`Board ${boardId} already has latest migration version`);
 
-         return null;
 
-       }
 
-       // Create migration job for this board
 
-       const jobId = `board_migration_${board._id}_${Date.now()}`;
 
-       
 
-       // Add to job queue with high priority
 
-       cronJobStorage.addToQueue(jobId, 'board_migration', 1, {
 
-         boardId: board._id,
 
-         boardTitle: board.title,
 
-         migrationType: 'full_board_migration'
 
-       });
 
-       // Save initial job status
 
-       cronJobStorage.saveJobStatus(jobId, {
 
-         jobType: 'board_migration',
 
-         status: 'pending',
 
-         progress: 0,
 
-         boardId: board._id,
 
-         boardTitle: board.title,
 
-         migrationType: 'full_board_migration',
 
-         createdAt: new Date()
 
-       });
 
-       // Remove from unmigrated list
 
-       const currentUnmigrated = unmigratedBoards.get();
 
-       const updatedUnmigrated = currentUnmigrated.filter(b => b._id !== board._id);
 
-       unmigratedBoards.set(updatedUnmigrated);
 
-       return jobId;
 
-     } catch (error) {
 
-       console.error(`Error starting migration for board ${boardId}:`, error);
 
-       throw error;
 
-     }
 
-   }
 
-   /**
 
-    * Get migration statistics
 
-    */
 
-   getMigrationStats() {
 
-     const unmigrated = unmigratedBoards.get();
 
-     const lastScan = lastMigrationScan.get();
 
-     const isScanning = migrationScanInProgress.get();
 
-     return {
 
-       unmigratedCount: unmigrated.length,
 
-       lastScanTime: lastScan,
 
-       isScanning,
 
-       nextScanIn: this.scanInterval ? this.scanInterval / 1000 : 0
 
-     };
 
-   }
 
-   /**
 
-    * Force a full scan of all boards
 
-    */
 
-   async forceScan() {
 
-       // Forcing full board migration scan
 
-     await this.scanUnmigratedBoards();
 
-   }
 
-   /**
 
-    * Get detailed migration status for a specific board
 
-    */
 
-   getBoardMigrationStatus(boardId) {
 
-     const unmigrated = unmigratedBoards.get();
 
-     const isUnmigrated = unmigrated.some(b => b._id === boardId);
 
-     
 
-     if (!isUnmigrated) {
 
-       return { needsMigration: false, reason: 'Board is already migrated' };
 
-     }
 
-     const migrationMarkers = this.getMigrationMarkers(boardId);
 
-     const needsMigration = !migrationMarkers.listsMigrated || 
 
-                           !migrationMarkers.attachmentsMigrated || 
 
-                           !migrationMarkers.swimlanesMigrated;
 
-     return {
 
-       needsMigration,
 
-       migrationMarkers,
 
-       reason: needsMigration ? 'Board requires migration' : 'Board is up to date'
 
-     };
 
-   }
 
-   /**
 
-    * Mark a board as migrated
 
-    */
 
-   markBoardAsMigrated(boardId, migrationType) {
 
-     try {
 
-       // Update migration markers
 
-       const updateQuery = {};
 
-       updateQuery[`migrationMarkers.${migrationType}Migrated`] = true;
 
-       updateQuery['migrationMarkers.lastMigration'] = new Date();
 
-       Boards.update(boardId, { $set: updateQuery });
 
-       // Remove from unmigrated list if present
 
-       const currentUnmigrated = unmigratedBoards.get();
 
-       const updatedUnmigrated = currentUnmigrated.filter(b => b._id !== boardId);
 
-       unmigratedBoards.set(updatedUnmigrated);
 
-       // Marked board as migrated
 
-     } catch (error) {
 
-       console.error(`Error marking board ${boardId} as migrated:`, error);
 
-     }
 
-   }
 
- }
 
- // Export singleton instance
 
- export const boardMigrationDetector = new BoardMigrationDetector();
 
- // Start the detector on server startup
 
- Meteor.startup(() => {
 
-   // Wait a bit for the system to initialize
 
-   Meteor.setTimeout(() => {
 
-     boardMigrationDetector.start();
 
-   }, 10000); // Start after 10 seconds
 
- });
 
- // Meteor methods for client access
 
- Meteor.methods({
 
-   'boardMigration.getStats'() {
 
-     if (!this.userId) {
 
-       throw new Meteor.Error('not-authorized');
 
-     }
 
-     
 
-     return boardMigrationDetector.getMigrationStats();
 
-   },
 
-   'boardMigration.forceScan'() {
 
-     if (!this.userId) {
 
-       throw new Meteor.Error('not-authorized');
 
-     }
 
-     
 
-     return boardMigrationDetector.forceScan();
 
-   },
 
-   'boardMigration.getBoardStatus'(boardId) {
 
-     check(boardId, String);
 
-     
 
-     if (!this.userId) {
 
-       throw new Meteor.Error('not-authorized');
 
-     }
 
-     
 
-     return boardMigrationDetector.getBoardMigrationStatus(boardId);
 
-   },
 
-   'boardMigration.markAsMigrated'(boardId, migrationType) {
 
-     check(boardId, String);
 
-     check(migrationType, String);
 
-     
 
-     if (!this.userId) {
 
-       throw new Meteor.Error('not-authorized');
 
-     }
 
-     
 
-     return boardMigrationDetector.markBoardAsMigrated(boardId, migrationType);
 
-   },
 
-   'boardMigration.startBoardMigration'(boardId) {
 
-     check(boardId, String);
 
-     
 
-     if (!this.userId) {
 
-       throw new Meteor.Error('not-authorized');
 
-     }
 
-     
 
-     return boardMigrationDetector.startBoardMigration(boardId);
 
-   }
 
- });
 
 
  |