| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485 | import { Meteor } from 'meteor/meteor';import { WebApp } from 'meteor/webapp';import { ReactiveCache } from '/imports/reactiveCache';import { getAttachmentWithBackwardCompatibility, getOldAttachmentStream } from '/models/lib/attachmentBackwardCompatibility';// Ensure this file is loadedif (process.env.DEBUG === 'true') {  console.log('Legacy attachments route loaded');}/** * Legacy attachment download route for CollectionFS compatibility * Handles downloads from old CollectionFS structure */if (Meteor.isServer) {  // Handle legacy attachment downloads  WebApp.connectHandlers.use('/cfs/files/attachments', (req, res, next) => {    const attachmentId = req.url.split('/').pop();    if (!attachmentId) {      res.writeHead(404);      res.end('Attachment not found');      return;    }    try {      // Try to get attachment with backward compatibility      const attachment = getAttachmentWithBackwardCompatibility(attachmentId);      if (!attachment) {        res.writeHead(404);        res.end('Attachment not found');        return;      }      // Check permissions      const board = ReactiveCache.getBoard(attachment.meta.boardId);      if (!board) {        res.writeHead(404);        res.end('Board not found');        return;      }      // Check if user has permission to download      const userId = Meteor.userId();      if (!board.isPublic() && (!userId || !board.hasMember(userId))) {        res.writeHead(403);        res.end('Access denied');        return;      }      // Set appropriate headers      res.setHeader('Content-Type', attachment.type || 'application/octet-stream');      res.setHeader('Content-Length', attachment.size || 0);      // Force attachment disposition for SVG files to prevent XSS attacks      const isSvgFile = attachment.name && attachment.name.toLowerCase().endsWith('.svg');      const disposition = isSvgFile ? 'attachment' : 'attachment'; // Always use attachment for legacy files      res.setHeader('Content-Disposition', `${disposition}; filename="${attachment.name}"`);      // Add security headers for SVG files      if (isSvgFile) {        res.setHeader('Content-Security-Policy', "default-src 'none'; script-src 'none'; object-src 'none';");        res.setHeader('X-Content-Type-Options', 'nosniff');        res.setHeader('X-Frame-Options', 'DENY');      }      // Get GridFS stream for legacy attachment      const fileStream = getOldAttachmentStream(attachmentId);      if (fileStream) {        res.writeHead(200);        fileStream.pipe(res);      } else {        res.writeHead(404);        res.end('File not found in GridFS');      }    } catch (error) {      console.error('Error serving legacy attachment:', error);      res.writeHead(500);      res.end('Internal server error');    }  });}
 |