import * as plugins from '../../plugins.js'; import * as paths from '../../paths.js'; import { logger } from '../../logger.js'; import { Email } from './classes.email.js'; /** * Template category definitions */ export var TemplateCategory; (function (TemplateCategory) { TemplateCategory["NOTIFICATION"] = "notification"; TemplateCategory["TRANSACTIONAL"] = "transactional"; TemplateCategory["MARKETING"] = "marketing"; TemplateCategory["SYSTEM"] = "system"; })(TemplateCategory || (TemplateCategory = {})); /** * Enhanced template manager using Email class for template rendering */ export class TemplateManager { templates = new Map(); defaultConfig; constructor(defaultConfig) { // Set default configuration this.defaultConfig = { from: defaultConfig?.from || 'noreply@mail.lossless.com', replyTo: defaultConfig?.replyTo, footerHtml: defaultConfig?.footerHtml || '', footerText: defaultConfig?.footerText || '' }; // Initialize with built-in templates this.registerBuiltinTemplates(); } /** * Register built-in email templates */ registerBuiltinTemplates() { // Welcome email this.registerTemplate({ id: 'welcome', name: 'Welcome Email', description: 'Sent to users when they first sign up', from: this.defaultConfig.from, subject: 'Welcome to {{serviceName}}!', category: TemplateCategory.TRANSACTIONAL, bodyHtml: `

Welcome, {{firstName}}!

Thank you for joining {{serviceName}}. We're excited to have you on board.

To get started, visit your account.

`, bodyText: `Welcome, {{firstName}}! Thank you for joining {{serviceName}}. We're excited to have you on board. To get started, visit your account: {{accountUrl}} `, sampleData: { firstName: 'John', accountUrl: 'https://example.com/account' } }); // Password reset this.registerTemplate({ id: 'password-reset', name: 'Password Reset', description: 'Sent when a user requests a password reset', from: this.defaultConfig.from, subject: 'Password Reset Request', category: TemplateCategory.TRANSACTIONAL, bodyHtml: `

Password Reset Request

You recently requested to reset your password. Click the link below to reset it:

Reset Password

This link will expire in {{expiryHours}} hours.

If you didn't request a password reset, please ignore this email.

`, sampleData: { resetUrl: 'https://example.com/reset-password?token=abc123', expiryHours: 24 } }); // System notification this.registerTemplate({ id: 'system-notification', name: 'System Notification', description: 'General system notification template', from: this.defaultConfig.from, subject: '{{subject}}', category: TemplateCategory.SYSTEM, bodyHtml: `

{{title}}

{{message}}
`, sampleData: { subject: 'Important System Notification', title: 'System Maintenance', message: 'The system will be undergoing maintenance on Saturday from 2-4am UTC.' } }); } /** * Register a new email template * @param template The email template to register */ registerTemplate(template) { if (this.templates.has(template.id)) { logger.log('warn', `Template with ID '${template.id}' already exists and will be overwritten`); } // Add footer to templates if configured if (this.defaultConfig.footerHtml && template.bodyHtml) { template.bodyHtml += this.defaultConfig.footerHtml; } if (this.defaultConfig.footerText && template.bodyText) { template.bodyText += this.defaultConfig.footerText; } this.templates.set(template.id, template); logger.log('info', `Registered email template: ${template.id}`); } /** * Get an email template by ID * @param templateId The template ID * @returns The template or undefined if not found */ getTemplate(templateId) { return this.templates.get(templateId); } /** * List all available templates * @param category Optional category filter * @returns Array of email templates */ listTemplates(category) { const templates = Array.from(this.templates.values()); if (category) { return templates.filter(template => template.category === category); } return templates; } /** * Create an Email instance from a template * @param templateId The template ID * @param context The template context data * @returns A configured Email instance */ async createEmail(templateId, context) { const template = this.getTemplate(templateId); if (!template) { throw new Error(`Template with ID '${templateId}' not found`); } // Build attachments array for Email const attachments = []; if (template.attachments && template.attachments.length > 0) { for (const attachment of template.attachments) { try { const attachmentPath = plugins.path.isAbsolute(attachment.path) ? attachment.path : plugins.path.join(paths.MtaAttachmentsDir, attachment.path); // Read the file const fileBuffer = await plugins.fs.promises.readFile(attachmentPath); attachments.push({ filename: attachment.name, content: fileBuffer, contentType: attachment.contentType || 'application/octet-stream' }); } catch (error) { logger.log('error', `Failed to add attachment '${attachment.name}': ${error.message}`); } } } // Create Email instance with template content const emailOptions = { from: template.from || this.defaultConfig.from, subject: template.subject, text: template.bodyText || '', html: template.bodyHtml, // Note: 'to' is intentionally omitted for templates attachments, variables: context || {} }; return new Email(emailOptions); } /** * Create and completely process an Email instance from a template * @param templateId The template ID * @param context The template context data * @returns A complete, processed Email instance ready to send */ async prepareEmail(templateId, context = {}) { const email = await this.createEmail(templateId, context); // Email class processes variables when needed, no pre-compilation required return email; } /** * Create a MIME-formatted email from a template * @param templateId The template ID * @param context The template context data * @returns A MIME-formatted email string */ async createMimeEmail(templateId, context = {}) { const email = await this.prepareEmail(templateId, context); return email.toRFC822String(context); } /** * Load templates from a directory * @param directory The directory containing template JSON files */ async loadTemplatesFromDirectory(directory) { try { // Ensure directory exists if (!plugins.fs.existsSync(directory)) { logger.log('error', `Template directory does not exist: ${directory}`); return; } // Get all JSON files const files = plugins.fs.readdirSync(directory) .filter(file => file.endsWith('.json')); for (const file of files) { try { const filePath = plugins.path.join(directory, file); const content = plugins.fs.readFileSync(filePath, 'utf8'); const template = JSON.parse(content); // Validate template if (!template.id || !template.subject || (!template.bodyHtml && !template.bodyText)) { logger.log('warn', `Invalid template in ${file}: missing required fields`); continue; } this.registerTemplate(template); } catch (error) { logger.log('error', `Error loading template from ${file}: ${error.message}`); } } logger.log('info', `Loaded ${this.templates.size} email templates`); } catch (error) { logger.log('error', `Failed to load templates from directory: ${error.message}`); throw error; } } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy50ZW1wbGF0ZW1hbmFnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL2NvcmUvY2xhc3Nlcy50ZW1wbGF0ZW1hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxrQkFBa0IsQ0FBQztBQUM1QyxPQUFPLEtBQUssS0FBSyxNQUFNLGdCQUFnQixDQUFDO0FBQ3hDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUN6QyxPQUFPLEVBQUUsS0FBSyxFQUF3QyxNQUFNLG9CQUFvQixDQUFDO0FBNkJqRjs7R0FFRztBQUNILE1BQU0sQ0FBTixJQUFZLGdCQUtYO0FBTEQsV0FBWSxnQkFBZ0I7SUFDMUIsaURBQTZCLENBQUE7SUFDN0IsbURBQStCLENBQUE7SUFDL0IsMkNBQXVCLENBQUE7SUFDdkIscUNBQWlCLENBQUE7QUFDbkIsQ0FBQyxFQUxXLGdCQUFnQixLQUFoQixnQkFBZ0IsUUFLM0I7QUFFRDs7R0FFRztBQUNILE1BQU0sT0FBTyxlQUFlO0lBQ2xCLFNBQVMsR0FBZ0MsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQUNuRCxhQUFhLENBS25CO0lBRUYsWUFBWSxhQUtYO1FBQ0MsNEJBQTRCO1FBQzVCLElBQUksQ0FBQyxhQUFhLEdBQUc7WUFDbkIsSUFBSSxFQUFFLGFBQWEsRUFBRSxJQUFJLElBQUksMkJBQTJCO1lBQ3hELE9BQU8sRUFBRSxhQUFhLEVBQUUsT0FBTztZQUMvQixVQUFVLEVBQUUsYUFBYSxFQUFFLFVBQVUsSUFBSSxFQUFFO1lBQzNDLFVBQVUsRUFBRSxhQUFhLEVBQUUsVUFBVSxJQUFJLEVBQUU7U0FDNUMsQ0FBQztRQUVGLHFDQUFxQztRQUNyQyxJQUFJLENBQUMsd0JBQXdCLEVBQUUsQ0FBQztJQUNsQyxDQUFDO0lBRUQ7O09BRUc7SUFDSyx3QkFBd0I7UUFDOUIsZ0JBQWdCO1FBQ2hCLElBQUksQ0FBQyxnQkFBZ0IsQ0FHbEI7WUFDRCxFQUFFLEVBQUUsU0FBUztZQUNiLElBQUksRUFBRSxlQUFlO1lBQ3JCLFdBQVcsRUFBRSx1Q0FBdUM7WUFDcEQsSUFBSSxFQUFFLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSTtZQUM3QixPQUFPLEVBQUUsNkJBQTZCO1lBQ3RDLFFBQVEsRUFBRSxnQkFBZ0IsQ0FBQyxhQUFhO1lBQ3hDLFFBQVEsRUFBRTs7OztPQUlUO1lBQ0QsUUFBUSxFQUNOOzs7OztTQUtDO1lBQ0gsVUFBVSxFQUFFO2dCQUNWLFNBQVMsRUFBRSxNQUFNO2dCQUNqQixVQUFVLEVBQUUsNkJBQTZCO2FBQzFDO1NBQ0YsQ0FBQyxDQUFDO1FBRUgsaUJBQWlCO1FBQ2pCLElBQUksQ0FBQyxnQkFBZ0IsQ0FHbEI7WUFDRCxFQUFFLEVBQUUsZ0JBQWdCO1lBQ3BCLElBQUksRUFBRSxnQkFBZ0I7WUFDdEIsV0FBVyxFQUFFLDRDQUE0QztZQUN6RCxJQUFJLEVBQUUsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJO1lBQzdCLE9BQU8sRUFBRSx3QkFBd0I7WUFDakMsUUFBUSxFQUFFLGdCQUFnQixDQUFDLGFBQWE7WUFDeEMsUUFBUSxFQUFFOzs7Ozs7T0FNVDtZQUNELFVBQVUsRUFBRTtnQkFDVixRQUFRLEVBQUUsaURBQWlEO2dCQUMzRCxXQUFXLEVBQUUsRUFBRTthQUNoQjtTQUNGLENBQUMsQ0FBQztRQUVILHNCQUFzQjtRQUN0QixJQUFJLENBQUMsZ0JBQWdCLENBQUM7WUFDcEIsRUFBRSxFQUFFLHFCQUFxQjtZQUN6QixJQUFJLEVBQUUscUJBQXFCO1lBQzNCLFdBQVcsRUFBRSxzQ0FBc0M7WUFDbkQsSUFBSSxFQUFFLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSTtZQUM3QixPQUFPLEVBQUUsYUFBYTtZQUN0QixRQUFRLEVBQUUsZ0JBQWdCLENBQUMsTUFBTTtZQUNqQyxRQUFRLEVBQUU7OztPQUdUO1lBQ0QsVUFBVSxFQUFFO2dCQUNWLE9BQU8sRUFBRSwrQkFBK0I7Z0JBQ3hDLEtBQUssRUFBRSxvQkFBb0I7Z0JBQzNCLE9BQU8sRUFBRSx1RUFBdUU7YUFDakY7U0FDRixDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ksZ0JBQWdCLENBQVUsUUFBMkI7UUFDMUQsSUFBSSxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQztZQUNwQyxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxxQkFBcUIsUUFBUSxDQUFDLEVBQUUsMENBQTBDLENBQUMsQ0FBQztRQUNqRyxDQUFDO1FBRUQsd0NBQXdDO1FBQ3hDLElBQUksSUFBSSxDQUFDLGFBQWEsQ0FBQyxVQUFVLElBQUksUUFBUSxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ3ZELFFBQVEsQ0FBQyxRQUFRLElBQUksSUFBSSxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUM7UUFDckQsQ0FBQztRQUVELElBQUksSUFBSSxDQUFDLGFBQWEsQ0FBQyxVQUFVLElBQUksUUFBUSxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ3ZELFFBQVEsQ0FBQyxRQUFRLElBQUksSUFBSSxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUM7UUFDckQsQ0FBQztRQUVELElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxFQUFFLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDMUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsOEJBQThCLFFBQVEsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ2xFLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksV0FBVyxDQUFVLFVBQWtCO1FBQzVDLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFzQixDQUFDO0lBQzdELENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksYUFBYSxDQUFDLFFBQTJCO1FBQzlDLE1BQU0sU0FBUyxHQUFHLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO1FBQ3RELElBQUksUUFBUSxFQUFFLENBQUM7WUFDYixPQUFPLFNBQVMsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxRQUFRLENBQUMsUUFBUSxLQUFLLFFBQVEsQ0FBQyxDQUFDO1FBQ3RFLENBQUM7UUFDRCxPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSSxLQUFLLENBQUMsV0FBVyxDQUN0QixVQUFrQixFQUNsQixPQUEwQjtRQUUxQixNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBRTlDLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUNkLE1BQU0sSUFBSSxLQUFLLENBQUMscUJBQXFCLFVBQVUsYUFBYSxDQUFDLENBQUM7UUFDaEUsQ0FBQztRQUVELG9DQUFvQztRQUNwQyxNQUFNLFdBQVcsR0FBa0IsRUFBRSxDQUFDO1FBRXRDLElBQUksUUFBUSxDQUFDLFdBQVcsSUFBSSxRQUFRLENBQUMsV0FBVyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUM1RCxLQUFLLE1BQU0sVUFBVSxJQUFJLFFBQVEsQ0FBQyxXQUFXLEVBQUUsQ0FBQztnQkFDOUMsSUFBSSxDQUFDO29CQUNILE1BQU0sY0FBYyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUM7d0JBQzdELENBQUMsQ0FBQyxVQUFVLENBQUMsSUFBSTt3QkFDakIsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxpQkFBaUIsRUFBRSxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUM7b0JBRWhFLGdCQUFnQjtvQkFDaEIsTUFBTSxVQUFVLEdBQUcsTUFBTSxPQUFPLENBQUMsRUFBRSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsY0FBYyxDQUFDLENBQUM7b0JBRXRFLFdBQVcsQ0FBQyxJQUFJLENBQUM7d0JBQ2YsUUFBUSxFQUFFLFVBQVUsQ0FBQyxJQUFJO3dCQUN6QixPQUFPLEVBQUUsVUFBVTt3QkFDbkIsV0FBVyxFQUFFLFVBQVUsQ0FBQyxXQUFXLElBQUksMEJBQTBCO3FCQUNsRSxDQUFDLENBQUM7Z0JBQ0wsQ0FBQztnQkFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO29CQUNmLE1BQU0sQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLDZCQUE2QixVQUFVLENBQUMsSUFBSSxNQUFNLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO2dCQUN6RixDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUM7UUFFRCw4Q0FBOEM7UUFDOUMsTUFBTSxZQUFZLEdBQWtCO1lBQ2xDLElBQUksRUFBRSxRQUFRLENBQUMsSUFBSSxJQUFJLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSTtZQUM5QyxPQUFPLEVBQUUsUUFBUSxDQUFDLE9BQU87WUFDekIsSUFBSSxFQUFFLFFBQVEsQ0FBQyxRQUFRLElBQUksRUFBRTtZQUM3QixJQUFJLEVBQUUsUUFBUSxDQUFDLFFBQVE7WUFDdkIsb0RBQW9EO1lBQ3BELFdBQVc7WUFDWCxTQUFTLEVBQUUsT0FBTyxJQUFJLEVBQUU7U0FDekIsQ0FBQztRQUVGLE9BQU8sSUFBSSxLQUFLLENBQUMsWUFBWSxDQUFDLENBQUM7SUFDakMsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0ksS0FBSyxDQUFDLFlBQVksQ0FDdkIsVUFBa0IsRUFDbEIsVUFBNEIsRUFBRTtRQUU5QixNQUFNLEtBQUssR0FBRyxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUksVUFBVSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBRTdELDJFQUEyRTtRQUUzRSxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLEtBQUssQ0FBQyxlQUFlLENBQzFCLFVBQWtCLEVBQ2xCLFVBQTRCLEVBQUU7UUFFOUIsTUFBTSxLQUFLLEdBQUcsTUFBTSxJQUFJLENBQUMsWUFBWSxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUMzRCxPQUFPLEtBQUssQ0FBQyxjQUFjLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDdkMsQ0FBQztJQUdEOzs7T0FHRztJQUNJLEtBQUssQ0FBQywwQkFBMEIsQ0FBQyxTQUFpQjtRQUN2RCxJQUFJLENBQUM7WUFDSCwwQkFBMEI7WUFDMUIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUM7Z0JBQ3RDLE1BQU0sQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLHNDQUFzQyxTQUFTLEVBQUUsQ0FBQyxDQUFDO2dCQUN2RSxPQUFPO1lBQ1QsQ0FBQztZQUVELHFCQUFxQjtZQUNyQixNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsRUFBRSxDQUFDLFdBQVcsQ0FBQyxTQUFTLENBQUM7aUJBQzVDLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztZQUUxQyxLQUFLLE1BQU0sSUFBSSxJQUFJLEtBQUssRUFBRSxDQUFDO2dCQUN6QixJQUFJLENBQUM7b0JBQ0gsTUFBTSxRQUFRLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLElBQUksQ0FBQyxDQUFDO29CQUNwRCxNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsRUFBRSxDQUFDLFlBQVksQ0FBQyxRQUFRLEVBQUUsTUFBTSxDQUFDLENBQUM7b0JBQzFELE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFtQixDQUFDO29CQUV2RCxvQkFBb0I7b0JBQ3BCLElBQUksQ0FBQyxRQUFRLENBQUMsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sSUFBSSxDQUFDLENBQUMsUUFBUSxDQUFDLFFBQVEsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO3dCQUNwRixNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSx1QkFBdUIsSUFBSSwyQkFBMkIsQ0FBQyxDQUFDO3dCQUMzRSxTQUFTO29CQUNYLENBQUM7b0JBRUQsSUFBSSxDQUFDLGdCQUFnQixDQUFDLFFBQVEsQ0FBQyxDQUFDO2dCQUNsQyxDQUFDO2dCQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7b0JBQ2YsTUFBTSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsK0JBQStCLElBQUksS0FBSyxLQUFLLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztnQkFDL0UsQ0FBQztZQUNILENBQUM7WUFFRCxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxVQUFVLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxrQkFBa0IsQ0FBQyxDQUFDO1FBQ3RFLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsTUFBTSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsNENBQTRDLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1lBQ2pGLE1BQU0sS0FBSyxDQUFDO1FBQ2QsQ0FBQztJQUNILENBQUM7Q0FDRiJ9