refactor: use new mailer struct
This commit is contained in:
parent
760e19522b
commit
8b470f2d4f
@ -1,11 +1,11 @@
|
|||||||
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
|
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
|
||||||
use lettre::{SmtpTransport, Transport};
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
mail::Mailer,
|
||||||
models::{Function, Registration, Role, User},
|
models::{Function, Registration, Role, User},
|
||||||
utils::{email, ApplicationError},
|
utils::ApplicationError,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@ -22,7 +22,7 @@ pub async fn post_new(
|
|||||||
user: web::ReqData<User>,
|
user: web::ReqData<User>,
|
||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
form: web::Form<NewUserForm>,
|
form: web::Form<NewUserForm>,
|
||||||
mailer: web::Data<SmtpTransport>,
|
mailer: web::Data<Mailer>,
|
||||||
) -> Result<impl Responder, ApplicationError> {
|
) -> Result<impl Responder, ApplicationError> {
|
||||||
if user.role != Role::AreaManager && user.role != Role::Admin {
|
if user.role != Role::AreaManager && user.role != Role::Admin {
|
||||||
return Err(ApplicationError::Unauthorized);
|
return Err(ApplicationError::Unauthorized);
|
||||||
@ -49,9 +49,7 @@ pub async fn post_new(
|
|||||||
|
|
||||||
let registration = Registration::insert_new_for_user(pool.get_ref(), id).await?;
|
let registration = Registration::insert_new_for_user(pool.get_ref(), id).await?;
|
||||||
let new_user = User::read_by_id(pool.get_ref(), id).await?.unwrap();
|
let new_user = User::read_by_id(pool.get_ref(), id).await?.unwrap();
|
||||||
let message = email::build_registration_message(&new_user, ®istration.token)?;
|
mailer.send_registration_mail(&new_user, ®istration.token)?;
|
||||||
|
|
||||||
mailer.send(&message)?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Found()
|
Ok(HttpResponse::Found()
|
||||||
.insert_header((LOCATION, "/users"))
|
.insert_header((LOCATION, "/users"))
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
use actix_web::{web, HttpResponse, Responder};
|
use actix_web::{web, HttpResponse, Responder};
|
||||||
use lettre::{SmtpTransport, Transport};
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
endpoints::user::handle_password_change_request,
|
endpoints::user::handle_password_change_request,
|
||||||
|
mail::Mailer,
|
||||||
models::{PasswordReset, User},
|
models::{PasswordReset, User},
|
||||||
utils::{email, ApplicationError},
|
utils::ApplicationError,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
@ -22,7 +22,7 @@ struct ResetPasswordForm {
|
|||||||
async fn post(
|
async fn post(
|
||||||
form: web::Form<ResetPasswordForm>,
|
form: web::Form<ResetPasswordForm>,
|
||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
mailer: web::Data<SmtpTransport>,
|
mailer: web::Data<Mailer>,
|
||||||
) -> Result<impl Responder, ApplicationError> {
|
) -> Result<impl Responder, ApplicationError> {
|
||||||
if form.email.is_some()
|
if form.email.is_some()
|
||||||
&& form.token.is_none()
|
&& form.token.is_none()
|
||||||
@ -31,10 +31,7 @@ async fn post(
|
|||||||
{
|
{
|
||||||
if let Ok(user) = User::read_for_login(pool.get_ref(), form.email.as_ref().unwrap()).await {
|
if let Ok(user) = User::read_for_login(pool.get_ref(), form.email.as_ref().unwrap()).await {
|
||||||
let reset = PasswordReset::insert_new_for_user(pool.get_ref(), user.id).await?;
|
let reset = PasswordReset::insert_new_for_user(pool.get_ref(), user.id).await?;
|
||||||
|
mailer.send_forgot_password_mail(&user, &reset.token)?;
|
||||||
let message = email::build_forgot_password_message(&user, &reset.token);
|
|
||||||
|
|
||||||
mailer.send(&message)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(HttpResponse::Ok().body("E-Mail versandt!"));
|
return Ok(HttpResponse::Ok().body("E-Mail versandt!"));
|
||||||
|
@ -0,0 +1,96 @@
|
|||||||
|
use lettre::{
|
||||||
|
message::{Mailbox, MultiPart, SinglePart},
|
||||||
|
Message, Transport,
|
||||||
|
};
|
||||||
|
use rinja::Template;
|
||||||
|
|
||||||
|
use crate::{models::User, utils::ApplicationError};
|
||||||
|
|
||||||
|
use super::Mailer;
|
||||||
|
|
||||||
|
impl Mailer {
|
||||||
|
pub fn send_forgot_password_mail(
|
||||||
|
&self,
|
||||||
|
user: &User,
|
||||||
|
token: &str,
|
||||||
|
) -> Result<(), ApplicationError> {
|
||||||
|
let message = build(&self.hostname, &user.name, &user.email, token)?;
|
||||||
|
self.transport.send(&message)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "emails/forgot_password.txt")]
|
||||||
|
struct ForgotPasswordMailTemplatePlain<'a> {
|
||||||
|
name: &'a str,
|
||||||
|
reset_url: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "emails/forgot_password.html")]
|
||||||
|
struct ForgotPasswordMailTemplateHtml<'a> {
|
||||||
|
name: &'a str,
|
||||||
|
reset_url: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build(
|
||||||
|
hostname: &str,
|
||||||
|
name: &str,
|
||||||
|
email: &str,
|
||||||
|
token: &str,
|
||||||
|
) -> Result<Message, ApplicationError> {
|
||||||
|
let reset_url = format!("https://{hostname}/reset-password?token={token}");
|
||||||
|
|
||||||
|
let plain = ForgotPasswordMailTemplatePlain {
|
||||||
|
name,
|
||||||
|
reset_url: &reset_url,
|
||||||
|
}
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let html = ForgotPasswordMailTemplateHtml {
|
||||||
|
name,
|
||||||
|
reset_url: &reset_url,
|
||||||
|
}
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let message = Message::builder()
|
||||||
|
.from("noreply <noreply@brasiwa-leipzig.de>".parse()?)
|
||||||
|
.reply_to("noreply <noreply@brasiwa-leipzig.de>".parse()?)
|
||||||
|
.to(Mailbox::new(Some(name.to_string()), email.parse()?))
|
||||||
|
.subject("Brass: Zurücksetzen des Passworts angefordert")
|
||||||
|
.multipart(
|
||||||
|
MultiPart::alternative()
|
||||||
|
.singlepart(SinglePart::plain(plain))
|
||||||
|
.singlepart(SinglePart::html(html)),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Ok(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::utils::test_helper::assert_mail_snapshot;
|
||||||
|
use lettre::transport::stub::StubTransport;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_mail_snapshot() {
|
||||||
|
let message = build(
|
||||||
|
"brasiwa-leipzig.de",
|
||||||
|
"Max Mustermann",
|
||||||
|
"max@example.com",
|
||||||
|
"123456789",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let sender = StubTransport::new_ok();
|
||||||
|
sender.send(&message).unwrap();
|
||||||
|
let messages = sender.messages();
|
||||||
|
let (_, sent_message) = messages.first().unwrap();
|
||||||
|
|
||||||
|
assert_mail_snapshot!(sent_message);
|
||||||
|
}
|
||||||
|
}
|
@ -11,14 +11,18 @@ use crate::utils::ApplicationError;
|
|||||||
|
|
||||||
mod forgot_password;
|
mod forgot_password;
|
||||||
mod registration;
|
mod registration;
|
||||||
|
mod testmail;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Mailer {
|
pub struct Mailer {
|
||||||
transport: Transports,
|
transport: Transports,
|
||||||
hostname: String,
|
hostname: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
enum Transports {
|
enum Transports {
|
||||||
SmtpTransport(SmtpTransport),
|
SmtpTransport(SmtpTransport),
|
||||||
|
#[allow(unused)]
|
||||||
StubTransport(StubTransport),
|
StubTransport(StubTransport),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,3 +79,14 @@ impl Mailer {
|
|||||||
Ok(mailer)
|
Ok(mailer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl Mailer {
|
||||||
|
pub fn new_stub() -> Self {
|
||||||
|
Mailer {
|
||||||
|
transport: Transports::StubTransport(StubTransport::new_ok()),
|
||||||
|
hostname: String::from("testhostname")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use lettre::{
|
use lettre::{
|
||||||
message::{header::ContentType, Mailbox, MultiPart, SinglePart},
|
message::{Mailbox, MultiPart, SinglePart},
|
||||||
Message, Transport,
|
Message, Transport,
|
||||||
};
|
};
|
||||||
use rinja::Template;
|
use rinja::Template;
|
||||||
@ -62,35 +62,34 @@ fn build(
|
|||||||
.subject("Brass: Registrierung deines Accounts")
|
.subject("Brass: Registrierung deines Accounts")
|
||||||
.multipart(
|
.multipart(
|
||||||
MultiPart::alternative()
|
MultiPart::alternative()
|
||||||
.singlepart(
|
.singlepart(SinglePart::plain(plain))
|
||||||
SinglePart::builder()
|
.singlepart(SinglePart::html(html)),
|
||||||
.header(ContentType::TEXT_PLAIN)
|
|
||||||
.body(plain),
|
|
||||||
)
|
|
||||||
.singlepart(
|
|
||||||
SinglePart::builder()
|
|
||||||
.header(ContentType::TEXT_HTML)
|
|
||||||
.body(html),
|
|
||||||
),
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(message)
|
Ok(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[cfg(test)]
|
||||||
fn build_mail_snapshot() {
|
mod tests {
|
||||||
let message = build(
|
use super::*;
|
||||||
"brasiwa-leipzig.de",
|
use crate::utils::test_helper::assert_mail_snapshot;
|
||||||
"Max Mustermann",
|
use lettre::transport::stub::StubTransport;
|
||||||
"max@example.com",
|
|
||||||
"123456789",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let sender = lettre::transport::stub::StubTransport::new_ok();
|
#[test]
|
||||||
sender.send(&message).unwrap();
|
fn build_mail_snapshot() {
|
||||||
let messages = sender.messages();
|
let message = build(
|
||||||
let (_, sent_message) = messages.first().unwrap();
|
"brasiwa-leipzig.de",
|
||||||
|
"Max Mustermann",
|
||||||
|
"max@example.com",
|
||||||
|
"123456789",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
crate::utils::test_helper::assert_mail_snapshot!(sent_message);
|
let sender = StubTransport::new_ok();
|
||||||
|
sender.send(&message).unwrap();
|
||||||
|
let messages = sender.messages();
|
||||||
|
let (_, sent_message) = messages.first().unwrap();
|
||||||
|
|
||||||
|
assert_mail_snapshot!(sent_message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
---
|
||||||
|
source: web/src/mail/forgot_password.rs
|
||||||
|
expression: sent_message
|
||||||
|
snapshot_kind: text
|
||||||
|
---
|
||||||
|
From: noreply <noreply@brasiwa-leipzig.de>
|
||||||
|
Reply-To: noreply <noreply@brasiwa-leipzig.de>
|
||||||
|
To: "Max Mustermann" <max@example.com>
|
||||||
|
Subject: Brass: =?utf-8?b?WnVyw7xja3NldHplbg==?= des Passworts angefordert
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Date: Date
|
||||||
|
Content-Type: multipart/alternative;
|
||||||
|
boundary="boundary"
|
||||||
|
|
||||||
|
--boundary
|
||||||
|
Content-Type: text/plain; charset=utf-8
|
||||||
|
Content-Transfer-Encoding: quoted-printable
|
||||||
|
|
||||||
|
Hallo Max Mustermann,
|
||||||
|
|
||||||
|
du hast angefordert, dein Passwort zur=C3=BCckzusetzen. Kopiere daf=C3=BCr =
|
||||||
|
folgenden Link in deinen Browser:
|
||||||
|
|
||||||
|
https://brasiwa-leipzig.de/reset-password?token=3D123456789
|
||||||
|
|
||||||
|
Bitte beachte, dass der Link nur 24 Stunden g=C3=BCltig ist. Solltest du ni=
|
||||||
|
chts angefordert haben, dann musst du nichts weiter tun.
|
||||||
|
|
||||||
|
Viele Gr=C3=BC=C3=9Fe
|
||||||
|
--boundary
|
||||||
|
Content-Type: text/html; charset=utf-8
|
||||||
|
Content-Transfer-Encoding: quoted-printable
|
||||||
|
|
||||||
|
<p>Hallo Max Mustermann,</p>
|
||||||
|
|
||||||
|
<p>du hast angefordert, dein Passwort zur=C3=BCckzusetzen. Klicke daf=C3=BC=
|
||||||
|
r <a href=3D"https://brasiwa-leipzig.de/reset-password?token=3D123456789" t=
|
||||||
|
arget=3D"_blank">hier</a> oder kopiere folgenden Link in deinen Browser:</p>
|
||||||
|
|
||||||
|
<p>https://brasiwa-leipzig.de/reset-password?token=3D123456789</p>
|
||||||
|
|
||||||
|
<p>Bitte beachte, dass der Link <b>nur 24 Stunden g=C3=BCltig</b> ist. Soll=
|
||||||
|
test du nichts angefordert haben, dann musst du nichts weiter tun.</p>
|
||||||
|
|
||||||
|
<p>Viele Gr=C3=BC=C3=9Fe</p>
|
||||||
|
--boundary--
|
21
web/src/mail/testmail.rs
Normal file
21
web/src/mail/testmail.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use lettre::{message::SinglePart, Message, Transport};
|
||||||
|
|
||||||
|
use crate::utils::ApplicationError;
|
||||||
|
|
||||||
|
use super::Mailer;
|
||||||
|
|
||||||
|
impl Mailer {
|
||||||
|
pub fn send_test_mail(&self, to: &str) -> Result<(), ApplicationError> {
|
||||||
|
let message = Message::builder()
|
||||||
|
.from("noreply <noreply@brasiwa-leipzig.de>".parse()?)
|
||||||
|
.reply_to("noreply <noreply@brasiwa-leipzig.de>".parse()?)
|
||||||
|
.to(to.parse()?)
|
||||||
|
.subject("Brass: Test E-Mail")
|
||||||
|
.singlepart(SinglePart::plain(
|
||||||
|
"Testmail von Brass. E-Mail Versand funktioniert!".to_string(),
|
||||||
|
))?;
|
||||||
|
|
||||||
|
self.transport.send(&message)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,7 @@ use actix_web::dev::{ServiceFactory, ServiceRequest, ServiceResponse};
|
|||||||
use actix_web::{web, App, HttpServer};
|
use actix_web::{web, App, HttpServer};
|
||||||
use actix_web_static_files::ResourceFiles;
|
use actix_web_static_files::ResourceFiles;
|
||||||
use brass_config::{get_env, load_config, Config};
|
use brass_config::{get_env, load_config, Config};
|
||||||
use lettre::Transport;
|
use mail::Mailer;
|
||||||
use sqlx::postgres::PgPool;
|
use sqlx::postgres::PgPool;
|
||||||
use sqlx::{Pool, Postgres};
|
use sqlx::{Pool, Postgres};
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
let args = parse_args()?;
|
let args = parse_args()?;
|
||||||
|
|
||||||
let pool = PgPool::connect(&config.database_url).await?;
|
let pool = PgPool::connect(&config.database_url).await?;
|
||||||
let mailer = utils::email::get_mailer(&config)?;
|
let mailer = Mailer::new(&config)?;
|
||||||
|
|
||||||
handle_command(args.command, &pool, &mailer).await?;
|
handle_command(args.command, &pool, &mailer).await?;
|
||||||
|
|
||||||
@ -52,10 +52,10 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_app<T>(
|
pub fn create_app(
|
||||||
config: Config,
|
config: Config,
|
||||||
pool: Pool<Postgres>,
|
pool: Pool<Postgres>,
|
||||||
mailer: T,
|
mailer: Mailer,
|
||||||
) -> App<
|
) -> App<
|
||||||
impl ServiceFactory<
|
impl ServiceFactory<
|
||||||
ServiceRequest,
|
ServiceRequest,
|
||||||
@ -64,7 +64,7 @@ pub fn create_app<T>(
|
|||||||
InitError = (),
|
InitError = (),
|
||||||
Error = actix_web::error::Error,
|
Error = actix_web::error::Error,
|
||||||
>,
|
>,
|
||||||
> where T: Transport + 'static {
|
> {
|
||||||
let generated = generate();
|
let generated = generate();
|
||||||
let secret_key = Key::from(config.secret_key.as_bytes());
|
let secret_key = Key::from(config.secret_key.as_bytes());
|
||||||
let store = SqlxPostgresqlSessionStore::from_pool(pool.clone().into());
|
let store = SqlxPostgresqlSessionStore::from_pool(pool.clone().into());
|
||||||
|
@ -1,114 +0,0 @@
|
|||||||
use std::env;
|
|
||||||
|
|
||||||
use brass_config::{Config, SmtpTlsType};
|
|
||||||
use lettre::{
|
|
||||||
message::{header::ContentType, Mailbox, MultiPart, SinglePart},
|
|
||||||
transport::smtp::{authentication::Credentials, extension::ClientId},
|
|
||||||
Message, SmtpTransport,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::models::User;
|
|
||||||
|
|
||||||
use super::ApplicationError;
|
|
||||||
|
|
||||||
pub fn get_mailer(config: &Config) -> anyhow::Result<SmtpTransport> {
|
|
||||||
let mut builder = match config.smtp_tlstype {
|
|
||||||
SmtpTlsType::StartTLS => SmtpTransport::starttls_relay(&config.smtp_server)?.port(config.smtp_port),
|
|
||||||
SmtpTlsType::TLS => SmtpTransport::relay(&config.smtp_server)?.port(config.smtp_port),
|
|
||||||
SmtpTlsType::NoTLS => SmtpTransport::builder_dangerous(&config.smtp_server).port(config.smtp_port),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let (Some(login), Some(password)) = (config.smtp_login.as_ref(), config.smtp_password.as_ref()) {
|
|
||||||
builder = builder.credentials(Credentials::new(login.to_string(), password.to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mailer = builder
|
|
||||||
.hello_name(ClientId::Domain(config.hostname.clone()))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
Ok(mailer)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_forgot_password_message(user: &User, token: &str) -> Message {
|
|
||||||
let hostname = env::var("HOSTNAME").unwrap();
|
|
||||||
let reset_url = format!("https://{hostname}/reset-password?token={token}");
|
|
||||||
|
|
||||||
let message = Message::builder()
|
|
||||||
.from("noreply <noreply@brasiwa-leipzig.de>".parse().unwrap())
|
|
||||||
.reply_to("noreply <noreply@brasiwa-leipzig.de>".parse().unwrap())
|
|
||||||
.to(format!("{} <{}>", user.name, user.email).parse().unwrap())
|
|
||||||
.subject("Brass: Zurücksetzen des Passworts angefordert")
|
|
||||||
.multipart(
|
|
||||||
MultiPart::alternative()
|
|
||||||
.singlepart(
|
|
||||||
SinglePart::builder()
|
|
||||||
.header(ContentType::TEXT_PLAIN)
|
|
||||||
.body(format!(r##"Hallo {},
|
|
||||||
|
|
||||||
du hast angefordert, dein Passwort zurückzusetzen. Kopiere dafür folgenden Link in deinen Browser:
|
|
||||||
|
|
||||||
{reset_url}
|
|
||||||
|
|
||||||
Bitte beachte, dass der Link nur 24 Stunden gültig ist. Solltest du nichts angefordert haben, dann musst du nichts weiter tun.
|
|
||||||
|
|
||||||
Viele Grüße"##, user.name))
|
|
||||||
)
|
|
||||||
.singlepart(
|
|
||||||
SinglePart::builder()
|
|
||||||
.header(ContentType::TEXT_HTML)
|
|
||||||
.body(format!(r##"<p>Hallo {},</p>
|
|
||||||
|
|
||||||
<p>du hast angefordert, dein Passwort zurückzusetzen. Klicke dafür <a href="{reset_url}" target="_blank">hier</a> oder kopiere folgenden Link in deinen Browser:</p>
|
|
||||||
|
|
||||||
<p>{reset_url}</p>
|
|
||||||
|
|
||||||
<p>Bitte beachte, dass der Link <b>nur 24 Stunden gültig</b> ist. Solltest du nichts angefordert haben, dann musst du nichts weiter tun.</p>
|
|
||||||
|
|
||||||
<p>Viele Grüße</p>"##, user.name))
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_registration_message(user: &User, token: &str) -> Result<Message, ApplicationError> {
|
|
||||||
let hostname = env::var("HOSTNAME")?;
|
|
||||||
let register_url = format!("https://{hostname}/register?token={token}");
|
|
||||||
|
|
||||||
let message = Message::builder()
|
|
||||||
.from("noreply <noreply@brasiwa-leipzig.de>".parse()?)
|
|
||||||
.reply_to("noreply <noreply@brasiwa-leipzig.de>".parse()?)
|
|
||||||
.to(Mailbox::new(Some(user.name.clone()), user.email.parse()?))
|
|
||||||
.subject("Brass: Registrierung deines Accounts")
|
|
||||||
.multipart(
|
|
||||||
MultiPart::alternative()
|
|
||||||
.singlepart(
|
|
||||||
SinglePart::builder()
|
|
||||||
.header(ContentType::TEXT_PLAIN)
|
|
||||||
.body(format!(r##"Hallo {},
|
|
||||||
|
|
||||||
dein Account für https:://{hostname} wurde erstellt. Du musst nur noch ein Passwort festlegen. Kopiere dafür folgenden Link in deinen Browser:
|
|
||||||
|
|
||||||
{register_url}
|
|
||||||
|
|
||||||
Bitte beachte, dass der Link nur 24 Stunden gültig ist.
|
|
||||||
|
|
||||||
Viele Grüße"##, user.name))
|
|
||||||
)
|
|
||||||
.singlepart(
|
|
||||||
SinglePart::builder()
|
|
||||||
.header(ContentType::TEXT_HTML)
|
|
||||||
.body(format!(r##"<p>Hallo {},</p>
|
|
||||||
|
|
||||||
<p>dein Account für <a href="https:://{hostname}" target="_blank">https://{hostname}</a> wurde erstellt. Du musst nur noch ein Passwort festlegen. Klicke dafür <a href="{register_url}" target="_blank">hier</a> oder kopiere folgenden Link in deinen Browser:</p>
|
|
||||||
|
|
||||||
<p>{register_url}</p>
|
|
||||||
|
|
||||||
<p>Bitte beachte, dass der Link <b>nur 24 Stunden gültig</b> ist.</p>
|
|
||||||
|
|
||||||
<p>Viele Grüße</p>"##, user.name))
|
|
||||||
))
|
|
||||||
?;
|
|
||||||
|
|
||||||
Ok(message)
|
|
||||||
}
|
|
@ -4,13 +4,10 @@ use std::{
|
|||||||
process::exit,
|
process::exit,
|
||||||
};
|
};
|
||||||
|
|
||||||
use lettre::{
|
|
||||||
message::{header::ContentType, SinglePart},
|
|
||||||
Message, SmtpTransport, Transport,
|
|
||||||
};
|
|
||||||
use sqlx::{Pool, Postgres};
|
use sqlx::{Pool, Postgres};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
mail::Mailer,
|
||||||
models::{Function, Role, User},
|
models::{Function, Role, User},
|
||||||
utils::auth::generate_salt_and_hash_plain_password,
|
utils::auth::generate_salt_and_hash_plain_password,
|
||||||
};
|
};
|
||||||
@ -65,7 +62,7 @@ fn prompt(prompt: &str) -> anyhow::Result<String> {
|
|||||||
pub async fn handle_command(
|
pub async fn handle_command(
|
||||||
command: Option<Command>,
|
command: Option<Command>,
|
||||||
pool: &Pool<Postgres>,
|
pool: &Pool<Postgres>,
|
||||||
mailer: &SmtpTransport,
|
mailer: &Mailer,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
match command {
|
match command {
|
||||||
Some(Command::Migrate) => {
|
Some(Command::Migrate) => {
|
||||||
@ -101,19 +98,7 @@ pub async fn handle_command(
|
|||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
Some(Command::TestMail(to)) => {
|
Some(Command::TestMail(to)) => {
|
||||||
let message = Message::builder()
|
match mailer.send_test_mail(&to) {
|
||||||
.from("noreply <noreply@brasiwa-leipzig.de>".parse().unwrap())
|
|
||||||
.reply_to("noreply <noreply@brasiwa-leipzig.de>".parse().unwrap())
|
|
||||||
.to(to.parse().unwrap())
|
|
||||||
.subject("Brass: Test E-Mail")
|
|
||||||
.singlepart(
|
|
||||||
SinglePart::builder()
|
|
||||||
.header(ContentType::TEXT_HTML)
|
|
||||||
.body("Testmail von Brass. E-Mail Versand funktioniert!".to_string()),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
match mailer.send(&message) {
|
|
||||||
Ok(_) => println!("Successfully sent mail to {to}."),
|
Ok(_) => println!("Successfully sent mail to {to}."),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if let Some(source) = e.source() {
|
if let Some(source) = e.source() {
|
||||||
@ -123,7 +108,6 @@ pub async fn handle_command(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
None => (),
|
None => (),
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
mod application_error;
|
mod application_error;
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod email;
|
|
||||||
pub mod event_planning_template;
|
pub mod event_planning_template;
|
||||||
pub mod manage_commands;
|
pub mod manage_commands;
|
||||||
pub mod password_help;
|
pub mod password_help;
|
||||||
|
@ -6,10 +6,9 @@ use actix_web::{
|
|||||||
dev::{Service, ServiceResponse},
|
dev::{Service, ServiceResponse},
|
||||||
test::init_service,
|
test::init_service,
|
||||||
};
|
};
|
||||||
use lettre::transport::stub::StubTransport;
|
|
||||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||||
|
|
||||||
use crate::create_app;
|
use crate::{create_app, mail::Mailer};
|
||||||
use brass_config::{load_config, Config, Environment};
|
use brass_config::{load_config, Config, Environment};
|
||||||
use regex::{Captures, Regex};
|
use regex::{Captures, Regex};
|
||||||
use sqlx::{
|
use sqlx::{
|
||||||
@ -33,7 +32,7 @@ impl DbTestContext {
|
|||||||
init_service(create_app(
|
init_service(create_app(
|
||||||
self.config.clone(),
|
self.config.clone(),
|
||||||
self.db_pool.clone(),
|
self.db_pool.clone(),
|
||||||
StubTransport::new_ok(),
|
Mailer::new_stub(),
|
||||||
))
|
))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
9
web/templates/emails/forgot_password.html
Normal file
9
web/templates/emails/forgot_password.html
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<p>Hallo {{ name }},</p>
|
||||||
|
|
||||||
|
<p>du hast angefordert, dein Passwort zurückzusetzen. Klicke dafür <a href="{{ reset_url }}" target="_blank">hier</a> oder kopiere folgenden Link in deinen Browser:</p>
|
||||||
|
|
||||||
|
<p>{{ reset_url }}</p>
|
||||||
|
|
||||||
|
<p>Bitte beachte, dass der Link <b>nur 24 Stunden gültig</b> ist. Solltest du nichts angefordert haben, dann musst du nichts weiter tun.</p>
|
||||||
|
|
||||||
|
<p>Viele Grüße</p>
|
9
web/templates/emails/forgot_password.txt
Normal file
9
web/templates/emails/forgot_password.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
Hallo {{ name }},
|
||||||
|
|
||||||
|
du hast angefordert, dein Passwort zurückzusetzen. Kopiere dafür folgenden Link in deinen Browser:
|
||||||
|
|
||||||
|
{{ reset_url }}
|
||||||
|
|
||||||
|
Bitte beachte, dass der Link nur 24 Stunden gültig ist. Solltest du nichts angefordert haben, dann musst du nichts weiter tun.
|
||||||
|
|
||||||
|
Viele Grüße
|
Loading…
x
Reference in New Issue
Block a user