Skip to content

Instantly share code, notes, and snippets.

@njbotkin
Last active July 13, 2020 15:04
Show Gist options
  • Save njbotkin/dbaf83dddabfca8be416b9eba663bb13 to your computer and use it in GitHub Desktop.
Save njbotkin/dbaf83dddabfca8be416b9eba663bb13 to your computer and use it in GitHub Desktop.
Simple email relay built with node.js and mailgun
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