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.

Revisions

  1. voidnerd created this gist Oct 4, 2022.
    242 changes: 242 additions & 0 deletions mail.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,242 @@
    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)
    }
    }