Implemented mail notifications.

This commit is contained in:
2024-02-12 17:17:26 +01:00
parent d18ef72d03
commit 1b6f544e55
9 changed files with 720 additions and 78 deletions
+106 -15
View File
@@ -13,8 +13,16 @@ cfg_if! { if #[cfg(feature = "ssr")] {
use std::ops::DerefMut;
use std::str::FromStr;
use futures_util::future::join_all;
use crate::backend::data::{ReservationSum, ReservationState, ResWithProperty, Customer};
use log::warn;
use crate::backend::data::{ReservationSum, ReservationState, ResWithProperty, Customer, Message, MessageType};
use crate::backend::get_pool;
use crate::backend::get_mailing;
use crate::backend::mail::MailMessage;
use crate::backend::mail::get_message;
use crate::error::AppError;
use sqlx::PgPool;
use crate::backend::user::admin_email;
use crate::backend::user::emails_for_notify;
async fn find_sum_by_uuid(uuid: &Uuid, tx: &mut Transaction<'_, Postgres>) -> Result<ReservationSum, Error> {
let reservation = query_as::<_, ReservationSum>("SELECT * FROM reservation_sum WHERE uuid = $1")
@@ -43,6 +51,39 @@ cfg_if! { if #[cfg(feature = "ssr")] {
}
}
async fn reservation_by_uuid(uuid: Uuid) -> Result<ResSumWithItems, ServerFnError> {
let pool = get_pool().await?;
let summary = query_as::<_, ReservationSum>("SELECT * FROM reservation_sum WHERE uuid = $1")
.bind(uuid)
.fetch_one(&pool)
.await?;
let sum_id = summary.id();
let cust_id = summary.customer;
Ok(ResSumWithItems{
summary,
customer: customer_for_reservation(cust_id, &pool).await?,
reservations: items_for_reservation(sum_id, &pool).await?
})
}
async fn items_for_reservation(id: i32, pool: &PgPool) -> Result<Vec<ResWithProperty>, ServerFnError> {
Ok(query_as::<_, ResWithProperty>(
"SELECT r.id, r.from, r.to, r.property, r.summary, p.name, p,description \
FROM reservation as r \
JOIN property as p ON r.property = p.id WHERE r.summary = $1")
.bind(id)
.fetch_all(pool)
.await?)
}
async fn customer_for_reservation(id: i32, pool: &PgPool) -> Result<Customer, ServerFnError> {
Ok(query_as::<_, Customer>("SELECT * FROM customer WHERE id = $1")
.bind(id)
.fetch_one(pool)
.await?)
}
async fn reservations_in_range(from: &NaiveDate, to: &NaiveDate, state: Option<ReservationState>) -> Result<Vec<ResSumWithItems>, ServerFnError> {
let pool = get_pool().await?;
let sums = if let Some(s) = state {
@@ -74,18 +115,9 @@ cfg_if! { if #[cfg(feature = "ssr")] {
return Ok(vec![])
}
let res: Result<Vec<ResSumWithItems>, Error> = join_all(sums.into_iter().map(|s| async {
let reservations = query_as::<_, ResWithProperty>(
"SELECT r.id, r.from, r.to, r.property, r.summary, p.name, p,description \
FROM reservation as r \
JOIN property as p ON r.property = p.id WHERE r.summary = $1")
.bind(s.id())
.fetch_all(&pool)
.await?;
let customer = query_as::<_, Customer>("SELECT * FROM customer WHERE id = $1")
.bind(s.customer)
.fetch_one(&pool)
.await?;
let res: Result<Vec<ResSumWithItems>, ServerFnError> = join_all(sums.into_iter().map(|s| async {
let reservations = items_for_reservation(s.id(), &pool).await?;
let customer = customer_for_reservation(s.customer, &pool).await?;
Ok(ResSumWithItems {
summary: s,
customer,
@@ -106,6 +138,51 @@ cfg_if! { if #[cfg(feature = "ssr")] {
Ok(())
}
async fn notify_new_all(admin_mail: String, reservation: &ResSumWithItems) -> Result<(), AppError> {
let mailing = get_mailing().await?;
let msg = get_message(MessageType::NewReservation).await?;
for m in emails_for_notify().await? {
mailing.send_mail(MailMessage::new(admin_mail.clone(), m, msg.clone(), reservation)).await?;
}
Ok(())
}
async fn notify_new(uuid: Uuid) -> Result<(), AppError> {
let mailing = get_mailing().await?;
let msg = get_message(MessageType::NewReservationCust).await?;
let reservation = reservation_by_uuid(uuid).await?;
let admin_mail = admin_email().await;
if admin_mail.is_none() {
return Err(AppError::MailSendError("No admin mail".to_string()))
}
mailing.send_mail(MailMessage::new(admin_mail.clone().unwrap(), reservation.customer.email.clone(), msg, &reservation)).await?;
notify_new_all(admin_mail.unwrap(), &reservation).await
}
async fn send_notify(uuid: Uuid, msg: Message) -> Result<(), AppError> {
let mailing = get_mailing().await?;
let reservation = reservation_by_uuid(uuid).await?;
let admin_mail = admin_email().await;
if admin_mail.is_none() {
return Err(AppError::MailSendError("No admin mail".to_string()))
}
mailing.send_mail(MailMessage::new(admin_mail.unwrap(), reservation.customer.email.clone(), msg, &reservation)).await
}
async fn notify_approve(uuid: Uuid) -> Result<(), AppError> {
send_notify(uuid, get_message(MessageType::ReservationApp).await?).await
}
async fn notify_cancel(uuid: Uuid) -> Result<(), AppError> {
send_notify(uuid, get_message(MessageType::ReservationCanceled).await?).await
}
}}
#[server]
@@ -206,6 +283,10 @@ pub async fn create_reservation(reservation: CrReservation) -> Result<ApiRespons
tx.commit().await?;
if let Err(e) = notify_new(res_uuid).await {
warn!("Notification not send: {}", e);
}
Ok(ApiResponse::Data(reservation.date()))
}
@@ -247,7 +328,12 @@ pub async fn approve(uuid: String) -> Result<ApiResponse<()>, ServerFnError> {
use crate::backend::data::ReservationState;
perm_check!(is_logged_in);
set_state(Uuid::parse_str(&uuid)?, ReservationState::Approved).await?;
let uuid = Uuid::parse_str(&uuid)?;
set_state(uuid, ReservationState::Approved).await?;
if let Err(e) = notify_approve(uuid).await {
warn!("Approve notification not send: {}", e);
}
Ok(ApiResponse::Data(()))
}
@@ -258,7 +344,12 @@ pub async fn cancel(uuid: String) -> Result<ApiResponse<()>, ServerFnError> {
use crate::backend::data::ReservationState;
perm_check!(is_logged_in);
set_state(Uuid::parse_str(&uuid)?, ReservationState::Canceled).await?;
let uuid = Uuid::parse_str(&uuid)?;
set_state(uuid, ReservationState::Canceled).await?;
if let Err(e) = notify_cancel(uuid).await {
warn!("Cancel notification not send: {}", e);
}
Ok(ApiResponse::Data(()))
}