From fafb5d06a7aa7f76d6f7ec7a3f23674c6df5ede7 Mon Sep 17 00:00:00 2001 From: Mayank Mishra Date: Fri, 2 Jan 2026 18:21:08 +0530 Subject: [PATCH] made upload and delete image from s3 --- serverless/functions/host.yml | 32 +++++++++ .../host/handlers/mediaDeleteFromS3.ts | 40 +++++++++++ src/modules/host/handlers/mediaUploadToS3.ts | 66 +++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 src/modules/host/handlers/mediaDeleteFromS3.ts create mode 100644 src/modules/host/handlers/mediaUploadToS3.ts diff --git a/serverless/functions/host.yml b/serverless/functions/host.yml index 6874df0..ca7714a 100644 --- a/serverless/functions/host.yml +++ b/serverless/functions/host.yml @@ -398,3 +398,35 @@ resendOTPmail: - httpApi: path: /resend-otp method: post + + +mediaUploadTos3: + handler: src/modules/host/handlers/mediaUploadToS3.handler + memorySize: 512 + package: + patterns: + - 'src/modules/host/handlers/mediaUploadToS3/**' + - ${file(./serverless/patterns/base.yml):pattern1} + - ${file(./serverless/patterns/base.yml):pattern2} + - ${file(./serverless/patterns/base.yml):pattern3} + - ${file(./serverless/patterns/base.yml):pattern4} + events: + - httpApi: + path: /media/upload/activity/{activityXid} + method: post + + +mediaDeleteFroms3: + handler: src/modules/host/handlers/mediaDeleteFromS3.handler + memorySize: 512 + package: + patterns: + - 'src/modules/host/handlers/mediaDeleteFromS3/**' + - ${file(./serverless/patterns/base.yml):pattern1} + - ${file(./serverless/patterns/base.yml):pattern2} + - ${file(./serverless/patterns/base.yml):pattern3} + - ${file(./serverless/patterns/base.yml):pattern4} + events: + - httpApi: + path: /media/delete + method: delete diff --git a/src/modules/host/handlers/mediaDeleteFromS3.ts b/src/modules/host/handlers/mediaDeleteFromS3.ts new file mode 100644 index 0000000..fe2e305 --- /dev/null +++ b/src/modules/host/handlers/mediaDeleteFromS3.ts @@ -0,0 +1,40 @@ +// mediaDelete.ts +import { APIGatewayProxyHandler } from 'aws-lambda'; +import { S3Client, DeleteObjectCommand } from '@aws-sdk/client-s3'; +import config from '../../../config/config'; + +const s3 = new S3Client({ region: config.aws.region, }); + +export const handler: APIGatewayProxyHandler = async (event) => { + try { + const body = JSON.parse(event.body || '{}'); + const { key } = body; + + if (!key) { + return response(400, 'S3 key is required'); + } + + await s3.send( + new DeleteObjectCommand({ + Bucket: config.aws.bucketName!, + Key: key, + }) + ); + + return response(200, { success: true }); + } catch (err: any) { + console.error(err); + return response(500, 'Failed to delete file'); + } +}; + +function response(statusCode: number, body: any) { + return { + statusCode, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + }, + body: JSON.stringify(body), + }; +} diff --git a/src/modules/host/handlers/mediaUploadToS3.ts b/src/modules/host/handlers/mediaUploadToS3.ts new file mode 100644 index 0000000..728eeb8 --- /dev/null +++ b/src/modules/host/handlers/mediaUploadToS3.ts @@ -0,0 +1,66 @@ +// mediaPresignUpload.ts +import { APIGatewayProxyHandler } from 'aws-lambda'; +import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'; +import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; +import { v4 as uuid } from 'uuid'; +import ApiError from '../../../common/utils/helper/ApiError'; +import config from '../../../config/config'; + +const s3 = new S3Client({ region: config.aws.region }); + +export const handler: APIGatewayProxyHandler = async (event) => { + try { + const body = JSON.parse(event.body || '{}'); + const { fileName, mimeType } = body; + + if (!fileName || !mimeType) { + throw new ApiError(400, 'fileName and mimeType are required'); + } + + const activityXid = event.pathParameters?.activityXid; + if (!activityXid) { + throw new ApiError(400, 'activityXid is required in path parameters'); + } + + const safeFileName = fileName + .trim() + .replace(/\s+/g, '_') + .replace(/[^a-zA-Z0-9._-]/g, '') + .toLowerCase(); + + // const key = `temporary/uploads/activity/activity_${activityXid}/${uuid()}_${safeFileName}`; + const key = `ActivityOnboarding/Activity_${activityXid}/Artifacts/${uuid()}_${safeFileName}`; + + const command = new PutObjectCommand({ + Bucket: config.aws.bucketName!, + Key: key, + ContentType: mimeType, // IMPORTANT: must match frontend header + // ❌ DO NOT SET ACL + }); + + const uploadUrl = await getSignedUrl(s3, command, { + expiresIn: 300, // 5 minutes + }); + + return response(200, { + uploadUrl, + key, + fileUrl: `https://${config.aws.bucketName}.s3.${config.aws.region}.amazonaws.com/${key}`, + }); + + } catch (err) { + console.error(err); + return response(500, 'Failed to generate presigned URL'); + } +}; + +function response(statusCode: number, body: any) { + return { + statusCode, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + }, + body: JSON.stringify(body), + }; +}