Files
AstralRinth/apps/labrinth/src/auth/email/mod.rs
Erb3 6f902e2107 feat(labrinth): environment variables for more customizable SMTP (#2886)
* refactor: move .env to .env.example

* refactor(labrinth): allow setting SMTP port and TLS

This will help setting up labrinth for local development. You can now use a mock SMTP server such as smtp4dev. The TLS options will stay the same as before if set to `true`, and disabled when `false`.

Depends on #2883

* chore(labrinth): lint

* chore(labrinth): conflicts

* chore(labrinth): conflicts

* fix: use TLS port by default

Co-authored-by: AlexTMjugador<me@alegon.dev>

Co-authored-by: Alejandro González <7822554+AlexTMjugador@users.noreply.github.com>
Signed-off-by: Erb3 <49862976+Erb3@users.noreply.github.com>

* fix(labrinth): correct deafult SMTP port in .env

* feat(labrinth): expose all SMTP TLS settings

Replaced if/else with a switch statement. The new values for `SMPT_TLS` are `none`, `opportunistic_start_tls`, `requires_start_tls`, `tls`. When none of these values are supplied, it defaults to full TLS (`tls`), and throws a warning.

Resolves PR review

* fix(labrinth): correct SMTP TLS example .env setting

Signed-off-by: Erb3 <49862976+Erb3@users.noreply.github.com>

* fix(labrinth) SMTP tls env var check

Co-authored-by: Alejandro González <7822554+AlexTMjugador@users.noreply.github.com>
Signed-off-by: Erb3 <49862976+Erb3@users.noreply.github.com>

---------

Signed-off-by: Erb3 <49862976+Erb3@users.noreply.github.com>
Co-authored-by: Alejandro González <7822554+AlexTMjugador@users.noreply.github.com>
2025-04-22 11:29:14 +00:00

94 lines
2.8 KiB
Rust

use lettre::message::header::ContentType;
use lettre::message::Mailbox;
use lettre::transport::smtp::authentication::Credentials;
use lettre::transport::smtp::client::{Tls, TlsParameters};
use lettre::{Address, Message, SmtpTransport, Transport};
use thiserror::Error;
use tracing::warn;
#[derive(Error, Debug)]
pub enum MailError {
#[error("Environment Error")]
Env(#[from] dotenvy::Error),
#[error("Mail Error: {0}")]
Mail(#[from] lettre::error::Error),
#[error("Address Parse Error: {0}")]
Address(#[from] lettre::address::AddressError),
#[error("SMTP Error: {0}")]
Smtp(#[from] lettre::transport::smtp::Error),
}
pub fn send_email_raw(
to: String,
subject: String,
body: String,
) -> Result<(), MailError> {
let email = Message::builder()
.from(Mailbox::new(
Some("Modrinth".to_string()),
Address::new("no-reply", "mail.modrinth.com")?,
))
.to(to.parse()?)
.subject(subject)
.header(ContentType::TEXT_HTML)
.body(body)?;
let username = dotenvy::var("SMTP_USERNAME")?;
let password = dotenvy::var("SMTP_PASSWORD")?;
let host = dotenvy::var("SMTP_HOST")?;
let port = dotenvy::var("SMTP_PORT")?.parse::<u16>().unwrap_or(465);
let creds = Credentials::new(username, password);
let tls_setting = match dotenvy::var("SMTP_TLS")?.as_str() {
"none" => Tls::None,
"opportunistic_start_tls" => {
Tls::Opportunistic(TlsParameters::new(host.to_string())?)
}
"requires_start_tls" => {
Tls::Required(TlsParameters::new(host.to_string())?)
}
"tls" => Tls::Wrapper(TlsParameters::new(host.to_string())?),
_ => {
warn!("Unrecognized SMTP TLS setting. Defaulting to TLS.");
Tls::Wrapper(TlsParameters::new(host.to_string())?)
}
};
let mailer = SmtpTransport::relay(&host)?
.port(port)
.tls(tls_setting)
.credentials(creds)
.build();
mailer.send(&email)?;
Ok(())
}
pub fn send_email(
to: String,
email_title: &str,
email_description: &str,
line_two: &str,
button_info: Option<(&str, &str)>,
) -> Result<(), MailError> {
let mut email = if button_info.is_some() {
include_str!("button_notif.html")
} else {
include_str!("auth_notif.html")
}
.replace("{{ email_title }}", email_title)
.replace("{{ email_description }}", email_description)
.replace("{{ line_one }}", email_description)
.replace("{{ line_two }}", line_two);
if let Some((button_title, button_link)) = button_info {
email = email
.replace("{{ button_title }}", button_title)
.replace("{{ button_link }}", button_link);
}
send_email_raw(to, email_title.to_string(), email)?;
Ok(())
}