first draft of normannen deployment
This commit is contained in:
@@ -1,319 +0,0 @@
|
||||
use crate::model::{
|
||||
boat::{Boat, BoatToAdd, BoatToUpdate},
|
||||
location::Location,
|
||||
log::Log,
|
||||
user::{AdminUser, User, UserWithDetails},
|
||||
};
|
||||
use rocket::{
|
||||
form::Form,
|
||||
get, post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
routes, Route, State,
|
||||
};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
#[get("/boat")]
|
||||
async fn index(
|
||||
db: &State<SqlitePool>,
|
||||
admin: AdminUser,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
) -> Template {
|
||||
let boats = Boat::all(db).await;
|
||||
let locations = Location::all(db).await;
|
||||
let users = User::all(db).await;
|
||||
|
||||
let mut context = Context::new();
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
context.insert("boats", &boats);
|
||||
context.insert("locations", &locations);
|
||||
context.insert("users", &users);
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithDetails::from_user(admin.user, db).await,
|
||||
);
|
||||
|
||||
Template::render("admin/boat/index", context.into_json())
|
||||
}
|
||||
|
||||
#[get("/boat/<boat>/delete")]
|
||||
async fn delete(db: &State<SqlitePool>, admin: AdminUser, boat: i32) -> Flash<Redirect> {
|
||||
let boat = Boat::find_by_id(db, boat).await;
|
||||
Log::create(db, format!("{} deleted boat: {boat:?}", admin.user.name)).await;
|
||||
|
||||
match boat {
|
||||
Some(boat) => {
|
||||
boat.delete(db).await;
|
||||
Flash::success(
|
||||
Redirect::to("/admin/boat"),
|
||||
format!("Boot {} gelöscht", boat.name),
|
||||
)
|
||||
}
|
||||
None => Flash::error(Redirect::to("/admin/boat"), "Boat does not exist"),
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/boat/<boat_id>", data = "<data>")]
|
||||
async fn update(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<BoatToUpdate<'_>>,
|
||||
boat_id: i32,
|
||||
_admin: AdminUser,
|
||||
) -> Flash<Redirect> {
|
||||
let boat = Boat::find_by_id(db, boat_id).await;
|
||||
let Some(boat) = boat else {
|
||||
return Flash::error(Redirect::to("/admin/boat"), "Boat does not exist!");
|
||||
};
|
||||
|
||||
match boat.update(db, data.into_inner()).await {
|
||||
Ok(_) => Flash::success(Redirect::to("/admin/boat"), "Boot bearbeitet"),
|
||||
Err(e) => Flash::error(Redirect::to("/admin/boat"), e),
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/boat/new", data = "<data>")]
|
||||
async fn create(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<BoatToAdd<'_>>,
|
||||
_admin: AdminUser,
|
||||
) -> Flash<Redirect> {
|
||||
match Boat::create(db, data.into_inner()).await {
|
||||
Ok(_) => Flash::success(Redirect::to("/admin/boat"), "Boot hinzugefügt"),
|
||||
Err(e) => Flash::error(Redirect::to("/admin/boat"), e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![index, create, delete, update]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use rocket::{
|
||||
http::{ContentType, Status},
|
||||
local::asynchronous::Client,
|
||||
};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
use crate::tera::admin::boat::Boat;
|
||||
use crate::testdb;
|
||||
|
||||
#[sqlx::test]
|
||||
fn test_boat_index() {
|
||||
let db = testdb!();
|
||||
|
||||
let rocket = rocket::build().manage(db.clone());
|
||||
let rocket = crate::tera::config(rocket);
|
||||
|
||||
let client = Client::tracked(rocket).await.unwrap();
|
||||
let login = client
|
||||
.post("/auth")
|
||||
.header(ContentType::Form) // Set the content type to form
|
||||
.body("name=admin&password=admin"); // Add the form data to the request body;
|
||||
login.dispatch().await;
|
||||
|
||||
let req = client.get("/admin/boat");
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
let text = response.into_string().await.unwrap();
|
||||
assert!(&text.contains("Neues Boot"));
|
||||
assert!(&text.contains("Kaputtes Boot :-("));
|
||||
assert!(&text.contains("Haichenbach"));
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
||||
fn test_succ_update() {
|
||||
let db = testdb!();
|
||||
|
||||
let boat = Boat::find_by_id(&db, 1).await.unwrap();
|
||||
assert_eq!(boat.name, "Haichenbach");
|
||||
|
||||
let rocket = rocket::build().manage(db.clone());
|
||||
let rocket = crate::tera::config(rocket);
|
||||
|
||||
let client = Client::tracked(rocket).await.unwrap();
|
||||
let login = client
|
||||
.post("/auth")
|
||||
.header(ContentType::Form) // Set the content type to form
|
||||
.body("name=admin&password=admin"); // Add the form data to the request body;
|
||||
login.dispatch().await;
|
||||
|
||||
let req = client
|
||||
.post("/admin/boat/1")
|
||||
.header(ContentType::Form)
|
||||
.body("name=Haichiii&amount_seats=1&location_id=1");
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(
|
||||
response.headers().get("Location").next(),
|
||||
Some("/admin/boat")
|
||||
);
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
.get("_flash")
|
||||
.expect("Expected flash cookie");
|
||||
|
||||
assert_eq!(flash_cookie.value(), "7:successBoot bearbeitet");
|
||||
|
||||
let boat = Boat::find_by_id(&db, 1).await.unwrap();
|
||||
assert_eq!(boat.name, "Haichiii");
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
||||
fn test_update_wrong_boat() {
|
||||
let db = testdb!();
|
||||
|
||||
let rocket = rocket::build().manage(db.clone());
|
||||
let rocket = crate::tera::config(rocket);
|
||||
|
||||
let client = Client::tracked(rocket).await.unwrap();
|
||||
let login = client
|
||||
.post("/auth")
|
||||
.header(ContentType::Form) // Set the content type to form
|
||||
.body("name=admin&password=admin"); // Add the form data to the request body;
|
||||
login.dispatch().await;
|
||||
|
||||
let req = client
|
||||
.post("/admin/boat/1337")
|
||||
.header(ContentType::Form)
|
||||
.body("name=Haichiii&amount_seats=1&location_id=1");
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(
|
||||
response.headers().get("Location").next(),
|
||||
Some("/admin/boat")
|
||||
);
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
.get("_flash")
|
||||
.expect("Expected flash cookie");
|
||||
|
||||
assert_eq!(flash_cookie.value(), "5:errorBoat does not exist!");
|
||||
|
||||
let boat = Boat::find_by_id(&db, 1).await.unwrap();
|
||||
assert_eq!(boat.name, "Haichenbach");
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
||||
fn test_update_wrong_foreign() {
|
||||
let db = testdb!();
|
||||
|
||||
let rocket = rocket::build().manage(db.clone());
|
||||
let rocket = crate::tera::config(rocket);
|
||||
|
||||
let client = Client::tracked(rocket).await.unwrap();
|
||||
let login = client
|
||||
.post("/auth")
|
||||
.header(ContentType::Form) // Set the content type to form
|
||||
.body("name=admin&password=admin"); // Add the form data to the request body;
|
||||
login.dispatch().await;
|
||||
|
||||
let req = client
|
||||
.post("/admin/boat/1")
|
||||
.header(ContentType::Form)
|
||||
.body("name=Haichiii&amount_seats=1&location_id=999");
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(
|
||||
response.headers().get("Location").next(),
|
||||
Some("/admin/boat")
|
||||
);
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
.get("_flash")
|
||||
.expect("Expected flash cookie");
|
||||
|
||||
assert_eq!(
|
||||
flash_cookie.value(),
|
||||
"5:errorerror returned from database: (code: 787) FOREIGN KEY constraint failed"
|
||||
);
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
||||
fn test_succ_create() {
|
||||
let db = testdb!();
|
||||
|
||||
let rocket = rocket::build().manage(db.clone());
|
||||
let rocket = crate::tera::config(rocket);
|
||||
assert!(Boat::find_by_name(&db, "completely-new-boat".into())
|
||||
.await
|
||||
.is_none());
|
||||
|
||||
let client = Client::tracked(rocket).await.unwrap();
|
||||
let login = client
|
||||
.post("/auth")
|
||||
.header(ContentType::Form) // Set the content type to form
|
||||
.body("name=admin&password=admin"); // Add the form data to the request body;
|
||||
login.dispatch().await;
|
||||
|
||||
let req = client
|
||||
.post("/admin/boat/new")
|
||||
.header(ContentType::Form)
|
||||
.body("name=completely-new-boat&amount_seats=1&location_id=1");
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(
|
||||
response.headers().get("Location").next(),
|
||||
Some("/admin/boat")
|
||||
);
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
.get("_flash")
|
||||
.expect("Expected flash cookie");
|
||||
|
||||
assert_eq!(flash_cookie.value(), "7:successBoot hinzugefügt");
|
||||
|
||||
Boat::find_by_name(&db, "completely-new-boat".into())
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
||||
fn test_create_db_error() {
|
||||
let db = testdb!();
|
||||
|
||||
let rocket = rocket::build().manage(db.clone());
|
||||
let rocket = crate::tera::config(rocket);
|
||||
|
||||
let client = Client::tracked(rocket).await.unwrap();
|
||||
let login = client
|
||||
.post("/auth")
|
||||
.header(ContentType::Form) // Set the content type to form
|
||||
.body("name=admin&password=admin"); // Add the form data to the request body;
|
||||
login.dispatch().await;
|
||||
|
||||
let req = client
|
||||
.post("/admin/boat/new")
|
||||
.header(ContentType::Form)
|
||||
.body("name=Haichenbach&amount_seats=1&location_id=1");
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(
|
||||
response.headers().get("Location").next(),
|
||||
Some("/admin/boat")
|
||||
);
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
.get("_flash")
|
||||
.expect("Expected flash cookie");
|
||||
|
||||
assert_eq!(
|
||||
flash_cookie.value(),
|
||||
"5:errorerror returned from database: (code: 2067) UNIQUE constraint failed: boat.name"
|
||||
);
|
||||
}
|
||||
}
|
@@ -45,7 +45,7 @@ async fn create(
|
||||
)
|
||||
.await;
|
||||
|
||||
Flash::success(Redirect::to("/planned"), "Event hinzugefügt")
|
||||
Flash::success(Redirect::to("/"), "Event hinzugefügt")
|
||||
}
|
||||
|
||||
//TODO: add constraints (e.g. planned_amount_cox > 0)
|
||||
@@ -79,21 +79,21 @@ async fn update(
|
||||
match Event::find_by_id(db, data.id).await {
|
||||
Some(planned_event) => {
|
||||
planned_event.update(db, &update).await;
|
||||
Flash::success(Redirect::to("/planned"), "Event erfolgreich bearbeitet")
|
||||
Flash::success(Redirect::to("/"), "Event erfolgreich bearbeitet")
|
||||
}
|
||||
None => Flash::error(Redirect::to("/planned"), "Planned event id not found"),
|
||||
None => Flash::error(Redirect::to("/"), "Planned event id not found"),
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/planned-event/<id>/delete")]
|
||||
async fn delete(db: &State<SqlitePool>, id: i64, _admin: EventUser) -> Flash<Redirect> {
|
||||
let Some(event) = Event::find_by_id(db, id).await else {
|
||||
return Flash::error(Redirect::to("/planned"), "Event does not exist");
|
||||
return Flash::error(Redirect::to("/"), "Event does not exist");
|
||||
};
|
||||
|
||||
match event.delete(db).await {
|
||||
Ok(()) => Flash::success(Redirect::to("/planned"), "Event gelöscht"),
|
||||
Err(e) => Flash::error(Redirect::to("/planned"), e),
|
||||
Ok(()) => Flash::success(Redirect::to("/"), "Event gelöscht"),
|
||||
Err(e) => Flash::error(Redirect::to("/"), e),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ mod test {
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
@@ -163,7 +163,7 @@ mod test {
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
@@ -199,7 +199,7 @@ mod test {
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
@@ -238,7 +238,7 @@ mod test {
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
@@ -269,7 +269,7 @@ mod test {
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
|
@@ -1,86 +0,0 @@
|
||||
use rocket::form::Form;
|
||||
use rocket::fs::TempFile;
|
||||
use rocket::response::{Flash, Redirect};
|
||||
use rocket::{get, request::FlashMessage, routes, Route, State};
|
||||
use rocket::{post, FromForm};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
use crate::model::log::Log;
|
||||
use crate::model::mail::Mail;
|
||||
use crate::model::role::Role;
|
||||
use crate::model::user::UserWithDetails;
|
||||
use crate::model::user::{AdminUser, VorstandUser};
|
||||
use crate::tera::Config;
|
||||
|
||||
#[get("/mail")]
|
||||
async fn index(
|
||||
db: &State<SqlitePool>,
|
||||
admin: VorstandUser,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
) -> Template {
|
||||
let mut context = Context::new();
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
let roles = Role::all(db).await;
|
||||
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithDetails::from_user(admin.user, db).await,
|
||||
);
|
||||
context.insert("roles", &roles);
|
||||
|
||||
Template::render("admin/mail", context.into_json())
|
||||
}
|
||||
|
||||
#[get("/mail/fee")]
|
||||
async fn fee(db: &State<SqlitePool>, admin: AdminUser, config: &State<Config>) -> &'static str {
|
||||
Log::create(db, format!("{admin:?} trying to send fee")).await;
|
||||
Mail::fees(db, config.smtp_pw.clone()).await;
|
||||
"SUCC"
|
||||
}
|
||||
|
||||
#[get("/mail/fee-final")]
|
||||
async fn fee_final(
|
||||
db: &State<SqlitePool>,
|
||||
admin: AdminUser,
|
||||
config: &State<Config>,
|
||||
) -> &'static str {
|
||||
Log::create(db, format!("{admin:?} trying to send fee_final")).await;
|
||||
Mail::fees_final(db, config.smtp_pw.clone()).await;
|
||||
"SUCC"
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct MailToSend<'a> {
|
||||
pub(crate) role_id: i32,
|
||||
pub(crate) subject: String,
|
||||
pub(crate) body: String,
|
||||
pub(crate) files: Vec<TempFile<'a>>,
|
||||
}
|
||||
|
||||
#[post("/mail", data = "<data>")]
|
||||
async fn update(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<MailToSend<'_>>,
|
||||
config: &State<Config>,
|
||||
admin: VorstandUser,
|
||||
) -> Flash<Redirect> {
|
||||
let d = data.into_inner();
|
||||
Log::create(db, format!("{admin:?} trying to send this mail: {d:?}")).await;
|
||||
if Mail::send(db, d, config.smtp_pw.clone()).await {
|
||||
Log::create(db, "Mail successfully sent".into()).await;
|
||||
Flash::success(Redirect::to("/admin/mail"), "Mail versendet")
|
||||
} else {
|
||||
Log::create(db, "Error sending the mail".into()).await;
|
||||
Flash::error(Redirect::to("/admin/mail"), "Fehler")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![index, update, fee, fee_final]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {}
|
@@ -3,16 +3,13 @@ use rocket::{form::Form, get, post, routes, FromForm, Route, State};
|
||||
use rocket_dyn_templates::{context, Template};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
use super::notification;
|
||||
use crate::{
|
||||
model::{log::Log, role::Role, user::AdminUser},
|
||||
tera::Config,
|
||||
};
|
||||
|
||||
pub mod boat;
|
||||
pub mod event;
|
||||
pub mod mail;
|
||||
pub mod notification;
|
||||
pub mod schnupper;
|
||||
pub mod user;
|
||||
|
||||
#[get("/rss?<key>")]
|
||||
@@ -76,10 +73,7 @@ async fn list(db: &State<SqlitePool>, _admin: AdminUser, list_form: Form<ListFor
|
||||
pub fn routes() -> Vec<Route> {
|
||||
let mut ret = Vec::new();
|
||||
ret.append(&mut user::routes());
|
||||
ret.append(&mut schnupper::routes());
|
||||
ret.append(&mut boat::routes());
|
||||
ret.append(&mut notification::routes());
|
||||
ret.append(&mut mail::routes());
|
||||
ret.append(&mut event::routes());
|
||||
ret.append(&mut routes![rss, show_rss, show_list, list]);
|
||||
ret
|
||||
|
@@ -1,116 +0,0 @@
|
||||
use crate::model::{
|
||||
log::Log,
|
||||
notification::Notification,
|
||||
role::Role,
|
||||
user::{User, UserWithDetails, VorstandUser},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use rocket::{
|
||||
form::Form,
|
||||
get, post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
routes, FromForm, Route, State,
|
||||
};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
#[get("/notification")]
|
||||
async fn index(
|
||||
db: &State<SqlitePool>,
|
||||
user: VorstandUser,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
) -> Template {
|
||||
let mut context = Context::new();
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithDetails::from_user(user.user, db).await,
|
||||
);
|
||||
|
||||
let users: Vec<User> = User::all(db)
|
||||
.await
|
||||
.into_iter()
|
||||
.filter(|u| u.last_access.is_some()) // Not useful to send notifications to people who are
|
||||
// not logging in
|
||||
.sorted_by_key(|u| u.name.clone())
|
||||
.collect();
|
||||
|
||||
context.insert("roles", &Role::all(db).await);
|
||||
context.insert("users", &users);
|
||||
|
||||
Template::render("admin/notification", context.into_json())
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct NotificationToSendGroup {
|
||||
pub(crate) role_id: i32,
|
||||
pub(crate) category: String,
|
||||
pub(crate) message: String,
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct NotificationToSendUser {
|
||||
pub(crate) user_id: i32,
|
||||
pub(crate) category: String,
|
||||
pub(crate) message: String,
|
||||
}
|
||||
|
||||
#[post("/notification/group", data = "<data>")]
|
||||
async fn send_group(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<NotificationToSendGroup>,
|
||||
admin: VorstandUser,
|
||||
) -> Flash<Redirect> {
|
||||
let d = data.into_inner();
|
||||
Log::create(
|
||||
db,
|
||||
format!("{admin:?} trying to send this notification: {d:?}"),
|
||||
)
|
||||
.await;
|
||||
|
||||
let Some(role) = Role::find_by_id(db, d.role_id).await else {
|
||||
return Flash::error(Redirect::to("/admin/notification"), "Rolle gibt's ned");
|
||||
};
|
||||
|
||||
for user in User::all_with_role(db, &role).await {
|
||||
Notification::create(db, &user, &d.message, &d.category, None, None).await;
|
||||
}
|
||||
Log::create(db, "Notification successfully sent".into()).await;
|
||||
Flash::success(
|
||||
Redirect::to("/admin/notification"),
|
||||
"Nachricht ausgeschickt",
|
||||
)
|
||||
}
|
||||
|
||||
#[post("/notification/user", data = "<data>")]
|
||||
async fn send_user(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<NotificationToSendUser>,
|
||||
admin: VorstandUser,
|
||||
) -> Flash<Redirect> {
|
||||
let d = data.into_inner();
|
||||
Log::create(
|
||||
db,
|
||||
format!("{admin:?} trying to send this notification: {d:?}"),
|
||||
)
|
||||
.await;
|
||||
|
||||
let Some(user) = User::find_by_id(db, d.user_id).await else {
|
||||
return Flash::error(Redirect::to("/admin/notification"), "User gibt's ned");
|
||||
};
|
||||
|
||||
Notification::create(db, &user, &d.message, &d.category, None, None).await;
|
||||
|
||||
Log::create(db, "Notification successfully sent".into()).await;
|
||||
Flash::success(
|
||||
Redirect::to("/admin/notification"),
|
||||
"Nachricht ausgeschickt",
|
||||
)
|
||||
}
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![index, send_user, send_group]
|
||||
}
|
@@ -1,40 +0,0 @@
|
||||
use crate::model::{
|
||||
role::Role,
|
||||
user::{SchnupperBetreuerUser, User, UserWithDetails},
|
||||
};
|
||||
use futures::future::join_all;
|
||||
use rocket::{get, request::FlashMessage, routes, Route, State};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
#[get("/schnupper")]
|
||||
async fn index(
|
||||
db: &State<SqlitePool>,
|
||||
user: SchnupperBetreuerUser,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
) -> Template {
|
||||
let schnupperant = Role::find_by_name(db, "schnupperant").await.unwrap();
|
||||
|
||||
let user_futures: Vec<_> = User::all_with_role(db, &schnupperant)
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|u| async move { UserWithDetails::from_user(u, db).await })
|
||||
.collect();
|
||||
let users: Vec<UserWithDetails> = join_all(user_futures).await;
|
||||
|
||||
let mut context = Context::new();
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
context.insert("schnupperanten", &users);
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithDetails::from_user(user.user, db).await,
|
||||
);
|
||||
|
||||
Template::render("admin/schnupper/index", context.into_json())
|
||||
}
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![index]
|
||||
}
|
@@ -1,25 +1,15 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
model::{
|
||||
family::Family,
|
||||
log::Log,
|
||||
logbook::Logbook,
|
||||
role::Role,
|
||||
user::{
|
||||
AdminUser, AllowedToEditPaymentStatusUser, ManageUserUser, SchnupperBetreuerUser, User,
|
||||
UserWithDetails, UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser,
|
||||
},
|
||||
},
|
||||
tera::Config,
|
||||
use crate::model::{
|
||||
log::Log,
|
||||
role::Role,
|
||||
user::{AdminUser, ManageUserUser, User, UserWithDetails},
|
||||
};
|
||||
use futures::future::join_all;
|
||||
use lettre::Address;
|
||||
use rocket::{
|
||||
form::Form,
|
||||
fs::TempFile,
|
||||
get,
|
||||
http::{ContentType, Status},
|
||||
http::Status,
|
||||
post,
|
||||
request::{FlashMessage, FromRequest, Outcome},
|
||||
response::{Flash, Redirect},
|
||||
@@ -46,22 +36,21 @@ impl<'r> FromRequest<'r> for Referer {
|
||||
#[get("/user")]
|
||||
async fn index(
|
||||
db: &State<SqlitePool>,
|
||||
user: VorstandUser,
|
||||
user: ManageUserUser,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
) -> Template {
|
||||
let user_futures: Vec<_> = User::all(db)
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|u| async move { UserWithRolesAndMembershipPdf::from_user(db, u).await })
|
||||
.map(|u| async move { UserWithDetails::from_user(u, db).await })
|
||||
.collect();
|
||||
|
||||
let user: User = user.into_inner();
|
||||
let allowed_to_edit = ManageUserUser::new(db, user.clone()).await.is_some();
|
||||
|
||||
let users: Vec<UserWithRolesAndMembershipPdf> = join_all(user_futures).await;
|
||||
let users: Vec<UserWithDetails> = join_all(user_futures).await;
|
||||
|
||||
let roles = Role::all(db).await;
|
||||
let families = Family::all_with_members(db).await;
|
||||
|
||||
let mut context = Context::new();
|
||||
if let Some(msg) = flash {
|
||||
@@ -70,7 +59,6 @@ async fn index(
|
||||
context.insert("allowed_to_edit", &allowed_to_edit);
|
||||
context.insert("users", &users);
|
||||
context.insert("roles", &roles);
|
||||
context.insert("families", &families);
|
||||
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);
|
||||
|
||||
Template::render("admin/user/index", context.into_json())
|
||||
@@ -85,15 +73,14 @@ async fn index_admin(
|
||||
let user_futures: Vec<_> = User::all(db)
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|u| async move { UserWithRolesAndMembershipPdf::from_user(db, u).await })
|
||||
.map(|u| async move { UserWithDetails::from_user(u, db).await })
|
||||
.collect();
|
||||
let users: Vec<UserWithRolesAndMembershipPdf> = join_all(user_futures).await;
|
||||
let users: Vec<UserWithDetails> = join_all(user_futures).await;
|
||||
|
||||
let user: User = user.user;
|
||||
let allowed_to_edit = ManageUserUser::new(db, user.clone()).await.is_some();
|
||||
|
||||
let roles = Role::all(db).await;
|
||||
let families = Family::all_with_members(db).await;
|
||||
|
||||
let mut context = Context::new();
|
||||
if let Some(msg) = flash {
|
||||
@@ -102,137 +89,11 @@ async fn index_admin(
|
||||
context.insert("allowed_to_edit", &allowed_to_edit);
|
||||
context.insert("users", &users);
|
||||
context.insert("roles", &roles);
|
||||
context.insert("families", &families);
|
||||
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);
|
||||
|
||||
Template::render("admin/user/index", context.into_json())
|
||||
}
|
||||
|
||||
#[get("/user/fees")]
|
||||
async fn fees(
|
||||
db: &State<SqlitePool>,
|
||||
user: VorstandUser,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
) -> Template {
|
||||
let mut context = Context::new();
|
||||
|
||||
let users = User::all_payer_groups(db).await;
|
||||
let mut fees = Vec::new();
|
||||
for user in users {
|
||||
if let Some(fee) = user.fee(db).await {
|
||||
fees.push(fee);
|
||||
}
|
||||
}
|
||||
|
||||
context.insert("fees", &fees);
|
||||
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithDetails::from_user(user.into_inner(), db).await,
|
||||
);
|
||||
|
||||
Template::render("admin/user/fees", context.into_json())
|
||||
}
|
||||
|
||||
#[get("/user/scheckbuch")]
|
||||
async fn scheckbuch(
|
||||
db: &State<SqlitePool>,
|
||||
user: VorstandUser,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
) -> Template {
|
||||
let mut context = Context::new();
|
||||
|
||||
let scheckbooks = Role::find_by_name(db, "scheckbuch").await.unwrap();
|
||||
let scheckbooks = User::all_with_role(db, &scheckbooks).await;
|
||||
let mut scheckbooks_with_roles = Vec::new();
|
||||
for s in scheckbooks {
|
||||
scheckbooks_with_roles.push((
|
||||
Logbook::completed_with_user(db, &s).await,
|
||||
UserWithDetails::from_user(s, db).await,
|
||||
))
|
||||
}
|
||||
|
||||
context.insert("scheckbooks", &scheckbooks_with_roles);
|
||||
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithDetails::from_user(user.into_inner(), db).await,
|
||||
);
|
||||
|
||||
Template::render("admin/user/scheckbuch", context.into_json())
|
||||
}
|
||||
|
||||
#[get("/user/fees/paid?<user_ids>")]
|
||||
async fn fees_paid(
|
||||
db: &State<SqlitePool>,
|
||||
calling_user: AllowedToEditPaymentStatusUser,
|
||||
user_ids: Vec<i32>,
|
||||
referer: Referer,
|
||||
) -> Flash<Redirect> {
|
||||
let mut res = String::new();
|
||||
for user_id in user_ids {
|
||||
let user = User::find_by_id(db, user_id).await.unwrap();
|
||||
res.push_str(&format!("{} + ", user.name));
|
||||
if user.has_role(db, "paid").await {
|
||||
Log::create(
|
||||
db,
|
||||
format!(
|
||||
"{} set fees NOT paid for '{}'",
|
||||
calling_user.user.name, user.name
|
||||
),
|
||||
)
|
||||
.await;
|
||||
user.remove_role(db, &Role::find_by_name(db, "paid").await.unwrap())
|
||||
.await;
|
||||
} else {
|
||||
Log::create(
|
||||
db,
|
||||
format!(
|
||||
"{} set fees paid for '{}'",
|
||||
calling_user.user.name, user.name
|
||||
),
|
||||
)
|
||||
.await;
|
||||
user.add_role(db, &Role::find_by_name(db, "paid").await.unwrap())
|
||||
.await
|
||||
.expect("paid role has no group");
|
||||
}
|
||||
}
|
||||
|
||||
res.truncate(res.len() - 3); // remove ' + ' from the end
|
||||
|
||||
Flash::success(
|
||||
Redirect::to(referer.0),
|
||||
format!("Zahlungsstatus von {} erfolgreich geändert", res),
|
||||
)
|
||||
}
|
||||
|
||||
#[get("/user/<user>/send-welcome-mail")]
|
||||
async fn send_welcome_mail(
|
||||
db: &State<SqlitePool>,
|
||||
_admin: ManageUserUser,
|
||||
config: &State<Config>,
|
||||
user: i32,
|
||||
) -> Flash<Redirect> {
|
||||
let Some(user) = User::find_by_id(db, user).await else {
|
||||
return Flash::error(Redirect::to("/admin/user"), "User does not exist");
|
||||
};
|
||||
|
||||
match user.send_welcome_email(db, &config.smtp_pw).await {
|
||||
Ok(()) => Flash::success(
|
||||
Redirect::to("/admin/user"),
|
||||
format!("Willkommens-Email wurde an {} versandt.", user.name),
|
||||
),
|
||||
Err(e) => Flash::error(Redirect::to("/admin/user"), e),
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/user/<user>/reset-pw")]
|
||||
async fn resetpw(db: &State<SqlitePool>, admin: ManageUserUser, user: i32) -> Flash<Redirect> {
|
||||
let user = User::find_by_id(db, user).await;
|
||||
@@ -270,27 +131,16 @@ async fn delete(db: &State<SqlitePool>, admin: ManageUserUser, user: i32) -> Fla
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct UserEditForm<'a> {
|
||||
pub struct UserEditForm {
|
||||
pub(crate) id: i32,
|
||||
pub(crate) dob: Option<String>,
|
||||
pub(crate) weight: Option<String>,
|
||||
pub(crate) sex: Option<String>,
|
||||
pub(crate) name: String,
|
||||
pub(crate) roles: HashMap<String, String>,
|
||||
pub(crate) member_since_date: Option<String>,
|
||||
pub(crate) birthdate: Option<String>,
|
||||
pub(crate) mail: Option<String>,
|
||||
pub(crate) nickname: Option<String>,
|
||||
pub(crate) notes: Option<String>,
|
||||
pub(crate) phone: Option<String>,
|
||||
pub(crate) address: Option<String>,
|
||||
pub(crate) family_id: Option<i64>,
|
||||
pub(crate) membership_pdf: Option<TempFile<'a>>,
|
||||
}
|
||||
|
||||
#[post("/user", data = "<data>", format = "multipart/form-data")]
|
||||
async fn update(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<UserEditForm<'_>>,
|
||||
data: Form<UserEditForm>,
|
||||
admin: ManageUserUser,
|
||||
) -> Flash<Redirect> {
|
||||
let user = User::find_by_id(db, data.id).await;
|
||||
@@ -307,31 +157,14 @@ async fn update(
|
||||
};
|
||||
|
||||
match user.update(db, data.into_inner()).await {
|
||||
Ok(_) => Flash::success(Redirect::to("/admin/user"), "Successfully updated user"),
|
||||
Ok(_) => Flash::success(
|
||||
Redirect::to("/admin/user"),
|
||||
"Mitglied erfolgreich geändert!",
|
||||
),
|
||||
Err(e) => Flash::error(Redirect::to("/admin/user"), e),
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/user/<user>/membership")]
|
||||
async fn download_membership_pdf(
|
||||
db: &State<SqlitePool>,
|
||||
admin: ManageUserUser,
|
||||
user: i32,
|
||||
) -> (ContentType, Vec<u8>) {
|
||||
let user = User::find_by_id(db, user).await.unwrap();
|
||||
let user = UserWithMembershipPdf::from(db, user).await;
|
||||
Log::create(
|
||||
db,
|
||||
format!(
|
||||
"{} downloaded membership application for user: {}",
|
||||
admin.user.name, user.user.name
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
(ContentType::PDF, user.membership_pdf.unwrap())
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
struct UserAddForm<'r> {
|
||||
name: &'r str,
|
||||
@@ -349,132 +182,18 @@ async fn create(
|
||||
format!("{} created new user: {data:?}", admin.user.name),
|
||||
)
|
||||
.await;
|
||||
Flash::success(Redirect::to("/admin/user"), "Successfully created user")
|
||||
Flash::success(
|
||||
Redirect::to("/admin/user"),
|
||||
"Mitglied erfolgreich angelegt!",
|
||||
)
|
||||
} else {
|
||||
Flash::error(
|
||||
Redirect::to("/admin/user"),
|
||||
format!("User {} already exists", data.name),
|
||||
format!("Mitlgied {} gibt/gab es bereits", data.name),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
struct UserAddScheckbuchForm<'r> {
|
||||
name: &'r str,
|
||||
mail: &'r str,
|
||||
}
|
||||
|
||||
#[post("/user/new/scheckbuch", data = "<data>")]
|
||||
async fn create_scheckbuch(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<UserAddScheckbuchForm<'_>>,
|
||||
admin: VorstandUser,
|
||||
config: &State<Config>,
|
||||
) -> Flash<Redirect> {
|
||||
// 1. Check mail adress
|
||||
let mail = data.mail.trim();
|
||||
if mail.parse::<Address>().is_err() {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user/scheckbuch"),
|
||||
"Keine gültige Mailadresse".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
// 2. Check name
|
||||
let name = data.name.trim();
|
||||
if User::find_by_name(db, name).await.is_some() {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user/scheckbuch"),
|
||||
"Kann kein Scheckbuch erstellen, der Name wird bereits von einem User verwendet"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
// 3. Create user
|
||||
User::create_with_mail(db, name, mail).await;
|
||||
let user = User::find_by_name(db, name).await.unwrap();
|
||||
|
||||
// 4. Add 'scheckbuch' role
|
||||
let scheckbuch = Role::find_by_name(db, "scheckbuch").await.unwrap();
|
||||
user.add_role(db, &scheckbuch)
|
||||
.await
|
||||
.expect("new user has no roles yet");
|
||||
|
||||
// 4. Send welcome mail (+ notification)
|
||||
user.send_welcome_email(db, &config.smtp_pw).await.unwrap();
|
||||
|
||||
Log::create(
|
||||
db,
|
||||
format!("{} created new scheckbuch: {data:?}", admin.name),
|
||||
)
|
||||
.await;
|
||||
Flash::success(Redirect::to("/admin/user/scheckbuch"), &format!("Scheckbuch erfolgreich erstellt. Eine E-Mail in der alles erklärt wird, wurde an {mail} verschickt."))
|
||||
}
|
||||
|
||||
#[get("/user/move/schnupperant/<id>/to/scheckbuch")]
|
||||
async fn schnupper_to_scheckbuch(
|
||||
db: &State<SqlitePool>,
|
||||
id: i32,
|
||||
admin: SchnupperBetreuerUser,
|
||||
config: &State<Config>,
|
||||
) -> Flash<Redirect> {
|
||||
let Some(user) = User::find_by_id(db, id).await else {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/schnupper"),
|
||||
"user id not found".to_string(),
|
||||
);
|
||||
};
|
||||
|
||||
if !user.has_role(db, "schnupperant").await {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/schnupper"),
|
||||
"kein schnupperant...".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
let schnupperant = Role::find_by_name(db, "schnupperant").await.unwrap();
|
||||
let paid = Role::find_by_name(db, "paid").await.unwrap();
|
||||
user.remove_role(db, &schnupperant).await;
|
||||
user.remove_role(db, &paid).await;
|
||||
|
||||
let scheckbuch = Role::find_by_name(db, "scheckbuch").await.unwrap();
|
||||
user.add_role(db, &scheckbuch)
|
||||
.await
|
||||
.expect("just removed 'schnupperant' thus can't have a role with that group");
|
||||
|
||||
if let Some(no_einschreibgebuehr) = Role::find_by_name(db, "no-einschreibgebuehr").await {
|
||||
user.add_role(db, &no_einschreibgebuehr)
|
||||
.await
|
||||
.expect("role doesn't have a group");
|
||||
}
|
||||
|
||||
user.send_welcome_email(db, &config.smtp_pw).await.unwrap();
|
||||
|
||||
Log::create(
|
||||
db,
|
||||
format!(
|
||||
"{} created new scheckbuch (from schnupperant): {}",
|
||||
admin.name, user.name
|
||||
),
|
||||
)
|
||||
.await;
|
||||
Flash::success(Redirect::to("/admin/schnupper"), &format!("Scheckbuch erfolgreich erstellt. Eine E-Mail in der alles erklärt wird, wurde an {} verschickt.", user.mail.unwrap()))
|
||||
}
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![
|
||||
index,
|
||||
index_admin,
|
||||
resetpw,
|
||||
update,
|
||||
create,
|
||||
create_scheckbuch,
|
||||
schnupper_to_scheckbuch,
|
||||
delete,
|
||||
fees,
|
||||
fees_paid,
|
||||
scheckbuch,
|
||||
download_membership_pdf,
|
||||
send_welcome_mail
|
||||
]
|
||||
routes![index, index_admin, resetpw, update, create, delete]
|
||||
}
|
||||
|
@@ -73,7 +73,7 @@ async fn login(
|
||||
);
|
||||
}
|
||||
Err(_) => {
|
||||
return Flash::error(Redirect::to("/auth"), "Falscher Benutzername/Passwort. Du bist Vereinsmitglied und der Login klappt nicht? Kontaktiere Philipp H. (Tel.nr. siehe Signalgruppe) oder schreibe eine Mail an it@rudernlinz.at!");
|
||||
return Flash::error(Redirect::to("/auth"), "Falscher Benutzername/Passwort. Du bist Vereinsmitglied und der Login klappt nicht? Melde dich bitte unter it@verein.tld!");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -88,15 +88,7 @@ async fn login(
|
||||
)
|
||||
.await;
|
||||
|
||||
// Check for redirect_url cookie and redirect accordingly
|
||||
match cookies.get_private("redirect_url") {
|
||||
Some(redirect_cookie) => {
|
||||
let redirect_url = redirect_cookie.value().to_string();
|
||||
cookies.remove_private(redirect_cookie); // Remove the cookie after using it
|
||||
Flash::success(Redirect::to(redirect_url), "Login erfolgreich")
|
||||
}
|
||||
None => Flash::success(Redirect::to("/"), "Login erfolgreich"),
|
||||
}
|
||||
Flash::success(Redirect::to("/"), "Login erfolgreich")
|
||||
}
|
||||
|
||||
#[get("/set-pw/<userid>")]
|
||||
|
@@ -1,46 +0,0 @@
|
||||
use crate::model::{
|
||||
personal::Achievements,
|
||||
role::Role,
|
||||
user::{User, UserWithDetails, VorstandUser},
|
||||
};
|
||||
use rocket::{get, request::FlashMessage, routes, Route, State};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
#[get("/achievement")]
|
||||
async fn index(
|
||||
db: &State<SqlitePool>,
|
||||
admin: VorstandUser,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
) -> Template {
|
||||
let mut context = Context::new();
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
|
||||
let role = Role::find_by_name(&db, "Donau Linz").await.unwrap();
|
||||
let users = User::all_with_role(&db, &role).await;
|
||||
let mut people = Vec::new();
|
||||
let mut rowingbadge_year = None;
|
||||
for user in users {
|
||||
let achievement = Achievements::for_user(&db, &user).await;
|
||||
if let Some(badge) = &achievement.rowingbadge {
|
||||
rowingbadge_year = Some(badge.year);
|
||||
}
|
||||
people.push((user, achievement));
|
||||
}
|
||||
|
||||
context.insert("people", &people);
|
||||
context.insert("rowingbadge_year", &rowingbadge_year.unwrap());
|
||||
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithDetails::from_user(admin.into_inner(), db).await,
|
||||
);
|
||||
|
||||
Template::render("achievement", context.into_json())
|
||||
}
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![index]
|
||||
}
|
@@ -1,92 +0,0 @@
|
||||
use crate::model::{
|
||||
boat::Boat,
|
||||
boathouse::Boathouse,
|
||||
user::{AdminUser, UserWithDetails, VorstandUser},
|
||||
};
|
||||
use rocket::{
|
||||
form::Form,
|
||||
get, post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
routes, FromForm, Route, State,
|
||||
};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
#[get("/boathouse")]
|
||||
async fn index(
|
||||
db: &State<SqlitePool>,
|
||||
admin: VorstandUser,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
) -> Template {
|
||||
let mut context = Context::new();
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
|
||||
let boats = Boat::all_for_boatshouse(db).await;
|
||||
let mut final_boats = Vec::new();
|
||||
for boat in boats {
|
||||
if boat.boat.boathouse(db).await.is_none() && !boat.boat.external {
|
||||
final_boats.push(boat);
|
||||
}
|
||||
}
|
||||
|
||||
context.insert("boats", &final_boats);
|
||||
|
||||
let boathouse = Boathouse::get(db).await;
|
||||
context.insert("boathouse", &boathouse);
|
||||
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithDetails::from_user(admin.into_inner(), db).await,
|
||||
);
|
||||
|
||||
Template::render("board/boathouse", context.into_json())
|
||||
}
|
||||
|
||||
#[derive(FromForm)]
|
||||
pub struct FormBoathouseToAdd {
|
||||
pub boat_id: i32,
|
||||
pub aisle: String,
|
||||
pub side: String,
|
||||
pub level: i32,
|
||||
}
|
||||
#[post("/boathouse", data = "<data>")]
|
||||
async fn new<'r>(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<FormBoathouseToAdd>,
|
||||
_admin: AdminUser,
|
||||
) -> Flash<Redirect> {
|
||||
match Boathouse::create(db, data.into_inner()).await {
|
||||
Ok(_) => Flash::success(Redirect::to("/board/boathouse"), "Boot hinzugefügt"),
|
||||
Err(e) => Flash::error(Redirect::to("/board/boathouse"), e),
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/boathouse/<boathouse_id>/delete")]
|
||||
async fn delete(db: &State<SqlitePool>, _admin: AdminUser, boathouse_id: i32) -> Flash<Redirect> {
|
||||
let boat = Boathouse::find_by_id(db, boathouse_id).await;
|
||||
match boat {
|
||||
Some(boat) => {
|
||||
boat.delete(db).await;
|
||||
Flash::success(Redirect::to("/board/boathouse"), "Bootsplatz gelöscht")
|
||||
}
|
||||
None => Flash::error(Redirect::to("/board/boathouse"), "Boatplace does not exist"),
|
||||
}
|
||||
}
|
||||
//#[post("/boat/new", data = "<data>")]
|
||||
//async fn create(
|
||||
// db: &State<SqlitePool>,
|
||||
// data: Form<BoatToAdd<'_>>,
|
||||
// _admin: AdminUser,
|
||||
//) -> Flash<Redirect> {
|
||||
// match Boat::create(db, data.into_inner()).await {
|
||||
// Ok(_) => Flash::success(Redirect::to("/admin/boat"), "Boot hinzugefügt"),
|
||||
// Err(e) => Flash::error(Redirect::to("/admin/boat"), e),
|
||||
// }
|
||||
//}
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![index, new, delete]
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
use rocket::Route;
|
||||
|
||||
pub mod achievement;
|
||||
pub mod boathouse;
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
let mut ret = Vec::new();
|
||||
ret.append(&mut boathouse::routes());
|
||||
ret.append(&mut achievement::routes());
|
||||
ret
|
||||
}
|
@@ -1,181 +0,0 @@
|
||||
use rocket::{
|
||||
form::Form,
|
||||
get, post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
routes, FromForm, Route, State,
|
||||
};
|
||||
use rocket_dyn_templates::Template;
|
||||
use sqlx::SqlitePool;
|
||||
use tera::Context;
|
||||
|
||||
use crate::{
|
||||
model::{
|
||||
boat::Boat,
|
||||
boatdamage::{BoatDamage, BoatDamageFixed, BoatDamageToAdd, BoatDamageVerified},
|
||||
user::{DonauLinzUser, SteeringUser, TechUser, User, UserWithDetails},
|
||||
},
|
||||
tera::log::KioskCookie,
|
||||
};
|
||||
|
||||
#[get("/")]
|
||||
async fn index_kiosk(
|
||||
db: &State<SqlitePool>,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
_kiosk: KioskCookie,
|
||||
) -> Template {
|
||||
let boatdamages = BoatDamage::all(db).await;
|
||||
let boats = Boat::all(db).await;
|
||||
let user = User::all(db).await;
|
||||
|
||||
let mut context = Context::new();
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
|
||||
context.insert("boatdamages", &boatdamages);
|
||||
context.insert("boats", &boats);
|
||||
context.insert("user", &user);
|
||||
context.insert("show_kiosk_header", &true);
|
||||
|
||||
Template::render("boatdamages", context.into_json())
|
||||
}
|
||||
|
||||
#[get("/", rank = 2)]
|
||||
async fn index(
|
||||
db: &State<SqlitePool>,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
user: DonauLinzUser,
|
||||
) -> Template {
|
||||
let boatdamages = BoatDamage::all(db).await;
|
||||
let boats = Boat::all(db).await;
|
||||
|
||||
let mut context = Context::new();
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
|
||||
context.insert("boatdamages", &boatdamages);
|
||||
context.insert("boats", &boats);
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithDetails::from_user(user.into_inner(), db).await,
|
||||
);
|
||||
|
||||
Template::render("boatdamages", context.into_json())
|
||||
}
|
||||
|
||||
#[derive(FromForm)]
|
||||
pub struct FormBoatDamageToAdd<'r> {
|
||||
pub boat_id: i64,
|
||||
pub desc: &'r str,
|
||||
pub lock_boat: bool,
|
||||
}
|
||||
|
||||
#[post("/", data = "<data>", rank = 2)]
|
||||
async fn create<'r>(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<FormBoatDamageToAdd<'r>>,
|
||||
user: DonauLinzUser,
|
||||
) -> Flash<Redirect> {
|
||||
let user: User = user.into_inner();
|
||||
let boatdamage_to_add = BoatDamageToAdd {
|
||||
boat_id: data.boat_id,
|
||||
desc: data.desc,
|
||||
lock_boat: data.lock_boat,
|
||||
user_id_created: user.id as i32,
|
||||
};
|
||||
match BoatDamage::create(db, boatdamage_to_add).await {
|
||||
Ok(_) => Flash::success(
|
||||
Redirect::to("/boatdamage"),
|
||||
"Bootsschaden erfolgreich hinzugefügt",
|
||||
),
|
||||
Err(e) => Flash::error(Redirect::to("/boatdamage"), format!("Fehler: {e}")),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromForm)]
|
||||
pub struct FormBoatDamageToAddKiosk<'r> {
|
||||
pub boat_id: i64,
|
||||
pub desc: &'r str,
|
||||
pub lock_boat: bool,
|
||||
pub user_id: i32,
|
||||
}
|
||||
|
||||
#[post("/", data = "<data>")]
|
||||
async fn create_from_kiosk<'r>(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<FormBoatDamageToAddKiosk<'r>>,
|
||||
_kiosk: KioskCookie,
|
||||
) -> Flash<Redirect> {
|
||||
let boatdamage_to_add = BoatDamageToAdd {
|
||||
boat_id: data.boat_id,
|
||||
desc: data.desc,
|
||||
lock_boat: data.lock_boat,
|
||||
user_id_created: data.user_id,
|
||||
};
|
||||
match BoatDamage::create(db, boatdamage_to_add).await {
|
||||
Ok(_) => Flash::success(
|
||||
Redirect::to("/boatdamage"),
|
||||
"Bootsschaden erfolgreich hinzugefügt",
|
||||
),
|
||||
Err(e) => Flash::error(Redirect::to("/boatdamage"), format!("Fehler: {e}")),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromForm)]
|
||||
pub struct FormBoatDamageFixed<'r> {
|
||||
pub desc: &'r str,
|
||||
}
|
||||
|
||||
#[post("/<boatdamage_id>/fixed", data = "<data>")]
|
||||
async fn fixed<'r>(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<FormBoatDamageFixed<'r>>,
|
||||
boatdamage_id: i32,
|
||||
coxuser: SteeringUser,
|
||||
) -> Flash<Redirect> {
|
||||
let boatdamage = BoatDamage::find_by_id(db, boatdamage_id).await.unwrap(); //TODO: Fix
|
||||
let boatdamage_fixed = BoatDamageFixed {
|
||||
desc: data.desc,
|
||||
user_id_fixed: coxuser.id as i32,
|
||||
};
|
||||
match boatdamage.fixed(db, boatdamage_fixed).await {
|
||||
Ok(_) => Flash::success(Redirect::to("/boatdamage"), "Bootsschaden behoben."),
|
||||
Err(e) => Flash::error(Redirect::to("/boatdamage"), format!("Error: {e}")),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromForm)]
|
||||
pub struct FormBoatDamageVerified<'r> {
|
||||
pub desc: &'r str,
|
||||
}
|
||||
|
||||
#[post("/<boatdamage_id>/verified", data = "<data>")]
|
||||
async fn verified<'r>(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<FormBoatDamageFixed<'r>>,
|
||||
boatdamage_id: i32,
|
||||
techuser: TechUser,
|
||||
) -> Flash<Redirect> {
|
||||
let boatdamage = BoatDamage::find_by_id(db, boatdamage_id).await.unwrap(); //TODO: Fix
|
||||
let boatdamage_verified = BoatDamageVerified {
|
||||
desc: data.desc,
|
||||
user_id_verified: techuser.id as i32,
|
||||
};
|
||||
match boatdamage.verified(db, boatdamage_verified).await {
|
||||
Ok(_) => Flash::success(Redirect::to("/boatdamage"), "Bootsschaden verifiziert"),
|
||||
Err(e) => Flash::error(Redirect::to("/boatdamage"), format!("Error: {e}")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![
|
||||
index,
|
||||
index_kiosk,
|
||||
create,
|
||||
fixed,
|
||||
verified,
|
||||
create_from_kiosk
|
||||
]
|
||||
}
|
@@ -1,223 +0,0 @@
|
||||
use chrono::NaiveDate;
|
||||
use rocket::{
|
||||
form::Form,
|
||||
get, post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
routes, FromForm, Route, State,
|
||||
};
|
||||
use rocket_dyn_templates::Template;
|
||||
use sqlx::SqlitePool;
|
||||
use tera::Context;
|
||||
|
||||
use crate::{
|
||||
model::{
|
||||
boat::Boat,
|
||||
boatreservation::{BoatReservation, BoatReservationToAdd},
|
||||
log::Log,
|
||||
user::{DonauLinzUser, User, UserWithDetails},
|
||||
},
|
||||
tera::log::KioskCookie,
|
||||
};
|
||||
|
||||
#[get("/")]
|
||||
async fn index_kiosk(
|
||||
db: &State<SqlitePool>,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
_kiosk: KioskCookie,
|
||||
) -> Template {
|
||||
let boatreservations = BoatReservation::all_future(db).await;
|
||||
|
||||
let mut context = Context::new();
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
|
||||
let linz_boats = Boat::all_for_boatshouse(db).await;
|
||||
let mut boats = Vec::new();
|
||||
for boat in linz_boats {
|
||||
if boat.boat.owner.is_none() {
|
||||
boats.push(boat);
|
||||
}
|
||||
}
|
||||
|
||||
context.insert("boatreservations", &boatreservations);
|
||||
context.insert("boats", &boats);
|
||||
context.insert("user", &User::all(db).await);
|
||||
context.insert("show_kiosk_header", &true);
|
||||
|
||||
Template::render("boatreservations", context.into_json())
|
||||
}
|
||||
|
||||
#[get("/", rank = 2)]
|
||||
async fn index(
|
||||
db: &State<SqlitePool>,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
user: DonauLinzUser,
|
||||
) -> Template {
|
||||
let boatreservations = BoatReservation::all_future(db).await;
|
||||
|
||||
let mut context = Context::new();
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
|
||||
let linz_boats = Boat::all_for_boatshouse(db).await;
|
||||
let mut boats = Vec::new();
|
||||
for boat in linz_boats {
|
||||
if boat.boat.owner.is_none() {
|
||||
boats.push(boat);
|
||||
}
|
||||
}
|
||||
|
||||
context.insert("boatreservations", &boatreservations);
|
||||
context.insert("boats", &boats);
|
||||
context.insert("user", &User::all(db).await);
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithDetails::from_user(user.into_inner(), db).await,
|
||||
);
|
||||
|
||||
Template::render("boatreservations", context.into_json())
|
||||
}
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct FormBoatReservationToAdd<'r> {
|
||||
pub boat_id: i64,
|
||||
pub start_date: &'r str,
|
||||
pub end_date: &'r str,
|
||||
pub time_desc: &'r str,
|
||||
pub usage: &'r str,
|
||||
pub user_id_applicant: Option<i64>,
|
||||
}
|
||||
|
||||
#[post("/new", data = "<data>", rank = 2)]
|
||||
async fn create<'r>(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<FormBoatReservationToAdd<'r>>,
|
||||
user: DonauLinzUser,
|
||||
) -> Flash<Redirect> {
|
||||
let user_applicant: User = user.into_inner();
|
||||
let boat = Boat::find_by_id(db, data.boat_id as i32).await.unwrap();
|
||||
let boatreservation_to_add = BoatReservationToAdd {
|
||||
boat: &boat,
|
||||
start_date: NaiveDate::parse_from_str(data.start_date, "%Y-%m-%d").unwrap(),
|
||||
end_date: NaiveDate::parse_from_str(data.end_date, "%Y-%m-%d").unwrap(),
|
||||
time_desc: data.time_desc,
|
||||
usage: data.usage,
|
||||
user_applicant: &user_applicant,
|
||||
};
|
||||
match BoatReservation::create(db, boatreservation_to_add).await {
|
||||
Ok(_) => Flash::success(
|
||||
Redirect::to("/boatreservation"),
|
||||
"Reservierung erfolgreich hinzugefügt",
|
||||
),
|
||||
Err(e) => Flash::error(Redirect::to("/boatreservation"), format!("Fehler: {e}")),
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/new", data = "<data>")]
|
||||
async fn create_from_kiosk<'r>(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<FormBoatReservationToAdd<'r>>,
|
||||
_kiosk: KioskCookie,
|
||||
) -> Flash<Redirect> {
|
||||
let user_applicant: User = User::find_by_id(db, data.user_id_applicant.unwrap() as i32)
|
||||
.await
|
||||
.unwrap();
|
||||
let boat = Boat::find_by_id(db, data.boat_id as i32).await.unwrap();
|
||||
let boatreservation_to_add = BoatReservationToAdd {
|
||||
boat: &boat,
|
||||
start_date: NaiveDate::parse_from_str(data.start_date, "%Y-%m-%d").unwrap(),
|
||||
end_date: NaiveDate::parse_from_str(data.end_date, "%Y-%m-%d").unwrap(),
|
||||
time_desc: data.time_desc,
|
||||
usage: data.usage,
|
||||
user_applicant: &user_applicant,
|
||||
};
|
||||
match BoatReservation::create(db, boatreservation_to_add).await {
|
||||
Ok(_) => Flash::success(
|
||||
Redirect::to("/boatreservation"),
|
||||
"Reservierung erfolgreich hinzugefügt",
|
||||
),
|
||||
Err(e) => Flash::error(Redirect::to("/boatreservation"), format!("Fehler: {e}")),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct ReservationEditForm {
|
||||
pub(crate) id: i32,
|
||||
pub(crate) time_desc: String,
|
||||
pub(crate) usage: String,
|
||||
}
|
||||
|
||||
#[post("/", data = "<data>")]
|
||||
async fn update(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<ReservationEditForm>,
|
||||
user: User,
|
||||
) -> Flash<Redirect> {
|
||||
let Some(reservation) = BoatReservation::find_by_id(db, data.id).await else {
|
||||
return Flash::error(
|
||||
Redirect::to("/boatreservation"),
|
||||
format!("Reservation with ID {} does not exist!", data.id),
|
||||
);
|
||||
};
|
||||
|
||||
if user.id != reservation.user_id_applicant && !user.has_role(db, "admin").await {
|
||||
return Flash::error(
|
||||
Redirect::to("/boatreservation"),
|
||||
"Not allowed to update reservation (only admins + creator do so).".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
Log::create(
|
||||
db,
|
||||
format!(
|
||||
"{} updated reservation from {reservation:?} to {data:?}",
|
||||
user.name
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
reservation.update(db, data.into_inner()).await;
|
||||
|
||||
Flash::success(
|
||||
Redirect::to("/boatreservation"),
|
||||
"Reservierung erfolgreich bearbeitet",
|
||||
)
|
||||
}
|
||||
|
||||
#[get("/<reservation_id>/delete")]
|
||||
async fn delete<'r>(
|
||||
db: &State<SqlitePool>,
|
||||
reservation_id: i32,
|
||||
user: DonauLinzUser,
|
||||
) -> Flash<Redirect> {
|
||||
let reservation = BoatReservation::find_by_id(db, reservation_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
if user.id == reservation.user_id_applicant || user.has_role(db, "admin").await {
|
||||
reservation.delete(db).await;
|
||||
Flash::success(
|
||||
Redirect::to("/boatreservation"),
|
||||
"Reservierung erfolgreich gelöscht",
|
||||
)
|
||||
} else {
|
||||
Flash::error(
|
||||
Redirect::to("/boatreservation"),
|
||||
"Nur der Reservierer darf die Reservierung löschen.".to_string(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![
|
||||
index,
|
||||
index_kiosk,
|
||||
create,
|
||||
create_from_kiosk,
|
||||
delete,
|
||||
update
|
||||
]
|
||||
}
|
105
src/tera/cox.rs
105
src/tera/cox.rs
@@ -11,32 +11,9 @@ use crate::model::{
|
||||
log::Log,
|
||||
trip::{self, CoxHelpError, Trip, TripDeleteError, TripHelpDeleteError, TripUpdateError},
|
||||
tripdetails::{TripDetails, TripDetailsToAdd},
|
||||
user::{AllowedToUpdateTripToAlwaysBeShownUser, ErgoUser, SteeringUser, User},
|
||||
user::{AllowedToUpdateTripToAlwaysBeShownUser, SteeringUser, User},
|
||||
};
|
||||
|
||||
#[post("/trip", data = "<data>", rank = 2)]
|
||||
async fn create_ergo(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<TripDetailsToAdd<'_>>,
|
||||
cox: ErgoUser,
|
||||
) -> Flash<Redirect> {
|
||||
let trip_details_id = TripDetails::create(db, data.into_inner()).await;
|
||||
let trip_details = TripDetails::find_by_id(db, trip_details_id).await.unwrap(); //Okay, bc just
|
||||
//created
|
||||
Trip::new_own_ergo(db, &cox, trip_details).await; //TODO: fix
|
||||
|
||||
//Log::create(
|
||||
// db,
|
||||
// format!(
|
||||
// "Cox {} created trip on {} @ {} for {} rower",
|
||||
// cox.name, trip_details.day, trip_details.planned_starting_time, trip_details.max_people,
|
||||
// ),
|
||||
//)
|
||||
//.await;
|
||||
|
||||
Flash::success(Redirect::to("/planned"), "Ausfahrt erfolgreich erstellt.")
|
||||
}
|
||||
|
||||
#[post("/trip", data = "<data>")]
|
||||
async fn create(
|
||||
db: &State<SqlitePool>,
|
||||
@@ -57,7 +34,7 @@ async fn create(
|
||||
//)
|
||||
//.await;
|
||||
|
||||
Flash::success(Redirect::to("/planned"), "Ausfahrt erfolgreich erstellt.")
|
||||
Flash::success(Redirect::to("/"), "Ausfahrt erfolgreich erstellt.")
|
||||
}
|
||||
|
||||
#[derive(FromForm)]
|
||||
@@ -85,23 +62,19 @@ async fn update(
|
||||
is_locked: data.is_locked,
|
||||
};
|
||||
match Trip::update_own(db, &update).await {
|
||||
Ok(_) => Flash::success(
|
||||
Redirect::to("/planned"),
|
||||
"Ausfahrt erfolgreich aktualisiert.",
|
||||
),
|
||||
Ok(_) => Flash::success(Redirect::to("/"), "Ausfahrt erfolgreich aktualisiert."),
|
||||
Err(TripUpdateError::NotYourTrip) => {
|
||||
Flash::error(Redirect::to("/planned"), "Nicht deine Ausfahrt!")
|
||||
Flash::error(Redirect::to("/"), "Nicht deine Ausfahrt!")
|
||||
}
|
||||
Err(TripUpdateError::TripTypeNotAllowed) => {
|
||||
Flash::error(Redirect::to("/"), "Du darfst nur Ergo-Events erstellen")
|
||||
}
|
||||
Err(TripUpdateError::TripTypeNotAllowed) => Flash::error(
|
||||
Redirect::to("/planned"),
|
||||
"Du darfst nur Ergo-Events erstellen",
|
||||
),
|
||||
Err(TripUpdateError::TripDetailsDoesNotExist) => {
|
||||
Flash::error(Redirect::to("/planned"), "Ausfahrt gibt's nicht")
|
||||
Flash::error(Redirect::to("/"), "Ausfahrt gibt's nicht")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Flash::error(Redirect::to("/planned"), "Ausfahrt gibt's nicht")
|
||||
Flash::error(Redirect::to("/"), "Ausfahrt gibt's nicht")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,12 +86,9 @@ async fn toggle_always_show(
|
||||
) -> Flash<Redirect> {
|
||||
if let Some(trip) = Trip::find_by_id(db, trip_id).await {
|
||||
trip.toggle_always_show(db).await;
|
||||
Flash::success(
|
||||
Redirect::to("/planned"),
|
||||
"'Immer anzeigen' erfolgreich gesetzt!",
|
||||
)
|
||||
Flash::success(Redirect::to("/"), "'Immer anzeigen' erfolgreich gesetzt!")
|
||||
} else {
|
||||
Flash::error(Redirect::to("/planned"), "Ausfahrt gibt's nicht")
|
||||
Flash::error(Redirect::to("/"), "Ausfahrt gibt's nicht")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,24 +105,24 @@ async fn join(db: &State<SqlitePool>, planned_event_id: i64, cox: SteeringUser)
|
||||
),
|
||||
)
|
||||
.await;
|
||||
Flash::success(Redirect::to("/planned"), "Danke für's helfen!")
|
||||
Flash::success(Redirect::to("/"), "Danke für's helfen!")
|
||||
}
|
||||
Err(CoxHelpError::CanceledEvent) => {
|
||||
Flash::error(Redirect::to("/planned"), "Die Ausfahrt wurde leider abgesagt...")
|
||||
Flash::error(Redirect::to("/"), "Die Ausfahrt wurde leider abgesagt...")
|
||||
}
|
||||
Err(CoxHelpError::AlreadyRegisteredAsCox) => {
|
||||
Flash::error(Redirect::to("/planned"), "Du hilfst bereits aus!")
|
||||
Flash::error(Redirect::to("/"), "Du hilfst bereits aus!")
|
||||
}
|
||||
Err(CoxHelpError::AlreadyRegisteredAsRower) => Flash::error(
|
||||
Redirect::to("/planned"),
|
||||
Redirect::to("/"),
|
||||
"Du hast dich bereits als Ruderer angemeldet!",
|
||||
),
|
||||
Err(CoxHelpError::DetailsLocked) => {
|
||||
Flash::error(Redirect::to("/planned"), "Die Bootseinteilung wurde bereits gemacht. Wenn du noch steuern möchtest, frag bitte bei einer bereits angemeldeten Steuerperson nach, ob das noch möglich ist.")
|
||||
Flash::error(Redirect::to("/"), "Die Bootseinteilung wurde bereits gemacht. Wenn du noch steuern möchtest, frag bitte bei einer bereits angemeldeten Steuerperson nach, ob das noch möglich ist.")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Flash::error(Redirect::to("/planned"), "Event gibt's nicht")
|
||||
Flash::error(Redirect::to("/"), "Event gibt's nicht")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,18 +130,18 @@ async fn join(db: &State<SqlitePool>, planned_event_id: i64, cox: SteeringUser)
|
||||
async fn remove_trip(db: &State<SqlitePool>, trip_id: i64, cox: User) -> Flash<Redirect> {
|
||||
let trip = Trip::find_by_id(db, trip_id).await;
|
||||
match trip {
|
||||
None => Flash::error(Redirect::to("/planned"), "Trip gibt's nicht!"),
|
||||
None => Flash::error(Redirect::to("/"), "Trip gibt's nicht!"),
|
||||
Some(trip) => match trip.delete(db, &cox).await {
|
||||
Ok(_) => {
|
||||
Log::create(db, format!("Cox {} deleted trip.id={}", cox.name, trip_id)).await;
|
||||
Flash::success(Redirect::to("/planned"), "Erfolgreich gelöscht!")
|
||||
Flash::success(Redirect::to("/"), "Erfolgreich gelöscht!")
|
||||
}
|
||||
Err(TripDeleteError::SomebodyAlreadyRegistered) => Flash::error(
|
||||
Redirect::to("/planned"),
|
||||
Redirect::to("/"),
|
||||
"Ausfahrt kann nicht gelöscht werden, da bereits jemand registriert ist!",
|
||||
),
|
||||
Err(TripDeleteError::NotYourTrip) => {
|
||||
Flash::error(Redirect::to("/planned"), "Nicht deine Ausfahrt!")
|
||||
Flash::error(Redirect::to("/"), "Nicht deine Ausfahrt!")
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -195,24 +165,23 @@ async fn remove(
|
||||
)
|
||||
.await;
|
||||
|
||||
Flash::success(Redirect::to("/planned"), "Erfolgreich abgemeldet!")
|
||||
Flash::success(Redirect::to("/"), "Erfolgreich abgemeldet!")
|
||||
}
|
||||
Err(TripHelpDeleteError::DetailsLocked) => {
|
||||
Flash::error(Redirect::to("/planned"), "Die Bootseinteilung wurde bereits gemacht. Wenn du doch nicht steuern kannst, melde dich bitte unbedingt schnellstmöglich bei einer anderen Steuerperson!")
|
||||
Flash::error(Redirect::to("/"), "Die Bootseinteilung wurde bereits gemacht. Wenn du doch nicht steuern kannst, melde dich bitte unbedingt schnellstmöglich bei einer anderen Steuerperson!")
|
||||
}
|
||||
Err(TripHelpDeleteError::CoxNotHelping) => {
|
||||
Flash::error(Redirect::to("/planned"), "Steuermann hilft nicht aus...")
|
||||
Flash::error(Redirect::to("/"), "Steuermann hilft nicht aus...")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Flash::error(Redirect::to("/planned"), "Planned_event does not exist.")
|
||||
Flash::error(Redirect::to("/"), "Planned_event does not exist.")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![
|
||||
create,
|
||||
create_ergo,
|
||||
join,
|
||||
remove,
|
||||
remove_trip,
|
||||
@@ -260,7 +229,7 @@ mod test {
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
@@ -310,7 +279,7 @@ mod test {
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
@@ -349,7 +318,7 @@ mod test {
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
@@ -389,7 +358,7 @@ mod test {
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
@@ -417,7 +386,7 @@ mod test {
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
@@ -430,7 +399,7 @@ mod test {
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
@@ -454,14 +423,14 @@ mod test {
|
||||
.body("name=cox&password=cox"); // Add the form data to the request body;
|
||||
login.dispatch().await;
|
||||
|
||||
let req = client.get("/planned/join/1");
|
||||
let req = client.get("/join/1");
|
||||
let _ = req.dispatch().await;
|
||||
|
||||
let req = client.get("/cox/join/1");
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
@@ -492,7 +461,7 @@ mod test {
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
@@ -533,7 +502,7 @@ mod test {
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
@@ -561,7 +530,7 @@ mod test {
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
@@ -589,7 +558,7 @@ mod test {
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
|
365
src/tera/ergo.rs
365
src/tera/ergo.rs
@@ -1,365 +0,0 @@
|
||||
use std::env;
|
||||
|
||||
use chrono::{Datelike, Utc};
|
||||
use rocket::{
|
||||
form::Form,
|
||||
fs::TempFile,
|
||||
get,
|
||||
http::ContentType,
|
||||
post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
routes, FromForm, Route, State,
|
||||
};
|
||||
use rocket_dyn_templates::{context, Template};
|
||||
use serde::Serialize;
|
||||
use sqlx::SqlitePool;
|
||||
use tera::Context;
|
||||
|
||||
use crate::model::{
|
||||
log::Log,
|
||||
notification::Notification,
|
||||
role::Role,
|
||||
user::{AdminUser, User, UserWithDetails},
|
||||
};
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ErgoStat {
|
||||
id: i64,
|
||||
name: String,
|
||||
dob: Option<String>,
|
||||
weight: Option<String>,
|
||||
sex: Option<String>,
|
||||
result: Option<String>,
|
||||
}
|
||||
|
||||
#[get("/final")]
|
||||
async fn send(db: &State<SqlitePool>, _user: AdminUser) -> Template {
|
||||
let thirty = sqlx::query_as!(
|
||||
ErgoStat,
|
||||
"SELECT id, name, dirty_thirty as result, dob, weight, sex FROM user WHERE deleted = 0 AND dirty_thirty is not null ORDER BY result DESC"
|
||||
)
|
||||
.fetch_all(db.inner())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let dozen= sqlx::query_as!(
|
||||
ErgoStat,
|
||||
"SELECT id, name, dirty_dozen as result, dob, weight, sex FROM user WHERE deleted = 0 AND dirty_dozen is not null ORDER BY result DESC"
|
||||
)
|
||||
.fetch_all(db.inner())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Template::render(
|
||||
"ergo/final",
|
||||
context!(loggedin_user: &UserWithDetails::from_user(_user.user, db).await, thirty, dozen),
|
||||
)
|
||||
}
|
||||
|
||||
#[get("/reset")]
|
||||
async fn reset(db: &State<SqlitePool>, _user: AdminUser) -> Flash<Redirect> {
|
||||
sqlx::query!("UPDATE user SET dirty_thirty = NULL, dirty_dozen = NULL;")
|
||||
.execute(db.inner())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Flash::success(
|
||||
Redirect::to("/ergo"),
|
||||
"Erfolgreich zurückgesetzt (Bilder müssen manuell gelöscht werden!)",
|
||||
)
|
||||
}
|
||||
|
||||
#[get("/<challenge>/user/<user_id>/new?<new>")]
|
||||
async fn update(
|
||||
db: &State<SqlitePool>,
|
||||
_admin: AdminUser,
|
||||
challenge: &str,
|
||||
user_id: i64,
|
||||
new: &str,
|
||||
) -> Flash<Redirect> {
|
||||
if challenge == "thirty" {
|
||||
sqlx::query!("UPDATE user SET dirty_thirty = ? WHERE id=?", new, user_id)
|
||||
.execute(db.inner())
|
||||
.await
|
||||
.unwrap();
|
||||
Flash::success(Redirect::to("/ergo"), "Succ")
|
||||
} else if challenge == "dozen" {
|
||||
sqlx::query!("UPDATE user SET dirty_dozen = ? WHERE id=?", new, user_id)
|
||||
.execute(db.inner())
|
||||
.await
|
||||
.unwrap();
|
||||
Flash::success(Redirect::to("/ergo"), "Succ")
|
||||
} else {
|
||||
Flash::error(
|
||||
Redirect::to("/ergo"),
|
||||
"Challenge not found (should be thirty or dozen)",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_>>) -> Template {
|
||||
let mut context = Context::new();
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithDetails::from_user(user.clone(), db).await,
|
||||
);
|
||||
|
||||
if !user.has_role(db, "ergo").await {
|
||||
return Template::render("ergo/missing-data", context.into_json());
|
||||
}
|
||||
|
||||
let users = User::ergo(db).await;
|
||||
|
||||
let thirty = sqlx::query_as!(
|
||||
ErgoStat,
|
||||
"SELECT id, name, dirty_thirty as result, dob, weight, sex FROM user WHERE deleted = 0 AND dirty_thirty is not null ORDER BY result DESC"
|
||||
)
|
||||
.fetch_all(db.inner())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let dozen= sqlx::query_as!(
|
||||
ErgoStat,
|
||||
"SELECT id, name, dirty_dozen as result, dob, weight, sex FROM user WHERE deleted = 0 AND dirty_dozen is not null ORDER BY result DESC"
|
||||
)
|
||||
.fetch_all(db.inner())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
context.insert("users", &users);
|
||||
context.insert("thirty", &thirty);
|
||||
context.insert("dozen", &dozen);
|
||||
|
||||
Template::render("ergo/index", context.into_json())
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct UserAdd {
|
||||
birthyear: i32,
|
||||
weight: i64,
|
||||
sex: String,
|
||||
}
|
||||
|
||||
#[post("/set-data", data = "<data>")]
|
||||
async fn new_user(db: &State<SqlitePool>, data: Form<UserAdd>, user: User) -> Flash<Redirect> {
|
||||
if user.has_role(db, "ergo").await {
|
||||
return Flash::error(Redirect::to("/ergo"), "Du hast deine Daten schon eingegeben. Wenn du sie updaten willst, melde dich bitte bei it@rudernlinz.at");
|
||||
}
|
||||
|
||||
// check data
|
||||
if data.birthyear < 1900 || data.birthyear > chrono::Utc::now().year() - 5 {
|
||||
return Flash::error(Redirect::to("/ergo"), "Bitte überprüfe dein Geburtsjahr...");
|
||||
}
|
||||
if data.weight < 20 || data.weight > 200 {
|
||||
return Flash::error(Redirect::to("/ergo"), "Bitte überprüfe dein Gewicht...");
|
||||
}
|
||||
if &data.sex != "f" && &data.sex != "m" {
|
||||
return Flash::error(Redirect::to("/ergo"), "Bitte überprüfe dein Geschlecht...");
|
||||
}
|
||||
|
||||
// set data
|
||||
user.update_ergo(db, data.birthyear, data.weight, &data.sex)
|
||||
.await;
|
||||
|
||||
// inform all other `ergo` users
|
||||
let ergo = Role::find_by_name(db, "ergo").await.unwrap();
|
||||
Notification::create_for_role(
|
||||
db,
|
||||
&ergo,
|
||||
&format!("{} nimmt heuer an der Ergochallenge teil 💪", user.name),
|
||||
"Ergo Challenge",
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
// add to `ergo` group
|
||||
user.add_role(db, &ergo).await.unwrap();
|
||||
|
||||
Flash::success(
|
||||
Redirect::to("/ergo"),
|
||||
"Du hast deine Daten erfolgreich eingegeben. Viel Spaß beim Schwitzen :-)",
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct ErgoToAdd<'a> {
|
||||
user: i64,
|
||||
result: String,
|
||||
proof: TempFile<'a>,
|
||||
}
|
||||
|
||||
#[post("/thirty", data = "<data>", format = "multipart/form-data")]
|
||||
async fn new_thirty(
|
||||
db: &State<SqlitePool>,
|
||||
mut data: Form<ErgoToAdd<'_>>,
|
||||
created_by: User,
|
||||
) -> Flash<Redirect> {
|
||||
let user = User::find_by_id(db, data.user as i32).await.unwrap();
|
||||
|
||||
let extension = if data.proof.content_type() == Some(&ContentType::JPEG) {
|
||||
"jpg"
|
||||
} else {
|
||||
return Flash::error(Redirect::to("/ergo"), "Es werden nur JPG Bilder akzeptiert");
|
||||
};
|
||||
let base_dir = env::current_dir().unwrap();
|
||||
let file_path = base_dir.join(format!(
|
||||
"data-ergo/thirty/{}_{}.{extension}",
|
||||
user.name,
|
||||
Utc::now()
|
||||
));
|
||||
if let Err(e) = data.proof.move_copy_to(file_path).await {
|
||||
eprintln!("Failed to persist file: {:?}", e);
|
||||
}
|
||||
|
||||
let result = data.result.trim_start_matches(|c| c == '0' || c == ' ');
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE user SET dirty_thirty = ? where id = ?",
|
||||
result,
|
||||
data.user
|
||||
)
|
||||
.execute(db.inner())
|
||||
.await
|
||||
.unwrap(); //Okay, because we can only create a User of a valid id
|
||||
|
||||
Log::create(
|
||||
db,
|
||||
format!("{} created thirty-ergo entry: {data:?}", created_by.name),
|
||||
)
|
||||
.await;
|
||||
|
||||
let ergo = Role::find_by_name(db, "ergo").await.unwrap();
|
||||
Notification::create_for_role(
|
||||
db,
|
||||
&ergo,
|
||||
&format!(
|
||||
"{} ist gerade die Dirty Thirty Challenge gefahren 🥵",
|
||||
user.name
|
||||
),
|
||||
"Ergo Challenge",
|
||||
Some("/ergo"),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
Flash::success(Redirect::to("/ergo"), "Erfolgreich eingetragen")
|
||||
}
|
||||
|
||||
fn format_time(input: &str) -> String {
|
||||
let input = if input.starts_with(":") {
|
||||
&format!("00{input}")
|
||||
} else {
|
||||
input
|
||||
};
|
||||
let mut parts: Vec<&str> = input.split(':').collect();
|
||||
|
||||
// If there's only seconds (e.g., "24.2"), treat it as "00:00:24.2"
|
||||
if parts.len() == 1 {
|
||||
parts.insert(0, "0"); // Add "0" for hours
|
||||
parts.insert(0, "0"); // Add "0" for minutes
|
||||
}
|
||||
|
||||
// If there are two parts (e.g., "4:24.2"), treat it as "00:04:24.2"
|
||||
if parts.len() == 2 {
|
||||
parts.insert(0, "0"); // Add "0" for hours
|
||||
}
|
||||
|
||||
// Now parts should have [hours, minutes, seconds]
|
||||
let hours = if parts[0].len() == 1 {
|
||||
format!("0{}", parts[0])
|
||||
} else {
|
||||
parts[0].to_string()
|
||||
};
|
||||
let minutes = if parts[1].len() == 1 {
|
||||
format!("0{}", parts[1])
|
||||
} else {
|
||||
parts[1].to_string()
|
||||
};
|
||||
let seconds = parts[2];
|
||||
|
||||
// Split seconds into whole and fractional parts
|
||||
let (sec_int, sec_frac) = seconds.split_once('.').unwrap_or((seconds, "0"));
|
||||
|
||||
// Format the time as "hh:mm:ss.s"
|
||||
format!(
|
||||
"{}:{}:{}.{:1}",
|
||||
hours,
|
||||
minutes,
|
||||
sec_int,
|
||||
sec_frac.chars().next().unwrap_or('0')
|
||||
)
|
||||
}
|
||||
|
||||
#[post("/dozen", data = "<data>", format = "multipart/form-data")]
|
||||
async fn new_dozen(
|
||||
db: &State<SqlitePool>,
|
||||
mut data: Form<ErgoToAdd<'_>>,
|
||||
created_by: User,
|
||||
) -> Flash<Redirect> {
|
||||
let user = User::find_by_id(db, data.user as i32).await.unwrap();
|
||||
|
||||
let extension = if data.proof.content_type() == Some(&ContentType::JPEG) {
|
||||
"jpg"
|
||||
} else {
|
||||
return Flash::error(Redirect::to("/ergo"), "Es werden nur JPG Bilder akzeptiert");
|
||||
};
|
||||
let base_dir = env::current_dir().unwrap();
|
||||
let file_path = base_dir.join(format!(
|
||||
"data-ergo/dozen/{}_{}.{extension}",
|
||||
user.name,
|
||||
Utc::now()
|
||||
));
|
||||
if let Err(e) = data.proof.move_copy_to(file_path).await {
|
||||
eprintln!("Failed to persist file: {:?}", e);
|
||||
}
|
||||
let result = data.result.trim_start_matches(|c| c == '0' || c == ' ');
|
||||
let result = if result.contains(":") || result.contains(".") {
|
||||
format_time(result)
|
||||
} else {
|
||||
result.to_string()
|
||||
};
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE user SET dirty_dozen = ? where id = ?",
|
||||
result,
|
||||
data.user
|
||||
)
|
||||
.execute(db.inner())
|
||||
.await
|
||||
.unwrap(); //Okay, because we can only create a User of a valid id
|
||||
|
||||
Log::create(
|
||||
db,
|
||||
format!("{} created dozen-ergo entry: {data:?}", created_by.name),
|
||||
)
|
||||
.await;
|
||||
|
||||
let ergo = Role::find_by_name(db, "ergo").await.unwrap();
|
||||
Notification::create_for_role(
|
||||
db,
|
||||
&ergo,
|
||||
&format!(
|
||||
"{} ist gerade die Dirty Dozen Challenge gefahren 🥵",
|
||||
user.name
|
||||
),
|
||||
"Ergo Challenge",
|
||||
Some("/ergo"),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
Flash::success(Redirect::to("/ergo"), "Erfolgreich eingetragen")
|
||||
}
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![index, new_thirty, new_dozen, send, reset, update, new_user]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {}
|
1111
src/tera/log.rs
1111
src/tera/log.rs
File diff suppressed because it is too large
Load Diff
132
src/tera/mod.rs
132
src/tera/mod.rs
@@ -1,14 +1,12 @@
|
||||
use std::{fs::OpenOptions, io::Write};
|
||||
|
||||
use chrono::{Datelike, Local};
|
||||
use chrono::Local;
|
||||
use rocket::{
|
||||
catch, catchers,
|
||||
fairing::{AdHoc, Fairing, Info, Kind},
|
||||
form::Form,
|
||||
fs::FileServer,
|
||||
get,
|
||||
http::Cookie,
|
||||
post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
routes,
|
||||
@@ -20,30 +18,17 @@ use serde::Deserialize;
|
||||
use sqlx::SqlitePool;
|
||||
use tera::Context;
|
||||
|
||||
use crate::{
|
||||
model::{
|
||||
logbook::Logbook,
|
||||
notification::Notification,
|
||||
personal::Achievements,
|
||||
role::Role,
|
||||
user::{User, UserWithDetails},
|
||||
},
|
||||
SCHECKBUCH,
|
||||
use crate::model::{
|
||||
role::Role,
|
||||
user::{User, UserWithDetails},
|
||||
};
|
||||
|
||||
pub(crate) mod admin;
|
||||
mod auth;
|
||||
pub(crate) mod board;
|
||||
mod boatdamage;
|
||||
pub(crate) mod boatreservation;
|
||||
mod cox;
|
||||
mod ergo;
|
||||
mod log;
|
||||
mod misc;
|
||||
mod notification;
|
||||
mod planned;
|
||||
mod stat;
|
||||
pub(crate) mod trailerreservation;
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
struct LoginForm<'r> {
|
||||
@@ -51,31 +36,6 @@ struct LoginForm<'r> {
|
||||
password: &'r str,
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_>>) -> Template {
|
||||
let mut context = Context::new();
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
|
||||
if user.has_role(db, "scheckbuch").await {
|
||||
let last_trips = Logbook::completed_with_user(db, &user).await;
|
||||
context.insert("last_trips", &last_trips);
|
||||
}
|
||||
|
||||
let date = chrono::Utc::now();
|
||||
if date.month() <= 3 || date.month() >= 10 {
|
||||
context.insert("show_quick_ergo_button", "yes");
|
||||
}
|
||||
|
||||
context.insert("achievements", &Achievements::for_user(db, &user).await);
|
||||
context.insert("notifications", &Notification::for_user(db, &user).await);
|
||||
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);
|
||||
context.insert("costs_scheckbuch", &SCHECKBUCH);
|
||||
|
||||
Template::render("index", context.into_json())
|
||||
}
|
||||
|
||||
#[get("/impressum")]
|
||||
async fn impressum(db: &State<SqlitePool>, user: Option<User>) -> Template {
|
||||
let mut context = Context::new();
|
||||
@@ -107,22 +67,6 @@ async fn steering(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage
|
||||
Template::render("steering", context.into_json())
|
||||
}
|
||||
|
||||
#[post("/", data = "<login>")]
|
||||
async fn wikiauth(db: &State<SqlitePool>, login: Form<LoginForm<'_>>) -> String {
|
||||
if let Ok(user) = User::login(db, login.name, login.password).await {
|
||||
if user.has_role(db, "allow_website_login").await {
|
||||
return String::from("SUCC");
|
||||
}
|
||||
if user.has_role(db, "admin").await {
|
||||
return String::from("SUCC");
|
||||
}
|
||||
if user.has_role(db, "Vorstand").await {
|
||||
return String::from("SUCC");
|
||||
}
|
||||
}
|
||||
"FAIL".into()
|
||||
}
|
||||
|
||||
#[catch(401)] //Unauthorized
|
||||
fn unauthorized_error(req: &Request) -> Redirect {
|
||||
// Save the URL the user tried to access, to be able to go there once logged in
|
||||
@@ -134,60 +78,6 @@ fn unauthorized_error(req: &Request) -> Redirect {
|
||||
Redirect::to("/auth")
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
struct NewBlogpostForm<'r> {
|
||||
article_url: &'r str,
|
||||
article_title: &'r str,
|
||||
pw: &'r str,
|
||||
}
|
||||
|
||||
#[post("/", data = "<blogpost>")]
|
||||
async fn new_blogpost(
|
||||
db: &State<SqlitePool>,
|
||||
blogpost: Form<NewBlogpostForm<'_>>,
|
||||
config: &State<Config>,
|
||||
) -> String {
|
||||
if blogpost.pw == config.wordpress_key {
|
||||
let member = Role::find_by_name(db, "Donau Linz").await.unwrap();
|
||||
Notification::create_for_role(
|
||||
db,
|
||||
&member,
|
||||
&urlencoding::decode(blogpost.article_title).expect("UTF-8"),
|
||||
"Neuer Blogpost",
|
||||
Some(blogpost.article_url),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
"ACK".into()
|
||||
} else {
|
||||
"WRONG pw".into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
struct BlogpostUnpublishedForm<'r> {
|
||||
article_url: &'r str,
|
||||
pw: &'r str,
|
||||
}
|
||||
|
||||
#[post("/", data = "<blogpost>")]
|
||||
async fn blogpost_unpublished(
|
||||
db: &State<SqlitePool>,
|
||||
blogpost: Form<BlogpostUnpublishedForm<'_>>,
|
||||
config: &State<Config>,
|
||||
) -> String {
|
||||
if blogpost.pw == config.wordpress_key {
|
||||
Notification::delete_by_link(
|
||||
db,
|
||||
&urlencoding::decode(blogpost.article_url).expect("UTF-8"),
|
||||
)
|
||||
.await;
|
||||
"ACK".into()
|
||||
} else {
|
||||
"WRONG pw".into()
|
||||
}
|
||||
}
|
||||
|
||||
#[catch(403)] //forbidden
|
||||
fn forbidden_error() -> Flash<Redirect> {
|
||||
Flash::error(Redirect::to("/"), "Keine Berechtigung für diese Aktion. Wenn du der Meinung bist, dass du das machen darfst, melde dich bitte bei it@rudernlinz.at.")
|
||||
@@ -262,22 +152,12 @@ pub struct Config {
|
||||
|
||||
pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {
|
||||
rocket
|
||||
.mount("/", routes![index, steering, impressum])
|
||||
.mount("/", routes![steering, impressum])
|
||||
.mount("/auth", auth::routes())
|
||||
.mount("/wikiauth", routes![wikiauth])
|
||||
.mount("/new-blogpost", routes![new_blogpost])
|
||||
.mount("/blogpost-unpublished", routes![blogpost_unpublished])
|
||||
.mount("/log", log::routes())
|
||||
.mount("/planned", planned::routes())
|
||||
.mount("/ergo", ergo::routes())
|
||||
.mount("/", planned::routes())
|
||||
.mount("/notification", notification::routes())
|
||||
.mount("/stat", stat::routes())
|
||||
.mount("/boatdamage", boatdamage::routes())
|
||||
.mount("/boatreservation", boatreservation::routes())
|
||||
.mount("/trailerreservation", trailerreservation::routes())
|
||||
.mount("/cox", cox::routes())
|
||||
.mount("/admin", admin::routes())
|
||||
.mount("/board", board::routes())
|
||||
.mount("/", misc::routes())
|
||||
.mount("/public", FileServer::from("static/"))
|
||||
.register("/", catchers![unauthorized_error, forbidden_error])
|
||||
|
@@ -13,20 +13,14 @@ use crate::{
|
||||
log::Log,
|
||||
tripdetails::TripDetails,
|
||||
triptype::TripType,
|
||||
user::{AllowedForPlannedTripsUser, User, UserWithDetails},
|
||||
user::{User, UserWithDetails},
|
||||
usertrip::{UserTrip, UserTripDeleteError, UserTripError},
|
||||
},
|
||||
AMOUNT_DAYS_TO_SHOW_TRIPS_AHEAD,
|
||||
};
|
||||
|
||||
#[get("/")]
|
||||
async fn index(
|
||||
db: &State<SqlitePool>,
|
||||
user: AllowedForPlannedTripsUser,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
) -> Template {
|
||||
let user: User = user.into_inner();
|
||||
|
||||
async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_>>) -> Template {
|
||||
let mut context = Context::new();
|
||||
|
||||
if user.allowed_to_steer(db).await
|
||||
@@ -47,25 +41,22 @@ async fn index(
|
||||
"allowed_to_update_always_show_trip",
|
||||
&user.allowed_to_update_always_show_trip(db).await,
|
||||
);
|
||||
context.insert("fee", &user.fee(db).await);
|
||||
context.insert(
|
||||
"amount_days_to_show_trips_ahead",
|
||||
&AMOUNT_DAYS_TO_SHOW_TRIPS_AHEAD,
|
||||
);
|
||||
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);
|
||||
context.insert("days", &days);
|
||||
Template::render("planned", context.into_json())
|
||||
Template::render("index", context.into_json())
|
||||
}
|
||||
|
||||
#[get("/join/<trip_details_id>?<user_note>")]
|
||||
async fn join(
|
||||
db: &State<SqlitePool>,
|
||||
trip_details_id: i64,
|
||||
user: AllowedForPlannedTripsUser,
|
||||
user: User,
|
||||
user_note: Option<String>,
|
||||
) -> Flash<Redirect> {
|
||||
let user: User = user.into_inner();
|
||||
|
||||
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
|
||||
return Flash::error(Redirect::to("/"), "Trip_details do not exist.");
|
||||
};
|
||||
@@ -90,35 +81,35 @@ async fn join(
|
||||
),
|
||||
).await;
|
||||
}
|
||||
Flash::success(Redirect::to("/planned"), "Erfolgreich angemeldet!")
|
||||
Flash::success(Redirect::to("/"), "Erfolgreich angemeldet!")
|
||||
}
|
||||
Err(UserTripError::EventAlreadyFull) => {
|
||||
Flash::error(Redirect::to("/planned"), "Event bereits ausgebucht!")
|
||||
Flash::error(Redirect::to("/"), "Event bereits ausgebucht!")
|
||||
}
|
||||
Err(UserTripError::AlreadyRegistered) => {
|
||||
Flash::error(Redirect::to("/planned"), "Du nimmst bereits teil!")
|
||||
Flash::error(Redirect::to("/"), "Du nimmst bereits teil!")
|
||||
}
|
||||
Err(UserTripError::AlreadyRegisteredAsCox) => {
|
||||
Flash::error(Redirect::to("/planned"), "Du hilfst bereits als Steuerperson aus!")
|
||||
Flash::error(Redirect::to("/"), "Du hilfst bereits als Steuerperson aus!")
|
||||
}
|
||||
Err(UserTripError::CantRegisterAtOwnEvent) => Flash::error(
|
||||
Redirect::to("/planned"),
|
||||
Redirect::to("/"),
|
||||
"Du kannst bei einer selbst ausgeschriebenen Fahrt nicht mitrudern ;)",
|
||||
),
|
||||
Err(UserTripError::GuestNotAllowedForThisEvent) => Flash::error(
|
||||
Redirect::to("/planned"),
|
||||
Redirect::to("/"),
|
||||
"Bei dieser Ausfahrt können leider keine Gäste mitfahren.",
|
||||
),
|
||||
Err(UserTripError::NotAllowedToAddGuest) => Flash::error(
|
||||
Redirect::to("/planned"),
|
||||
Redirect::to("/"),
|
||||
"Du darfst keine Gäste hinzufügen.",
|
||||
),
|
||||
Err(UserTripError::NotVisibleToUser) => Flash::error(
|
||||
Redirect::to("/planned"),
|
||||
Redirect::to("/"),
|
||||
"Du kannst dich nicht registrieren, weil du die Ausfahrt gar nicht sehen solltest.",
|
||||
),
|
||||
Err(UserTripError::DetailsLocked) => Flash::error(
|
||||
Redirect::to("/planned"),
|
||||
Redirect::to("/"),
|
||||
"Die Bootseinteilung wurde bereits gemacht. Wenn du noch mitrudern möchtest, frag bitte bei einer angemeldeten Steuerperson nach, ob das noch möglich ist.",
|
||||
),
|
||||
}
|
||||
@@ -128,13 +119,11 @@ async fn join(
|
||||
async fn remove_guest(
|
||||
db: &State<SqlitePool>,
|
||||
trip_details_id: i64,
|
||||
user: AllowedForPlannedTripsUser,
|
||||
user: User,
|
||||
name: String,
|
||||
) -> Flash<Redirect> {
|
||||
let user: User = user.into_inner();
|
||||
|
||||
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
|
||||
return Flash::error(Redirect::to("/planned"), "TripDetailsId does not exist");
|
||||
return Flash::error(Redirect::to("/"), "TripDetailsId does not exist");
|
||||
};
|
||||
|
||||
match UserTrip::delete(db, &user, &trip_details, Some(name)).await {
|
||||
@@ -148,7 +137,7 @@ async fn remove_guest(
|
||||
)
|
||||
.await;
|
||||
|
||||
Flash::success(Redirect::to("/planned"), "Erfolgreich abgemeldet!")
|
||||
Flash::success(Redirect::to("/"), "Erfolgreich abgemeldet!")
|
||||
}
|
||||
Err(UserTripDeleteError::DetailsLocked) => {
|
||||
Log::create(
|
||||
@@ -160,32 +149,26 @@ async fn remove_guest(
|
||||
)
|
||||
.await;
|
||||
|
||||
Flash::error(Redirect::to("/planned"), "Die Bootseinteilung wurde bereits gemacht. Wenn du doch nicht mitrudern kannst, melde dich bitte unbedingt schnellstmöglich bei einer angemeldeten Steuerperson!")
|
||||
Flash::error(Redirect::to("/"), "Die Bootseinteilung wurde bereits gemacht. Wenn du doch nicht mitrudern kannst, melde dich bitte unbedingt schnellstmöglich bei einer angemeldeten Steuerperson!")
|
||||
}
|
||||
Err(UserTripDeleteError::GuestNotParticipating) => {
|
||||
Flash::error(Redirect::to("/planned"), "Gast nicht angemeldet.")
|
||||
Flash::error(Redirect::to("/"), "Gast nicht angemeldet.")
|
||||
}
|
||||
Err(UserTripDeleteError::NotVisibleToUser) => Flash::error(
|
||||
Redirect::to("/planned"),
|
||||
Redirect::to("/"),
|
||||
"Du kannst dich nicht abmelden, weil du die Ausfahrt gar nicht sehen solltest.",
|
||||
),
|
||||
Err(UserTripDeleteError::NotAllowedToDeleteGuest) => Flash::error(
|
||||
Redirect::to("/planned"),
|
||||
Redirect::to("/"),
|
||||
"Keine Berechtigung um den Gast zu entfernen.",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/remove/<trip_details_id>")]
|
||||
async fn remove(
|
||||
db: &State<SqlitePool>,
|
||||
trip_details_id: i64,
|
||||
user: AllowedForPlannedTripsUser,
|
||||
) -> Flash<Redirect> {
|
||||
let user: User = user.into_inner();
|
||||
|
||||
async fn remove(db: &State<SqlitePool>, trip_details_id: i64, user: User) -> Flash<Redirect> {
|
||||
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
|
||||
return Flash::error(Redirect::to("/planned"), "TripDetailsId does not exist");
|
||||
return Flash::error(Redirect::to("/"), "TripDetailsId does not exist");
|
||||
};
|
||||
|
||||
match UserTrip::delete(db, &user, &trip_details, None).await {
|
||||
@@ -199,7 +182,7 @@ async fn remove(
|
||||
)
|
||||
.await;
|
||||
|
||||
Flash::success(Redirect::to("/planned"), "Erfolgreich abgemeldet!")
|
||||
Flash::success(Redirect::to("/"), "Erfolgreich abgemeldet!")
|
||||
}
|
||||
Err(UserTripDeleteError::DetailsLocked) => {
|
||||
Log::create(
|
||||
@@ -211,7 +194,7 @@ async fn remove(
|
||||
)
|
||||
.await;
|
||||
|
||||
Flash::error(Redirect::to("/planned"), "Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.")
|
||||
Flash::error(Redirect::to("/"), "Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.")
|
||||
}
|
||||
Err(UserTripDeleteError::NotVisibleToUser) => {
|
||||
Log::create(
|
||||
@@ -223,7 +206,7 @@ async fn remove(
|
||||
)
|
||||
.await;
|
||||
|
||||
Flash::error(Redirect::to("/planned"), "Abmeldung nicht möglich, da du dieses Event eigentlich gar nicht sehen solltest...")
|
||||
Flash::error(Redirect::to("/"), "Abmeldung nicht möglich, da du dieses Event eigentlich gar nicht sehen solltest...")
|
||||
}
|
||||
Err(_) => {
|
||||
panic!("Not possible to be here");
|
||||
@@ -259,11 +242,11 @@ mod test {
|
||||
.body("name=rower&password=rower"); // Add the form data to the request body;
|
||||
login.dispatch().await;
|
||||
|
||||
let req = client.get("/planned/join/1");
|
||||
let req = client.get("/join/1");
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
@@ -272,11 +255,11 @@ mod test {
|
||||
|
||||
assert_eq!(flash_cookie.value(), "7:successErfolgreich angemeldet!");
|
||||
|
||||
let req = client.get("/planned/remove/1");
|
||||
let req = client.get("/remove/1");
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/planned"));
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
@@ -300,7 +283,7 @@ mod test {
|
||||
.body("name=rower&password=rower"); // Add the form data to the request body;
|
||||
login.dispatch().await;
|
||||
|
||||
let req = client.get("/planned/join/9999");
|
||||
let req = client.get("/join/9999");
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
|
@@ -1,63 +0,0 @@
|
||||
use rocket::{get, routes, Route, State};
|
||||
use rocket_dyn_templates::{context, Template};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
use crate::model::{
|
||||
stat::{self, BoatStat, Stat},
|
||||
user::{DonauLinzUser, UserWithDetails},
|
||||
};
|
||||
|
||||
use super::log::KioskCookie;
|
||||
|
||||
#[get("/boats", rank = 2)]
|
||||
async fn index_boat(db: &State<SqlitePool>, user: DonauLinzUser) -> Template {
|
||||
let stat = BoatStat::get(db).await;
|
||||
let kiosk = false;
|
||||
|
||||
Template::render(
|
||||
"stat.boats",
|
||||
context!(loggedin_user: &UserWithDetails::from_user(user.into_inner(), db).await, stat, kiosk),
|
||||
)
|
||||
}
|
||||
|
||||
#[get("/boats")]
|
||||
async fn index_boat_kiosk(db: &State<SqlitePool>, _kiosk: KioskCookie) -> Template {
|
||||
let stat = BoatStat::get(db).await;
|
||||
let kiosk = true;
|
||||
|
||||
Template::render("stat.boats", context!(stat, kiosk, show_kiosk_header: true))
|
||||
}
|
||||
|
||||
#[get("/?<year>", rank = 2)]
|
||||
async fn index(db: &State<SqlitePool>, user: DonauLinzUser, year: Option<i32>) -> Template {
|
||||
let stat = Stat::people(db, year).await;
|
||||
let club_km = Stat::sum_people(db, year).await;
|
||||
let guest_km = Stat::guest(db, year).await;
|
||||
let personal = stat::get_personal(db, &user).await;
|
||||
let kiosk = false;
|
||||
|
||||
Template::render(
|
||||
"stat.people",
|
||||
context!(loggedin_user: &UserWithDetails::from_user(user.into_inner(), db).await, stat, personal, kiosk, guest_km, club_km),
|
||||
)
|
||||
}
|
||||
|
||||
#[get("/?<year>")]
|
||||
async fn index_kiosk(db: &State<SqlitePool>, _kiosk: KioskCookie, year: Option<i32>) -> Template {
|
||||
let stat = Stat::people(db, year).await;
|
||||
let club_km = Stat::sum_people(db, year).await;
|
||||
let guest_km = Stat::guest(db, year).await;
|
||||
let kiosk = true;
|
||||
|
||||
Template::render(
|
||||
"stat.people",
|
||||
context!(stat, kiosk, show_kiosk_header: true, guest_km, club_km),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![index, index_kiosk, index_boat, index_boat_kiosk]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {}
|
@@ -1,211 +0,0 @@
|
||||
use chrono::NaiveDate;
|
||||
use rocket::{
|
||||
form::Form,
|
||||
get, post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
routes, FromForm, Route, State,
|
||||
};
|
||||
use rocket_dyn_templates::Template;
|
||||
use sqlx::SqlitePool;
|
||||
use tera::Context;
|
||||
|
||||
use crate::{
|
||||
model::{
|
||||
log::Log,
|
||||
trailer::Trailer,
|
||||
trailerreservation::{TrailerReservation, TrailerReservationToAdd},
|
||||
user::{DonauLinzUser, User, UserWithDetails},
|
||||
},
|
||||
tera::log::KioskCookie,
|
||||
};
|
||||
|
||||
#[get("/")]
|
||||
async fn index_kiosk(
|
||||
db: &State<SqlitePool>,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
_kiosk: KioskCookie,
|
||||
) -> Template {
|
||||
let trailerreservations = TrailerReservation::all_future(db).await;
|
||||
|
||||
let mut context = Context::new();
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
|
||||
context.insert("trailerreservations", &trailerreservations);
|
||||
context.insert("trailers", &Trailer::all(db).await);
|
||||
context.insert("user", &User::all(db).await);
|
||||
context.insert("show_kiosk_header", &true);
|
||||
|
||||
Template::render("trailerreservations", context.into_json())
|
||||
}
|
||||
|
||||
#[get("/", rank = 2)]
|
||||
async fn index(
|
||||
db: &State<SqlitePool>,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
user: DonauLinzUser,
|
||||
) -> Template {
|
||||
let trailerreservations = TrailerReservation::all_future(db).await;
|
||||
|
||||
let mut context = Context::new();
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
|
||||
context.insert("trailerreservations", &trailerreservations);
|
||||
context.insert("trailers", &Trailer::all(db).await);
|
||||
context.insert("user", &User::all(db).await);
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithDetails::from_user(user.into_inner(), db).await,
|
||||
);
|
||||
|
||||
Template::render("trailerreservations", context.into_json())
|
||||
}
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct FormTrailerReservationToAdd<'r> {
|
||||
pub trailer_id: i64,
|
||||
pub start_date: &'r str,
|
||||
pub end_date: &'r str,
|
||||
pub time_desc: &'r str,
|
||||
pub usage: &'r str,
|
||||
pub user_id_applicant: Option<i64>,
|
||||
}
|
||||
|
||||
#[post("/new", data = "<data>", rank = 2)]
|
||||
async fn create<'r>(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<FormTrailerReservationToAdd<'r>>,
|
||||
user: DonauLinzUser,
|
||||
) -> Flash<Redirect> {
|
||||
let user_applicant: User = user.into_inner();
|
||||
let trailer = Trailer::find_by_id(db, data.trailer_id as i32)
|
||||
.await
|
||||
.unwrap();
|
||||
let trailerreservation_to_add = TrailerReservationToAdd {
|
||||
trailer: &trailer,
|
||||
start_date: NaiveDate::parse_from_str(data.start_date, "%Y-%m-%d").unwrap(),
|
||||
end_date: NaiveDate::parse_from_str(data.end_date, "%Y-%m-%d").unwrap(),
|
||||
time_desc: data.time_desc,
|
||||
usage: data.usage,
|
||||
user_applicant: &user_applicant,
|
||||
};
|
||||
match TrailerReservation::create(db, trailerreservation_to_add).await {
|
||||
Ok(_) => Flash::success(
|
||||
Redirect::to("/trailerreservation"),
|
||||
"Reservierung erfolgreich hinzugefügt",
|
||||
),
|
||||
Err(e) => Flash::error(Redirect::to("/trailerreservation"), format!("Fehler: {e}")),
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/new", data = "<data>")]
|
||||
async fn create_from_kiosk<'r>(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<FormTrailerReservationToAdd<'r>>,
|
||||
_kiosk: KioskCookie,
|
||||
) -> Flash<Redirect> {
|
||||
let user_applicant: User = User::find_by_id(db, data.user_id_applicant.unwrap() as i32)
|
||||
.await
|
||||
.unwrap();
|
||||
let trailer = Trailer::find_by_id(db, data.trailer_id as i32)
|
||||
.await
|
||||
.unwrap();
|
||||
let trailerreservation_to_add = TrailerReservationToAdd {
|
||||
trailer: &trailer,
|
||||
start_date: NaiveDate::parse_from_str(data.start_date, "%Y-%m-%d").unwrap(),
|
||||
end_date: NaiveDate::parse_from_str(data.end_date, "%Y-%m-%d").unwrap(),
|
||||
time_desc: data.time_desc,
|
||||
usage: data.usage,
|
||||
user_applicant: &user_applicant,
|
||||
};
|
||||
match TrailerReservation::create(db, trailerreservation_to_add).await {
|
||||
Ok(_) => Flash::success(
|
||||
Redirect::to("/trailerreservation"),
|
||||
"Reservierung erfolgreich hinzugefügt",
|
||||
),
|
||||
Err(e) => Flash::error(Redirect::to("/trailerreservation"), format!("Fehler: {e}")),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct ReservationEditForm {
|
||||
pub(crate) id: i32,
|
||||
pub(crate) time_desc: String,
|
||||
pub(crate) usage: String,
|
||||
}
|
||||
|
||||
#[post("/", data = "<data>")]
|
||||
async fn update(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<ReservationEditForm>,
|
||||
user: User,
|
||||
) -> Flash<Redirect> {
|
||||
let Some(reservation) = TrailerReservation::find_by_id(db, data.id).await else {
|
||||
return Flash::error(
|
||||
Redirect::to("/trailerreservation"),
|
||||
format!("Reservation with ID {} does not exist!", data.id),
|
||||
);
|
||||
};
|
||||
|
||||
if user.id != reservation.user_id_applicant && !user.has_role(db, "admin").await {
|
||||
return Flash::error(
|
||||
Redirect::to("/trailerreservation"),
|
||||
"Not allowed to update reservation (only admins + creator do so).".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
Log::create(
|
||||
db,
|
||||
format!(
|
||||
"{} updated reservation from {reservation:?} to {data:?}",
|
||||
user.name
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
reservation.update(db, data.into_inner()).await;
|
||||
|
||||
Flash::success(
|
||||
Redirect::to("/trailerreservation"),
|
||||
"Reservierung erfolgreich bearbeitet",
|
||||
)
|
||||
}
|
||||
|
||||
#[get("/<reservation_id>/delete")]
|
||||
async fn delete<'r>(
|
||||
db: &State<SqlitePool>,
|
||||
reservation_id: i32,
|
||||
user: DonauLinzUser,
|
||||
) -> Flash<Redirect> {
|
||||
let reservation = TrailerReservation::find_by_id(db, reservation_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
if user.id == reservation.user_id_applicant || user.has_role(db, "admin").await {
|
||||
reservation.delete(db).await;
|
||||
Flash::success(
|
||||
Redirect::to("/trailerreservation"),
|
||||
"Reservierung erfolgreich gelöscht",
|
||||
)
|
||||
} else {
|
||||
Flash::error(
|
||||
Redirect::to("/trailerreservation"),
|
||||
"Nur der Reservierer darf die Reservierung löschen.".to_string(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![
|
||||
index,
|
||||
index_kiosk,
|
||||
create,
|
||||
create_from_kiosk,
|
||||
delete,
|
||||
update
|
||||
]
|
||||
}
|
Reference in New Issue
Block a user