bugfix parsing empty string and sending singlepart plaintext emails

This commit is contained in:
Ryan Heywood 2024-09-04 22:36:22 -04:00
parent 7895bff847
commit c2d001b060
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
1 changed files with 34 additions and 6 deletions

View File

@ -5,37 +5,62 @@ use axum::{
Extension, Form, Router, Extension, Form, Router,
}; };
use lettre::message::Mailbox; use lettre::message::{header::ContentType, Mailbox, SinglePart};
use lettre::transport::smtp::AsyncSmtpTransport; use lettre::transport::smtp::AsyncSmtpTransport;
use lettre::{AsyncTransport, Message, Tokio1Executor}; use lettre::{AsyncTransport, Message, Tokio1Executor};
use serde::Deserialize; use serde::{Deserialize, Deserializer};
use std::error::Error; use std::error::Error;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::Arc; use std::sync::Arc;
fn deserialize_empty_string_as_none<'de, D, T>(de: D) -> Result<Option<T>, D::Error>
where
D: Deserializer<'de>,
T: std::str::FromStr + std::fmt::Debug,
T::Err: std::fmt::Display + std::fmt::Debug,
{
let opt: Option<String> = Option::deserialize(de)?;
match opt.as_deref() {
None | Some("") => Ok(None),
Some(s) => std::str::FromStr::from_str(s)
.map_err(serde::de::Error::custom)
.map(Some),
}
}
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
struct FormData { struct FormData {
#[serde(default, deserialize_with = "deserialize_empty_string_as_none")]
name: Option<String>, name: Option<String>,
email: String, email: String,
#[serde(default, deserialize_with = "deserialize_empty_string_as_none")]
company_name: Option<String>, company_name: Option<String>,
service: String, service: String,
help: String, help: String,
#[serde(default, deserialize_with = "deserialize_empty_string_as_none")]
hear_about: Option<String>, hear_about: Option<String>,
#[serde(default, deserialize_with = "deserialize_empty_string_as_none")]
other_source: Option<String>, other_source: Option<String>,
} }
struct State { struct State {
username: String, username: String,
password: String, password: String,
target_address: String,
} }
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
let username = std::env::var("WEBMAIL_USERNAME")?; let username = std::env::var("WEBMAIL_USERNAME")?;
let password = std::env::var("WEBMAIL_PASSWORD")?; let password = std::env::var("WEBMAIL_PASSWORD")?;
let target_address = std::env::var("WEBMAIL_TARGET")?;
let state = State { username, password }; let state = State {
username,
password,
target_address,
};
let app = Router::new() let app = Router::new()
.route("/submit-email", post(handle_form)) .route("/submit-email", post(handle_form))
@ -105,10 +130,13 @@ async fn send_email(form_data: FormData, state: &State) -> Result<(), Box<dyn Er
let email = Message::builder() let email = Message::builder()
.from(username.parse()?) .from(username.parse()?)
.reply_to(email) .reply_to(email)
.to("ryan@distrust.co".parse()?) .to(state.target_address.parse()?)
.cc("anton@distrust.co".parse()?)
.subject("New Website Form Inquiry") .subject("New Website Form Inquiry")
.body(body)?; .singlepart(
SinglePart::builder()
.header(ContentType::TEXT_PLAIN)
.body(body),
)?;
let credentials = lettre::transport::smtp::authentication::Credentials::new( let credentials = lettre::transport::smtp::authentication::Credentials::new(
username.clone(), username.clone(),