migrationRunner.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. /**
  2. * Server-side Migration Runner
  3. * Handles actual execution of database migrations with progress tracking
  4. */
  5. import { Meteor } from 'meteor/meteor';
  6. import { Migrations } from 'meteor/percolate:migrations';
  7. import { ReactiveVar } from 'meteor/reactive-var';
  8. // Server-side reactive variables for migration progress
  9. export const serverMigrationProgress = new ReactiveVar(0);
  10. export const serverMigrationStatus = new ReactiveVar('');
  11. export const serverMigrationCurrentStep = new ReactiveVar('');
  12. export const serverMigrationSteps = new ReactiveVar([]);
  13. export const serverIsMigrating = new ReactiveVar(false);
  14. class ServerMigrationRunner {
  15. constructor() {
  16. this.migrationSteps = this.initializeMigrationSteps();
  17. this.currentStepIndex = 0;
  18. this.startTime = null;
  19. }
  20. /**
  21. * Initialize migration steps with their actual migration functions
  22. */
  23. initializeMigrationSteps() {
  24. return [
  25. {
  26. id: 'board-background-color',
  27. name: 'Board Background Colors',
  28. description: 'Setting up board background colors',
  29. weight: 1,
  30. completed: false,
  31. progress: 0,
  32. migrationFunction: this.runBoardBackgroundColorMigration
  33. },
  34. {
  35. id: 'add-cardcounterlist-allowed',
  36. name: 'Card Counter List Settings',
  37. description: 'Adding card counter list permissions',
  38. weight: 1,
  39. completed: false,
  40. progress: 0,
  41. migrationFunction: this.runCardCounterListMigration
  42. },
  43. {
  44. id: 'add-boardmemberlist-allowed',
  45. name: 'Board Member List Settings',
  46. description: 'Adding board member list permissions',
  47. weight: 1,
  48. completed: false,
  49. progress: 0,
  50. migrationFunction: this.runBoardMemberListMigration
  51. },
  52. {
  53. id: 'lowercase-board-permission',
  54. name: 'Board Permission Standardization',
  55. description: 'Converting board permissions to lowercase',
  56. weight: 1,
  57. completed: false,
  58. progress: 0,
  59. migrationFunction: this.runLowercaseBoardPermissionMigration
  60. },
  61. {
  62. id: 'change-attachments-type-for-non-images',
  63. name: 'Attachment Type Standardization',
  64. description: 'Updating attachment types for non-images',
  65. weight: 2,
  66. completed: false,
  67. progress: 0,
  68. migrationFunction: this.runAttachmentTypeMigration
  69. },
  70. {
  71. id: 'card-covers',
  72. name: 'Card Covers System',
  73. description: 'Setting up card cover functionality',
  74. weight: 2,
  75. completed: false,
  76. progress: 0,
  77. migrationFunction: this.runCardCoversMigration
  78. },
  79. {
  80. id: 'use-css-class-for-boards-colors',
  81. name: 'Board Color CSS Classes',
  82. description: 'Converting board colors to CSS classes',
  83. weight: 2,
  84. completed: false,
  85. progress: 0,
  86. migrationFunction: this.runBoardColorCSSMigration
  87. },
  88. {
  89. id: 'denormalize-star-number-per-board',
  90. name: 'Board Star Counts',
  91. description: 'Calculating star counts per board',
  92. weight: 3,
  93. completed: false,
  94. progress: 0,
  95. migrationFunction: this.runStarNumberMigration
  96. },
  97. {
  98. id: 'add-member-isactive-field',
  99. name: 'Member Activity Status',
  100. description: 'Adding member activity tracking',
  101. weight: 2,
  102. completed: false,
  103. progress: 0,
  104. migrationFunction: this.runMemberIsActiveMigration
  105. },
  106. {
  107. id: 'add-sort-checklists',
  108. name: 'Checklist Sorting',
  109. description: 'Adding sort order to checklists',
  110. weight: 2,
  111. completed: false,
  112. progress: 0,
  113. migrationFunction: this.runSortChecklistsMigration
  114. },
  115. {
  116. id: 'add-swimlanes',
  117. name: 'Swimlanes System',
  118. description: 'Setting up swimlanes functionality',
  119. weight: 4,
  120. completed: false,
  121. progress: 0,
  122. migrationFunction: this.runSwimlanesMigration
  123. },
  124. {
  125. id: 'add-views',
  126. name: 'Board Views',
  127. description: 'Adding board view options',
  128. weight: 2,
  129. completed: false,
  130. progress: 0,
  131. migrationFunction: this.runViewsMigration
  132. },
  133. {
  134. id: 'add-checklist-items',
  135. name: 'Checklist Items',
  136. description: 'Setting up checklist items system',
  137. weight: 3,
  138. completed: false,
  139. progress: 0,
  140. migrationFunction: this.runChecklistItemsMigration
  141. },
  142. {
  143. id: 'add-card-types',
  144. name: 'Card Types',
  145. description: 'Adding card type functionality',
  146. weight: 2,
  147. completed: false,
  148. progress: 0,
  149. migrationFunction: this.runCardTypesMigration
  150. },
  151. {
  152. id: 'add-custom-fields-to-cards',
  153. name: 'Custom Fields',
  154. description: 'Adding custom fields to cards',
  155. weight: 3,
  156. completed: false,
  157. progress: 0,
  158. migrationFunction: this.runCustomFieldsMigration
  159. },
  160. {
  161. id: 'migrate-attachments-collectionFS-to-ostrioFiles',
  162. name: 'Migrate Attachments to Meteor-Files',
  163. description: 'Migrating attachments from CollectionFS to Meteor-Files',
  164. weight: 8,
  165. completed: false,
  166. progress: 0,
  167. migrationFunction: this.runAttachmentMigration
  168. },
  169. {
  170. id: 'migrate-avatars-collectionFS-to-ostrioFiles',
  171. name: 'Migrate Avatars to Meteor-Files',
  172. description: 'Migrating avatars from CollectionFS to Meteor-Files',
  173. weight: 6,
  174. completed: false,
  175. progress: 0,
  176. migrationFunction: this.runAvatarMigration
  177. },
  178. {
  179. id: 'migrate-lists-to-per-swimlane',
  180. name: 'Migrate Lists to Per-Swimlane',
  181. description: 'Migrating lists to per-swimlane structure',
  182. weight: 5,
  183. completed: false,
  184. progress: 0,
  185. migrationFunction: this.runListsToPerSwimlaneMigration
  186. }
  187. ];
  188. }
  189. /**
  190. * Start migration process
  191. */
  192. async startMigration() {
  193. if (serverIsMigrating.get()) {
  194. return; // Already migrating
  195. }
  196. serverIsMigrating.set(true);
  197. serverMigrationSteps.set([...this.migrationSteps]);
  198. this.startTime = Date.now();
  199. try {
  200. for (let i = 0; i < this.migrationSteps.length; i++) {
  201. const step = this.migrationSteps[i];
  202. this.currentStepIndex = i;
  203. if (step.completed) {
  204. continue; // Skip already completed steps
  205. }
  206. serverMigrationCurrentStep.set(step.name);
  207. serverMigrationStatus.set(`Running: ${step.description}`);
  208. // Run the migration step
  209. await this.runMigrationStep(step);
  210. // Mark as completed
  211. step.completed = true;
  212. step.progress = 100;
  213. // Update progress
  214. this.updateProgress();
  215. // Allow other processes to run
  216. await new Promise(resolve => setTimeout(resolve, 100));
  217. }
  218. // Migration completed
  219. serverMigrationStatus.set('All migrations completed successfully!');
  220. serverMigrationProgress.set(100);
  221. serverMigrationCurrentStep.set('');
  222. // Clear status after delay
  223. setTimeout(() => {
  224. serverIsMigrating.set(false);
  225. serverMigrationStatus.set('');
  226. serverMigrationProgress.set(0);
  227. }, 3000);
  228. } catch (error) {
  229. console.error('Migration failed:', error);
  230. serverMigrationStatus.set(`Migration failed: ${error.message}`);
  231. serverIsMigrating.set(false);
  232. }
  233. }
  234. /**
  235. * Run a single migration step
  236. */
  237. async runMigrationStep(step) {
  238. try {
  239. // Update progress during migration
  240. const progressSteps = 10;
  241. for (let i = 0; i <= progressSteps; i++) {
  242. step.progress = (i / progressSteps) * 100;
  243. this.updateProgress();
  244. // Run actual migration function
  245. if (i === progressSteps) {
  246. await step.migrationFunction.call(this);
  247. }
  248. // Allow other processes to run
  249. await new Promise(resolve => setTimeout(resolve, 50));
  250. }
  251. } catch (error) {
  252. console.error(`Migration step ${step.name} failed:`, error);
  253. throw error;
  254. }
  255. }
  256. /**
  257. * Update progress variables
  258. */
  259. updateProgress() {
  260. const totalWeight = this.migrationSteps.reduce((total, step) => total + step.weight, 0);
  261. const completedWeight = this.migrationSteps.reduce((total, step) => {
  262. return total + (step.completed ? step.weight : step.progress * step.weight / 100);
  263. }, 0);
  264. const progress = Math.round((completedWeight / totalWeight) * 100);
  265. serverMigrationProgress.set(progress);
  266. serverMigrationSteps.set([...this.migrationSteps]);
  267. }
  268. // Individual migration functions
  269. async runBoardBackgroundColorMigration() {
  270. // Implementation for board background color migration
  271. console.log('Running board background color migration');
  272. }
  273. async runCardCounterListMigration() {
  274. // Implementation for card counter list migration
  275. console.log('Running card counter list migration');
  276. }
  277. async runBoardMemberListMigration() {
  278. // Implementation for board member list migration
  279. console.log('Running board member list migration');
  280. }
  281. async runLowercaseBoardPermissionMigration() {
  282. // Implementation for lowercase board permission migration
  283. console.log('Running lowercase board permission migration');
  284. }
  285. async runAttachmentTypeMigration() {
  286. // Implementation for attachment type migration
  287. console.log('Running attachment type migration');
  288. }
  289. async runCardCoversMigration() {
  290. // Implementation for card covers migration
  291. console.log('Running card covers migration');
  292. }
  293. async runBoardColorCSSMigration() {
  294. // Implementation for board color CSS migration
  295. console.log('Running board color CSS migration');
  296. }
  297. async runStarNumberMigration() {
  298. // Implementation for star number migration
  299. console.log('Running star number migration');
  300. }
  301. async runMemberIsActiveMigration() {
  302. // Implementation for member is active migration
  303. console.log('Running member is active migration');
  304. }
  305. async runSortChecklistsMigration() {
  306. // Implementation for sort checklists migration
  307. console.log('Running sort checklists migration');
  308. }
  309. async runSwimlanesMigration() {
  310. // Implementation for swimlanes migration
  311. console.log('Running swimlanes migration');
  312. }
  313. async runViewsMigration() {
  314. // Implementation for views migration
  315. console.log('Running views migration');
  316. }
  317. async runChecklistItemsMigration() {
  318. // Implementation for checklist items migration
  319. console.log('Running checklist items migration');
  320. }
  321. async runCardTypesMigration() {
  322. // Implementation for card types migration
  323. console.log('Running card types migration');
  324. }
  325. async runCustomFieldsMigration() {
  326. // Implementation for custom fields migration
  327. console.log('Running custom fields migration');
  328. }
  329. async runAttachmentMigration() {
  330. // Implementation for attachment migration from CollectionFS to Meteor-Files
  331. console.log('Running attachment migration from CollectionFS to Meteor-Files');
  332. }
  333. async runAvatarMigration() {
  334. // Implementation for avatar migration from CollectionFS to Meteor-Files
  335. console.log('Running avatar migration from CollectionFS to Meteor-Files');
  336. }
  337. async runListsToPerSwimlaneMigration() {
  338. // Implementation for lists to per-swimlane migration
  339. console.log('Running lists to per-swimlane migration');
  340. }
  341. }
  342. // Export singleton instance
  343. export const serverMigrationRunner = new ServerMigrationRunner();
  344. // Meteor methods for client-server communication
  345. Meteor.methods({
  346. 'migration.start'() {
  347. if (!this.userId) {
  348. throw new Meteor.Error('not-authorized');
  349. }
  350. return serverMigrationRunner.startMigration();
  351. },
  352. 'migration.getProgress'() {
  353. return {
  354. progress: serverMigrationProgress.get(),
  355. status: serverMigrationStatus.get(),
  356. currentStep: serverMigrationCurrentStep.get(),
  357. steps: serverMigrationSteps.get(),
  358. isMigrating: serverIsMigrating.get()
  359. };
  360. }
  361. });