2024-02-10 19:52:20 +01:00
|
|
|
use cfg_if::cfg_if;
|
|
|
|
|
use leptos::*;
|
|
|
|
|
use validator::Validate;
|
|
|
|
|
use crate::backend::data::{ApiResponse, Message, MessageType};
|
|
|
|
|
use crate::components::data_form::ForValidation;
|
|
|
|
|
|
|
|
|
|
cfg_if! { if #[cfg(feature = "ssr")] {
|
|
|
|
|
use sqlx::{PgPool, query, query_as};
|
|
|
|
|
use sqlx::Error;
|
|
|
|
|
use crate::backend::get_pool;
|
|
|
|
|
use crate::error::AppError;
|
|
|
|
|
use log::info;
|
2024-02-12 17:17:26 +01:00
|
|
|
use crate::config::Mailing;
|
|
|
|
|
use crate::config::MailTransport;
|
|
|
|
|
use crate::backend::data::ResSumWithItems;
|
|
|
|
|
use lettre::message::Message as LettreMessage;
|
|
|
|
|
use lettre::{AsyncSmtpTransport, AsyncFileTransport, AsyncTransport, Tokio1Executor};
|
|
|
|
|
use lettre::transport::smtp::authentication::Credentials;
|
|
|
|
|
use std::ops::Add;
|
2024-02-10 19:52:20 +01:00
|
|
|
|
|
|
|
|
pub async fn message_for_type(msg_type: &MessageType, pool: &PgPool) -> Result<Message, Error> {
|
|
|
|
|
Ok(query_as::<_, Message>("SELECT * FROM message WHERE msg_type = $1")
|
|
|
|
|
.bind(msg_type)
|
|
|
|
|
.fetch_one(pool)
|
|
|
|
|
.await?)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn init_message(msg_type: &MessageType, pool: &PgPool) -> Result<(), Error> {
|
|
|
|
|
query("INSERT INTO message(msg_type, subject, text) VALUES($1, '', '')")
|
|
|
|
|
.bind(msg_type)
|
|
|
|
|
.execute(pool)
|
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn check_messages(pool: &PgPool) -> Result<(), AppError> {
|
|
|
|
|
let types = [
|
|
|
|
|
MessageType::NewReservation,
|
|
|
|
|
MessageType::NewReservationCust,
|
|
|
|
|
MessageType::ReservationApp,
|
|
|
|
|
MessageType::ReservationCanceled];
|
|
|
|
|
|
|
|
|
|
for msg_type in types {
|
|
|
|
|
let msg = message_for_type(&msg_type, pool).await;
|
|
|
|
|
if let Err(e) = msg {
|
|
|
|
|
if matches!(e, Error::RowNotFound) {
|
|
|
|
|
info!("Creating initial message for type {:?}", msg_type);
|
|
|
|
|
init_message(&msg_type, pool).await?;
|
|
|
|
|
} else {
|
|
|
|
|
return Err(e.into())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2024-02-12 17:17:26 +01:00
|
|
|
|
|
|
|
|
pub struct MailMessage {
|
|
|
|
|
reply_to: String,
|
|
|
|
|
to: String,
|
|
|
|
|
subject: String,
|
|
|
|
|
text: String
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl MailMessage {
|
|
|
|
|
pub fn new(reply_to: String, to: String, message: Message, reservation: &ResSumWithItems) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
reply_to,
|
|
|
|
|
to,
|
|
|
|
|
subject: message.subject,
|
|
|
|
|
text: Self::replace_body_vars(message.text, &reservation)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn replace_body_vars(text: String, reservation: &ResSumWithItems) -> String {
|
|
|
|
|
text
|
|
|
|
|
.replace("#date#", &reservation.summary.date.format("%d. %m. %Y").to_string())
|
|
|
|
|
.replace("#summary#", &{
|
|
|
|
|
let mut sum = "".to_string();
|
|
|
|
|
for p in &reservation.reservations {
|
|
|
|
|
sum = sum.add(&format!("{}: {} - {}",
|
|
|
|
|
p.property.name,
|
|
|
|
|
p.reservation.from.format("%H:%M").to_string(), p.reservation.to.format("%H:%M").to_string()));
|
|
|
|
|
}
|
|
|
|
|
sum
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn build_mail(&self, from: String) -> Result<LettreMessage, AppError> {
|
|
|
|
|
Ok(LettreMessage::builder()
|
|
|
|
|
.from(from.parse()?)
|
|
|
|
|
.reply_to(self.reply_to.parse()?)
|
|
|
|
|
.to(self.to.parse()?)
|
|
|
|
|
.subject(&self.subject)
|
|
|
|
|
.body(self.text.clone())?)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Mailing {
|
|
|
|
|
pub async fn send_mail(&self, msg: MailMessage) -> Result<(), AppError> {
|
|
|
|
|
match self.transport() {
|
|
|
|
|
MailTransport::Smtp => {
|
|
|
|
|
let transport = if self.tls().unwrap_or(false) {
|
|
|
|
|
AsyncSmtpTransport::<Tokio1Executor>::starttls_relay(&self.server().clone().unwrap_or_default())
|
|
|
|
|
} else {
|
|
|
|
|
AsyncSmtpTransport::<Tokio1Executor>::relay(&self.server().clone().unwrap_or_default())
|
|
|
|
|
}.expect("Cannot create SMTP mail transport");
|
2024-02-16 11:22:34 +01:00
|
|
|
let transport = if let Some(p) = self.port() {
|
|
|
|
|
transport.port(p)
|
|
|
|
|
} else {
|
|
|
|
|
transport
|
|
|
|
|
};
|
2024-02-12 17:17:26 +01:00
|
|
|
if self.user().is_some() && self.password().is_some() {
|
|
|
|
|
let cred = Credentials::new(self.user().clone().unwrap(), self.password().clone().unwrap());
|
|
|
|
|
transport.credentials(cred).build().send(msg.build_mail(self.from().to_string())?).await?;
|
|
|
|
|
} else {
|
|
|
|
|
transport.build().send(msg.build_mail(self.from().to_string())?).await?;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
MailTransport::File => {
|
|
|
|
|
AsyncFileTransport::<Tokio1Executor>::new(self.path().clone().unwrap_or_default())
|
|
|
|
|
.send(msg.build_mail(self.from().to_string())?).await?;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-02-10 19:52:20 +01:00
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
#[server]
|
|
|
|
|
pub async fn get_message(msg_type: MessageType) -> Result<Message, ServerFnError> {
|
|
|
|
|
let pool = get_pool().await?;
|
|
|
|
|
|
|
|
|
|
Ok(message_for_type(&msg_type, &pool).await?)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[server]
|
|
|
|
|
pub async fn update_message(message: Message) -> Result<ApiResponse<()>, ServerFnError> {
|
|
|
|
|
use crate::perm_check;
|
|
|
|
|
|
|
|
|
|
perm_check!(is_admin);
|
|
|
|
|
let pool = get_pool().await?;
|
|
|
|
|
|
|
|
|
|
query("UPDATE message SET subject = $1, text = $2 WHERE msg_type = $3")
|
|
|
|
|
.bind(message.subject)
|
|
|
|
|
.bind(message.text)
|
|
|
|
|
.bind(message.msg_type)
|
|
|
|
|
.execute(&pool)
|
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
|
|
Ok(ApiResponse::Data(()))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ForValidation for UpdateMessage {
|
|
|
|
|
fn entity(&self) -> &dyn Validate {
|
|
|
|
|
&self.message
|
|
|
|
|
}
|
|
|
|
|
}
|