UNPKG

@454creative/easy-email

Version:

A framework-agnostic email service library for Node.js with observability and monitoring

142 lines (124 loc) 4.59 kB
import nodemailer, { Transporter } from 'nodemailer'; import sgMail from '@sendgrid/mail'; import { SESClient } from '@aws-sdk/client-ses'; import { fromEnv } from '@aws-sdk/credential-providers'; import { EmailProviderType, EmailProviderConfig, SmtpConfig, SendGridConfig, SesConfig, } from '../interfaces/email-options.interface'; import { ConfigurationError } from '../errors'; /** * Factory for creating email provider configurations */ export class ProviderFactory { /** * Create SMTP transporter */ static createSmtpTransporter(config: SmtpConfig): Transporter { return nodemailer.createTransport(config); } /** * Configure SendGrid */ static configureSendGrid(config: SendGridConfig): void { sgMail.setApiKey(config.apiKey); } /** * Create AWS SES client */ static createSesClient(config: SesConfig): SESClient { const clientConfig: any = { region: config.region, apiVersion: config.apiVersion || '2010-12-01', maxAttempts: config.maxRetries || 3, }; // Configure AWS credentials if (config.credentials) { clientConfig.credentials = config.credentials; } else if (config.accessKeyId && config.secretAccessKey) { clientConfig.credentials = { accessKeyId: config.accessKeyId, secretAccessKey: config.secretAccessKey, sessionToken: config.sessionToken, }; } else { // Use AWS credential providers chain (environment variables, IAM roles, etc.) clientConfig.credentials = fromEnv(); } // Configure AWS SES endpoint if provided if (config.endpoint) { clientConfig.endpoint = config.endpoint; } // Configure AWS SES HTTP options if (config.httpOptions) { clientConfig.requestHandler = { httpOptions: config.httpOptions, }; } return new SESClient(clientConfig); } /** * Create provider configuration from legacy SMTP config */ static createProviderConfig(config: SmtpConfig | EmailProviderConfig): EmailProviderConfig { if (!('type' in config)) { return { type: EmailProviderType.SMTP, config: config as SmtpConfig, }; } return config as EmailProviderConfig; } /** * Validate provider configuration */ static validateProviderConfig(config: EmailProviderConfig): void { if (!config.type) { throw new ConfigurationError('Provider type is required'); } if (!config.config) { throw new ConfigurationError('Provider configuration is required'); } switch (config.type) { case EmailProviderType.SMTP: const smtpConfig = config.config as SmtpConfig; if (!smtpConfig.host || !smtpConfig.port) { throw new ConfigurationError('SMTP host and port are required'); } break; case EmailProviderType.SENDGRID: const sendGridConfig = config.config as SendGridConfig; if (!sendGridConfig.apiKey) { throw new ConfigurationError('SendGrid API key is required'); } // Check for basic API key format (SendGrid keys typically start with "SG.") if (typeof sendGridConfig.apiKey === 'string' && !sendGridConfig.apiKey.startsWith('SG.')) { throw new ConfigurationError('SendGrid API key appears to be invalid. Valid SendGrid API keys typically start with "SG."'); } // Check for minimum length (SendGrid keys are typically 69 characters) if (typeof sendGridConfig.apiKey === 'string' && sendGridConfig.apiKey.length < 20) { throw new ConfigurationError('SendGrid API key appears to be too short. Please verify your API key.'); } break; case EmailProviderType.SES: // AWS SES Configuration Validation const sesConfig = config.config as SesConfig; if (!sesConfig.region) { throw new ConfigurationError('AWS SES region is required'); } // Validate that at least one credential method is provided const hasCredentials = sesConfig.credentials || (sesConfig.accessKeyId && sesConfig.secretAccessKey) || process.env.AWS_ACCESS_KEY_ID; if (!hasCredentials) { throw new ConfigurationError('AWS SES credentials are required (accessKeyId/secretAccessKey, credentials object, or AWS environment variables)'); } break; default: throw new ConfigurationError(`Unsupported provider type: ${config.type}`); } } }