Last active
July 13, 2020 15:04
-
-
Save njbotkin/dbaf83dddabfca8be416b9eba663bb13 to your computer and use it in GitHub Desktop.
Simple email relay built with node.js and mailgun
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const addrs = require('email-addresses') | |
const RFC2821 = require('address-rfc2821') | |
const formBody = require('body/form') | |
const multiparty = require('multiparty') | |
const pify = require('pify') | |
const send = require('@polka/send-type') | |
/* global apiKey domain read send_email */ | |
const mailgun = require('mailgun-js')({ | |
apiKey, | |
domain | |
}) | |
const messages = mailgun.messages() | |
// https://en.wikipedia.org/wiki/Sender_Rewriting_Scheme | |
// https://github.com/jichu4n/srslib/blob/master/srslib.py - there are no JS SRS libraries that work. | |
function srs_reverse(address) { | |
// SRS1 is not supported on account of it being so rare. Low priority TODO | |
const match = /SRS0[-=+]([^=]+)=([^=]+)=([^=]+)=(.+)/.exec(address) | |
if(!match) return address | |
const [,,, domain, forwarder] = match | |
let { user } = new RFC2821.Address(forwarder) | |
return `${user}@${domain}` | |
} | |
function mailgun_webhook(req, res) { | |
if(req.headers['content-type'] == 'application/x-www-form-urlencoded') { | |
req.body = await pify(formBody)(req, res) | |
} else { | |
let form = new multiparty.Form() | |
// ignore files | |
let { err, fields } = await new Promise(r => form.parse(req, (err, fields) => r({ err, fields }))) | |
if(err) return send(res, 500, err) | |
for(let key in fields) { | |
if(fields[key].length == 1) { | |
fields[key] = fields[key][0] | |
} | |
} | |
req.body = fields | |
} | |
if(!mailgun.validateWebhook(req.body.timestamp, req.body.token, req.body.signature)) { | |
return send(res, 403) | |
} | |
let sender = srs_reverse(req.body.sender) | |
let recipient_relay | |
try { | |
// fetch user's true email from the database | |
recipient_relay = await read('relay', req.body.recipient.replace(`@${domain}`, '')) | |
} catch(e) { | |
// just a convenience function | |
await send_email({ | |
to: sender, | |
template: 'no_such_email' | |
}) | |
return | |
} | |
// ensure that the sender's email has a matching system address | |
let sender_relay = await ensure_relay(sender) | |
await messages.send({ | |
from: `${addrs.parseOneAddress(req.body.from).name} <${sender_relay._key}@${domain}>`, | |
to: recipient_relay.email, | |
subject: req.body.subject, | |
html: req.body['body-html'], | |
text: req.body['body-plain'] | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment