feat: add --smtp-port
Enables changing the smtp-port for alert emails. Also enables two additional smtp modes: opportunistic and plaintext.
This commit is contained in:
parent
938f12a943
commit
508f6ecc28
@ -1,13 +1,54 @@
|
|||||||
use crate::model::app_state::AppState;
|
use crate::model::app_state::AppState;
|
||||||
|
use crate::model::config::AlertConfig;
|
||||||
use crate::model::config::Config;
|
use crate::model::config::Config;
|
||||||
use crate::model::config::SmtpMode;
|
use crate::model::config::SmtpMode;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use lettre::transport::smtp::authentication::Credentials;
|
|
||||||
use lettre::{AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor};
|
use lettre::{AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor};
|
||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
|
|
||||||
pub fn can_send_alerts() -> bool {
|
// pub fn alert_params_configured() -> bool {
|
||||||
Config::parse().alert_config().is_some()
|
// Config::parse().alert_config().is_some()
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub fn check_alert_params() -> bool {
|
||||||
|
match Config::parse().alert_config() {
|
||||||
|
Some(alert) => match gen_smtp_transport(&alert) {
|
||||||
|
Ok(_) => true,
|
||||||
|
Err(e) => {
|
||||||
|
warn!(
|
||||||
|
"invalid smtp parameters. alert emails disabled. error: {:?}",
|
||||||
|
e.to_string()
|
||||||
|
);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
warn!("alert emails disabled. consider configuring smtp parameters.");
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gen_smtp_transport(
|
||||||
|
alert: &AlertConfig,
|
||||||
|
) -> Result<AsyncSmtpTransport<Tokio1Executor>, lettre::transport::smtp::Error> {
|
||||||
|
// corresponds to table at:
|
||||||
|
// https://docs.rs/lettre/0.11.7/lettre/transport/smtp/struct.AsyncSmtpTransport.html#method.from_url
|
||||||
|
let (scheme, tls_arg) = match alert.smtp_mode {
|
||||||
|
SmtpMode::Smtps => ("smtps", ""),
|
||||||
|
SmtpMode::Starttls => ("smtp", "?tls=required"),
|
||||||
|
SmtpMode::Opportunistic => ("smtp", "?tls=opportunistic"),
|
||||||
|
SmtpMode::Plaintext => ("smtp", ""),
|
||||||
|
};
|
||||||
|
|
||||||
|
let user = &alert.smtp_user;
|
||||||
|
let pass = &alert.smtp_pass;
|
||||||
|
let host = &alert.smtp_host;
|
||||||
|
let port = alert.smtp_port;
|
||||||
|
|
||||||
|
let smtp_url = format!("{scheme}://{user}:{pass}@{host}:{port}{tls_arg}");
|
||||||
|
|
||||||
|
Ok(AsyncSmtpTransport::<Tokio1Executor>::from_url(&smtp_url)?.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
@ -15,43 +56,33 @@ pub async fn send(
|
|||||||
subject: &str,
|
subject: &str,
|
||||||
body: String,
|
body: String,
|
||||||
) -> std::result::Result<bool, anyhow::Error> {
|
) -> std::result::Result<bool, anyhow::Error> {
|
||||||
match state.load().config.alert_config() {
|
// this will log warnings if smtp not configured or mis-configured.
|
||||||
None => {
|
check_alert_params();
|
||||||
warn!("Alert emails disabled. alert not sent. consider confiuring smtp parameters. subject: {subject}");
|
|
||||||
Ok(false)
|
if let Some(alert) = state.load().config.alert_config() {
|
||||||
|
let email = Message::builder()
|
||||||
|
.from(alert.smtp_from_email.parse()?)
|
||||||
|
.to(alert.admin_email.parse()?)
|
||||||
|
.subject(subject)
|
||||||
|
.body(body)?;
|
||||||
|
|
||||||
|
// note: this error case is already checked/logged by check_alert_params.
|
||||||
|
let mailer = gen_smtp_transport(&alert)?;
|
||||||
|
|
||||||
|
// Attempt to send the email via the SMTP transport
|
||||||
|
match mailer.send(email).await {
|
||||||
|
Ok(_) => info!(
|
||||||
|
"alert email sent successfully. to: {}, subject: {subject}, smtp_host: {}:{}",
|
||||||
|
alert.admin_email, alert.smtp_host, alert.smtp_port
|
||||||
|
),
|
||||||
|
Err(e) => warn!(
|
||||||
|
"error sending alert email. to: {}, subject: {subject}, smtp_host: {}:{}. error: {:?}",
|
||||||
|
alert.admin_email, alert.smtp_host, alert.smtp_port, e
|
||||||
|
),
|
||||||
}
|
}
|
||||||
Some(alert) => {
|
|
||||||
let email = Message::builder()
|
|
||||||
.from(alert.smtp_from_email.parse()?)
|
|
||||||
.to(alert.admin_email.parse()?)
|
|
||||||
.subject(subject)
|
|
||||||
.body(body)?;
|
|
||||||
|
|
||||||
// Create SMTP client credentials using username and password
|
Ok(true)
|
||||||
let creds = Credentials::new(alert.smtp_user.to_string(), alert.smtp_pass.to_string());
|
} else {
|
||||||
|
Ok(false)
|
||||||
// Open a secure connection to the SMTP server, possibly using STARTTLS
|
|
||||||
let relay = match alert.smtp_mode {
|
|
||||||
SmtpMode::Starttls => {
|
|
||||||
AsyncSmtpTransport::<Tokio1Executor>::starttls_relay(&alert.smtp_host)?
|
|
||||||
}
|
|
||||||
SmtpMode::Smtps => AsyncSmtpTransport::<Tokio1Executor>::relay(&alert.smtp_host)?,
|
|
||||||
};
|
|
||||||
let mailer = relay.credentials(creds).build();
|
|
||||||
|
|
||||||
// Attempt to send the email via the SMTP transport
|
|
||||||
match mailer.send(email).await {
|
|
||||||
Ok(_) => info!(
|
|
||||||
"alert email sent successfully. to: {}, subject: {subject}, smtp_host: {}",
|
|
||||||
alert.admin_email, alert.smtp_host
|
|
||||||
),
|
|
||||||
Err(e) => warn!(
|
|
||||||
"error send alert email. to: {}, subject: {subject}, smtp_host: {}. error: {:?}",
|
|
||||||
alert.admin_email, alert.smtp_host, e
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ use neptune_explorer::rpc::block_digest::block_digest;
|
|||||||
use neptune_explorer::rpc::block_info::block_info;
|
use neptune_explorer::rpc::block_info::block_info;
|
||||||
use neptune_explorer::rpc::utxo_digest::utxo_digest;
|
use neptune_explorer::rpc::utxo_digest::utxo_digest;
|
||||||
use tower_http::services::ServeDir;
|
use tower_http::services::ServeDir;
|
||||||
use tracing::{info, warn};
|
use tracing::info;
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@ -33,9 +33,8 @@ async fn main() -> Result<(), anyhow::Error> {
|
|||||||
.await
|
.await
|
||||||
.with_context(|| format!("Failed to bind to port {port}"))?;
|
.with_context(|| format!("Failed to bind to port {port}"))?;
|
||||||
|
|
||||||
if !alert_email::can_send_alerts() {
|
// this will log warnings if smtp not configured or mis-configured.
|
||||||
warn!("alert emails disabled. consider configuring smtp parameters.");
|
alert_email::check_alert_params();
|
||||||
}
|
|
||||||
|
|
||||||
tokio::task::spawn(neptune_rpc::watchdog(app_state));
|
tokio::task::spawn(neptune_rpc::watchdog(app_state));
|
||||||
|
|
||||||
|
|||||||
@ -14,8 +14,8 @@
|
|||||||
clap::ArgGroup::new("value")
|
clap::ArgGroup::new("value")
|
||||||
.required(false)
|
.required(false)
|
||||||
.multiple(true)
|
.multiple(true)
|
||||||
.requires_all(&["admin_email", "smtp_host", "smtp_user", "smtp_pass", "smtp_from_email"])
|
.requires_all(&["admin_email", "smtp_host", "smtp_port", "smtp_user", "smtp_pass", "smtp_from_email"])
|
||||||
.args(&["admin_email", "smtp_host", "smtp_user", "smtp_pass", "smtp_from_email"])
|
.args(&["admin_email", "smtp_host", "smtp_port", "smtp_user", "smtp_pass", "smtp_from_email"])
|
||||||
))]
|
))]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
/// Sets the website name
|
/// Sets the website name
|
||||||
@ -46,6 +46,10 @@ pub struct Config {
|
|||||||
#[arg(long, value_name = "host")]
|
#[arg(long, value_name = "host")]
|
||||||
pub smtp_host: Option<String>,
|
pub smtp_host: Option<String>,
|
||||||
|
|
||||||
|
/// smtp port for alert emails
|
||||||
|
#[arg(long, value_name = "port", default_value = "25")]
|
||||||
|
pub smtp_port: Option<u16>,
|
||||||
|
|
||||||
/// smtp username for alert emails
|
/// smtp username for alert emails
|
||||||
#[arg(long, value_name = "user")]
|
#[arg(long, value_name = "user")]
|
||||||
pub smtp_user: Option<String>,
|
pub smtp_user: Option<String>,
|
||||||
@ -68,6 +72,7 @@ impl Config {
|
|||||||
match (
|
match (
|
||||||
&self.admin_email,
|
&self.admin_email,
|
||||||
&self.smtp_host,
|
&self.smtp_host,
|
||||||
|
&self.smtp_port,
|
||||||
&self.smtp_user,
|
&self.smtp_user,
|
||||||
&self.smtp_pass,
|
&self.smtp_pass,
|
||||||
&self.smtp_from_email,
|
&self.smtp_from_email,
|
||||||
@ -76,6 +81,7 @@ impl Config {
|
|||||||
(
|
(
|
||||||
Some(admin_email),
|
Some(admin_email),
|
||||||
Some(smtp_host),
|
Some(smtp_host),
|
||||||
|
Some(smtp_port),
|
||||||
Some(smtp_user),
|
Some(smtp_user),
|
||||||
Some(smtp_pass),
|
Some(smtp_pass),
|
||||||
Some(smtp_from_email),
|
Some(smtp_from_email),
|
||||||
@ -83,6 +89,7 @@ impl Config {
|
|||||||
) => Some(AlertConfig {
|
) => Some(AlertConfig {
|
||||||
admin_email: admin_email.clone(),
|
admin_email: admin_email.clone(),
|
||||||
smtp_host: smtp_host.clone(),
|
smtp_host: smtp_host.clone(),
|
||||||
|
smtp_port: *smtp_port,
|
||||||
smtp_user: smtp_user.clone(),
|
smtp_user: smtp_user.clone(),
|
||||||
smtp_pass: smtp_pass.clone(),
|
smtp_pass: smtp_pass.clone(),
|
||||||
smtp_from_email: smtp_from_email.clone(),
|
smtp_from_email: smtp_from_email.clone(),
|
||||||
@ -101,6 +108,9 @@ pub struct AlertConfig {
|
|||||||
/// smtp host for alert emails
|
/// smtp host for alert emails
|
||||||
pub smtp_host: String,
|
pub smtp_host: String,
|
||||||
|
|
||||||
|
/// smtp host for alert emails
|
||||||
|
pub smtp_port: u16,
|
||||||
|
|
||||||
/// smtp username for alert emails
|
/// smtp username for alert emails
|
||||||
pub smtp_user: String,
|
pub smtp_user: String,
|
||||||
|
|
||||||
@ -115,10 +125,18 @@ pub struct AlertConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, clap::ValueEnum)]
|
#[derive(Debug, Clone, clap::ValueEnum)]
|
||||||
|
/// Determines SMTP encryption mode.
|
||||||
|
/// See: https://docs.rs/lettre/0.11.7/lettre/transport/smtp/struct.AsyncSmtpTransport.html#method.from_url
|
||||||
pub enum SmtpMode {
|
pub enum SmtpMode {
|
||||||
/// smtps
|
/// smtps
|
||||||
Smtps,
|
Smtps,
|
||||||
|
|
||||||
/// starttls
|
/// starttls required
|
||||||
Starttls,
|
Starttls,
|
||||||
|
|
||||||
|
/// use starttls if available. insecure.
|
||||||
|
Opportunistic,
|
||||||
|
|
||||||
|
/// plain text. insecure.
|
||||||
|
Plaintext,
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user