UNPKG

waitlist-mailer

Version:

Modern, modular TypeScript library for managing waitlists with pluggable storage and mail providers. Supports MongoDB, SQL databases, and custom adapters with zero required dependencies for basic usage.

155 lines (141 loc) 4.6 kB
/** * Nodemailer-based mail provider for waitlist-mailer. * Sends emails through SMTP servers using Nodemailer. * Requires: nodemailer (already a core dependency) */ import { MailProvider, EmailContext } from '../../types'; import nodemailer, { Transporter, SendMailOptions } from 'nodemailer'; /** * Nodemailer SMTP configuration. */ export interface NodemailerConfig { host: string; port: number; user: string; pass: string; secure?: boolean; from?: string; name?: string; } /** * Nodemailer-based mail provider implementation. * Sends confirmation emails via SMTP. */ export class NodemailerProvider implements MailProvider { private transporter: Transporter; private fromEmail: string; private fromName: string; /** * Create a new NodemailerProvider instance. * @param config - SMTP configuration * @throws {Error} If nodemailer is not installed */ constructor(private config: NodemailerConfig) { // Verify nodemailer is available try { if (!nodemailer || !nodemailer.createTransport) { throw new Error('nodemailer is not properly initialized'); } } catch (error) { throw new Error( 'NodemailerProvider requires nodemailer to be installed. ' + 'Install it with: npm install nodemailer' ); } this.fromEmail = config.from || config.user; this.fromName = config.name || 'Waitlist Manager'; this.transporter = nodemailer.createTransport({ host: config.host, port: config.port, secure: config.secure ?? config.port === 465, auth: { user: config.user, pass: config.pass, }, }); } /** * Verify the transporter configuration. * @returns true if configuration is valid */ async verify(): Promise<boolean> { try { await this.transporter.verify(); return true; } catch (error) { console.error('Nodemailer verification failed:', error); return false; } } /** * Send a confirmation email. * @param email - Recipient email address * @param context - Template context with variables * @returns true if the email was sent successfully */ async sendConfirmation(email: string, context: EmailContext): Promise<boolean> { try { const subject = this.buildSubject(context); const html = this.buildHtml(context); const mailOptions: SendMailOptions = { from: `"${this.fromName}" <${this.fromEmail}>`, to: email, subject, html, }; await this.transporter.sendMail(mailOptions); return true; } catch (error) { console.error(`Failed to send email to ${email}:`, error); return false; } } /** * Build the email subject. * Allows customization through context. */ private buildSubject(context: EmailContext): string { return context.subject || `Welcome to ${context.companyName || 'our platform'}`; } /** * Build the HTML body. * Provides a default template if customHtml is not provided. */ private buildHtml(context: EmailContext): string { if (context.customHtml) { return context.customHtml; } // Default HTML template return ` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <style> body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; } .container { max-width: 600px; margin: 0 auto; padding: 20px; } .header { background-color: #007bff; color: white; padding: 20px; text-align: center; } .content { padding: 20px; } .footer { text-align: center; padding: 20px; color: #666; font-size: 12px; } </style> </head> <body> <div class="container"> <div class="header"> <h1>Welcome to ${context.companyName || 'Our Platform'}</h1> </div> <div class="content"> <p>Hello,</p> <p>Thank you for joining our waitlist! We're excited to have you on board.</p> <p>You'll be among the first to know when we launch.</p> ${context.customUrl ? `<p><a href="${context.customUrl}" style="background-color: #007bff; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px; display: inline-block;">Learn More</a></p>` : ''} </div> <div class="footer"> <p>&copy; 2024 ${context.companyName || 'Our Platform'}. All rights reserved.</p> </div> </div> </body> </html> `; } }