| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- /**
- * Avatar File Server
- * Handles serving avatar files from the /cdn/storage/avatars/ path
- */
- import { Meteor } from 'meteor/meteor';
- import { WebApp } from 'meteor/webapp';
- import { ReactiveCache } from '/imports/reactiveCache';
- import Avatars from '/models/avatars';
- import { fileStoreStrategyFactory } from '/models/lib/fileStoreStrategy';
- import fs from 'fs';
- import path from 'path';
- if (Meteor.isServer) {
- // Handle avatar file downloads
- WebApp.connectHandlers.use('/cdn/storage/avatars/([^/]+)', (req, res, next) => {
- if (req.method !== 'GET') {
- return next();
- }
- try {
- const fileName = req.params[0];
-
- if (!fileName) {
- res.writeHead(400);
- res.end('Invalid avatar file name');
- return;
- }
- // Extract file ID from filename (format: fileId-original-filename)
- const fileId = fileName.split('-original-')[0];
-
- if (!fileId) {
- res.writeHead(400);
- res.end('Invalid avatar file format');
- return;
- }
- // Get avatar file from database
- const avatar = ReactiveCache.getAvatar(fileId);
- if (!avatar) {
- res.writeHead(404);
- res.end('Avatar not found');
- return;
- }
- // Check if user has permission to view this avatar
- // For avatars, we allow viewing by any logged-in user
- const userId = Meteor.userId();
- if (!userId) {
- res.writeHead(401);
- res.end('Authentication required');
- return;
- }
- // Get file strategy
- const strategy = fileStoreStrategyFactory.getFileStrategy(avatar, 'original');
- const readStream = strategy.getReadStream();
- if (!readStream) {
- res.writeHead(404);
- res.end('Avatar file not found in storage');
- return;
- }
- // Set appropriate headers
- res.setHeader('Content-Type', avatar.type || 'image/jpeg');
- res.setHeader('Content-Length', avatar.size || 0);
- res.setHeader('Cache-Control', 'public, max-age=31536000'); // Cache for 1 year
- res.setHeader('ETag', `"${avatar._id}"`);
-
- // Handle conditional requests
- const ifNoneMatch = req.headers['if-none-match'];
- if (ifNoneMatch && ifNoneMatch === `"${avatar._id}"`) {
- res.writeHead(304);
- res.end();
- return;
- }
- // Stream the file
- res.writeHead(200);
- readStream.pipe(res);
- readStream.on('error', (error) => {
- console.error('Avatar stream error:', error);
- if (!res.headersSent) {
- res.writeHead(500);
- res.end('Error reading avatar file');
- }
- });
- } catch (error) {
- console.error('Avatar server error:', error);
- if (!res.headersSent) {
- res.writeHead(500);
- res.end('Internal server error');
- }
- }
- });
- // Handle legacy avatar URLs (from CollectionFS)
- WebApp.connectHandlers.use('/cfs/files/avatars/([^/]+)', (req, res, next) => {
- if (req.method !== 'GET') {
- return next();
- }
- try {
- const fileName = req.params[0];
-
- // Redirect to new avatar URL format
- const newUrl = `/cdn/storage/avatars/${fileName}`;
- res.writeHead(301, { 'Location': newUrl });
- res.end();
-
- } catch (error) {
- console.error('Legacy avatar redirect error:', error);
- res.writeHead(500);
- res.end('Internal server error');
- }
- });
- console.log('Avatar server routes initialized');
- }
|