diff --git a/functions/package-lock.json b/functions/package-lock.json index d37a055..87a62c1 100644 --- a/functions/package-lock.json +++ b/functions/package-lock.json @@ -11,14 +11,14 @@ "@aws-sdk/client-ses": "^3.796.0", "@types/node-fetch": "^2.6.12", "aws-sdk": "^2.1692.0", - "axios": "^1.8.4", + "axios": "^1.9.0", "cors": "^2.8.5", "firebase-admin": "^12.6.0", "firebase-functions": "^6.0.1", "form-data": "^4.0.1", "functions": "file:", "html-to-text": "^9.0.5", - "long": "^4.0.0", + "long": "^5.3.2", "mailgun.js": "^10.4.0", "node-fetch": "^2.7.0", "pdfjs-dist": "^5.0.375", @@ -1373,12 +1373,6 @@ "node": ">=6" } }, - "node_modules/@grpc/proto-loader/node_modules/long": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", - "optional": true - }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -2755,6 +2749,7 @@ "integrity": "sha512-eQs9RsucA/LNjnMoJvWG/nXa7Pot/RbBzilF/QRIU/xRl+0ApxrSUFsV5lmf01SvSlqMzJ7Zwxe440wmz2SJGA==", "deprecated": "This is a stub types definition. long provides its own type definitions, so you do not need this installed.", "dev": true, + "license": "MIT", "dependencies": { "long": "*" } @@ -3051,6 +3046,7 @@ "version": "1.9.0", "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -6059,9 +6055,10 @@ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" }, "node_modules/lru-cache": { "version": "5.1.1", @@ -6687,11 +6684,6 @@ "node": ">=12.0.0" } }, - "node_modules/protobufjs/node_modules/long": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", diff --git a/functions/package.json b/functions/package.json index 4d2e4a9..abdc7e0 100644 --- a/functions/package.json +++ b/functions/package.json @@ -18,14 +18,14 @@ "@aws-sdk/client-ses": "^3.796.0", "@types/node-fetch": "^2.6.12", "aws-sdk": "^2.1692.0", - "axios": "^1.8.4", + "axios": "^1.9.0", "cors": "^2.8.5", "firebase-admin": "^12.6.0", "firebase-functions": "^6.0.1", "form-data": "^4.0.1", "functions": "file:", "html-to-text": "^9.0.5", - "long": "^4.0.0", + "long": "^5.3.2", "mailgun.js": "^10.4.0", "node-fetch": "^2.7.0", "pdfjs-dist": "^5.0.375", diff --git a/functions/src/email/sendEmailSES.ts b/functions/src/email/sendEmailSES.ts index a99c3b1..78e6875 100644 --- a/functions/src/email/sendEmailSES.ts +++ b/functions/src/email/sendEmailSES.ts @@ -6,6 +6,7 @@ import { SESClient } from "@aws-sdk/client-ses"; import { SendEmailCommand, SendRawEmailCommand } from "@aws-sdk/client-ses"; import { HttpsError } from "firebase-functions/v2/https"; import * as mime from 'mime-types'; +import axios from 'axios'; const logger = getLogger(); const corsHandler = getCorsHandler(); @@ -18,6 +19,8 @@ interface EmailRequest { from: string; replyTo?: string; attachments?: Attachment[]; + fileUrl?: string; + fileName?: string; } interface Attachment { @@ -121,31 +124,71 @@ async function sendEmailWithAttachments(data: EmailRequest, recipients: string[] return { messageId: result.MessageId }; } +async function downloadFileFromUrl(url: string): Promise { + try { + const response = await axios.get(url, { responseType: 'arraybuffer' }); + return Buffer.from(response.data); + } catch (error) { + logger.error(`Error downloading file from URL: ${error}`); + throw new Error(`Failed to download file: ${error}`); + } +} + export const sendEmailSES = onRequest({ region: 'asia-south1' }, (request: Request, response) => { return corsHandler(request, response, async () => { - const toAddress = request.body.toAddress; - const subject = request.body.subject; - const message = request.body.message; - const data: EmailRequest = { - to: toAddress, - subject: subject, - html: message, - text: stripHtml(message), - from: process.env.SES_FROM_EMAIL || 'support@fitlien.com', - replyTo: process.env.SES_REPLY_TO_EMAIL || 'support@fitlien.com', - attachments: request.body.attachments as Attachment[] || [] - }; try { + const toAddress = request.body.toAddress; + const subject = request.body.subject; + const message = request.body.message; + + // Initialize data with basic fields + const data: EmailRequest = { + to: toAddress, + html: message, + subject: subject, + text: stripHtml(message), + from: process.env.SES_FROM_EMAIL || 'support@fitlien.com', + replyTo: process.env.SES_REPLY_TO_EMAIL || 'support@fitlien.com', + attachments: request.body.attachments as Attachment[] || [] + }; + + // Handle file URL if provided + if (request.body.fileUrl && request.body.fileName) { + logger.info(`Downloading attachment from URL: ${request.body.fileUrl}`); + try { + const fileContent = await downloadFileFromUrl(request.body.fileUrl); + + // If attachments array doesn't exist, create it + if (!data.attachments) { + data.attachments = []; + } + + // Add the downloaded file as an attachment + data.attachments.push({ + filename: request.body.fileName, + content: fileContent, + contentType: mime.lookup(request.body.fileName) || 'application/octet-stream' + }); + + logger.info(`Successfully downloaded attachment: ${request.body.fileName}`); + } catch (downloadError) { + logger.error(`Failed to download attachment: ${downloadError}`); + throw new Error(`Failed to process attachment: ${downloadError}`); + } + } + if (!data.to || !data.subject || !data.html || !data.from) { throw new HttpsError( 'invalid-argument', 'Missing required email fields' ); } + logger.info(`Sending Email '${data.subject}' to '${data.to}' from '${data.from}'`); const recipients = Array.isArray(data.to) ? data.to : [data.to]; + if (data.attachments && data.attachments.length > 0) { const messageResult = await sendEmailWithAttachments(data, recipients); response.status(200).json(messageResult);