Skip to content

Instantly share code, notes, and snippets.

@voidnerd
Created October 4, 2022 14:28
Show Gist options
  • Save voidnerd/8818b25e05efa0255e8f5d89beac1200 to your computer and use it in GitHub Desktop.
Save voidnerd/8818b25e05efa0255e8f5d89beac1200 to your computer and use it in GitHub Desktop.
Typescript Mail Service (SMTP, Mailgun)
import nodemailer from 'nodemailer'
import ViewService from './view' // https://gist.github.com/ndiecodes/6e964e716a2b5b2aa22438912da2b3f3
import Mailgun from 'mailgun.js'
import FormData from 'form-data'
import { MailgunMessageData } from 'mailgun.js/interfaces/Messages'
interface LooseObject {
[key: string]: any
}
interface Attachment {
filename: string
content?: Buffer
}
interface _Attachment {
filename: string
content?: Buffer
data?: Buffer
}
/** Start Mail Helper
* MailDrivers: smtp, mailgun
*/
export default class Mail {
public SUBJECT!: string
public FROM: string | null = process.env.MAIL_FROM_ADDRESS || null
public DATA: LooseObject | null = null
public TO: string | undefined = ''
public CC: string | undefined = undefined
public BCC: string | undefined = undefined
public ATTACHMENTS: _Attachment[] = []
public TEXT: string | undefined = undefined
public TEMPLATE!: string
/**
*
* @param {String} subject
* @returns {Mail}
*/
subject(subject: string) {
this.SUBJECT = subject
return this
}
/**
*
* @param {String|Array} from
* @returns {Mail}
*/
from(from: string) {
this.FROM = from
return this
}
/**
*
* @param {String|Array} to
* @returns {Mail}
*/
to(to: string) {
this.TO = to
return this
}
/**
*
* @param {String|Array} cc
* @returns {Mail}
*/
cc(cc: string) {
this.CC = cc
return this
}
/**
*
* @param {String|Array} bcc
* @returns {Mail}
*/
bcc(bcc: string) {
this.BCC = bcc
return this
}
/**
* Attach Single File using Buffer
* @param {Array} attachments
* @returns {Mail}
*/
attachFile(attachment: Attachment) {
if (process.env.MAIL_DRIVER === 'mailgun') {
this._attachMailgunFile(attachment)
} else {
this.ATTACHMENTS.push(attachment)
}
return this
}
/**
* Format attachment object for mailgun API
* @param attachment
* @returns
*/
private _attachMailgunFile(attachment: Attachment) {
return this.ATTACHMENTS.push({
filename: attachment.filename,
data: attachment.content,
})
}
/**
* Attach multiple files
* @param attachments
* @returns
*/
attachFiles(attachments: Attachment[]) {
if (process.env.MAIL_DRIVER === 'mailgun') {
this._attachMailgunFiles(attachments)
} else {
this.ATTACHMENTS = attachments
}
return this
}
/**
* Format attachment array for mailgun API
* @param attachment
* @returns
*/
private _attachMailgunFiles(attachments: Attachment[]): _Attachment[] {
this.ATTACHMENTS = attachments.map((attachment) => {
return {
filename: attachment.filename,
data: attachment.content,
}
})
return this.ATTACHMENTS
}
/**
*
* @param {String|Array} template
* @returns {Mail}
*/
template(template: string) {
this.TEMPLATE = template
return this
}
/**
*
* @param {String} text
* @returns {Mail}
*/
text(text: string | undefined) {
this.TEXT = text
return this
}
/**
* This object(data) will be passed the email view/renderer
* @param {Object} data
* @returns {Mail}
*/
data(data: LooseObject) {
this.DATA = data
return this
}
/** Validate mail driver message params */
validate() {
if (!this.SUBJECT) {
throw new Error('Subject Required!')
}
if (!this.FROM) {
throw new Error('From Address Required!')
}
if (!this.TO) {
throw new Error('TO Address Required!')
}
if (!this.TEMPLATE) {
throw new Error('Path to Template File Required!')
}
}
private async mailThroughSMTP(messageParams: MailgunMessageData) {
const smtp = nodemailer.createTransport({
host: process.env.MAIL_HOST,
port: Number(process.env.MAIL_PORT),
secure: false,
auth: {
user: process.env.MAIL_USERNAME,
pass: process.env.MAIL_PASSWORD,
},
})
return smtp.sendMail(messageParams)
}
private async mailThroughMailgun(messageParams: MailgunMessageData) {
const mailgun = new Mailgun(FormData)
const mg = mailgun.client({
username: 'api',
key: process.env.MAILGUN_API_KEY || '',
})
return mg.messages.create(
process.env.MAILGUN_DOMAIN || '',
messageParams
)
}
/**
*
* @returns {Promise}
*/
async send() {
this.validate()
const html = await new ViewService(this.TEMPLATE, this.DATA).getHtml()
const mailOptions: MailgunMessageData = {
from: `"${process.env.MAIL_FROM_NAME}" <${this.FROM}>`,
to: this.TO,
subject: this.SUBJECT,
cc: this.CC,
bcc: this.BCC,
attachments: this.ATTACHMENTS,
text: this.TEXT,
html: html,
}
if (process.env.MAIL_DRIVER === 'mailgun') {
return this.mailThroughMailgun(mailOptions)
}
return this.mailThroughSMTP(mailOptions)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment