Add SMTP send_mail SQL function with configuration and lettre integration#1346
Add SMTP send_mail SQL function with configuration and lettre integration#1346lovasoa wants to merge 8 commits into
send_mail SQL function with configuration and lettre integration#1346Conversation
### Motivation - Provide a built-in `sqlpage.send_mail(...)` SQL function so pages can send plain-text emails from SQL code. - Allow the SMTP server to be configured via an environment / configuration option so the function can target a deployable SMTP endpoint. ### Description - Added a new function implementation at `src/webserver/database/sqlpage_functions/functions/send_mail.rs` implementing `sqlpage.send_mail(json)` which accepts a JSON object with required `recipient`, `subject`, and `body` and optional `sender` and `reply_to`, sends the message and returns `sent` on success. - Registered the function in the SQLPage function registry by adding `send_mail` to `src/webserver/database/sqlpage_functions/functions.rs`. - Added a configuration option `stmp_host: Option<String>` to `AppConfig` in `src/app_config.rs`, with `parse_stmp_host`/`validate_stmp_host` helpers that accept either `host` or `host:port` and default to port 25 when none is provided; validation is run from `AppConfig::validate`. - Added `lettre` to `Cargo.toml` and updated `Cargo.lock` to enable SMTP sending, and added official-site documentation and a migration at `examples/official-site/sqlpage/migrations/75_send_mail.sql` describing usage and parameters. - Documented the `stmp_host` option in `configuration.md`. ### Testing - Ran `cargo fmt --all`, which completed successfully. - Ran `git diff --check` which reported no immediate style errors. - Attempted `cargo clippy --all-targets --all-features -- -D warnings`, but it was blocked by a toolchain/build issue (a dependency `libsqlite3-sys` build script uses the unstable `cfg_select` feature) and did not complete. - Attempted `cargo test`, but it was similarly blocked by the same `libsqlite3-sys` build-script error and did not complete.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 72e695ec4a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| .body(mail_request.body.into_owned()) | ||
| .context("Unable to build email message")?; | ||
|
|
||
| let mut mailer_builder = AsyncSmtpTransport::<Tokio1Executor>::builder_dangerous(host).port(port); |
There was a problem hiding this comment.
Use TLS-capable SMTP transport for authenticated mail
When stmp_username is configured, this still builds the transport with builder_dangerous, whose defaults are plain SMTP without TLS. In practice, common authenticated relays on ports 587/465 require STARTTLS or implicit TLS, so send_mail will fail against them; any relay that does accept AUTH here receives the username/password in cleartext. Use lettre's TLS/STARTTLS builders or expose an explicit TLS mode before attaching credentials.
Useful? React with 👍 / 👎.
|
|
||
| /// SMTP server host used by the `sqlpage.send_mail` function. | ||
| /// Accepts either a bare host name or `host:port`. Defaults to port 25 when no port is specified. | ||
| pub stmp_host: Option<String>, |
There was a problem hiding this comment.
Rename the SMTP config fields before shipping
The new public configuration keys are spelled stmp_*, which means the environment loader only accepts STMP_HOST/SQLPAGE_STMP_HOST; users setting the conventional SMTP_HOST or SQLPAGE_SMTP_HOST will get the “requires STMP_HOST” error even though they configured SMTP. Because this becomes a documented public API once released, correcting the spelling later would be a breaking config change.
Useful? React with 👍 / 👎.
Rename the misspelled `stmp_*` configuration options to `smtp_*` and add a new `smtp_tls_mode` option (`starttls`, `tls`, `none`) to control encryption when connecting to the SMTP server. Reject credentials in plaintext mode. Change `sqlpage.send_mail` to return its JSON argument unchanged on success and update the example to use a local Mailpit SMTP server via Docker Compose.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 77797c909e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| encoding_rs = "0.8.35" | ||
| odbc-sys = { version = "0", optional = true } | ||
| regex = "1" | ||
| lettre = { version = "0.11", default-features = false, features = ["builder", "smtp-transport", "tokio1", "tokio1-native-tls"] } |
There was a problem hiding this comment.
Use a TLS backend Docker images can build and run
In the Docker build path I checked, scripts/setup-cross-compilation.sh only installs/stages gcc, libgcc and make, and the Dockerfile only copies /tmp/sqlpage-libs/* into the busybox runtime image. Enabling lettre's tokio1-native-tls feature here pulls native-tls/openssl-sys, so the Linux Docker builds now need OpenSSL headers/pkg-config at build time and libssl/libcrypto plus trust roots at runtime; without adding those for each target, the published minimal/duckdb images can fail to build or start. Prefer lettre's rustls backend or add the OpenSSL build/runtime dependencies explicitly.
Useful? React with 👍 / 👎.
Introduce a `max_email_attachment_size` configuration option and support for attaching files via data URLs with CC recipients in the send_mail function. Refactor data URL decoding into a shared utility.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 00b969fe7d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| pub smtp_username: Option<String>, | ||
|
|
||
| /// Optional SMTP password used by the `sqlpage.send_mail` function when `smtp_username` is set. | ||
| pub smtp_password: Option<String>, |
There was a problem hiding this comment.
Redact SMTP passwords from configuration debug logs
When SMTP auth is configured and debug logging is enabled, AppConfig::from_cli logs the full config with log::debug!("Loaded configuration: {config:#?}"); because this new password field is a plain Option<String> under the derived Debug, the relay password will be written verbatim to logs during startup. Please store this with a redacted debug representation or exclude it from the config dump before shipping SMTP credentials support.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 214a986adf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| select sqlpage.send_mail($mail) | ||
| where $message is not null; |
There was a problem hiding this comment.
Alias the guarded send_mail call
In this contact-form example, the unaliased sqlpage.send_mail($mail) is evaluated as a function parameter before the SQL statement runs, so the WHERE clause does not guard the call. On the initial GET/empty-message case the mail JSON contains null fields and the page errors before the predicate can filter it out, and other false predicates would still allow the send attempt; add an alias such as as sent so SQLPage delays the top-level function until after the row passes the WHERE, or otherwise move the condition outside the call.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1839738f67
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ''reply_to'', $email, | ||
| ''subject'', ''Website contact form'', | ||
| ''body'', $message |
There was a problem hiding this comment.
Use POST variables in the contact-form example
For this POST form, the submitted fields need to be read with :email and :message; in the current evaluator $email/$message read SET/URL variables instead. On a normal form submission without same-named query parameters, the mail body/reply-to and the later WHERE $message guard are NULL, so the documented contact form never sends an email.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 93afed919c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| pub smtp_username: Option<String>, | ||
|
|
||
| /// Optional SMTP password used by the `sqlpage.send_mail` function when `smtp_username` is set. | ||
| pub smtp_password: Option<String>, |
There was a problem hiding this comment.
Redact SMTP password from source trace logs
Fresh evidence beyond the prior debug-log fix: AppConfig::from_cli now redacts the final config dump, but load_from_file still traces config.cache before deserialization; when smtp_password is supplied via sqlpage.json or SMTP_PASSWORD, that cache contains the raw value. In deployments that enable trace logging to troubleshoot configuration, the new SMTP credential will still be written to logs, so this secret should be omitted or redacted from the source trace dump as well.
Useful? React with 👍 / 👎.
| pub smtp_host: Option<String>, | ||
|
|
||
| /// SMTP server port. Defaults to 25 for plaintext, 465 for implicit TLS, and 587 for STARTTLS. | ||
| pub smtp_port: Option<u16>, |
There was a problem hiding this comment.
Tolerate Kubernetes SMTP_PORT service variables
Because SQLPage loads unprefixed environment variables, a Kubernetes pod in a namespace with a Service named smtp receives SMTP_PORT=tcp://... automatically. This new field is deserialized as a bare Option<u16> unlike the existing web port, which uses deserialize_port to ignore Kubernetes service URI values, so config loading fails before validation even when the app does not use mail or relies on the default SMTP port. Apply the same URI-tolerant deserializer here or avoid consuming the unprefixed service variable.
Useful? React with 👍 / 👎.
Motivation
sqlpage.send_mailand exposing SMTP configuration options.hostorhost:portvalue and integrate it with the existing app config.Description
send_mailSQL function implementation insrc/webserver/database/sqlpage_functions/functions/send_mail.rsusinglettrewith optional authentication viastmp_username/stmp_passwordand host parsing viaparse_stmp_host.stmp_host,stmp_username, andstmp_passwordtoAppConfigwith validation invalidate_stmp_hostand usage inAppConfig::validate.examples/official-site/sqlpage/migrations/75_send_mail.sqland expose the function name in the function list.lettretoCargo.tomland updateCargo.lockwith necessary dependencies (lettre,native-tls,openssl,openssl-sys,quoted_printable,nomchanges, and related crates), and updateconfiguration.mdwith SMTP environment variables documentation.Testing
send_mail_authenticates_to_smtp_serverwhich exercises SMTP authentication against a local fake SMTP server and it passed.cargo testwhich completed successfully (all unit tests passed).Codex Task