email sent when personal information changed

This commit is contained in:
jackiettran
2025-11-21 16:47:39 -05:00
parent f7767dfd13
commit f2d42dffee
9 changed files with 701 additions and 153 deletions

View File

@@ -11,8 +11,16 @@ const { htmlToPlainText } = require("./emailUtils");
*/
class EmailClient {
constructor() {
// Singleton pattern - return existing instance if already created
if (EmailClient.instance) {
return EmailClient.instance;
}
this.sesClient = null;
this.initialized = false;
this.initializationPromise = null;
EmailClient.instance = this;
}
/**
@@ -20,19 +28,30 @@ class EmailClient {
* @returns {Promise<void>}
*/
async initialize() {
// If already initialized, return immediately
if (this.initialized) return;
try {
// Use centralized AWS configuration with credential profiles
const awsConfig = getAWSConfig();
this.sesClient = new SESClient(awsConfig);
this.initialized = true;
console.log("AWS SES Email Client initialized successfully");
} catch (error) {
console.error("Failed to initialize AWS SES Email Client:", error);
throw error;
// If initialization is in progress, wait for it
if (this.initializationPromise) {
return this.initializationPromise;
}
// Start initialization and store the promise
this.initializationPromise = (async () => {
try {
// Use centralized AWS configuration with credential profiles
const awsConfig = getAWSConfig();
this.sesClient = new SESClient(awsConfig);
this.initialized = true;
console.log("AWS SES Email Client initialized successfully");
} catch (error) {
console.error("Failed to initialize AWS SES Email Client:", error);
throw error;
}
})();
return this.initializationPromise;
}
/**

View File

@@ -11,8 +11,16 @@ const path = require("path");
*/
class TemplateManager {
constructor() {
// Singleton pattern - return existing instance if already created
if (TemplateManager.instance) {
return TemplateManager.instance;
}
this.templates = new Map();
this.initialized = false;
this.initializationPromise = null;
TemplateManager.instance = this;
}
/**
@@ -20,11 +28,22 @@ class TemplateManager {
* @returns {Promise<void>}
*/
async initialize() {
// If already initialized, return immediately
if (this.initialized) return;
await this.loadEmailTemplates();
this.initialized = true;
console.log("Email Template Manager initialized successfully");
// If initialization is in progress, wait for it
if (this.initializationPromise) {
return this.initializationPromise;
}
// Start initialization and store the promise
this.initializationPromise = (async () => {
await this.loadEmailTemplates();
this.initialized = true;
console.log("Email Template Manager initialized successfully");
})();
return this.initializationPromise;
}
/**
@@ -34,6 +53,14 @@ class TemplateManager {
async loadEmailTemplates() {
const templatesDir = path.join(__dirname, "..", "..", "..", "templates", "emails");
// Critical templates that must load for the app to function
const criticalTemplates = [
"emailVerificationToUser.html",
"passwordResetToUser.html",
"passwordChangedToUser.html",
"personalInfoChangedToUser.html",
];
try {
const templateFiles = [
"conditionCheckReminderToUser.html",
@@ -41,6 +68,7 @@ class TemplateManager {
"emailVerificationToUser.html",
"passwordResetToUser.html",
"passwordChangedToUser.html",
"personalInfoChangedToUser.html",
"lateReturnToCS.html",
"damageReportToCS.html",
"lostItemToCS.html",
@@ -69,6 +97,8 @@ class TemplateManager {
"forumCommentDeletionToAuthor.html",
];
const failedTemplates = [];
for (const templateFile of templateFiles) {
try {
const templatePath = path.join(templatesDir, templateFile);
@@ -84,16 +114,39 @@ class TemplateManager {
console.error(
` Template path: ${path.join(templatesDir, templateFile)}`
);
failedTemplates.push(templateFile);
}
}
console.log(
`Loaded ${this.templates.size} of ${templateFiles.length} email templates`
);
// Check if critical templates are missing
const missingCriticalTemplates = criticalTemplates.filter(
(template) => !this.templates.has(path.basename(template, ".html"))
);
if (missingCriticalTemplates.length > 0) {
const error = new Error(
`Critical email templates failed to load: ${missingCriticalTemplates.join(", ")}`
);
error.missingTemplates = missingCriticalTemplates;
throw error;
}
// Warn if non-critical templates failed
if (failedTemplates.length > 0) {
console.warn(
`⚠️ Non-critical templates failed to load: ${failedTemplates.join(", ")}`
);
console.warn("These templates will use fallback versions");
}
} catch (error) {
console.error("Failed to load email templates:", error);
console.error("Templates directory:", templatesDir);
console.error("Error stack:", error.stack);
throw error; // Re-throw to fail server startup
}
}