condition check lambda

This commit is contained in:
jackiettran
2026-01-13 17:14:19 -05:00
parent 2ee5571b5b
commit f5fdcbfb82
30 changed files with 14293 additions and 461 deletions

View File

@@ -0,0 +1,196 @@
const { SESClient, SendEmailCommand } = require("@aws-sdk/client-ses");
const fs = require("fs").promises;
const path = require("path");
let sesClient = null;
/**
* Get or create an SES client.
* Reuses client across Lambda invocations for better performance.
*/
function getSESClient() {
if (!sesClient) {
sesClient = new SESClient({
region: process.env.AWS_REGION || "us-east-1",
});
}
return sesClient;
}
/**
* Convert HTML to plain text for email fallback.
* @param {string} html - HTML content to convert
* @returns {string} Plain text version
*/
function htmlToPlainText(html) {
return html
.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "")
.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "")
.replace(/<br\s*\/?>/gi, "\n")
.replace(/<\/p>/gi, "\n\n")
.replace(/<\/div>/gi, "\n")
.replace(/<\/li>/gi, "\n")
.replace(/<\/h[1-6]>/gi, "\n\n")
.replace(/<li>/gi, "- ")
.replace(/<[^>]+>/g, "")
.replace(/&nbsp;/g, " ")
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
.replace(/\n\s*\n\s*\n/g, "\n\n")
.trim();
}
/**
* Render a template by replacing {{variable}} placeholders with values.
* @param {string} template - Template string with {{placeholders}}
* @param {Object} variables - Key-value pairs to substitute
* @returns {string} Rendered template
*/
function renderTemplate(template, variables = {}) {
let rendered = template;
Object.keys(variables).forEach((key) => {
const regex = new RegExp(`{{${key}}}`, "g");
rendered = rendered.replace(regex, variables[key] ?? "");
});
return rendered;
}
/**
* Load a template file from disk.
* @param {string} templatePath - Absolute path to the template file
* @returns {Promise<string>} Template content
*/
async function loadTemplate(templatePath) {
try {
return await fs.readFile(templatePath, "utf-8");
} catch (error) {
console.error(JSON.stringify({
level: "error",
message: "Failed to load email template",
templatePath,
error: error.message,
}));
throw error;
}
}
/**
* Send an email using AWS SES.
* @param {string|string[]} to - Recipient email address(es)
* @param {string} subject - Email subject line
* @param {string} htmlBody - HTML content of the email
* @param {string|null} textBody - Plain text content (auto-generated if not provided)
* @returns {Promise<{success: boolean, messageId?: string, error?: string}>}
*/
async function sendEmail(to, subject, htmlBody, textBody = null) {
// Check if email sending is enabled
if (process.env.EMAIL_ENABLED !== "true") {
console.log(JSON.stringify({
level: "info",
message: "Email sending disabled, skipping",
to,
subject,
}));
return { success: true, messageId: "disabled" };
}
const client = getSESClient();
// Auto-generate plain text from HTML if not provided
const plainText = textBody || htmlToPlainText(htmlBody);
// Build sender address with friendly name
const fromName = process.env.SES_FROM_NAME || "Village Share";
const fromEmail = process.env.SES_FROM_EMAIL;
if (!fromEmail) {
throw new Error("SES_FROM_EMAIL environment variable is required");
}
const source = `${fromName} <${fromEmail}>`;
const params = {
Source: source,
Destination: {
ToAddresses: Array.isArray(to) ? to : [to],
},
Message: {
Subject: {
Data: subject,
Charset: "UTF-8",
},
Body: {
Html: {
Data: htmlBody,
Charset: "UTF-8",
},
Text: {
Data: plainText,
Charset: "UTF-8",
},
},
},
};
// Add reply-to if configured
if (process.env.SES_REPLY_TO_EMAIL) {
params.ReplyToAddresses = [process.env.SES_REPLY_TO_EMAIL];
}
try {
const command = new SendEmailCommand(params);
const result = await client.send(command);
console.log(JSON.stringify({
level: "info",
message: "Email sent successfully",
to,
subject,
messageId: result.MessageId,
}));
return { success: true, messageId: result.MessageId };
} catch (error) {
console.error(JSON.stringify({
level: "error",
message: "Failed to send email",
to,
subject,
error: error.message,
}));
return { success: false, error: error.message };
}
}
/**
* Format a date for email display.
* @param {Date|string} date - Date to format
* @returns {string} Formatted date string
*/
function formatEmailDate(date) {
const dateObj = typeof date === "string" ? new Date(date) : date;
return dateObj.toLocaleString("en-US", {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "2-digit",
hour12: true,
timeZone: "America/New_York",
});
}
module.exports = {
sendEmail,
loadTemplate,
renderTemplate,
htmlToPlainText,
formatEmailDate,
};