diff --git a/package-lock.json b/package-lock.json index 0726f4283..7a3da6131 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19442,7 +19442,7 @@ }, "packages/evershop": { "name": "@evershop/evershop", - "version": "1.2.1", + "version": "1.2.2", "license": "GNU GENERAL PUBLIC LICENSE 3.0", "dependencies": { "@babel/cli": "^7.25.9", diff --git a/packages/s3_file_storage/services/awsFileUploader.js b/packages/s3_file_storage/services/awsFileUploader.js index 5f02d7aa1..15b0c810a 100644 --- a/packages/s3_file_storage/services/awsFileUploader.js +++ b/packages/s3_file_storage/services/awsFileUploader.js @@ -18,7 +18,9 @@ module.exports.awsFileUploader = { const params = { Bucket: bucketName, Key: fileName, - Body: fileContent + Body: fileContent, + ContentType: file.mimetype, + ACL: 'public-read' }; const uploadCommand = new PutObjectCommand(params); @@ -32,10 +34,7 @@ module.exports.awsFileUploader = { name: files[index].filename, path: path.join(requestedPath, files[index].filename), size: files[index].size, - url: `https://${bucketName}.s3.amazonaws.com/${path.join( - requestedPath, - files[index].filename - )}` + url: `https://s3.${getEnv('AWS_REGION')}.amazonaws.com/${bucketName}/${path.join(requestedPath, files[index].filename)}` }); }); diff --git a/packages/s3_file_storage/subscribers/product_image_added/awsGenerateProductImageVariant.js b/packages/s3_file_storage/subscribers/product_image_added/awsGenerateProductImageVariant.js index 3f8661420..fb7a6a5e9 100644 --- a/packages/s3_file_storage/subscribers/product_image_added/awsGenerateProductImageVariant.js +++ b/packages/s3_file_storage/subscribers/product_image_added/awsGenerateProductImageVariant.js @@ -11,10 +11,26 @@ const { update } = require('@evershop/postgres-query-builder'); const { pool } = require('@evershop/evershop/src/lib/postgres/connection'); const { error } = require('@evershop/evershop/src/lib/log/logger'); -async function downloadObjectToBuffer(objectUrl) { +async function downloadObject(s3Client, objectUrl) { + const parsedUrl = new URL(objectUrl); - const bucketName = parsedUrl.host.split('.')[0]; // Extract the bucket name - const objectKey = parsedUrl.pathname.substr(1); // Extract the object key (remove leading '/') + let bucketName; let objectKey; + + if (parsedUrl.host.startsWith("s3")) { + // Generic S3 URL format: s3..amazonaws.com// + const pathParts = parsedUrl.pathname.split('/'); + [,bucketName] = pathParts; // Extract bucket name + objectKey = pathParts.slice(2).join('/'); // Extract object key + } else { + // Bucket URL format: .s3.amazonaws.com/ + [bucketName] = parsedUrl.host.split('.'); // Extract bucket name + objectKey = parsedUrl.pathname.substr(1); // Extract object key (remove leading '/') + } + + if (!bucketName || !objectKey) { + throw new Error(`Failed to extract bucket name or object key from URL: ${objectUrl}`); + } + const params = { Bucket: bucketName, @@ -22,8 +38,12 @@ async function downloadObjectToBuffer(objectUrl) { }; const getObjectCommand = new GetObjectCommand(params); - const s3Client = new S3Client({ region: getEnv('AWS_REGION') }); const data = await s3Client.send(getObjectCommand); + + return data; +} + +async function objectToBuffer(data) { // Get content as a buffer from the data.Body object const buffer = await data.Body.transformToByteArray(); return buffer; @@ -31,13 +51,13 @@ async function downloadObjectToBuffer(objectUrl) { async function resizeAndUploadImage( s3Client, - originalObjectUrl, + originalImageBuffer, resizedObjectUrl, width, - height + height, + contentType ) { const bucketName = getEnv('AWS_BUCKET_NAME'); - const originalImageBuffer = await downloadObjectToBuffer(originalObjectUrl); // Resize the image const resizedImageBuffer = await sharp(originalImageBuffer) .resize({ width, height, fit: 'inside' }) @@ -45,12 +65,23 @@ async function resizeAndUploadImage( // Upload the resized image const parsedUrl = new URL(resizedObjectUrl); - const objectKey = parsedUrl.pathname.substr(1); // Extract the object key (remove leading '/') + let objectKey; // Extract the object key (remove leading '/') + + if (parsedUrl.host.startsWith("s3")) { + // Generic S3 URL: s3..amazonaws.com// + const pathParts = parsedUrl.pathname.split('/'); + objectKey = pathParts.slice(2).join('/'); // Extract only the object key + } else { + // Standard Bucket URL: .s3.amazonaws.com/ + objectKey = parsedUrl.pathname.substr(1); + } const uploadParams = { Bucket: bucketName, Key: objectKey, - Body: resizedImageBuffer + Body: resizedImageBuffer, + ACL: 'public-read', + ContentType: contentType }; const uploadCommand = new PutObjectCommand(uploadParams); @@ -76,31 +107,38 @@ module.exports = async function awsGenerateProductImageVariant(data) { `-thumbnail${ext}` ); + const s3ObjectData = await downloadObject(s3Client, originalObjectUrl); + const originalImageBuffer = await objectToBuffer(s3ObjectData); + const contentType = s3ObjectData.ContentType || 'application/octet-stream'; + // Upload the single variant const singleUrl = await resizeAndUploadImage( s3Client, - originalObjectUrl, + originalImageBuffer, singleObjectUrl, getConfig('catalog.product.image.single.width', 500), - getConfig('catalog.product.image.single.height', 500) + getConfig('catalog.product.image.single.height', 500), + contentType ); // Upload the listing variant const listingUrl = await resizeAndUploadImage( s3Client, - originalObjectUrl, + originalImageBuffer, listingObjectUrl, getConfig('catalog.product.image.listing.width', 250), - getConfig('catalog.product.image.listing.height', 250) + getConfig('catalog.product.image.listing.height', 250), + contentType ); // Upload the thumbnail variant - const thumnailUrl = await resizeAndUploadImage( + const thumbnailUrl = await resizeAndUploadImage( s3Client, - originalObjectUrl, + originalImageBuffer, thumbnailObjectUrl, getConfig('catalog.product.image.thumbnail.width', 100), - getConfig('catalog.product.image.thumbnail.height', 100) + getConfig('catalog.product.image.thumbnail.height', 100), + contentType ); // Update the record in the database with the new URLs in the variant columns @@ -108,7 +146,7 @@ module.exports = async function awsGenerateProductImageVariant(data) { .given({ single_image: singleUrl, listing_image: listingUrl, - thumb_image: thumnailUrl + thumb_image: thumbnailUrl }) .where('product_image_product_id', '=', data.product_image_product_id) .and('origin_image', '=', data.origin_image)