diff --git a/Cargo.lock b/Cargo.lock index 8e99c55..ebd3764 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -738,7 +738,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -2038,12 +2038,9 @@ dependencies = [ "nom", "percent-encoding", "quoted_printable", - "rustls", "socket2", "tokio", - "tokio-rustls", "url", - "webpki-roots", ] [[package]] @@ -2217,7 +2214,7 @@ checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -2241,7 +2238,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -2907,20 +2904,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - [[package]] name = "rstml" version = "0.12.1" @@ -2951,41 +2934,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustls" -version = "0.23.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" -dependencies = [ - "log", - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pki-types" -version = "1.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" -dependencies = [ - "zeroize", -] - -[[package]] -name = "rustls-webpki" -version = "0.103.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - [[package]] name = "rustversion" version = "1.0.22" @@ -3282,7 +3230,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -3552,7 +3500,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -3592,16 +3540,6 @@ dependencies = [ "whoami", ] -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - [[package]] name = "tokio-tungstenite" version = "0.29.0" @@ -4046,12 +3984,6 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - [[package]] name = "url" version = "2.5.8" @@ -4330,15 +4262,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki-roots" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "whoami" version = "2.1.2" @@ -4358,7 +4281,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys", ] [[package]] @@ -4367,15 +4290,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -4385,70 +4299,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - [[package]] name = "winnow" version = "0.7.15" @@ -4682,12 +4532,6 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" - [[package]] name = "zerotrie" version = "0.1.3" diff --git a/devenv.lock b/devenv.lock index 991fcf7..0f2de9a 100644 --- a/devenv.lock +++ b/devenv.lock @@ -16,6 +16,62 @@ "type": "github" } }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1767039857, + "owner": "NixOS", + "repo": "flake-compat", + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "flake-compat", + "type": "github" + } + }, + "git-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776796298, + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "3cfd774b0a530725a077e17354fbdb87ea1c4aad", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1762808025, + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "cb5e3fdca1de58ccbc3ef53de65bd372b48f567c", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1776734388, @@ -49,11 +105,15 @@ "root": { "inputs": { "devenv": "devenv", + "git-hooks": "git-hooks", "nixpkgs": "nixpkgs", - "nixpkgs-cmake3": "nixpkgs-cmake3" + "nixpkgs-cmake3": "nixpkgs-cmake3", + "pre-commit-hooks": [ + "git-hooks" + ] } } }, "root": "root", "version": 7 -} \ No newline at end of file +} diff --git a/module.nix b/module.nix index ec119ab..2577d13 100644 --- a/module.nix +++ b/module.nix @@ -48,14 +48,9 @@ in description = "SMTP server hostname."; }; port = mkOption { - type = types.nullOr types.port; - default = null; - description = "SMTP server port. Defaults to 465 when tls = true, 1025 otherwise."; - }; - tls = mkOption { - type = types.bool; - default = false; - description = "Use TLS (port 465). Required for Resend and other cloud SMTP providers."; + type = types.port; + default = 1025; + description = "SMTP server port."; }; from = mkOption { type = types.str; @@ -65,7 +60,7 @@ in user = mkOption { type = types.str; default = ""; - description = "SMTP username (leave empty to skip authentication). Use \"resend\" for Resend."; + description = "SMTP username (leave empty to skip authentication)."; }; passwordFile = mkOption { type = types.nullOr types.path; @@ -186,11 +181,8 @@ in DATABASE_URL = "postgresql://${cfg.user}@127.0.0.1/${cfg.user}"; APP_URL = "${cfg.protocol}://${cfg.hostname}"; SMTP_HOST = cfg.smtp.host; - SMTP_PORT = toString (if cfg.smtp.port != null then cfg.smtp.port - else if cfg.smtp.tls then 465 else 1025); + SMTP_PORT = toString cfg.smtp.port; SMTP_FROM = cfg.smtp.from; - } // optionalAttrs cfg.smtp.tls { - SMTP_TLS = "true"; } // optionalAttrs (cfg.smtp.user != "") { SMTP_USER = cfg.smtp.user; }; diff --git a/server/relay-server/Cargo.toml b/server/relay-server/Cargo.toml index ccb97fa..b4312d5 100644 --- a/server/relay-server/Cargo.toml +++ b/server/relay-server/Cargo.toml @@ -25,4 +25,4 @@ axum-login = "0.18" argon2 = "0.5" time = "0.3" thiserror = "1" -lettre = { version = "0.11", default-features = false, features = ["smtp-transport", "tokio1", "builder", "hostname", "tokio1-rustls-tls"] } +lettre = { version = "0.11", default-features = false, features = ["smtp-transport", "tokio1", "builder", "hostname"] } diff --git a/server/relay-server/src/smtp.rs b/server/relay-server/src/smtp.rs index bbcfa6e..37ebaa3 100644 --- a/server/relay-server/src/smtp.rs +++ b/server/relay-server/src/smtp.rs @@ -1,18 +1,15 @@ -//! SMTP mailer. +//! SMTP mailer (plain SMTP, no TLS). //! //! Configured via environment variables: -//! SMTP_HOST — default: 127.0.0.1 (mailpit in dev) -//! SMTP_PORT — default: 1025 (mailpit) / 465 when SMTP_TLS=true -//! SMTP_TLS — set to "true" to use TLS (required for Resend and other cloud SMTP) +//! SMTP_HOST — default: 127.0.0.1 (mailpit in dev) +//! SMTP_PORT — default: 1025 (mailpit default) //! SMTP_FROM — default: noreply@trictrac.local -//! SMTP_USER — optional SMTP credentials (use "resend" for Resend) -//! SMTP_PASSWORD — optional SMTP credentials (use Resend API key) +//! SMTP_USER — optional SMTP credentials +//! SMTP_PASSWORD — optional SMTP credentials //! APP_URL — default: http://localhost:9091 (frontend base URL for email links) //! -//! Production (Resend): -//! SMTP_HOST=smtp.resend.com SMTP_TLS=true -//! SMTP_USER=resend SMTP_PASSWORD=re_xxxx -//! SMTP_FROM=noreply@yourdomain.com +//! For production with TLS, run a local relay (e.g. Postfix) or use an SMTP +//! service that accepts plain connections on a local port and handles TLS externally. use lettre::{ AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor, @@ -29,47 +26,21 @@ pub struct Mailer { impl Mailer { pub fn from_env() -> Self { let host = std::env::var("SMTP_HOST").unwrap_or_else(|_| "127.0.0.1".to_string()); - let tls = std::env::var("SMTP_TLS").map(|v| v == "true").unwrap_or(false); + let port: u16 = std::env::var("SMTP_PORT") + .ok() + .and_then(|p| p.parse().ok()) + .unwrap_or(1025); let from_str = std::env::var("SMTP_FROM") .unwrap_or_else(|_| "noreply@trictrac.local".to_string()); let app_url = std::env::var("APP_URL") .unwrap_or_else(|_| "http://localhost:9091".to_string()); - let credentials = if let (Ok(user), Ok(pass)) = - (std::env::var("SMTP_USER"), std::env::var("SMTP_PASSWORD")) - { - Some(SmtpCredentials::new(user, pass)) - } else { - None - }; - - let transport = if tls { - // TLS on port 465 (Resend, SendGrid, etc.) - let default_port = 465u16; - let port: u16 = std::env::var("SMTP_PORT") - .ok() - .and_then(|p| p.parse().ok()) - .unwrap_or(default_port); - let mut builder = AsyncSmtpTransport::::relay(&host) - .expect("invalid SMTP_HOST for TLS relay") - .port(port); - if let Some(creds) = credentials { - builder = builder.credentials(creds); - } - builder.build() - } else { - // Plain SMTP (Mailpit dev, or local relay) - let port: u16 = std::env::var("SMTP_PORT") - .ok() - .and_then(|p| p.parse().ok()) - .unwrap_or(1025); - let mut builder = - AsyncSmtpTransport::::builder_dangerous(&host).port(port); - if let Some(creds) = credentials { - builder = builder.credentials(creds); - } - builder.build() - }; + let mut builder = + AsyncSmtpTransport::::builder_dangerous(&host).port(port); + if let (Ok(user), Ok(pass)) = (std::env::var("SMTP_USER"), std::env::var("SMTP_PASSWORD")) { + builder = builder.credentials(SmtpCredentials::new(user, pass)); + } + let transport = builder.build(); let from = from_str .parse()