|
|
|
@ -10,15 +10,18 @@ cfg_if! { if #[cfg(feature = "ssr")] {
|
|
|
|
|
use crate::backend::get_pool;
|
|
|
|
|
use crate::error::AppError;
|
|
|
|
|
use log::info;
|
|
|
|
|
use log::error;
|
|
|
|
|
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::{FileTransport, SmtpTransport, Transport};
|
|
|
|
|
use lettre::transport::smtp::client::{Tls, TlsParameters};
|
|
|
|
|
use lettre::transport::smtp::authentication::Credentials;
|
|
|
|
|
use lettre::transport::smtp::extension::ClientId;
|
|
|
|
|
use std::ops::Add;
|
|
|
|
|
use std::sync::mpsc::Sender;
|
|
|
|
|
use std::sync::mpsc;
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
@ -100,13 +103,26 @@ cfg_if! { if #[cfg(feature = "ssr")] {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Mailing {
|
|
|
|
|
pub async fn send_mail(&self, msg: MailMessage) -> Result<(), AppError> {
|
|
|
|
|
match self.transport() {
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
enum MailerType {
|
|
|
|
|
Smtp(SmtpTransport),
|
|
|
|
|
File(String)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct Mailer {
|
|
|
|
|
transport: MailerType,
|
|
|
|
|
from: String,
|
|
|
|
|
sender: Option<Sender<MailMessage>>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Mailer {
|
|
|
|
|
pub fn new(config: &Mailing) -> Self {
|
|
|
|
|
match config.transport() {
|
|
|
|
|
MailTransport::Smtp => {
|
|
|
|
|
let tls = if let Some(t) = self.accept_all_certs() {
|
|
|
|
|
let tls = if let Some(t) = config.accept_all_certs() {
|
|
|
|
|
if t {
|
|
|
|
|
let tls = TlsParameters::builder(self.server().clone().unwrap_or_default())
|
|
|
|
|
let tls = TlsParameters::builder(config.server().clone().unwrap_or_default())
|
|
|
|
|
.dangerous_accept_invalid_certs(true)
|
|
|
|
|
.dangerous_accept_invalid_hostnames(true);
|
|
|
|
|
Some(tls.build().expect("Cannot build TLS params"))
|
|
|
|
@ -116,8 +132,8 @@ cfg_if! { if #[cfg(feature = "ssr")] {
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
};
|
|
|
|
|
let transport = if self.tls().unwrap_or(false) {
|
|
|
|
|
let transport = AsyncSmtpTransport::<Tokio1Executor>::starttls_relay(&self.server().clone().unwrap_or_default())
|
|
|
|
|
let transport = if config.tls().unwrap_or(false) {
|
|
|
|
|
let transport = SmtpTransport::starttls_relay(&config.server().clone().unwrap_or_default())
|
|
|
|
|
.expect("Cannot create SMTP mail transport");
|
|
|
|
|
if let Some(t) = tls {
|
|
|
|
|
transport.tls(Tls::Required(t))
|
|
|
|
@ -125,7 +141,7 @@ cfg_if! { if #[cfg(feature = "ssr")] {
|
|
|
|
|
transport
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
let transport = AsyncSmtpTransport::<Tokio1Executor>::relay(&self.server().clone().unwrap_or_default())
|
|
|
|
|
let transport = SmtpTransport::relay(&config.server().clone().unwrap_or_default())
|
|
|
|
|
.expect("Cannot create SMTP mail transport");
|
|
|
|
|
if let Some(t) = tls {
|
|
|
|
|
transport.tls(Tls::Wrapper(t))
|
|
|
|
@ -133,30 +149,85 @@ cfg_if! { if #[cfg(feature = "ssr")] {
|
|
|
|
|
transport
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
let transport = if let Some(p) = self.port() {
|
|
|
|
|
let transport = if let Some(p) = config.port() {
|
|
|
|
|
transport.port(p)
|
|
|
|
|
} else {
|
|
|
|
|
transport
|
|
|
|
|
};
|
|
|
|
|
let transport = if let Some(hello) = self.hello_name() {
|
|
|
|
|
let transport = if let Some(hello) = config.hello_name() {
|
|
|
|
|
transport.hello_name(ClientId::Domain(hello.to_string()))
|
|
|
|
|
} else {
|
|
|
|
|
transport
|
|
|
|
|
};
|
|
|
|
|
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?;
|
|
|
|
|
if config.user().is_some() && config.password().is_some() {
|
|
|
|
|
let cred = Credentials::new(config.user().clone().unwrap(), config.password().clone().unwrap());
|
|
|
|
|
Self {
|
|
|
|
|
transport: MailerType::Smtp(transport.credentials(cred).build()),
|
|
|
|
|
from: config.from().to_string(),
|
|
|
|
|
sender: None
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
transport.build().send(msg.build_mail(self.from().to_string())?).await?;
|
|
|
|
|
Self {
|
|
|
|
|
transport: MailerType::Smtp(transport.build()),
|
|
|
|
|
from: config.from().to_string(),
|
|
|
|
|
sender: None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
MailTransport::File => {
|
|
|
|
|
AsyncFileTransport::<Tokio1Executor>::new(self.path().clone().unwrap_or_default())
|
|
|
|
|
.send(msg.build_mail(self.from().to_string())?).await?;
|
|
|
|
|
Self {
|
|
|
|
|
transport: MailerType::File(config.path().clone().unwrap_or("".to_string())),
|
|
|
|
|
from: config.from().to_string(),
|
|
|
|
|
sender: None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn send(&self, msg: &MailMessage) -> Result<(), AppError> {
|
|
|
|
|
let to_send = msg.build_mail(self.from.clone())?;
|
|
|
|
|
match &self.transport {
|
|
|
|
|
MailerType::Smtp(s) => {s.send(&to_send)?;},
|
|
|
|
|
MailerType::File(s) => {FileTransport::new(s).send(&to_send)?;}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn start_sender(&mut self) {
|
|
|
|
|
let (tx, rx) = mpsc::channel::<MailMessage>();
|
|
|
|
|
self.sender = Some(tx);
|
|
|
|
|
let mailer = self.clone();
|
|
|
|
|
|
|
|
|
|
std::thread::spawn(move || {
|
|
|
|
|
loop {
|
|
|
|
|
let msg = rx.recv();
|
|
|
|
|
if let Err(e) = msg {
|
|
|
|
|
error!("Mailer error: {}", e.to_string());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let msg = msg.unwrap();
|
|
|
|
|
|
|
|
|
|
if let Err(e) = mailer.send(&msg) {
|
|
|
|
|
error!("Mail send error: {}", e);
|
|
|
|
|
} else {
|
|
|
|
|
info!("Mail message for: {} has been successfully sent", msg.to);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
info!("Mail sender started");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn send_mail(&self, msg: MailMessage) -> Result<(), AppError> {
|
|
|
|
|
if let Err(e) = self.sender.as_ref().expect("Sender not started").send(msg) {
|
|
|
|
|
error!("Mail queue error: {}", e);
|
|
|
|
|
Err(AppError::MailSendError(e.to_string()))
|
|
|
|
|
} else {
|
|
|
|
|
info!("Message queued for send");
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
|
|