2022-08-07 11:38:07 +02:00
|
|
|
import * as plugins from './smartmail.plugins.js';
|
2025-05-07 13:18:41 +00:00
|
|
|
import { EmailAddressValidator } from './smartmail.classes.emailaddressvalidator.js';
|
|
|
|
|
|
|
|
export type EmailAddress = string;
|
|
|
|
export type EmailAddressList = EmailAddress[];
|
2018-09-29 23:57:25 +02:00
|
|
|
|
2020-01-13 15:19:47 +00:00
|
|
|
export interface ISmartmailOptions<T> {
|
2025-05-07 13:18:41 +00:00
|
|
|
from: EmailAddress;
|
|
|
|
to?: EmailAddressList;
|
|
|
|
cc?: EmailAddressList;
|
|
|
|
bcc?: EmailAddressList;
|
|
|
|
replyTo?: EmailAddress;
|
2019-10-26 23:45:35 +02:00
|
|
|
subject: string;
|
|
|
|
body: string;
|
2025-05-07 13:18:41 +00:00
|
|
|
htmlBody?: string;
|
2020-01-13 15:19:47 +00:00
|
|
|
creationObjectRef?: T;
|
2025-05-07 13:18:41 +00:00
|
|
|
headers?: Record<string, string>;
|
|
|
|
priority?: 'high' | 'normal' | 'low';
|
|
|
|
validateEmails?: boolean;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface IMimeAttachment {
|
|
|
|
filename: string;
|
|
|
|
content: Buffer;
|
|
|
|
contentType: string;
|
2019-10-26 23:45:35 +02:00
|
|
|
}
|
|
|
|
|
2018-09-29 23:57:25 +02:00
|
|
|
/**
|
2025-05-07 13:18:41 +00:00
|
|
|
* A standard representation for emails with advanced features
|
2018-09-29 23:57:25 +02:00
|
|
|
*/
|
2020-01-13 15:19:47 +00:00
|
|
|
export class Smartmail<T> {
|
|
|
|
public options: ISmartmailOptions<T>;
|
2025-05-07 13:18:41 +00:00
|
|
|
public attachments: plugins.smartfile.SmartFile[] = [];
|
|
|
|
private emailValidator: EmailAddressValidator;
|
2019-10-26 23:45:35 +02:00
|
|
|
|
2020-01-13 15:19:47 +00:00
|
|
|
constructor(optionsArg: ISmartmailOptions<T>) {
|
2025-05-07 13:18:41 +00:00
|
|
|
// Set default options
|
|
|
|
this.options = {
|
|
|
|
validateEmails: false,
|
|
|
|
to: [],
|
|
|
|
cc: [],
|
|
|
|
bcc: [],
|
|
|
|
headers: {},
|
|
|
|
priority: 'normal',
|
|
|
|
...optionsArg
|
|
|
|
};
|
|
|
|
|
|
|
|
this.emailValidator = new EmailAddressValidator();
|
2019-10-26 23:45:35 +02:00
|
|
|
}
|
|
|
|
|
2025-05-07 13:18:41 +00:00
|
|
|
/**
|
|
|
|
* Adds an attachment to the email
|
|
|
|
* @param smartfileArg The file to attach
|
|
|
|
*/
|
|
|
|
public addAttachment(smartfileArg: plugins.smartfile.SmartFile) {
|
2019-10-26 23:45:35 +02:00
|
|
|
this.attachments.push(smartfileArg);
|
|
|
|
}
|
|
|
|
|
2025-05-07 13:18:41 +00:00
|
|
|
/**
|
|
|
|
* Gets the creation object reference
|
|
|
|
* @returns The creation object reference
|
|
|
|
*/
|
2020-01-13 15:52:27 +00:00
|
|
|
public getCreationObject(): T {
|
2020-01-13 15:52:02 +00:00
|
|
|
return this.options.creationObjectRef;
|
|
|
|
}
|
|
|
|
|
2025-05-07 13:18:41 +00:00
|
|
|
/**
|
|
|
|
* Gets the processed subject with template variables applied
|
|
|
|
* @param dataArg Data to apply to the template
|
|
|
|
* @returns Processed subject
|
|
|
|
*/
|
|
|
|
public getSubject(dataArg: any = {}): string {
|
2019-10-26 23:45:35 +02:00
|
|
|
const smartmustache = new plugins.smartmustache.SmartMustache(this.options.subject);
|
|
|
|
return smartmustache.applyData(dataArg);
|
|
|
|
}
|
2025-05-07 14:57:50 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Applies variables to all template strings in the email
|
|
|
|
* @param variables Variables to apply to templates
|
|
|
|
*/
|
|
|
|
public applyVariables(variables: Record<string, any>): void {
|
|
|
|
if (!variables || typeof variables !== 'object') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process the subject, body, and HTML body with the provided variables
|
|
|
|
if (this.options.subject) {
|
|
|
|
const subjectMustache = new plugins.smartmustache.SmartMustache(this.options.subject);
|
|
|
|
this.options.subject = subjectMustache.applyData(variables);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.options.body) {
|
|
|
|
const bodyMustache = new plugins.smartmustache.SmartMustache(this.options.body);
|
|
|
|
this.options.body = bodyMustache.applyData(variables);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.options.htmlBody) {
|
|
|
|
const htmlBodyMustache = new plugins.smartmustache.SmartMustache(this.options.htmlBody);
|
|
|
|
this.options.htmlBody = htmlBodyMustache.applyData(variables);
|
|
|
|
}
|
|
|
|
}
|
2019-10-26 23:45:35 +02:00
|
|
|
|
2025-05-07 13:18:41 +00:00
|
|
|
/**
|
|
|
|
* Gets the processed plain text body with template variables applied
|
|
|
|
* @param dataArg Data to apply to the template
|
|
|
|
* @returns Processed body
|
|
|
|
*/
|
|
|
|
public getBody(dataArg: any = {}): string {
|
2019-10-26 23:45:35 +02:00
|
|
|
const smartmustache = new plugins.smartmustache.SmartMustache(this.options.body);
|
|
|
|
return smartmustache.applyData(dataArg);
|
|
|
|
}
|
2025-05-07 13:18:41 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the processed HTML body with template variables applied
|
|
|
|
* @param dataArg Data to apply to the template
|
|
|
|
* @returns Processed HTML body or null if not set
|
|
|
|
*/
|
|
|
|
public getHtmlBody(dataArg: any = {}): string | null {
|
|
|
|
if (!this.options.htmlBody) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
const smartmustache = new plugins.smartmustache.SmartMustache(this.options.htmlBody);
|
|
|
|
return smartmustache.applyData(dataArg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds a recipient to the email
|
|
|
|
* @param email Email address to add
|
|
|
|
* @param type Type of recipient (to, cc, bcc)
|
|
|
|
*/
|
|
|
|
public addRecipient(email: EmailAddress, type: 'to' | 'cc' | 'bcc' = 'to'): void {
|
|
|
|
if (!this.options[type]) {
|
|
|
|
this.options[type] = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
this.options[type]!.push(email);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds multiple recipients to the email
|
|
|
|
* @param emails Email addresses to add
|
|
|
|
* @param type Type of recipients (to, cc, bcc)
|
|
|
|
*/
|
|
|
|
public addRecipients(emails: EmailAddressList, type: 'to' | 'cc' | 'bcc' = 'to'): void {
|
|
|
|
if (!this.options[type]) {
|
|
|
|
this.options[type] = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
this.options[type] = [...this.options[type]!, ...emails];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the reply-to address
|
|
|
|
* @param email Email address for reply-to
|
|
|
|
*/
|
|
|
|
public setReplyTo(email: EmailAddress): void {
|
|
|
|
this.options.replyTo = email;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the priority of the email
|
|
|
|
* @param priority Priority level
|
|
|
|
*/
|
|
|
|
public setPriority(priority: 'high' | 'normal' | 'low'): void {
|
|
|
|
this.options.priority = priority;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds a custom header to the email
|
|
|
|
* @param name Header name
|
|
|
|
* @param value Header value
|
|
|
|
*/
|
|
|
|
public addHeader(name: string, value: string): void {
|
|
|
|
if (!this.options.headers) {
|
|
|
|
this.options.headers = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
this.options.headers[name] = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Validates all email addresses in the email
|
|
|
|
* @returns Promise resolving to validation results
|
|
|
|
*/
|
|
|
|
public async validateAllEmails(): Promise<Record<string, boolean>> {
|
|
|
|
const results: Record<string, boolean> = {};
|
|
|
|
const emails: EmailAddress[] = [];
|
|
|
|
|
|
|
|
// Collect all emails
|
|
|
|
if (this.options.from) emails.push(this.options.from);
|
|
|
|
if (this.options.replyTo) emails.push(this.options.replyTo);
|
|
|
|
if (this.options.to) emails.push(...this.options.to);
|
|
|
|
if (this.options.cc) emails.push(...this.options.cc);
|
|
|
|
if (this.options.bcc) emails.push(...this.options.bcc);
|
|
|
|
|
|
|
|
// Validate each email
|
|
|
|
for (const email of emails) {
|
|
|
|
const validationResult = await this.emailValidator.validate(email);
|
|
|
|
results[email] = validationResult.valid;
|
|
|
|
}
|
|
|
|
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Converts the email to a MIME format object for sending
|
|
|
|
* @param dataArg Data to apply to templates
|
|
|
|
* @returns MIME format object
|
|
|
|
*/
|
|
|
|
public async toMimeFormat(dataArg: any = {}): Promise<any> {
|
|
|
|
// Validate emails if option is enabled
|
|
|
|
if (this.options.validateEmails) {
|
|
|
|
const validationResults = await this.validateAllEmails();
|
|
|
|
const invalidEmails = Object.entries(validationResults)
|
|
|
|
.filter(([_, valid]) => !valid)
|
|
|
|
.map(([email]) => email);
|
|
|
|
|
|
|
|
if (invalidEmails.length > 0) {
|
|
|
|
throw new Error(`Invalid email addresses: ${invalidEmails.join(', ')}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build MIME parts
|
|
|
|
const subject = this.getSubject(dataArg);
|
|
|
|
const textBody = this.getBody(dataArg);
|
|
|
|
const htmlBody = this.getHtmlBody(dataArg);
|
|
|
|
|
|
|
|
// Convert attachments to MIME format
|
|
|
|
const mimeAttachments: IMimeAttachment[] = await Promise.all(
|
|
|
|
this.attachments.map(async (file) => {
|
|
|
|
return {
|
|
|
|
filename: file.path.split('/').pop()!,
|
|
|
|
content: file.contentBuffer,
|
|
|
|
contentType: 'application/octet-stream'
|
|
|
|
};
|
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
// Build email format object
|
|
|
|
const mimeObj: any = {
|
|
|
|
from: this.options.from,
|
|
|
|
subject,
|
|
|
|
text: textBody,
|
|
|
|
attachments: mimeAttachments,
|
|
|
|
headers: { ...this.options.headers }
|
|
|
|
};
|
|
|
|
|
|
|
|
// Add optional fields
|
|
|
|
if (this.options.to && this.options.to.length > 0) {
|
|
|
|
mimeObj.to = this.options.to;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.options.cc && this.options.cc.length > 0) {
|
|
|
|
mimeObj.cc = this.options.cc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.options.bcc && this.options.bcc.length > 0) {
|
|
|
|
mimeObj.bcc = this.options.bcc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.options.replyTo) {
|
|
|
|
mimeObj.replyTo = this.options.replyTo;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (htmlBody) {
|
|
|
|
mimeObj.html = htmlBody;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add priority headers if specified
|
|
|
|
if (this.options.priority === 'high') {
|
|
|
|
mimeObj.headers['X-Priority'] = '1';
|
|
|
|
mimeObj.headers['X-MSMail-Priority'] = 'High';
|
|
|
|
mimeObj.headers['Importance'] = 'High';
|
|
|
|
} else if (this.options.priority === 'low') {
|
|
|
|
mimeObj.headers['X-Priority'] = '5';
|
|
|
|
mimeObj.headers['X-MSMail-Priority'] = 'Low';
|
|
|
|
mimeObj.headers['Importance'] = 'Low';
|
|
|
|
}
|
|
|
|
|
|
|
|
return mimeObj;
|
|
|
|
}
|
2018-09-29 23:57:25 +02:00
|
|
|
}
|