/** * Elmeg Email Service - AWS SES v2 Integration * * Transactional email layer for user-initiated emails only. * Uses AWS SES stored templates for consistent, reliable delivery. */ import { SESv2Client, SendEmailCommand } from "@aws-sdk/client-sesv2"; // Configuration from environment variables const config = { region: process.env.AWS_SES_REGION || "us-east-1", fromAddress: process.env.EMAIL_FROM || "noreply@elmeg.xyz", appName: "Elmeg", supportEmail: process.env.SUPPORT_EMAIL || "support@elmeg.xyz", frontendUrl: process.env.FRONTEND_URL || "https://elmeg.xyz", }; // SES Template Names export const SES_TEMPLATES = { EMAIL_VERIFICATION: "ELMEG_EMAIL_VERIFICATION", PASSWORD_RESET: "ELMEG_PASSWORD_RESET", SECURITY_ALERT: "ELMEG_SECURITY_ALERT", } as const; // Initialize SES v2 client const sesClient = new SESv2Client({ region: config.region, // Credentials are loaded automatically from env vars: // AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY }); // ============================================================================= // Types // ============================================================================= export interface SendVerificationEmailParams { to: string; userName: string; verificationLink: string; } export interface SendPasswordResetEmailParams { to: string; userName: string; resetLink: string; } export interface SendSecurityAlertEmailParams { to: string; userName: string; securityEventDescription: string; } export interface EmailResult { success: boolean; messageId?: string; error?: { code: string; message: string; }; } export class EmailError extends Error { code: string; constructor(code: string, message: string) { super(message); this.name = "EmailError"; this.code = code; } } // ============================================================================= // Email Sending Functions // ============================================================================= /** * Send email verification email to new users */ export async function sendVerificationEmail( params: SendVerificationEmailParams ): Promise { const templateData = { user_name: params.userName, verification_link: params.verificationLink, app_name: config.appName, support_email: config.supportEmail, }; return sendTemplatedEmail( params.to, SES_TEMPLATES.EMAIL_VERIFICATION, templateData ); } /** * Send password reset email */ export async function sendPasswordResetEmail( params: SendPasswordResetEmailParams ): Promise { const templateData = { user_name: params.userName, reset_link: params.resetLink, app_name: config.appName, support_email: config.supportEmail, }; return sendTemplatedEmail( params.to, SES_TEMPLATES.PASSWORD_RESET, templateData ); } /** * Send security alert email for account events */ export async function sendSecurityAlertEmail( params: SendSecurityAlertEmailParams ): Promise { const templateData = { user_name: params.userName, security_event_description: params.securityEventDescription, app_name: config.appName, support_email: config.supportEmail, }; return sendTemplatedEmail( params.to, SES_TEMPLATES.SECURITY_ALERT, templateData ); } // ============================================================================= // Core Email Function // ============================================================================= async function sendTemplatedEmail( to: string, templateName: string, templateData: Record ): Promise { try { const command = new SendEmailCommand({ FromEmailAddress: config.fromAddress, Destination: { ToAddresses: [to], }, Content: { Template: { TemplateName: templateName, TemplateData: JSON.stringify(templateData), }, }, }); const response = await sesClient.send(command); return { success: true, messageId: response.MessageId, }; } catch (error: unknown) { const err = error as { name?: string; message?: string; Code?: string }; console.error(`[Email] Failed to send ${templateName} to ${to}:`, err.message); return { success: false, error: { code: err.Code || err.name || "UNKNOWN_ERROR", message: err.message || "Failed to send email", }, }; } } // ============================================================================= // Utility Functions // ============================================================================= /** * Generate a verification link for a user */ export function generateVerificationLink(token: string): string { return `${config.frontendUrl}/verify-email?token=${encodeURIComponent(token)}`; } /** * Generate a password reset link for a user */ export function generateResetLink(token: string): string { return `${config.frontendUrl}/reset-password?token=${encodeURIComponent(token)}`; } /** * Check if the email service is properly configured */ export function isEmailConfigured(): boolean { return !!( process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY && process.env.AWS_SES_REGION ); }