diff --git a/backend/src/email.rs b/backend/src/email.rs index d89dd65..be168ff 100644 --- a/backend/src/email.rs +++ b/backend/src/email.rs @@ -1,6 +1,6 @@ use dotenvy::var; use lettre::{ - message::{header::ContentType, Mailbox}, + message::{header::{ContentType, HeaderName, HeaderValue},Mailbox}, transport::smtp::authentication::Credentials, Message, SmtpTransport, Transport, }; @@ -8,7 +8,7 @@ use lettre::{ fn create_mailer() -> SmtpTransport { // Get the server settings from the env file let smtp_server_name = var("SMTP_SERVER_NAME").expect("SMTP_SERVER_NAME not set"); - let smtp_server_port = var("SMTP_SERVER_PORT").expect("SMTP_SERVER_PORT not set"); + //let smtp_server_port = var("SMTP_SERVER_PORT").expect("SMTP_SERVER_PORT not set"); // Get the username and password from the env file let username = var("EMAIL_USERNAME").expect("EMAIL_USERNAME not set"); @@ -26,20 +26,26 @@ fn create_mailer() -> SmtpTransport { .build() } -pub fn send_email(subject: String, recipient: String, body: String) { +pub fn send_emails(subject: String, recipients: String, body: String) { let username = var("EMAIL_USERNAME").expect("EMAIL_USERNAME not set"); // Build the email - let email = Message::builder() + let mut email = Message::builder() .from(username.parse::().unwrap()) - .to(recipient.parse::().unwrap()) + .to(username.parse::().unwrap()) .subject(subject) .header(ContentType::TEXT_HTML) - .body(body.to_string()) + .body(body) .unwrap(); + let headers = email.headers_mut(); + + //headers.set(To(recipients)); + headers.insert_raw(HeaderValue::new(HeaderName::new_from_ascii_str("To"), recipients.to_owned())); + let mailer = create_mailer(); + // TODO: Add logging // Send the email match mailer.send(&email) { Ok(_) => println!("Basic email sent!"), @@ -47,19 +53,4 @@ pub fn send_email(subject: String, recipient: String, body: String) { println!("Basic email failed to send. {:?}", error); } } -} - -pub fn send_emails(subject: String, recipients: Vec, body: String) { - let username = var("EMAIL_USERNAME").expect("EMAIL_USERNAME not set"); - - // Build the email - let mut email = Message::builder() - .from(username.parse::().unwrap()) - .subject(subject) - .header(ContentType::TEXT_HTML) - .body(body); - - for recipient in recipients { - email = email.to(recipient.parse::().unwrap()); - } } \ No newline at end of file diff --git a/backend/src/google_oauth.rs b/backend/src/google_oauth.rs index 0e82df4..a456742 100644 --- a/backend/src/google_oauth.rs +++ b/backend/src/google_oauth.rs @@ -22,6 +22,9 @@ use sqlx::SqlitePool; use std::collections::HashMap; use uuid::Uuid; +use crate::user::get_useremails_by_role; +use crate::email::send_emails; + use super::{AppError, UserData}; fn get_client(hostname: String) -> Result { @@ -201,16 +204,18 @@ pub async fn google_auth_return( // Add public role sqlx::query("INSERT INTO user_roles (created_at, created_by, updated_at, updated_by, user_id, role_id) VALUES (?, ?, ?, ?, ?, ?)") - .bind(now) - .bind(0 as i64)// Created by system - .bind(now) - .bind(0 as i64) // Updated by system - .bind(query.0) + .bind(now) + .bind(0 as i64)// Created by system + .bind(now) + .bind(0 as i64) // Updated by system + .bind(query.0) .bind("1") .execute(&db_pool) .await?; - // TODO - send email to admin regarding new user registration + // send email to admin regarding new user registration + let recipients = get_useremails_by_role("admin".to_string(), &db_pool).await; + send_emails("Jean-Marie website - New user registration".to_string(), recipients, "A new user has registered".to_string()); query.0 }; diff --git a/backend/src/main.rs b/backend/src/main.rs index 6f05902..ca6db55 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -2,7 +2,6 @@ use std::net::SocketAddr; use axum::{ middleware, routing::{get, get_service}, Extension, Router }; -use email::send_email; use sqlx::{sqlite::SqlitePoolOptions, SqlitePool}; use sqlx::migrate::Migrator; use tower_http::services::ServeDir; @@ -21,6 +20,7 @@ use google_oauth::{login, logout, google_auth_return}; use routes::{about, contact, cottagecalendar, dashboard, index, profile, user_profile, useradmin}; use user::{add_user_role, delete_user_role, UserData}; use wishlist::{user_wishlist, user_wishlist_add, user_wishlist_add_item, user_wishlist_bought_item, user_wishlist_received_item, wishlists}; +//use email::send_emails; #[derive(Clone)] pub struct AppState { @@ -71,12 +71,13 @@ async fn main() { .route("/logout", get(logout)) .route("/google_auth_return", get(google_auth_return)) .route_layer(middleware::from_fn_with_state(app_state.db_pool.clone(), inject_user_data)) - .with_state(app_state.db_pool) + .with_state(app_state.db_pool.clone()) .layer(Extension(user_data)) ; // Send email indicating server has started - //send_email("Server started".to_string(), "chris@jean-marie.ca".to_string(), "Server has been started".to_string()); + //let recipients = get_useremails_by_role("admin".to_string(), &app_state.db_pool).await; + //send_emails("Server started".to_string(), recipients, "Server has been started".to_string()); // run it let addr = SocketAddr::from(([0, 0, 0, 0], 40192)); diff --git a/backend/src/user.rs b/backend/src/user.rs index f96a0f8..b35347b 100644 --- a/backend/src/user.rs +++ b/backend/src/user.rs @@ -8,11 +8,11 @@ use chrono::Utc; ///User related structs and functions use serde::{Deserialize, Serialize}; -use sqlx::{prelude::FromRow, SqlitePool}; +use sqlx::{FromRow, SqlitePool}; use crate::middlewares::is_authorized; -#[derive(Default, Clone, Debug, FromRow, Serialize, Deserialize)] +#[derive(Default, Clone, Debug, Serialize, Deserialize, FromRow)] pub struct UserData { pub id: i64, pub created_at: i64, @@ -36,7 +36,7 @@ pub struct RoleData { pub description: String, } -#[derive(Default, Clone, Debug, FromRow, Serialize, Deserialize)] +#[derive(Default, Clone, Debug, Serialize, Deserialize)] pub struct UserRoles { pub id: i64, pub created_at: i64, @@ -47,7 +47,7 @@ pub struct UserRoles { pub role_id: i64, } -#[derive(Default, Clone, Debug, FromRow, Serialize, Deserialize)] +#[derive(Default, Clone, Debug, Serialize, Deserialize, FromRow)] pub struct UserRolesDisplay { pub id: i64, pub created_at: i64, @@ -60,7 +60,7 @@ pub struct UserRolesDisplay { pub role_name: String, } -#[derive(Default, Clone, Debug, FromRow, Serialize, Deserialize)] +#[derive(Default, Clone, Debug, Serialize, Deserialize, FromRow)] pub struct UserWishlistItem { pub id: i64, pub created_at: i64, @@ -135,7 +135,7 @@ pub async fn add_user_role( .unwrap(); // TODO - send email to user regarding role changes - + let redirect_url = format!("/users/{user_id}"); Redirect::to(&redirect_url).into_response() } else { @@ -165,7 +165,8 @@ pub async fn delete_user_role( pub async fn get_user_wishlist_items(user_id: i64, db_pool: &SqlitePool) -> Vec { // Get wish list items for the user let user_wishlist_items = sqlx::query_as( - r#"select id, created_at, created_by, updated_at, updated_by, user_id, item, item_url, purchased_by, received_at from wishlist_items where user_id = ?"# + r#"select id, created_at, created_by, updated_at, updated_by, user_id, item, item_url, purchased_by, received_at + from wishlist_items where user_id = ?"# ) .bind(user_id) .fetch_all(db_pool) @@ -174,3 +175,13 @@ pub async fn get_user_wishlist_items(user_id: i64, db_pool: &SqlitePool) -> Vec< user_wishlist_items } + +pub async fn get_useremails_by_role(role_name: String, db_pool: &SqlitePool) -> String { + let useremails: String = sqlx::query_scalar(r#"select group_concat(u.email) as email from user_roles ur, roles r, users u where u.id = ur.user_id and r.id = ur.role_id and r.name = ?"#) + .bind(role_name) + .fetch_one(db_pool) + .await + .unwrap(); + + useremails +} \ No newline at end of file