migrateAttachments.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import { Meteor } from 'meteor/meteor';
  2. import { ReactiveCache } from '/imports/reactiveCache';
  3. import { getOldAttachmentData, getOldAttachmentDataBuffer } from '/models/lib/attachmentBackwardCompatibility';
  4. /**
  5. * Migration script to convert old CollectionFS attachments to new Meteor-Files structure
  6. * This script can be run to migrate all old attachments to the new format
  7. */
  8. if (Meteor.isServer) {
  9. Meteor.methods({
  10. /**
  11. * Migrate a single attachment from old to new structure
  12. * @param {string} attachmentId - The old attachment ID
  13. * @returns {Object} - Migration result
  14. */
  15. migrateAttachment(attachmentId) {
  16. if (!this.userId) {
  17. throw new Meteor.Error('not-authorized', 'Must be logged in');
  18. }
  19. try {
  20. // Get old attachment data
  21. const oldAttachment = getOldAttachmentData(attachmentId);
  22. if (!oldAttachment) {
  23. return { success: false, error: 'Old attachment not found' };
  24. }
  25. // Check if already migrated
  26. const existingAttachment = ReactiveCache.getAttachment(attachmentId);
  27. if (existingAttachment) {
  28. return { success: true, message: 'Already migrated', attachmentId };
  29. }
  30. // Get file data from GridFS
  31. const fileData = getOldAttachmentDataBuffer(attachmentId);
  32. if (!fileData) {
  33. return { success: false, error: 'Could not read file data from GridFS' };
  34. }
  35. // Create new attachment using Meteor-Files
  36. const fileObj = new File([fileData], oldAttachment.name, {
  37. type: oldAttachment.type
  38. });
  39. const uploader = Attachments.insert({
  40. file: fileObj,
  41. meta: oldAttachment.meta,
  42. isBase64: false,
  43. transport: 'http'
  44. });
  45. if (uploader) {
  46. return {
  47. success: true,
  48. message: 'Migration successful',
  49. attachmentId,
  50. newAttachmentId: uploader._id
  51. };
  52. } else {
  53. return { success: false, error: 'Failed to create new attachment' };
  54. }
  55. } catch (error) {
  56. console.error('Error migrating attachment:', error);
  57. return { success: false, error: error.message };
  58. }
  59. },
  60. /**
  61. * Migrate all attachments for a specific card
  62. * @param {string} cardId - The card ID
  63. * @returns {Object} - Migration results
  64. */
  65. migrateCardAttachments(cardId) {
  66. if (!this.userId) {
  67. throw new Meteor.Error('not-authorized', 'Must be logged in');
  68. }
  69. const results = {
  70. success: 0,
  71. failed: 0,
  72. errors: []
  73. };
  74. try {
  75. // Get all old attachments for this card
  76. const oldAttachments = ReactiveCache.getAttachments({ 'meta.cardId': cardId });
  77. for (const attachment of oldAttachments) {
  78. const result = Meteor.call('migrateAttachment', attachment._id);
  79. if (result.success) {
  80. results.success++;
  81. } else {
  82. results.failed++;
  83. results.errors.push({
  84. attachmentId: attachment._id,
  85. error: result.error
  86. });
  87. }
  88. }
  89. return results;
  90. } catch (error) {
  91. console.error('Error migrating card attachments:', error);
  92. return { success: false, error: error.message };
  93. }
  94. },
  95. /**
  96. * Get migration status for attachments
  97. * @param {string} cardId - The card ID (optional)
  98. * @returns {Object} - Migration status
  99. */
  100. getAttachmentMigrationStatus(cardId) {
  101. if (!this.userId) {
  102. throw new Meteor.Error('not-authorized', 'Must be logged in');
  103. }
  104. try {
  105. const selector = cardId ? { 'meta.cardId': cardId } : {};
  106. const allAttachments = ReactiveCache.getAttachments(selector);
  107. const status = {
  108. total: allAttachments.length,
  109. newStructure: 0,
  110. oldStructure: 0,
  111. mixed: false
  112. };
  113. for (const attachment of allAttachments) {
  114. if (attachment.meta && attachment.meta.source === 'legacy') {
  115. status.oldStructure++;
  116. } else {
  117. status.newStructure++;
  118. }
  119. }
  120. status.mixed = status.oldStructure > 0 && status.newStructure > 0;
  121. return status;
  122. } catch (error) {
  123. console.error('Error getting migration status:', error);
  124. return { error: error.message };
  125. }
  126. }
  127. });
  128. }