irongit

Git hosting and a container registry in one Rust binary (axum + Astro)

Mail: STARTTLS on 587, irongit admin test-email

huncholanehuncholaneauthored
parent a4869c6commit 1bb042d572f197a6b3ee3d8c213984c99bdb3cf8Browse files

2 files changed, +27 -6

+13-6backend/src/mail.rs
@@ -6,10 +6,14 @@ use lettre::{
66 transport::smtp::authentication::Credentials,
77 };
88
9-use crate::state::AppState;
9+use crate::{config::Config, state::AppState};
1010
1111 pub async fn send(state: &AppState, to: &str, subject: &str, body: &str) -> anyhow::Result<()> {
12- let Some(smtp) = &state.config.smtp else {
12+ send_with(&state.config, to, subject, body).await
13+}
14+
15+pub async fn send_with(config: &Config, to: &str, subject: &str, body: &str) -> anyhow::Result<()> {
16+ let Some(smtp) = &config.smtp else {
1317 tracing::info!(target: "mail", to, subject, body, "SMTP not configured; email logged instead of sent");
1418 return Ok(());
1519 };
@@ -19,10 +23,13 @@ pub async fn send(state: &AppState, to: &str, subject: &str, body: &str) -> anyh
1923 .subject(subject)
2024 .header(ContentType::TEXT_PLAIN)
2125 .body(body.to_string())?;
22- let transport = AsyncSmtpTransport::<Tokio1Executor>::relay(&smtp.host)?
23- .port(smtp.port)
24- .credentials(Credentials::new(smtp.username.clone(), smtp.password.clone()))
25- .build();
26+ // 465 is TLS from the first byte; 587 (and anything else) upgrades with STARTTLS.
27+ let builder = if smtp.port == 465 {
28+ AsyncSmtpTransport::<Tokio1Executor>::relay(&smtp.host)?
29+ } else {
30+ AsyncSmtpTransport::<Tokio1Executor>::starttls_relay(&smtp.host)?
31+ };
32+ let transport = builder.port(smtp.port).credentials(Credentials::new(smtp.username.clone(), smtp.password.clone())).build();
2633 transport.send(message).await?;
2734 tracing::info!(to, subject, "email sent");
2835 Ok(())
+14-0backend/src/main.rs
@@ -78,6 +78,8 @@ enum AdminCommand {
7878 Demote { username: String },
7979 /// Write, read and delete a test object in R2 to verify credentials.
8080 CheckStorage,
81+ /// Send a test email through the configured SMTP server.
82+ TestEmail { to: String },
8183 /// Publish a CLI build to R2 so `ig upgrade` and install.sh serve it.
8284 PublishCli {
8385 /// Path to the built `ig` binary.
@@ -175,6 +177,18 @@ async fn admin(command: AdminCommand) -> anyhow::Result<()> {
175177 AdminCommand::Promote { username } => set_admin(&db, &username, true).await,
176178 AdminCommand::Demote { username } => set_admin(&db, &username, false).await,
177179 AdminCommand::CheckStorage => check_storage(&config).await,
180+ AdminCommand::TestEmail { to } => {
181+ anyhow::ensure!(config.smtp.is_some(), "SMTP is not configured (set SMTP_HOST, SMTP_USERNAME and SMTP_PASSWORD)");
182+ mail::send_with(
183+ &config,
184+ &to,
185+ "irongit test email",
186+ &format!("This is a test from {}. If you can read it, verification emails will arrive too.", config.base_url()),
187+ )
188+ .await?;
189+ println!("sent a test email to {to}");
190+ Ok(())
191+ }
178192 AdminCommand::PublishCli { path, version, target } => {
179193 let storage = Storage::new(&config.r2)?;
180194 api::release::publish(&db, &storage, &path, &version, &target).await