JS-Mailer is a simple webservice, that allows JavaScript-based websites to easily send form data, by providing a simple
API that can be accessed via JavaScript Fetch()
or XMLHttpRequest
.
- Single-binary webservice
- Multi-form support
- Multiple recipients per form
- Only display form-fields that are configured in for the form in the resulting mail
- Check for required form fields
- Anti-SPAM functionality via built-in, auto-expiring and single-use security token feature
- Anti-SPAM functionality via honeypot fields
- Limit form access to specific domains
- Per-form mail server configuration
- hCaptcha support
- reCaptcha v2 support
- Turnstile support
- Form field type validation (text, email, number, bool)
- Confirmation mail to poster
- Custom Reply-To header based on sending mail address
- Form body templates (possibly HTML)
There is a ready-to-use Docker image hosted on Github.
- Download the image:
$ docker pull ghcr.io/wneessen/js-mailer:main
- Get your config files in place
- Run the image:
$ docker run -p 8765:8765 -v /etc/js-mailer:/etc/js-mailer ghcr.io/wneessen/js-mailer:main
The server configuration, by default, is searched for in /etc/js-mailer/js-mailer.json
. The JSON syntax is very basic
and comes with sane defaults.
{
"forms": {
"path": "/etc/js-mailer/forms",
"maxlength": "10M"
},
"loglevel": "debug",
"server": {
"bind_addr": "0.0.0.0",
"port": 8765,
"timeout": "15s"
}
}
server (type: struct)
: The struct for the web api configurationbind_addr (type: string)
: The IP address to bind the web service toport (type: uint)
: The port for the webservice to listen ontimeout (type: time.Duration)
: The duration a request can take at max.
forms (type: struct)
: The struct for the forms configurationpath (type: string)
: The path in whichjs-mailer
will look for form configuration JSON filesmaxlength (type: string)
: Maximum size of the request body (default: "10M")
loglevel (type: string)
: The log level for the web service
Each form has its own configuration file. The configuration is searched in the forms path and are named by its id. Again the JSON syntax of the form configuration is very simple, yet flexible.
{
"id": "test_form",
"secret": "SuperSecretsString",
"recipients": [
"who@cares.net"
],
"sender": "website@example.com",
"domains": [
"www.example.com",
"example.com"
],
"content": {
"subject": "New message through the www.example.com contact form",
"fields": [
"name",
"email",
"message"
]
},
"replyto": {
"field": "email"
},
"confirmation": {
"enabled": true,
"rcpt_field": "email",
"subject": "Thank you for your message",
"content": "We have received your message via www.example.com and will tough base with you, shortly."
},
"validation": {
"hcaptcha": {
"enabled": true,
"secret_key": "0x01234567890"
},
"recaptcha": {
"enabled": true,
"secret_key": "0x01234567890"
},
"turnstile": {
"enabled": true,
"secret_key": "0x01234567890"
},
"honeypot": "street",
"fields": [
{
"name": "name",
"type": "text",
"required": true
},
{
"name": "mail_addr",
"type": "email",
"required": true
},
{
"name": "terms_checked",
"type": "matchval",
"value": "on",
"required": true
}
]
},
"server": {
"host": "mail.example.com",
"port": 25,
"username": "website@example.com",
"password": "verySecurePassword",
"timeout": "5s",
"force_tls": true
}
}
id (type: string)
: The id of the form (will be looked for in theformid
parameter of the token request)secret (type: string)
: Secret for the form. This will be used for the token generationrecipients (type: []string)
: List of recipients, that should receive the mails with the submitted form datadomains (type: []string)
: List of origin domains, that are allowed to use this formcontent (type: struct)
: The struct for the mail content configurationsubject (type: string)
: Subject for the mail notification of the form submissionfields (type: []string)
: List of field names that should show up in the mail notification
confirmation (type: struct)
: The struct for the mail confirmail mail configurationenabled (type: boolean)
: If true, the confirmation mail will be sentrcpt_field (type: string)
: Name of the form field holding the confirmation mail recipientsubject (type: string)
: Subject for the confirmation mailcontent (type: string)
: Content for the confirmation mailfields (type: []string)
: List of field names that should show up in the mail notification
validation (type: struct)
: The struct for the form validation configurationhcaptcha (type: struct)
: The struct for the forms hCaptcha configurationenabled (type: bool)
: Enable hCaptcha challenge-response validationsecret_key (type: string)
: Your hCaptcha secret key
recaptcha (type: struct)
: The struct for the forms reCaptcha configurationenabled (type: bool)
: Enable reCaptcha challenge-response validationsecret_key (type: string)
: Your reCaptcha secret key
turnstile (type: struct)
: The struct for the forms Turnstile configurationenabled (type: bool)
: Enable Turnstile challenge-response validationsecret_key (type: string)
: Your Turnstile secret key
honeypot (type: string)
: Name of the honeypot field, that is expected to be empty (Anti-SPAM)fields (type: []struct)
: Array of single field validation configurationsname (type: string)
: Field validation identifiertype (type: string)
: Type of validation to run on field (text, email, nummber, bool)required (type: boolean)
: If set to true, the field is required
replyto (type: struct)
: The struct for the reply to configurationrcpt_field (type: string)
: Name of the form field holding the reply-to mail sender address
server (type: struct)
: The struct for the forms mail server configurationhost (type: string)
: Hostname of the sending mail serverport (type: uint32)
: Port to connect to on the sending mail serverusername (type: string)
: Username for the mail server authenticationpassword (type: string)
: Password for the mail server authenticationtimeout (type: duration)
: Timeout duration for the mail server connectionforce_tls (type: boolean)
: If set to true, the mail server connection will require mandatory TLS
JS-Mailer
follows a two-step workflow. First your JavaScript requests a token from the API using the /api/v1/token
endpoint. If the request is valid and website is authorized to request a token, the API will respond with a
TokenResponseJson. This holds some data, which needs to be included into your form as hidden inputs. It will also
provide a submission URL endpoint /api/v1/send/<formid>/<token>
that can be used as action in your form. Once the form
is submitted, the API will then validate that all submitted data is correct and submit the form data to the configured
recipients.
The API basically responds with two different types of JSON objects. A success
response or an error
response.
The succss response JSON struct is very simple:
{
"status_code": 200,
"status": "Ok",
"data": {}
}
status_code (type: uint32)
: The HTTP status code of the success responsestatus (type: string)
: The HTTP status string of the success responsedata (type: object)
: An object with abritrary data, based on the type of response
The data
object of the success response for a successful token retrieval looks like this:
{
"token": "5b19fca2b154a2681f8d6014c63b5f81bdfdd01036a64f8a835465ab5247feff",
"form_id": "test_form",
"create_time": 1628670201,
"expire_time": 1628670801,
"url": "https://jsmailer.example.com/api/v1/send/test_form/5b19fca2b154a2681f8d6014c63b5f81bdfdd01036a64f8a835465ab5247feff",
"enc_type": "multipart/form-data",
"method": "post"
}
token (type: string)
: The security token of this send requestform_id (type: string)
: The form id of the current form (for reference or automatic inclusion via JS)create_time (type: int64)
: The epoch timestamp when the token was createdexpire_time (type: int64)
: The epoch timestamp when the token will expireurl (type: string)
: API endpoint to set your form action toenc_type (type: string)
: The enctype for your formmethod (type: string)
: The method for your form
The data
object of the success response for a successfully sent message looks like this:
The API response to a send request (/api/v1/send/<formid>/<token>
) looks like this:
{
"form_id": "test_form",
"send_time": 1628670331,
"confirmation_sent": true,
"confirmation_rcpt": "toni.tester@example.com"
}
form_id (type: string)
: The form id of the current form (for reference)send_time (type: int64)
: The epoch timestamp when the message was sentconfirmation_sent (type: boolean)
: Is set to true, if a confirmation was sent successfullyconfirmation_rcpt (type: string)
: The recipient mail address that the confirmation was sent to
The error response JSON struct is also very simple:
{
"status_code": 404,
"status": "Not Found",
"error_message": "Validation failed",
"error_data": "Not a valid send URL"
}
status_code (type: uint32)
: The HTTP status code of the success responsestatus (type: string)
: The HTTP status string of the success responseerror_message (type: string)
: The general error message why this request failederror_data (type: interface{})
: Optional details in addtion to the error message (i. e. missing fields)
A very basic HTML/JS example implementation for the JS-Mailer
system can be found in
the code-example directory