UNPKG

@454creative/easy-email

Version:

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

129 lines (111 loc) 4.39 kB
import { SmtpConfig, SendGridConfig, SesConfig, EmailProviderConfig, EmailProviderType, } from '../interfaces/email-options.interface'; import { ConfigurationError, ValidationError } from '../errors'; export class ConfigValidator { static validateSmtpConfig(config: SmtpConfig): void { if (!config.host) { throw new ConfigurationError('SMTP host is required'); } if (!config.port) { throw new ConfigurationError('SMTP port is required'); } if (config.auth) { if (!config.auth.user) { throw new ConfigurationError('SMTP username is required when auth is provided'); } if (!config.auth.pass) { throw new ConfigurationError('SMTP password is required when auth is provided'); } } } static validateSendGridConfig(config: SendGridConfig): void { if (!config.apiKey) { throw new ConfigurationError('SendGrid API key is required'); } // Check for basic API key format (SendGrid keys typically start with "SG.") if (typeof config.apiKey === 'string' && !config.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 config.apiKey === 'string' && config.apiKey.length < 20) { throw new ConfigurationError('SendGrid API key appears to be too short. Please verify your API key.'); } } static validateSesConfig(config: SesConfig): void { if (!config.region) { throw new ConfigurationError('SES region is required'); } // Validate that at least one credential method is provided const hasCredentials = config.credentials || (config.accessKeyId && config.secretAccessKey) || process.env.AWS_ACCESS_KEY_ID; if (!hasCredentials) { throw new ConfigurationError('SES credentials are required (accessKeyId/secretAccessKey, credentials object, or AWS environment variables)'); } // Validate region format (basic check) if (!/^[a-z0-9-]+$/.test(config.region)) { throw new ConfigurationError('Invalid SES region format'); } // Validate optional parameters if (config.maxRetries !== undefined && config.maxRetries < 0) { throw new ConfigurationError('SES maxRetries must be a non-negative number'); } if (config.httpOptions) { if (config.httpOptions.timeout !== undefined && config.httpOptions.timeout < 0) { throw new ConfigurationError('SES httpOptions timeout must be a non-negative number'); } if (config.httpOptions.connectTimeout !== undefined && config.httpOptions.connectTimeout < 0) { throw new ConfigurationError('SES httpOptions connectTimeout must be a non-negative number'); } } } static validateProviderConfig(config: EmailProviderConfig): void { if (!config.type) { throw new ConfigurationError('Provider type is required'); } switch (config.type) { case EmailProviderType.SMTP: this.validateSmtpConfig(config.config as SmtpConfig); break; case EmailProviderType.SENDGRID: this.validateSendGridConfig(config.config as SendGridConfig); break; case EmailProviderType.SES: this.validateSesConfig(config.config as SesConfig); break; default: throw new ConfigurationError(`Unsupported provider type: ${config.type}`); } } static validateEmail(email: string): void { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { throw new ValidationError(`Invalid email format: ${email}`); } } static validatePort(port: number): void { if (port < 1 || port > 65535) { throw new ConfigurationError(`Invalid port number: ${port}`); } } static validateTimeout(timeout: number): void { if (timeout < 0) { throw new ConfigurationError('Timeout must be a positive number'); } } static validateRetryAttempts(attempts: number): void { if (attempts < 0) { throw new ConfigurationError('Retry attempts must be a positive number'); } } static validateRetryDelay(delay: number): void { if (delay < 0) { throw new ConfigurationError('Retry delay must be a positive number'); } } }