From 9fb0ac4083a4d463a1edc3b4f7a2ac4f5541ebe7 Mon Sep 17 00:00:00 2001 From: gv Date: Sun, 24 Nov 2024 04:25:55 -0700 Subject: [PATCH] s3 is OPTIONAL not required --- src/utils/aws-s3.ts | 97 +++++++++++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 25 deletions(-) diff --git a/src/utils/aws-s3.ts b/src/utils/aws-s3.ts index 56f476a..1f3a39b 100644 --- a/src/utils/aws-s3.ts +++ b/src/utils/aws-s3.ts @@ -4,41 +4,88 @@ import { PutObjectCommand, PutObjectCommandInput, S3Client, + S3ServiceException, } from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; import { config } from "../config"; import { optimizeImage } from "./images"; -export const s3Client = new S3Client({ - region: config.s3.region, - credentials: { - accessKeyId: config.s3.accessKeyId, - secretAccessKey: config.s3.secretAccessKey, - }, -}); +// Helper to check if S3 configuration is complete +function isS3Configured(): boolean { + return !!( + config.s3?.region && + config.s3?.accessKeyId && + config.s3?.secretAccessKey && + config.s3?.bucketName + ); +} + +// Initialize S3 client only if properly configured +export const s3Client: S3Client | null = isS3Configured() + ? new S3Client({ + region: config.s3.region, + credentials: { + accessKeyId: config.s3.accessKeyId, + secretAccessKey: config.s3.secretAccessKey, + }, + }) + : null; -export async function uploadImageToS3(filename: string, file: Express.Multer.File) { - const optimizedImage = await optimizeImage(file.buffer); - const params: PutObjectCommandInput = { - Key: filename, - Body: optimizedImage, - ContentType: file.mimetype, - Bucket: config.s3.bucketName, - }; - const command = new PutObjectCommand(params); - return s3Client.send(command); +export async function uploadImageToS3( + filename: string, + file: Express.Multer.File +): Promise { + // If S3 is not configured, return early + if (!s3Client || !isS3Configured()) { + console.warn('S3 is not configured. Skipping upload.'); + return; + } + + try { + const optimizedImage = await optimizeImage(file.buffer); + const params: PutObjectCommandInput = { + Key: filename, + Body: optimizedImage, + ContentType: file.mimetype, + Bucket: config.s3.bucketName, + }; + const command = new PutObjectCommand(params); + await s3Client.send(command); + } catch (error) { + console.error('Error uploading to S3:', error); + if (error instanceof S3ServiceException) { + throw new Error(`Failed to upload image to S3: ${error.message}`); + } + throw new Error('Failed to upload image to S3'); + } } -export function getS3SignedUrl(filename: string): Promise { - const params: GetObjectCommandInput = { - Key: filename, - Bucket: config.s3.bucketName, - }; - const command = new GetObjectCommand(params); - return getSignedUrl(s3Client, command, { expiresIn: 3600 }); +export async function getS3SignedUrl(filename: string): Promise { + // If S3 is not configured, return null + if (!s3Client || !isS3Configured()) { + console.warn('S3 is not configured. Cannot generate signed URL.'); + return null; + } + + try { + const params: GetObjectCommandInput = { + Key: filename, + Bucket: config.s3.bucketName, + }; + const command = new GetObjectCommand(params); + return await getSignedUrl(s3Client, command, { expiresIn: 3600 }); + } catch (error) { + console.error('Error generating signed URL:', error); + if (error instanceof S3ServiceException) { + throw new Error(`Failed to generate signed URL: ${error.message}`); + } + throw new Error('Failed to generate signed URL'); + } } -export async function getS3SignedUrlIfExisted(filename?: string | null): Promise { +export async function getS3SignedUrlIfExisted( + filename?: string | null +): Promise { if (!filename) return null; return getS3SignedUrl(filename); }