emails funktionieren
This commit is contained in:
@@ -1,37 +1,101 @@
|
||||
use actix_web::{error, web, HttpResponse};
|
||||
use lettre::{
|
||||
transport::smtp::{authentication::Credentials, SmtpTransportBuilder},
|
||||
Message, SmtpTransport, Transport,
|
||||
};
|
||||
use terminwahl_typen::{PlannedAppointment, RequestState};
|
||||
use handlebars::Handlebars;
|
||||
use lettre::{message::header::ContentType, Message, SmtpTransport, Transport};
|
||||
use log::debug;
|
||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||
use serde_json::json;
|
||||
use terminwahl_typen::{Nutzer, PlannedAppointment, RequestState};
|
||||
|
||||
use crate::db::{self, Pool};
|
||||
use crate::db::{self, write::confirm_appointments, Pool};
|
||||
|
||||
pub async fn save_appointments_json(
|
||||
pool: web::Data<Pool>,
|
||||
mailer: web::Data<SmtpTransport>,
|
||||
appointments: web::Json<Vec<PlannedAppointment>>,
|
||||
handlebars: web::Data<Handlebars<'_>>,
|
||||
input: web::Json<(Vec<PlannedAppointment>, Nutzer)>,
|
||||
) -> Result<HttpResponse, error::Error> {
|
||||
db::write::save_appointments(&pool, &appointments)
|
||||
debug!("Extracting data");
|
||||
let (appointments, nutzer) = input.into_inner();
|
||||
debug!("Saving user");
|
||||
let nutzer_id = db::write::save_nutzer(&pool, &nutzer)
|
||||
.await
|
||||
.map_err(error::ErrorInternalServerError)?;
|
||||
debug!("Saving appointments");
|
||||
let validation_key: String = thread_rng()
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(30)
|
||||
.map(char::from)
|
||||
.collect();
|
||||
db::write::save_appointments(&pool, &appointments, nutzer_id, &validation_key)
|
||||
.await
|
||||
.map_err(error::ErrorInternalServerError)?;
|
||||
|
||||
let email = Message::builder()
|
||||
.from(
|
||||
"Franz Dietrich <franz.dietrich@uhlandshoehe.de>"
|
||||
.parse()
|
||||
.unwrap(),
|
||||
)
|
||||
.to("Franz Dietrich <dietrich@teilgedanken.de>".parse().unwrap())
|
||||
.subject("Happy new year")
|
||||
.body(String::from("Be happy!"))
|
||||
.unwrap();
|
||||
let mail_result = send_confirmation_request(&nutzer, &validation_key, &handlebars, &mailer);
|
||||
|
||||
// Send the email
|
||||
match mailer.send(&email) {
|
||||
Ok(_) => println!("Email sent successfully!"),
|
||||
Err(e) => panic!("Could not send email: {:?}", e),
|
||||
Ok(HttpResponse::Ok().json(mail_result))
|
||||
}
|
||||
|
||||
pub fn send_confirmation_request(
|
||||
nutzer: &Nutzer,
|
||||
validation_key: &str,
|
||||
handlebars: &Handlebars,
|
||||
mailer: &SmtpTransport,
|
||||
) -> RequestState {
|
||||
let data = json! {
|
||||
{
|
||||
"nutzer": nutzer,
|
||||
"validation_key": validation_key
|
||||
}};
|
||||
debug!("{:?}", handlebars.get_templates());
|
||||
if let Ok(email_text) = handlebars.render("email_confirm", &data) {
|
||||
let email = match Message::builder()
|
||||
.from(
|
||||
"Franz Dietrich <franz.dietrich@uhlandshoehe.de>"
|
||||
.parse()
|
||||
.expect("Should not fail"),
|
||||
)
|
||||
.to(
|
||||
match format!("{} <{}>", nutzer.name, nutzer.email).parse() {
|
||||
Ok(v) => v,
|
||||
Err(_) => return RequestState::Error,
|
||||
},
|
||||
)
|
||||
.subject("Elternsprechtag: Bestätigen Sie Ihre Termine")
|
||||
.header(ContentType::TEXT_PLAIN)
|
||||
.body(
|
||||
match lettre::message::Body::new_with_encoding(
|
||||
email_text,
|
||||
lettre::message::header::ContentTransferEncoding::Base64,
|
||||
) {
|
||||
Ok(body) => body,
|
||||
Err(_) => return RequestState::Error,
|
||||
},
|
||||
) {
|
||||
Ok(message) => message,
|
||||
Err(_) => return RequestState::Error,
|
||||
};
|
||||
|
||||
// Send the email
|
||||
match mailer.send(&email) {
|
||||
Ok(_) => RequestState::Success,
|
||||
Err(e) => {
|
||||
debug!("Failed to send: {e}");
|
||||
RequestState::Error
|
||||
}
|
||||
}
|
||||
} else {
|
||||
RequestState::Error
|
||||
}
|
||||
}
|
||||
|
||||
Ok(HttpResponse::Ok().json(RequestState::Success))
|
||||
pub async fn confirm_validation_key(
|
||||
pool: web::Data<Pool>,
|
||||
mailer: web::Data<SmtpTransport>,
|
||||
handlebars: web::Data<Handlebars<'_>>,
|
||||
validation_key: web::Path<String>,
|
||||
) -> Result<HttpResponse, error::Error> {
|
||||
match confirm_appointments(&pool, &validation_key).await {
|
||||
Ok(_) => Ok(HttpResponse::Ok().json(RequestState::Success)),
|
||||
Err(e) => Err(error::ErrorBadRequest(e)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ pub async fn get_unavailable(db: &Pool) -> Result<HashSet<SlotId>, sqlx::Error>
|
||||
SlotId,
|
||||
r#"
|
||||
SELECT teacher_id, slot_id
|
||||
FROM `appointments`"#,
|
||||
FROM `appointments` WHERE datetime(expires) > datetime('now');"#,
|
||||
)
|
||||
.fetch_all(db)
|
||||
.await
|
||||
|
||||
@@ -1,17 +1,62 @@
|
||||
use terminwahl_typen::PlannedAppointment;
|
||||
use chrono::{Duration, Local};
|
||||
use sqlx::query;
|
||||
use terminwahl_typen::{IdType, Nutzer, PlannedAppointment};
|
||||
|
||||
use super::Pool;
|
||||
|
||||
pub async fn save_appointments(
|
||||
pool: &Pool,
|
||||
appointments: &[PlannedAppointment],
|
||||
nutzer_id: IdType,
|
||||
validation_key: &str,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
for appointment in appointments {
|
||||
sqlx::query("INSERT INTO appointments (teacher_id, slot_id) VALUES ($1, $2)")
|
||||
.bind(appointment.teacher_id)
|
||||
.bind(appointment.slot_id)
|
||||
let _ = query!("DELETE FROM appointments WHERE datetime(expires) < datetime('now');")
|
||||
.execute(pool)
|
||||
.await?;
|
||||
.await;
|
||||
let now = Local::now().naive_local();
|
||||
let in_three_hours = now + Duration::hours(3);
|
||||
query!(
|
||||
"INSERT INTO appointments (teacher_id, slot_id, nutzer_id, validation_key, expires) VALUES ($1, $2, $3, $4, $5)",
|
||||
appointment.teacher_id,
|
||||
appointment.slot_id,
|
||||
nutzer_id,
|
||||
validation_key,
|
||||
in_three_hours
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn save_nutzer(pool: &Pool, nutzer: &Nutzer) -> Result<IdType, sqlx::Error> {
|
||||
query!(
|
||||
"INSERT INTO nutzer (name, schueler, email) VALUES ($1, $2, $3)",
|
||||
nutzer.name,
|
||||
nutzer.schüler,
|
||||
nutzer.email
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
let db_nutzer = query!(
|
||||
"SELECT id FROM nutzer WHERE name = ? and email = ?",
|
||||
nutzer.name,
|
||||
nutzer.email
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
|
||||
Ok(db_nutzer.id)
|
||||
}
|
||||
|
||||
pub async fn confirm_appointments(pool: &Pool, validation_key: &str) -> Result<(), sqlx::Error> {
|
||||
let _ = query!(
|
||||
"UPDATE appointments SET expires = NULL WHERE validation_key = ?",
|
||||
validation_key
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ use actix_web::{
|
||||
web, App, HttpServer,
|
||||
};
|
||||
use dotenv::dotenv;
|
||||
use handlebars::Handlebars;
|
||||
use lettre::{transport::smtp::authentication::Credentials, SmtpTransport};
|
||||
use log::debug;
|
||||
use std::env;
|
||||
use terminwahl_back::{api, db};
|
||||
|
||||
@@ -30,7 +32,13 @@ async fn main() -> std::io::Result<()> {
|
||||
// Connection pool settings
|
||||
.build();
|
||||
|
||||
let mut handlebars = Handlebars::new();
|
||||
handlebars
|
||||
.register_templates_directory(".hbs", "terminwahl_back/templates")
|
||||
.unwrap();
|
||||
|
||||
log::info!("starting HTTP server at http://localhost:8080");
|
||||
debug!("{:?}", handlebars.get_templates());
|
||||
|
||||
HttpServer::new(move || {
|
||||
log::debug!("Constructing the App");
|
||||
@@ -48,6 +56,7 @@ async fn main() -> std::io::Result<()> {
|
||||
App::new()
|
||||
.app_data(web::Data::new(pool.clone()))
|
||||
.app_data(web::Data::new(smtp_pool.clone()))
|
||||
.app_data(web::Data::new(handlebars.clone()))
|
||||
.wrap(Logger::default())
|
||||
.wrap(session_store)
|
||||
.wrap(error_handlers)
|
||||
@@ -66,6 +75,10 @@ async fn main() -> std::io::Result<()> {
|
||||
web::resource("/send/appointments")
|
||||
.route(web::post().to(api::write::save_appointments_json)),
|
||||
)
|
||||
.service(
|
||||
web::resource("/confirm/{validation_key}")
|
||||
.route(web::get().to(api::write::confirm_validation_key)),
|
||||
)
|
||||
.service(Files::new("/", "./terminwahl_front/dist/").index_file("index.html"))
|
||||
})
|
||||
.bind(("127.0.0.1", 8080))?
|
||||
|
||||
Reference in New Issue
Block a user