postmailer

HTTP POST -> SMTP proxy, as Express middleware

Usage no npm install needed!

<script type="module">
  import postmailer from 'https://cdn.skypack.dev/postmailer';
</script>

README

postmailer

An Express middleware implementing the "HTTP POST mail" principle as an HTTP-to-SMTP proxy.

The principle is that all POST requests to a user's contact URL (e.g. /~username) should be received as messages by that user. This module is a proxy to make this work using existing email infrastructure, by resending received messages using SMTP.

The module is an Express middleware, and accepts raw resources (e.g. text, HTML, images or other documents) or url-encoded forms. Multipart messages/forms are not currently supported - any external resources should be referenced by URL or sent as separate messages.

Usage

var express = require('express'), app = express();
var postmailer = require('postmailer');

app.use(postmailer({
    mapping: {
        url: '/contact',
        email: 'someone@example.com'
    }
}));

Unless Content-Disposition: is set to attachment in the POST request, plain-text and HTML are unchanged, and images are displayed inline in the email. Otherwise, the document is included as an attachment.

Options

  • options.mapping - defines the mapping between URLs and email addresses. See below for more detail.
  • options.domain - a protocol+domain (or list of such) to be used as a prefix (e.g. https://example.com) for relative URLs
  • options.transport - optional but recommended: transport for nodemailer to use (see below), defaults to direct mailer
  • options.maxInline - optional: maximum size (in bytes) of inline content, where larger documents are sent as attachments. Defaults to 500*1024 (500kb)
  • options.form - optional: whether a friendly page should be displayed on GET requests. Defaults to "ajax"
    • false - disable
    • reserved string - corresponding to built-in templates with various levels of interactivity: "plain" (no JavaScript), "ajax" (POST client) and "rich" (includes WebSocket chat if available). The "ajax" and "rich" clients have a cut-down UI when embedded in an IFrame
    • string - template file, with two parameters {{username}} and {{url}}
    • function - template function, with two arguments username and url
  • options.minifyForm - optional: whether the web form should be minified (HTML/JS/CSS) using the html-minifier package. Defaults to false

Transport

You can provide a custom transport for sending mail. This is passed to nodemailer.createTransport(), and can therefore be either a transport (e.g. from one of the nodemailer-*-transport modules) or an object with parameters for the SMTP transport (default):

options.transport = {
    host: 'smtp.mailer.example.com',
    port: 25,
    auth: {
        user: 'username',
        pass: 'password'
    }
}

If no details/transport is supplied, this module defaults to the direct mailer. Direct SMTP connections (particularly from non-static IPs) are rejected by some mail services (e.g. Gmail) - if you want to send to these services, consider using a mailer such as Mandrill or Mailgun.

Mapping

The options.mapping property can either be a mapping object, or an array of mapping objects.

A mapping object (used in options.mapping) has the following properties:

  • .url - a URI Template (or array of templates) for the URLs
  • .email - a URI Template for the email address
  • .acceptPost - whether this mapping actually accepts POSTs (defaults to true).

To convert between URLs and email addresses, the template for one is deconstructed, and the same parameters applied to the other. Order matters: when converting to URL(s), preference is given to mappings listed first, and when converting to email address, only the first is used.

For "local" URLs, it's simplest to use relative URLs with absolute paths (e.g. /contact). These will be converted to/from absolute URLs when necessary, using the options.domain parameter.

.acceptPost

If .acceptPost is set to false for a mapping, the mapping will be used to replace URLs with email addresses when forwarding messages via SMTP (e.g. translating From/CC/BCC headers) but will not accept POST requests.

The idea is that you should have a "catch-all" mapping at the end of your mapping list:

{
    url: "{+url}",
    email: "post+{url}@mydomain.com",
    allowPost: false
}

If you have a suitably configured SMTP->HTTP proxy (see below) then this allows replying to URLs using your email client (by translating reply emails from known addresses back into POSTs).

SMTP -> HTTP proxy

This module is only half of the puzzle: if you receive a message containing a URL in the From header and it is converted to an email, you should be able to reply to the email and the message is converted back to POSTs.

You would then need an SMTP-to-HTTP gateway that used the same mapping, and translated replies/etc. back into POST requests (the allowPost: false setting means that this proxy would only work if a known user was involved.).

This is currently under development.

Default web form

The default web form (provided on GET requests to a valid endpoint) contains a short description of what the endpoint is, some examples (with curl) and an AJAX interface to send messages directly from the browser.

It also detects (using JavaScript) whether it's embedded in an IFrame, and presents a cut-down UI with just two fields (email/message) and a "send" button, suitable for embedding as a contact form on other websites such as blogs.

TTD

  • bounce messages (both directions)