| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 | import { ReactiveCache } from '/imports/reactiveCache';import { Meteor } from 'meteor/meteor';import { MongoInternals } from 'meteor/mongo';/** * Backward compatibility layer for CollectionFS to Meteor-Files migration * Handles reading attachments from old CollectionFS database structure */// Old CollectionFS collectionsconst OldAttachmentsFiles = new Mongo.Collection('cfs_gridfs.attachments.files');const OldAttachmentsFileRecord = new Mongo.Collection('cfs.attachments.filerecord');/** * Check if an attachment exists in the new Meteor-Files structure * @param {string} attachmentId - The attachment ID to check * @returns {boolean} - True if exists in new structure */export function isNewAttachmentStructure(attachmentId) {  if (Meteor.isServer) {    return !!ReactiveCache.getAttachment(attachmentId);  }  return false;}/** * Get attachment data from old CollectionFS structure * @param {string} attachmentId - The attachment ID * @returns {Object|null} - Attachment data in new format or null if not found */export function getOldAttachmentData(attachmentId) {  if (Meteor.isServer) {    try {      // First try to get from old filerecord collection      const fileRecord = OldAttachmentsFileRecord.findOne({ _id: attachmentId });      if (!fileRecord) {        return null;      }      // Get file data from old files collection      const fileData = OldAttachmentsFiles.findOne({ _id: attachmentId });      if (!fileData) {        return null;      }      // Convert old structure to new structure      const convertedAttachment = {        _id: attachmentId,        name: fileRecord.original?.name || fileData.filename || 'Unknown',        size: fileRecord.original?.size || fileData.length || 0,        type: fileRecord.original?.type || fileData.contentType || 'application/octet-stream',        extension: getFileExtension(fileRecord.original?.name || fileData.filename || ''),        extensionWithDot: getFileExtensionWithDot(fileRecord.original?.name || fileData.filename || ''),        meta: {          boardId: fileRecord.boardId,          swimlaneId: fileRecord.swimlaneId,          listId: fileRecord.listId,          cardId: fileRecord.cardId,          userId: fileRecord.userId,          source: 'legacy'        },        uploadedAt: fileRecord.uploadedAt || fileData.uploadDate || new Date(),        updatedAt: fileRecord.original?.updatedAt || fileData.uploadDate || new Date(),        // Legacy compatibility fields        isImage: isImageFile(fileRecord.original?.type || fileData.contentType),        isVideo: isVideoFile(fileRecord.original?.type || fileData.contentType),        isAudio: isAudioFile(fileRecord.original?.type || fileData.contentType),        isText: isTextFile(fileRecord.original?.type || fileData.contentType),        isJSON: isJSONFile(fileRecord.original?.type || fileData.contentType),        isPDF: isPDFFile(fileRecord.original?.type || fileData.contentType),        // Legacy link method for compatibility        link: function(version = 'original') {          return `/cfs/files/attachments/${this._id}`;        },        // Legacy versions structure for compatibility        versions: {          original: {            path: `/cfs/files/attachments/${this._id}`,            size: this.size,            type: this.type,            storage: 'gridfs'          }        }      };      return convertedAttachment;    } catch (error) {      console.error('Error reading old attachment data:', error);      return null;    }  }  return null;}/** * Get file extension from filename * @param {string} filename - The filename * @returns {string} - File extension without dot */function getFileExtension(filename) {  if (!filename) return '';  const lastDot = filename.lastIndexOf('.');  if (lastDot === -1) return '';  return filename.substring(lastDot + 1).toLowerCase();}/** * Get file extension with dot * @param {string} filename - The filename * @returns {string} - File extension with dot */function getFileExtensionWithDot(filename) {  const ext = getFileExtension(filename);  return ext ? `.${ext}` : '';}/** * Check if file is an image * @param {string} mimeType - MIME type * @returns {boolean} - True if image */function isImageFile(mimeType) {  return mimeType && mimeType.startsWith('image/');}/** * Check if file is a video * @param {string} mimeType - MIME type * @returns {boolean} - True if video */function isVideoFile(mimeType) {  return mimeType && mimeType.startsWith('video/');}/** * Check if file is audio * @param {string} mimeType - MIME type * @returns {boolean} - True if audio */function isAudioFile(mimeType) {  return mimeType && mimeType.startsWith('audio/');}/** * Check if file is text * @param {string} mimeType - MIME type * @returns {boolean} - True if text */function isTextFile(mimeType) {  return mimeType && mimeType.startsWith('text/');}/** * Check if file is JSON * @param {string} mimeType - MIME type * @returns {boolean} - True if JSON */function isJSONFile(mimeType) {  return mimeType === 'application/json';}/** * Check if file is PDF * @param {string} mimeType - MIME type * @returns {boolean} - True if PDF */function isPDFFile(mimeType) {  return mimeType === 'application/pdf';}/** * Get attachment with backward compatibility * @param {string} attachmentId - The attachment ID * @returns {Object|null} - Attachment data or null if not found */export function getAttachmentWithBackwardCompatibility(attachmentId) {  // First try new structure  if (isNewAttachmentStructure(attachmentId)) {    return ReactiveCache.getAttachment(attachmentId);  }  // Fall back to old structure  return getOldAttachmentData(attachmentId);}/** * Get attachments for a card with backward compatibility * @param {Object} query - Query object * @returns {Array} - Array of attachments */export function getAttachmentsWithBackwardCompatibility(query) {  const newAttachments = ReactiveCache.getAttachments(query);  const oldAttachments = [];  if (Meteor.isServer) {    try {      // Query old structure for the same card      const cardId = query['meta.cardId'];      if (cardId) {        const oldFileRecords = OldAttachmentsFileRecord.find({ cardId }).fetch();        for (const fileRecord of oldFileRecords) {          const oldAttachment = getOldAttachmentData(fileRecord._id);          if (oldAttachment) {            oldAttachments.push(oldAttachment);          }        }      }    } catch (error) {      console.error('Error reading old attachments:', error);    }  }  // Combine and deduplicate  const allAttachments = [...newAttachments, ...oldAttachments];  const uniqueAttachments = allAttachments.filter((attachment, index, self) =>    index === self.findIndex(a => a._id === attachment._id)  );  return uniqueAttachments;}/** * Get file stream from old GridFS structure * @param {string} attachmentId - The attachment ID * @returns {Object|null} - GridFS file stream or null if not found */export function getOldAttachmentStream(attachmentId) {  if (Meteor.isServer) {    try {      const db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;      const bucket = new MongoInternals.NpmModule.GridFSBucket(db, {        bucketName: 'cfs_gridfs.attachments'      });      const downloadStream = bucket.openDownloadStreamByName(attachmentId);      return downloadStream;    } catch (error) {      console.error('Error creating GridFS stream:', error);      return null;    }  }  return null;}/** * Get file data from old GridFS structure * @param {string} attachmentId - The attachment ID * @returns {Buffer|null} - File data buffer or null if not found */export function getOldAttachmentDataBuffer(attachmentId) {  if (Meteor.isServer) {    try {      const db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;      const bucket = new MongoInternals.NpmModule.GridFSBucket(db, {        bucketName: 'cfs_gridfs.attachments'      });      return new Promise((resolve, reject) => {        const chunks = [];        const downloadStream = bucket.openDownloadStreamByName(attachmentId);        downloadStream.on('data', (chunk) => {          chunks.push(chunk);        });        downloadStream.on('end', () => {          resolve(Buffer.concat(chunks));        });        downloadStream.on('error', (error) => {          reject(error);        });      });    } catch (error) {      console.error('Error reading GridFS data:', error);      return null;    }  }  return null;}
 |