Document mailer and use async lettre

This commit is contained in:
Mo 2023-02-23 17:34:22 +01:00
parent 164094c42f
commit 2143da1766
4 changed files with 51 additions and 20 deletions

14
Cargo.lock generated
View file

@ -541,10 +541,12 @@ version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8033576bf9f051fce6cb92b6264114b4340896c352a9ff38b67bd4cde924635" checksum = "d8033576bf9f051fce6cb92b6264114b4340896c352a9ff38b67bd4cde924635"
dependencies = [ dependencies = [
"async-trait",
"base64 0.21.0", "base64 0.21.0",
"email-encoding", "email-encoding",
"email_address", "email_address",
"fastrand", "fastrand",
"futures-io",
"futures-util", "futures-util",
"hostname", "hostname",
"httpdate", "httpdate",
@ -557,6 +559,7 @@ dependencies = [
"rustls-pemfile", "rustls-pemfile",
"socket2", "socket2",
"tokio", "tokio",
"tokio-rustls",
"webpki-roots", "webpki-roots",
] ]
@ -1087,6 +1090,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "tokio-rustls"
version = "0.23.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
dependencies = [
"rustls",
"tokio",
"webpki",
]
[[package]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.7.7" version = "0.7.7"

View file

@ -12,7 +12,7 @@ askama = { git = "https://github.com/djc/askama.git" }
askama_axum = { git = "https://github.com/djc/askama.git", package = "askama_axum" } askama_axum = { git = "https://github.com/djc/askama.git", package = "askama_axum" }
axum = { version = "0.6", default-features = false, features = ["http1", "form", "tokio", "macros"] } axum = { version = "0.6", default-features = false, features = ["http1", "form", "tokio", "macros"] }
captcha = { version = "0.0.9", default-features = false } captcha = { version = "0.0.9", default-features = false }
lettre = { version = "0.10", default-features = false, features = ["smtp-transport", "hostname", "rustls-tls", "pool", "builder"] } lettre = { version = "0.10", default-features = false, features = ["smtp-transport", "hostname", "tokio1-rustls-tls", "pool", "builder"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.9" serde_yaml = "0.9"
time = "0.3" time = "0.3"

View file

@ -1,29 +1,39 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use lettre::{ use lettre::{
message::{Mailbox, MessageBuilder}, message::{Mailbox, Message, MessageBuilder},
transport::smtp::authentication::Credentials, transport::{
Message, SmtpTransport, Transport, smtp::{authentication::Credentials, AsyncSmtpTransport},
AsyncTransport,
},
Tokio1Executor,
}; };
use crate::config::Config; use crate::config::Config;
type ASmtpTransport = AsyncSmtpTransport<Tokio1Executor>;
/// Mail sender.
pub struct Mailer { pub struct Mailer {
mailer: SmtpTransport, mailer: ASmtpTransport,
message_builder: MessageBuilder, message_builder: MessageBuilder,
} }
impl Mailer { impl Mailer {
/// Tries to initialize the mailer.
pub fn build(config: &Config) -> Result<Self> { pub fn build(config: &Config) -> Result<Self> {
let creds = Credentials::new( // Mail server credentials for login.
let credentials = Credentials::new(
config.email_server.email.clone(), config.email_server.email.clone(),
config.email_server.password.clone(), config.email_server.password.clone(),
); );
let mailer = SmtpTransport::relay(&config.email_server.server_name) // Establish the connection.
let mailer = ASmtpTransport::relay(&config.email_server.server_name)
.context("Failed to connect to the email server!")? .context("Failed to connect to the email server!")?
.credentials(creds) .credentials(credentials)
.build(); .build();
// Set the From and To mailboxes for every sent mail.
let message_builder = Message::builder() let message_builder = Message::builder()
.from( .from(
config config
@ -42,13 +52,19 @@ impl Mailer {
}) })
} }
pub fn send(&self, name: &str, email: &str, telefon: &str, message: &str) -> Result<()> { /// Sends a mail with data from the contact form.
pub async fn send(&self, name: &str, email: &str, telefon: &str, message: &str) -> Result<()> {
let name = name.trim().to_string(); let name = name.trim().to_string();
let subject = "Message from ".to_string() + &name;
let reply_to = Mailbox::new(
Some(name),
email
.parse()
.context("Failed to parse the email from the form to an address!")?,
);
let telefon = telefon.trim(); let telefon = telefon.trim();
let message = message.trim().to_string(); let message = message.trim().to_string();
let subject = "Message from ".to_string() + &name;
let body = if !telefon.is_empty() { let body = if !telefon.is_empty() {
message + "\n\nTelefon: " + telefon message + "\n\nTelefon: " + telefon
} else { } else {
@ -58,17 +74,15 @@ impl Mailer {
let email = self let email = self
.message_builder .message_builder
.clone() .clone()
.reply_to(Mailbox::new( .reply_to(reply_to)
Some(name),
email
.parse()
.context("Failed to parse the email from the form to an address!")?,
))
.subject(subject) .subject(subject)
.body(body) .body(body)
.context("Failed to build email!")?; .context("Failed to build email!")?;
self.mailer.send(&email).context("Failed to send email!")?; self.mailer
.send(email)
.await
.context("Failed to send email!")?;
Ok(()) Ok(())
} }

View file

@ -112,7 +112,10 @@ pub async fn submit(
.await; .await;
} }
match mailer.send(&form.name, &form.email, &form.telefon, &form.message) { match mailer
.send(&form.name, &form.email, &form.telefon, &form.message)
.await
{
Ok(_) => (), Ok(_) => (),
Err(e) => { Err(e) => {
error!("{e:?}"); error!("{e:?}");