diff --git a/serverless/functions/host.yml b/serverless/functions/host.yml index 1dd2968..4368ed7 100644 --- a/serverless/functions/host.yml +++ b/serverless/functions/host.yml @@ -291,10 +291,28 @@ submitPQQ_Answer: - ${file(./serverless/patterns/base.yml):pattern2} - ${file(./serverless/patterns/base.yml):pattern3} - ${file(./serverless/patterns/base.yml):pattern4} + - 'node_modules/@aws-sdk/client-s3/**' + - 'node_modules/@aws-sdk/s3-request-presigner/**' + - 'node_modules/@aws-sdk/types/**' + - 'node_modules/@aws-sdk/middleware-logger/**' + - 'node_modules/@aws-sdk/util-utf8-node/**' + - 'node_modules/@aws-sdk/util-utf8-browser/**' + - 'node_modules/@smithy/**' + - 'node_modules/tslib/**' + # Remove these large/unnecessary packages: + - 'node_modules/fast-xml-parser/**' # Remove if not used + - 'node_modules/lambda-multipart-parser/**' # You're using busboy directly + - 'node_modules/busboy/**' + # Remove these AWS utility packages (included in main SDK): + - 'node_modules/@aws-crypto/**' + # - 'node_modules/uuid/**' # AWS SDK includes its own + # - 'node_modules/@aws/util-uri-escape/**' + # - 'node_modules/@aws/util-middleware/**' + - 'node_modules/@aws/smithy-client/**' events: - httpApi: path: /host/Activity_Hub/OnBoarding/submit-pqq-answer - method: post + method: patch updatePQQ_LastAnswer: handler: src/modules/host/handlers/Activity_Hub/OnBoarding/getPQQScore.handler diff --git a/src/modules/host/handlers/Activity_Hub/OnBoarding/submitPQQ_Answer.ts b/src/modules/host/handlers/Activity_Hub/OnBoarding/submitPQQ_Answer.ts index 1cbe851..8658c92 100644 --- a/src/modules/host/handlers/Activity_Hub/OnBoarding/submitPQQ_Answer.ts +++ b/src/modules/host/handlers/Activity_Hub/OnBoarding/submitPQQ_Answer.ts @@ -70,45 +70,43 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< // 2) Content-Type check const contentType = event.headers["content-type"] || event.headers["Content-Type"]; - if (!contentType?.startsWith("multipart/form-data")) + if (!contentType?.includes("multipart/form-data")) throw new ApiError(400, "Content-Type must be multipart/form-data"); - if (!event.isBase64Encoded) - throw new ApiError(400, "Body must be base64 encoded"); - - const bodyBuffer = Buffer.from(event.body!, "base64"); + // 3) Body decoding (FIXED – same as addCompanyDetails) + const bodyBuffer = event.isBase64Encoded + ? Buffer.from(event.body!, "base64") + : Buffer.from(event.body!, "binary"); const fields: any = {}; const files: Array<{ buffer: Buffer; mimeType: string; fileName: string; fieldName: string }> = []; - // 3) Parse multipart data + // 4) Parse multipart data (FIXED – using bb.write + bb.end exactly like working lambda) await new Promise((resolve, reject) => { const bb = Busboy({ headers: { 'content-type': contentType } }); bb.on('file', (fieldname, file, info) => { const { filename, mimeType } = info; - - // Skip if no filename (empty file field) + if (!filename) { file.resume(); return; } const chunks: Buffer[] = []; - let totalSize = 0; - const MAX_SIZE = 5 * 1024 * 1024; // 5 MB + let size = 0; + const MAX_SIZE = 5 * 1024 * 1024; - file.on('data', (chunk) => { - totalSize += chunk.length; - if (totalSize > MAX_SIZE) { - file.resume(); - return reject(new ApiError(400, `File ${filename} exceeds 5MB limit.`)); + file.on("data", (chunk) => { + size += chunk.length; + if (size > MAX_SIZE) { + file.destroy(new Error(`File ${filename} exceeds 5MB limit.`)); + return; } chunks.push(chunk); }); - file.on('end', () => { - // Only add file if we have data + file.on("end", () => { if (chunks.length > 0) { files.push({ buffer: Buffer.concat(chunks), @@ -119,16 +117,14 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< } }); - file.on('error', (err) => { - reject(new ApiError(400, `File upload error: ${err.message}`)); - }); + file.on("error", (err) => + reject(new ApiError(400, `File upload error: ${err.message}`)) + ); }); - bb.on('field', (fieldname, val) => { - // Handle empty or null values - if (val === '' || val === 'null' || val === 'undefined') { - fields[fieldname] = null; - } else { + bb.on("field", (fieldname, val) => { + if (val === '' || val === 'null' || val === 'undefined') fields[fieldname] = null; + else { try { fields[fieldname] = JSON.parse(val); } catch { @@ -137,26 +133,21 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< } }); - bb.on('close', () => { - console.log("✅ Busboy parsing completed"); - console.log("📌 Fields:", fields); - console.log("📁 Files:", files.length); - resolve(); - }); + bb.on("close", () => resolve()); + bb.on("error", (err) => + reject(new ApiError(400, `Multipart parsing error: ${err.message}`)) + ); - bb.on('error', (err) => { - console.error("❌ Busboy error:", err); - reject(new ApiError(400, `Multipart parsing error: ${err.message}`)); - }); - - bb.end(bodyBuffer); + // IMPORTANT FIX for HTTP API + bb.write(bodyBuffer); + bb.end(); }); // 4) Extract required fields - only activityXid, pqqQuestionXid, pqqAnswerXid are required const activityXid = Number(fields.activityXid); const pqqQuestionXid = Number(fields.pqqQuestionXid); const pqqAnswerXid = Number(fields.pqqAnswerXid); - + // Comments and files are optional const comments = fields.comments || null; @@ -204,7 +195,7 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< if (files.length > 0) { console.log("📤 Processing file uploads..."); - + for (let i = 0; i < files.length; i++) { const file = files[i]; const existingFile = existingSupportingFiles[i] || null; @@ -243,7 +234,7 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< if (existingSupportingFiles.length > files.length) { const filesToDelete = existingSupportingFiles.slice(files.length); console.log(`🗑️ Deleting ${filesToDelete.length} unused supporting files`); - + for (const fileToDelete of filesToDelete) { await pqqService.deleteSupportingFile(fileToDelete.id); // Also delete from S3 @@ -254,7 +245,7 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< } } else { console.log("📭 No files provided in request"); - + // If no files provided but existing files exist, delete them (cleanup) if (existingSupportingFiles.length > 0) { console.log(`🗑️ No new files provided, deleting ${existingSupportingFiles.length} existing files`); @@ -269,7 +260,7 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< // 9) Prepare response const responseMessage = existingHeader ? "PQQ answer updated successfully" : "PQQ answer submitted successfully"; - + return { statusCode: 200, headers: { @@ -290,8 +281,8 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< total: uploadedFiles.length }, operation: existingHeader ? 'updated' : 'created', - fileOperation: files.length > 0 ? - (existingSupportingFiles.length > 0 ? 'replaced' : 'added') : + fileOperation: files.length > 0 ? + (existingSupportingFiles.length > 0 ? 'replaced' : 'added') : (existingSupportingFiles.length > 0 ? 'removed' : 'unchanged') } }) diff --git a/src/modules/prepopulate/handlers/getCityByState.ts b/src/modules/prepopulate/handlers/getCityByState.ts index 26982fa..fc3c4cb 100644 --- a/src/modules/prepopulate/handlers/getCityByState.ts +++ b/src/modules/prepopulate/handlers/getCityByState.ts @@ -46,7 +46,7 @@ export const handler = safeHandler(async ( }, body: JSON.stringify({ success: true, - message: "Bank branches retrieved successfully", + message: "Cities retrieved successfully", data: branches }), };