-
-
Notifications
You must be signed in to change notification settings - Fork 175
Add SMTP send_mail SQL function with configuration and lettre integration
#1346
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
72e695e
77797c9
9659fed
00b969f
214a986
1839738
93afed9
e8e590d
e71fe45
71d0837
889277f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| INSERT INTO sqlpage_functions ( | ||
| "name", | ||
| "introduced_in_version", | ||
| "icon", | ||
| "description_md" | ||
| ) | ||
| VALUES ( | ||
| 'send_mail', | ||
| '0.45.0', | ||
| 'mail', | ||
| 'Sends an email using the SMTP server configured with `SMTP_HOST`. | ||
|
|
||
| `SMTP_HOST` contains the relay host name. Set `SMTP_PORT` when the relay does not use the default for the selected encryption mode: 587 for `starttls`, 465 for `tls`, or 25 for `none`. | ||
|
|
||
| `SMTP_TLS_MODE` defaults to `starttls`, which requires a STARTTLS upgrade before sending email or credentials. Set it to `tls` for implicit TLS, commonly used on port 465. Plaintext mode (`none`) is allowed only without credentials and should be used only for trusted local SMTP servers. | ||
|
|
||
| If your SMTP server requires authentication, configure `SMTP_USERNAME` and `SMTP_PASSWORD` as well. | ||
|
|
||
| The function accepts a single JSON object argument. The required properties are: | ||
|
|
||
| - `to`: email address to send to, optionally including a display name such as `"Jane Doe <jane@example.com>"`. | ||
| - `subject`: email subject. | ||
| - `body`: plain text email body. | ||
|
|
||
| Optional properties: | ||
|
|
||
| - `from`: sender address. It may be omitted when `SMTP_FROM` configures a default sender. | ||
| - `reply_to`: reply-to address. | ||
|
|
||
| The function returns `NULL` after the SMTP relay accepts the message and raises an error if the message cannot be sent. The argument is required; passing `NULL` is an error. | ||
|
|
||
| ### Example | ||
|
|
||
| ```sql | ||
| set message = json_object( | ||
| ''to'', ''admin@example.com'', | ||
| ''from'', ''contact@example.com'', | ||
| ''subject'', ''New contact form message'', | ||
| ''body'', ''Hello from SQLPage!'' | ||
| ); | ||
| select sqlpage.send_mail($message); | ||
| ``` | ||
|
|
||
| ### Contact form example | ||
|
|
||
| ```sql | ||
| select ''form'' as component, ''post'' as method; | ||
| select ''email'' as name, ''email'' as type, true as required; | ||
| select ''message'' as name, ''textarea'' as type, true as required; | ||
|
|
||
| set mail = json_object( | ||
| ''to'', ''admin@example.com'', | ||
| ''reply_to'', $email, | ||
| ''subject'', ''Website contact form'', | ||
| ''body'', $message | ||
| ); | ||
| select sqlpage.send_mail($mail) | ||
| where $message is not null; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In this contact-form example, the unaliased Useful? React with 👍 / 👎. |
||
| ``` | ||
| ' | ||
| ); | ||
|
|
||
| INSERT INTO sqlpage_function_parameters ( | ||
| "function", | ||
| "index", | ||
| "name", | ||
| "description_md", | ||
| "type" | ||
| ) | ||
| VALUES ( | ||
| 'send_mail', | ||
| 1, | ||
| 'message', | ||
| 'A JSON object containing the email to send. Required properties are `to`, `subject`, and `body`. Optional properties are `from` (required unless `SMTP_FROM` is configured) and `reply_to`. Unknown properties are rejected to catch misspellings.', | ||
| 'JSON' | ||
| ); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,76 +1,31 @@ | ||
| # Sending Emails with SQLPage | ||
|
|
||
| SQLPage lets you interact with any email service through their API, | ||
| using the [`sqlpage.fetch` function](https://sql-page.com/functions.sql?function=fetch). | ||
| This example sends plain-text email with [`sqlpage.send_mail`](https://sql-page.com/functions.sql?function=send_mail). The included Docker Compose setup uses [Mailpit](https://mailpit.axllent.org/) as a local SMTP server, so no email leaves your computer. | ||
|
|
||
| ## Why Use an Email Service? | ||
| Run the example: | ||
|
|
||
| Sending emails directly from your server can be challenging: | ||
| - Many ISPs block direct email sending to prevent spam | ||
| - Email deliverability requires proper setup of SPF, DKIM, and DMARC records | ||
| - Managing bounce handling and spam complaints is complex | ||
| - Direct sending can impact your server's IP reputation | ||
|
|
||
| Email services solve these problems by providing reliable APIs for sending emails while handling deliverability, tracking, and compliance. | ||
| ```sh | ||
| docker compose up | ||
| ``` | ||
|
|
||
| ## Popular Email Services | ||
| Open http://localhost:8080 to send an email, then inspect it in the Mailpit inbox at http://localhost:8025. | ||
|
|
||
| - [Mailgun](https://www.mailgun.com/) - Developer-friendly, great for transactional emails | ||
| - [SendGrid](https://sendgrid.com/) - Powerful features, owned by Twilio | ||
| - [Amazon SES](https://aws.amazon.com/ses/) - Cost-effective for high volume | ||
| - [Postmark](https://postmarkapp.com/) - Focused on transactional email delivery | ||
| - [SMTP2GO](https://www.smtp2go.com/) - Simple SMTP service with API options | ||
| The SMTP server is configured in [`docker-compose.yml`](./docker-compose.yml) with `SMTP_HOST=mailpit`, `SMTP_PORT=1025`, and `SMTP_TLS_MODE=none`. Plaintext mode is intended only for trusted local SMTP servers such as Mailpit. | ||
|
|
||
| ## Example: Sending Emails with Mailgun | ||
| For a remote SMTP relay, keep the default `SMTP_TLS_MODE=starttls`, or set it to `tls` when the relay requires implicit TLS. Configure `SMTP_USERNAME` and `SMTP_PASSWORD` when authentication is required; SQLPage rejects credentials in plaintext mode. | ||
|
|
||
| Here's a complete example using Mailgun's API to send emails through SQLPage: | ||
| The form handler sends the message with a single function call: | ||
|
|
||
| ### [`email.sql`](./email.sql) | ||
| ```sql | ||
| -- Configure the email request | ||
| set email_request = json_object( | ||
| 'url', 'https://api.mailgun.net/v3/' || sqlpage.environment_variable('MAILGUN_DOMAIN') || '/messages', | ||
| 'method', 'POST', | ||
| 'headers', json_object( | ||
| 'Content-Type', 'application/x-www-form-urlencoded', | ||
| 'Authorization', 'Basic ' || encode(('api:' || sqlpage.environment_variable('MAILGUN_API_KEY'))::bytea, 'base64') | ||
| ), | ||
| 'body', | ||
| 'from=Your Name <noreply@' || sqlpage.environment_variable('MAILGUN_DOMAIN') || '>' | ||
| || '&to=' || $to_email | ||
| || '&subject=' || $subject | ||
| || '&text=' || $message_text | ||
| || '&html=' || $message_html | ||
| set message = json_object( | ||
| 'to', :recipient, | ||
| 'from', :sender, | ||
| 'subject', :subject, | ||
| 'body', :body | ||
| ); | ||
|
|
||
| -- Send the email using sqlpage.fetch | ||
| set email_response = sqlpage.fetch($email_request); | ||
|
|
||
| -- Handle the response | ||
| select | ||
| 'alert' as component, | ||
| case | ||
| when $email_response->>'id' is not null then 'Email sent successfully' | ||
| else 'Failed to send email: ' || ($email_response->>'message') | ||
| end as title; | ||
| set _ = sqlpage.send_mail($message); | ||
| ``` | ||
|
|
||
| ### Setup Instructions | ||
|
|
||
| 1. Sign up for a [Mailgun account](https://signup.mailgun.com/new/signup) | ||
| 2. Verify your domain or use the sandbox domain for testing | ||
| 3. Get your API key from the Mailgun dashboard | ||
| 4. Set these environment variables in your SQLPage configuration: | ||
| ``` | ||
| MAILGUN_API_KEY=your-api-key-here | ||
| MAILGUN_DOMAIN=your-domain.com | ||
| ``` | ||
|
|
||
| ## Best Practices | ||
| `sqlpage.send_mail` returns `NULL` after the SMTP relay accepts the message. It raises an error when the relay rejects the message or cannot be reached, so statements after the call run only on success. | ||
|
|
||
| - If you share your code with others, it should not contain sensitive data like API keys | ||
| - Instead, use environment variables with [`sqlpage.environment_variable`](https://sql-page.com/functions.sql?function=environment_variable) | ||
| - Implement proper error handling | ||
| - Consider rate limiting for bulk sending | ||
| - Include unsubscribe links when sending marketing emails | ||
| - Follow email regulations (GDPR, CAN-SPAM Act) | ||
| Do not expose an unrestricted form like this publicly. In production, authenticate users, restrict recipients, validate input, and add rate limiting to prevent abuse. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For this POST form, the submitted fields need to be read with
:emailand:message; in the current evaluator$email/$messageread SET/URL variables instead. On a normal form submission without same-named query parameters, the mail body/reply-to and the laterWHERE $messageguard are NULL, so the documented contact form never sends an email.Useful? React with 👍 / 👎.