sendmail/sendgrid.js

/*
 *  Author: Vlad Seryakov vseryakov@gmail.com
 *  backendjs 2018
 */

const app = require(__dirname + '/../app');
const lib = require(__dirname + '/../lib');

module.exports = SendGridTransport;

/**
 * SendGrid transport for nodemailer
 *
 * Send options can include `key` property or env variable SENDGRID_API_KEY will be used.
 */

function SendGridTransport(options)
{
    this.options = options || {};
    this.name = 'SendGrid';
    this.version = "1.0.0";
}
 
SendGridTransport.prototype.send = function(mail, callback)
{
    mail.normalize((err, data) => {
        if (err) return lib.tryCall(callback, err);
 
        var req = {
            from: { email: data.from?.address, name: data.from?.name },
            subject: data.subject,
            personalizations: [{}],
        };
        if (lib.isArray(data.to)) {
            const to = data.to.filter((x) => (x.address)).map((x) => ({ email: x.address, name: x.name }));
            if (to.length) req.personalizations[0].to = to;
        }
        if (lib.isArray(data.cc)) {
            const cc = data.cc.filter((x) => (x.address)).map((x) => ({ email: x.address, name: x.name }));
            if (cc.length) req.personalizations[0].cc = cc;
        }
        if (lib.isArray(data.bcc)) {
            const bcc = data.bcc.filter((x) => (x.address)).map((x) => ({ email: x.address, name: x.name }));
            if (bcc.length) req.personalizations[0].bcc = bcc;
        }
 
        if (lib.isArray(data.replyTo)) {
            req.reply_to_list = data.replyTo.filter((x) => (x.address)).map((x) => ({ email: x.address, name: x.name }));
        }
        if (data.text) {
            if (!req.content) req.content = [];
            req.content.push({ type: "text/plain", value: data.text });
        }
        if (data.html) {
            if (!req.content) req.content = [];
            req.content.push({ type: "text/html", value: data.html });
        }
        if (lib.isArray(data.attachments)) {
            for (const a of data.attachments) {
                if (!a.content) continue;
                if (a.encoding != "base64") a.content = Buffer.from(a.content).toString('base64');
                if (!req.attachments) req.attachments = [];
                req.attachments.push({ type: a.contentType, content: a.content, filename: a.filename, disposition: a.disposition, content_id: a.cid });
            }
        }
        if (data.sendgrid) {
            for (const p of ["template_id", "categories", "headers", "custom_args", "batch_id", "asm", "ip_pool_name", "mail_settings", "tracking_settings"]) {
                if (data.sendgrid[p]) req[p] = data.sendgrid[p];
            }
        }
        if (this.options.dryrun) {
            return lib.tryCall(callback, null, { data, req });
        }
 
        app.fetch("https://api.sendgrid.com/v3/mail/send",
            { headers: {
                Authorization: `Bearer ${this.options.key || process.env.SENDGRID_API_KEY}`,
                "content-type": "application/json",
            },
            method: "POST",
            postdata: req,
            retryOnError: function() { return this.status == 429 || this.status >= 500 },
            retryCount: this.options.retryCount || 3,
            retryTimeout: this.options.retryTimeout || 5000,
        }, (err, rc) => {
           if (!err && rc.status >= 400) {
               err = { status: rc.status, message: rc.obj?.errors?.length && rc.obj.errors[0].message || rc.data };
           }
           lib.tryCall(callback, err, { messageId: data.messageId });
       });
    });
}