import { S3Client, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3'; import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; import { v4 as uuidv4 } from 'uuid'; import sharp from 'sharp'; import { logger } from '../config/logger.js'; const s3Client = new S3Client({ region: process.env.AWS_REGION || 'us-east-1', credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID || '', secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || '' } }); const BUCKET_NAME = process.env.AWS_S3_BUCKET || ''; export const s3Service = { getUploadUrl: async (fileName: string, fileType: string) => { const imageId = uuidv4(); const key = `uploads/${imageId}-${fileName}`; const command = new PutObjectCommand({ Bucket: BUCKET_NAME, Key: key, ContentType: fileType }); try { const uploadUrl = await getSignedUrl(s3Client, command, { expiresIn: 3600 }); logger.info(`Generated pre-signed URL for upload: ${key}`); return { uploadUrl, imageId, key }; } catch (error) { logger.error('Error generating pre-signed URL:', error); throw error; } }, optimizeAndUpload: async (buffer: Buffer, key: string, resolution: { width: number; height: number }) => { try { let sharpInstance = sharp(buffer); // Get image metadata const metadata = await sharpInstance.metadata(); // Resize if resolution is specified if (resolution.width > 0 && resolution.height > 0) { sharpInstance = sharpInstance.resize(resolution.width, resolution.height, { fit: 'inside', withoutEnlargement: true }); } // Convert to WebP for better compression const optimizedBuffer = await sharpInstance .webp({ quality: 80 }) .toBuffer(); // Upload optimized image const optimizedKey = key.replace(/\.[^/.]+$/, '.webp'); await s3Client.send(new PutObjectCommand({ Bucket: BUCKET_NAME, Key: optimizedKey, Body: optimizedBuffer, ContentType: 'image/webp' })); logger.info(`Successfully optimized and uploaded image: ${optimizedKey}`); return { key: optimizedKey, width: metadata.width, height: metadata.height, format: 'webp', size: optimizedBuffer.length }; } catch (error) { logger.error('Error optimizing and uploading image:', error); throw error; } } };