feat: add mail client
Untested
This commit is contained in:
parent
16ccf94631
commit
862e2d6709
6 changed files with 96 additions and 5 deletions
|
@ -37,6 +37,7 @@ diesel-async = { version = "0.5", features = ["deadpool", "postgres", "async-con
|
|||
diesel_migrations = { version = "2.2.0", features = ["postgres"] }
|
||||
thiserror = "2.0.12"
|
||||
actix-multipart = "0.7.2"
|
||||
lettre = { version = "0.11.16", features = ["tokio1", "tokio1-native-tls"] }
|
||||
|
||||
[dependencies.tokio]
|
||||
version = "1.44"
|
||||
|
|
|
@ -33,7 +33,7 @@ pub async fn get(req: HttpRequest, data: web::Data<Data>) -> Result<HttpResponse
|
|||
struct NewInfo {
|
||||
username: Option<String>,
|
||||
display_name: Option<String>,
|
||||
password: Option<String>,
|
||||
//password: Option<String>, will probably be handled through a reset password link
|
||||
email: Option<String>,
|
||||
}
|
||||
|
||||
|
@ -83,10 +83,6 @@ pub async fn update(
|
|||
todo!();
|
||||
}
|
||||
|
||||
if let Some(password) = &new_info.password {
|
||||
todo!();
|
||||
}
|
||||
|
||||
if let Some(email) = &new_info.email {
|
||||
todo!();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::error::Error;
|
||||
use bunny_api_tokio::edge_storage::Endpoint;
|
||||
use lettre::transport::smtp::authentication::Credentials;
|
||||
use log::debug;
|
||||
use serde::Deserialize;
|
||||
use tokio::fs::read_to_string;
|
||||
|
@ -12,6 +13,7 @@ pub struct ConfigBuilder {
|
|||
web: Option<WebBuilder>,
|
||||
instance: Option<Instance>,
|
||||
bunny: BunnyBuilder,
|
||||
mail: Mail,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
|
@ -52,6 +54,20 @@ struct BunnyBuilder {
|
|||
cdn_url: Url,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct Mail {
|
||||
pub smtp: Smtp,
|
||||
pub from: String,
|
||||
pub tls: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct Smtp {
|
||||
pub server: String,
|
||||
username: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
impl ConfigBuilder {
|
||||
pub async fn load(path: String) -> Result<Self, Error> {
|
||||
debug!("loading config from: {}", path);
|
||||
|
@ -101,6 +117,7 @@ impl ConfigBuilder {
|
|||
web,
|
||||
instance: self.instance.unwrap_or(Instance { registration: true }),
|
||||
bunny,
|
||||
mail: self.mail,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -112,6 +129,7 @@ pub struct Config {
|
|||
pub web: Web,
|
||||
pub instance: Instance,
|
||||
pub bunny: Bunny,
|
||||
pub mail: Mail,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -179,3 +197,9 @@ impl CacheDatabase {
|
|||
url
|
||||
}
|
||||
}
|
||||
|
||||
impl Smtp {
|
||||
pub fn credentials(&self) -> Credentials {
|
||||
Credentials::new(self.username.clone(), self.password.clone())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ use serde_json::Error as JsonError;
|
|||
use thiserror::Error;
|
||||
use tokio::task::JoinError;
|
||||
use toml::de::Error as TomlError;
|
||||
use lettre::{address::AddressError, transport::smtp::Error as SmtpError};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
|
@ -54,6 +55,10 @@ pub enum Error {
|
|||
PayloadError(#[from] PayloadError),
|
||||
#[error(transparent)]
|
||||
WsClosed(#[from] actix_ws::Closed),
|
||||
#[error(transparent)]
|
||||
SmtpError(#[from] SmtpError),
|
||||
#[error(transparent)]
|
||||
SmtpAddressError(#[from] AddressError),
|
||||
#[error("{0}")]
|
||||
PasswordHashError(String),
|
||||
#[error("{0}")]
|
||||
|
|
|
@ -6,6 +6,7 @@ use diesel_async::pooled_connection::AsyncDieselConnectionManager;
|
|||
use diesel_async::pooled_connection::deadpool::Pool;
|
||||
use error::Error;
|
||||
use simple_logger::SimpleLogger;
|
||||
use structs::MailClient;
|
||||
use std::time::SystemTime;
|
||||
mod config;
|
||||
use config::{Config, ConfigBuilder};
|
||||
|
@ -40,6 +41,7 @@ pub struct Data {
|
|||
pub argon2: Argon2<'static>,
|
||||
pub start_time: SystemTime,
|
||||
pub bunny_cdn: bunny_api_tokio::Client,
|
||||
pub mail_client: MailClient,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
|
@ -72,6 +74,10 @@ async fn main() -> Result<(), Error> {
|
|||
.init(bunny.api_key, bunny.endpoint, bunny.storage_zone)
|
||||
.await?;
|
||||
|
||||
let mail = config.mail.clone();
|
||||
|
||||
let mail_client = MailClient::new(mail.smtp.credentials(), mail.smtp.server, mail.from, mail.tls)?;
|
||||
|
||||
let database_url = config.database.url();
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
|
@ -112,6 +118,7 @@ async fn main() -> Result<(), Error> {
|
|||
argon2: Argon2::default(),
|
||||
start_time: SystemTime::now(),
|
||||
bunny_cdn,
|
||||
mail_client,
|
||||
};
|
||||
|
||||
HttpServer::new(move || {
|
||||
|
|
|
@ -5,6 +5,8 @@ use diesel::{
|
|||
update,
|
||||
};
|
||||
use diesel_async::{RunQueryDsl, pooled_connection::AsyncDieselConnectionManager};
|
||||
use lettre::{message::{Mailbox, MessageBuilder as EmailBuilder}, transport::smtp::authentication::Credentials, AsyncSmtpTransport, AsyncTransport, Message as Email, Tokio1Executor};
|
||||
use log::debug;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::task;
|
||||
use url::Url;
|
||||
|
@ -35,6 +37,62 @@ fn load_or_empty<T>(
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
pub enum MailTls {
|
||||
StartTls,
|
||||
Tls,
|
||||
}
|
||||
|
||||
impl From<String> for MailTls {
|
||||
fn from(value: String) -> Self {
|
||||
match &*value.to_lowercase() {
|
||||
"starttls" => Self::StartTls,
|
||||
_ => Self::Tls,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MailClient {
|
||||
creds: Credentials,
|
||||
smtp_server: String,
|
||||
mbox: Mailbox,
|
||||
tls: MailTls,
|
||||
}
|
||||
|
||||
impl MailClient {
|
||||
pub fn new<T: Into<MailTls>>(creds: Credentials, smtp_server: String, mbox: String, tls: T) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
creds,
|
||||
smtp_server,
|
||||
mbox: mbox.parse()?,
|
||||
tls: tls.into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn message_builder(&self) -> EmailBuilder {
|
||||
Email::builder()
|
||||
.from(self.mbox.clone())
|
||||
}
|
||||
|
||||
pub async fn send_mail(&self, email: Email) -> Result<(), Error> {
|
||||
let mailer: AsyncSmtpTransport<Tokio1Executor> = match self.tls {
|
||||
MailTls::StartTls => AsyncSmtpTransport::<Tokio1Executor>::starttls_relay(&self.smtp_server)?
|
||||
.credentials(self.creds.clone())
|
||||
.build(),
|
||||
MailTls::Tls => AsyncSmtpTransport::<Tokio1Executor>::relay(&self.smtp_server)?
|
||||
.credentials(self.creds.clone())
|
||||
.build(),
|
||||
};
|
||||
|
||||
let response = mailer.send(email).await?;
|
||||
|
||||
debug!("mail sending response: {:?}", response);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Queryable, Selectable, Insertable, Clone, Debug)]
|
||||
#[diesel(table_name = channels)]
|
||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue