diff options
author | Martin Ashby <martin@ashbysoft.com> | 2022-12-31 16:30:52 +0000 |
---|---|---|
committer | Martin Ashby <martin@ashbysoft.com> | 2022-12-31 16:30:52 +0000 |
commit | fdfb0e65d3de718beddcf3c882d716b7aff6a051 (patch) | |
tree | d94229e6dcfca049fce2299561a382ce372ae005 /comments/src/main.rs | |
parent | 97cb2fbe176645fa9a84451da3f7f7c9d0158555 (diff) | |
download | mfashby.net-fdfb0e65d3de718beddcf3c882d716b7aff6a051.tar.gz mfashby.net-fdfb0e65d3de718beddcf3c882d716b7aff6a051.tar.bz2 mfashby.net-fdfb0e65d3de718beddcf3c882d716b7aff6a051.tar.xz mfashby.net-fdfb0e65d3de718beddcf3c882d716b7aff6a051.zip |
Add email notifications to comments
Diffstat (limited to 'comments/src/main.rs')
-rw-r--r-- | comments/src/main.rs | 60 |
1 files changed, 59 insertions, 1 deletions
diff --git a/comments/src/main.rs b/comments/src/main.rs index 2bc11fa..7064bca 100644 --- a/comments/src/main.rs +++ b/comments/src/main.rs @@ -15,6 +15,10 @@ use axum::{ routing::get, Router, }; +use lettre::{ + AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor, + transport::smtp::authentication::Credentials +}; use serde::Deserialize; use sqlx::{ postgres::{PgPool, PgPoolOptions}, @@ -25,11 +29,20 @@ use sqlx::{ }; use std::{net::SocketAddr, time::Duration}; + // Useful global thingies #[derive(Clone)] struct Ctx { pool: PgPool, base_path: String, + mail_opts: Option<MailCtx>, +} + +#[derive(Clone)] +struct MailCtx { + notification_address: String, + smtp_server: String, + smtp_credentials: Credentials, } #[tokio::main] @@ -40,6 +53,18 @@ async fn main() { let db_connection_str = std::env::var("DATABASE_URL") .unwrap_or_else(|_| "postgres://postgres:password@localhost".to_string()); + let notification_address = std::env::var("NOTIFICATION_ADDRESS").ok(); + let mail_opts = notification_address.map(|na| { + let smtp_server = std::env::var("SMTP_SERVER").expect("SMTP_SERVER not supplied!"); + let smtp_username = std::env::var("SMTP_USERNAME").expect("SMTP_USERNAME not supplied!"); + let smtp_password = std::env::var("SMTP_PASSWORD").expect("SMTP_PASSWORD not supplied!"); + MailCtx{ + smtp_server: smtp_server, + smtp_credentials: Credentials::new(smtp_username, smtp_password), + notification_address: na, + } + }); + let pool = PgPoolOptions::new() .max_connections(5) .connect_timeout(Duration::from_secs(3)) @@ -51,7 +76,7 @@ async fn main() { .await .expect("failed to run migrations"); - let ctx = Ctx {pool, base_path: base_path.clone()}; + let ctx = Ctx {pool, base_path: base_path.clone(), mail_opts}; let app = Router::new() .nest(&base_path, Router::new() @@ -142,6 +167,14 @@ struct CapchaAnswer { answer:String } +#[derive(Template)] +#[template(path = "notification.txt")] +struct Notification<'a> { + url: &'a str, + comment: &'a str, + author: &'a str, +} + async fn post_comments( State(ctx): State<Ctx>, Form(post_comment): Form<PostComment>) -> Result<Redirect,(StatusCode,String)> { @@ -158,6 +191,31 @@ async fn post_comments( .execute(&ctx.pool) .await .map_err(internal_error)?; + + if let Some(mail_opts) = ctx.mail_opts { + let mail_body = Notification{ + url: &post_comment.url, + author: &post_comment.author, + comment: &post_comment.comment + }.render().unwrap(); + let email = Message::builder() + // TODO should be a config value + .from("Comments Service <comments@mfashby.net>".parse().unwrap()) + .to(mail_opts.notification_address.parse().unwrap()) + .subject(format!("New comment on {}", post_comment.url)) + .body(mail_body) + .unwrap(); + let mailer: AsyncSmtpTransport<Tokio1Executor> = + AsyncSmtpTransport::<Tokio1Executor>::relay(&mail_opts.smtp_server) + .unwrap() + .credentials(mail_opts.smtp_credentials) + .build(); + match mailer.send(email).await { + Ok(_) => (), + Err(e) => eprintln!("Could not send email: {:?}", e), + } + }; + Ok(Redirect::temporary(&post_comment.url)) } |