Buchung funktioniert email anfänglich

This commit is contained in:
2023-01-29 22:56:33 +01:00
parent 0cd75a9ff5
commit c708a4b79b
21 changed files with 728 additions and 38 deletions
+6 -3
View File
@@ -15,10 +15,13 @@ actix-session = { version = "0.7", features = ["cookie-session"] }
# traits have been altered. The overall architecture of this CRUD can still be reproduced with a
# newer version of sqlx, and the version will be updated in the future.
sqlx = { version = "0.6.2", features = ["sqlite", "runtime-actix-rustls", "chrono"] }
serde = {version="1.0", features = ["derive"]}
serde_json = "1.0"
uuid = { version = "1.2", features = ["serde", "v4"] }
dotenv = "*"
env_logger = "0.10"
log = "*"
chrono = {version="*", features = ["serde"]}
lettre = {version="0.10", default-features = false, features = ["smtp-transport", "tokio1-rustls-tls", "hostname", "builder", "pool"]}
terminwahl_typen={path="../terminwahl_typen/"}
serde = {workspace = true}
serde_json={workspace=true}
chrono={workspace=true}
+1
View File
@@ -1,2 +1,3 @@
pub mod errors;
pub mod read;
pub mod write;
+9 -4
View File
@@ -1,5 +1,3 @@
use std::collections::HashMap;
use actix_web::{error, web, Error, HttpResponse};
use crate::db::{self, Pool};
@@ -19,8 +17,15 @@ pub async fn get_subjects_json(pool: web::Data<Pool>) -> Result<HttpResponse, Er
Ok(HttpResponse::Ok().json(tasks))
}
pub async fn get_free_slots_json(pool: web::Data<Pool>) -> Result<HttpResponse, Error> {
let tasks = db::read::get_free_slots(&pool)
pub async fn get_slots_json(pool: web::Data<Pool>) -> Result<HttpResponse, Error> {
let tasks = db::read::get_slots(&pool)
.await
.map_err(error::ErrorInternalServerError)?;
Ok(HttpResponse::Ok().json(tasks))
}
pub async fn get_unavailable_json(pool: web::Data<Pool>) -> Result<HttpResponse, Error> {
let tasks = db::read::get_unavailable(&pool)
.await
.map_err(error::ErrorInternalServerError)?;
+37
View File
@@ -0,0 +1,37 @@
use actix_web::{error, web, HttpResponse};
use lettre::{
transport::smtp::{authentication::Credentials, SmtpTransportBuilder},
Message, SmtpTransport, Transport,
};
use terminwahl_typen::{PlannedAppointment, RequestState};
use crate::db::{self, Pool};
pub async fn save_appointments_json(
pool: web::Data<Pool>,
mailer: web::Data<SmtpTransport>,
appointments: web::Json<Vec<PlannedAppointment>>,
) -> Result<HttpResponse, error::Error> {
db::write::save_appointments(&pool, &appointments)
.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();
// Send the email
match mailer.send(&email) {
Ok(_) => println!("Email sent successfully!"),
Err(e) => panic!("Could not send email: {:?}", e),
}
Ok(HttpResponse::Ok().json(RequestState::Success))
}
+23 -5
View File
@@ -1,6 +1,10 @@
use std::collections::HashSet;
use sqlx::query_as;
use crate::types::{AppointmentSlot, AppointmentSlots, Subject, Subjects, Teacher, Teachers};
use terminwahl_typen::{
AppointmentSlot, AppointmentSlots, SlotId, Subject, Subjects, Teacher, Teachers,
};
use super::Pool;
@@ -11,7 +15,7 @@ pub async fn get_teachers(db: &Pool) -> Result<Teachers, sqlx::Error> {
SELECT *
FROM `teachers`"#,
)
.fetch_all(&*db)
.fetch_all(db)
.await
}
@@ -22,21 +26,35 @@ pub async fn get_subjects(db: &Pool) -> Result<Subjects, sqlx::Error> {
SELECT *
FROM `subjects`"#,
)
.fetch_all(&*db)
.fetch_all(db)
.await
}
pub async fn get_free_slots(db: &Pool) -> Result<AppointmentSlots, sqlx::Error> {
pub async fn get_slots(db: &Pool) -> Result<AppointmentSlots, sqlx::Error> {
match query_as!(
AppointmentSlot,
r#"
SELECT *
FROM `appointment_slots`"#,
)
.fetch_all(&*db)
.fetch_all(db)
.await
{
Ok(elems) => Ok(elems.into_iter().map(|e| (e.id, e)).collect()),
Err(e) => Err(e),
}
}
pub async fn get_unavailable(db: &Pool) -> Result<HashSet<SlotId>, sqlx::Error> {
match query_as!(
SlotId,
r#"
SELECT teacher_id, slot_id
FROM `appointments`"#,
)
.fetch_all(db)
.await
{
Ok(elems) => Ok(elems.into_iter().collect()),
Err(e) => Err(e),
}
}
+17
View File
@@ -0,0 +1,17 @@
use terminwahl_typen::PlannedAppointment;
use super::Pool;
pub async fn save_appointments(
pool: &Pool,
appointments: &[PlannedAppointment],
) -> 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)
.execute(pool)
.await?;
}
Ok(())
}
-1
View File
@@ -1,3 +1,2 @@
pub mod api;
pub mod db;
pub mod types;
+19 -6
View File
@@ -7,10 +7,8 @@ use actix_web::{
web, App, HttpServer,
};
use dotenv::dotenv;
use std::{
env,
sync::{Arc, Mutex},
};
use lettre::{transport::smtp::authentication::Credentials, SmtpTransport};
use std::env;
use terminwahl_back::{api, db};
#[actix_web::main]
@@ -22,6 +20,15 @@ async fn main() -> std::io::Result<()> {
let pool = db::init_pool(&database_url)
.await
.expect("Failed to create pool");
let smtp_user = env::var("SMTP_USER").expect("Failed to get smtp user");
let smtp_password = env::var("SMTP_PASSWORD").expect("Failed to get smtp password");
let credentials = Credentials::new(smtp_user, smtp_password);
let smtp_pool = SmtpTransport::relay("smtp.1und1.de")
.expect("Failed to connect to smtp")
// Add credentials for authentication
.credentials(credentials)
// Connection pool settings
.build();
log::info!("starting HTTP server at http://localhost:8080");
@@ -40,6 +47,7 @@ async fn main() -> std::io::Result<()> {
App::new()
.app_data(web::Data::new(pool.clone()))
.app_data(web::Data::new(smtp_pool.clone()))
.wrap(Logger::default())
.wrap(session_store)
.wrap(error_handlers)
@@ -49,9 +57,14 @@ async fn main() -> std::io::Result<()> {
.service(
web::resource("/get/subjects").route(web::get().to(api::read::get_subjects_json)),
)
.service(web::resource("/get/slots").route(web::get().to(api::read::get_slots_json)))
.service(
web::resource("/get/free_slots")
.route(web::get().to(api::read::get_free_slots_json)),
web::resource("/get/unavailable")
.route(web::get().to(api::read::get_unavailable_json)),
)
.service(
web::resource("/send/appointments")
.route(web::post().to(api::write::save_appointments_json)),
)
.service(Files::new("/", "./terminwahl_front/dist/").index_file("index.html"))
})
-39
View File
@@ -1,39 +0,0 @@
use std::collections::HashMap;
use chrono::{DateTime, Local, NaiveDateTime};
use serde::{Deserialize, Serialize};
pub type IdType = i64;
#[derive(Debug, Deserialize, Serialize)]
pub struct Teacher {
pub id: IdType,
pub ansprache: String,
pub last_name: String,
pub subject_id: IdType,
}
pub type Teachers = Vec<Teacher>;
#[derive(Debug, Deserialize, Serialize)]
pub struct Subject {
pub id: IdType,
pub name: String,
}
pub type Subjects = Vec<Subject>;
#[derive(Debug, Deserialize, Serialize)]
pub struct Appointment {
pub id: IdType,
pub teacher_id: IdType,
pub slot_id: IdType,
}
pub type AppointmentSlots = HashMap<IdType, AppointmentSlot>;
#[derive(Debug, Deserialize, Serialize)]
pub struct AppointmentSlot {
pub id: IdType,
pub start_time: NaiveDateTime,
pub end_time: NaiveDateTime,
}