317 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			317 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use rocket::{
 | |
|     get,
 | |
|     request::FlashMessage,
 | |
|     response::{Flash, Redirect},
 | |
|     routes, Route, State,
 | |
| };
 | |
| use rocket_dyn_templates::Template;
 | |
| use sqlx::SqlitePool;
 | |
| use tera::Context;
 | |
| 
 | |
| use crate::{
 | |
|     model::{
 | |
|         log::Log,
 | |
|         tripdetails::TripDetails,
 | |
|         triptype::TripType,
 | |
|         user::{AllowedForPlannedTripsUser, 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();
 | |
| 
 | |
|     let mut context = Context::new();
 | |
| 
 | |
|     if user.allowed_to_steer(db).await
 | |
|         || user.has_role(db, "manage_events").await
 | |
|         || user.has_role(db, "ergo").await
 | |
|     {
 | |
|         let triptypes = TripType::all(db).await;
 | |
|         context.insert("trip_types", &triptypes);
 | |
|     }
 | |
| 
 | |
|     let days = user.get_days(db).await;
 | |
| 
 | |
|     if let Some(msg) = flash {
 | |
|         context.insert("flash", &msg.into_inner());
 | |
|     }
 | |
| 
 | |
|     context.insert(
 | |
|         "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())
 | |
| }
 | |
| 
 | |
| #[get("/join/<trip_details_id>?<user_note>")]
 | |
| async fn join(
 | |
|     db: &State<SqlitePool>,
 | |
|     trip_details_id: i64,
 | |
|     user: AllowedForPlannedTripsUser,
 | |
|     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.");
 | |
|     };
 | |
| 
 | |
|     match UserTrip::create(db, &user, &trip_details, user_note).await {
 | |
|         Ok(registered_user) => {
 | |
|             if registered_user == user.name {
 | |
|                 Log::create(
 | |
|                     db,
 | |
|                     format!(
 | |
|                         "User {} registered for trip_details.id={}",
 | |
|                         user.name, trip_details_id
 | |
|                     ),
 | |
|                 )
 | |
|                 .await;
 | |
|             }else{
 | |
|                 Log::create(
 | |
|                     db,
 | |
|                     format!(
 | |
|                         "User {} registered the guest '{}' for trip_details.id={}",
 | |
|                         user.name, registered_user, trip_details_id
 | |
|                     ),
 | |
|                 ).await;
 | |
|             }
 | |
|             Flash::success(Redirect::to("/planned"), "Erfolgreich angemeldet!")
 | |
|         }
 | |
|         Err(UserTripError::EventAlreadyFull) => {
 | |
|             Flash::error(Redirect::to("/planned"), "Event bereits ausgebucht!")
 | |
|         }
 | |
|         Err(UserTripError::AlreadyRegistered) => {
 | |
|             Flash::error(Redirect::to("/planned"), "Du nimmst bereits teil!")
 | |
|         }
 | |
|         Err(UserTripError::AlreadyRegisteredAsCox) => {
 | |
|             Flash::error(Redirect::to("/planned"), "Du hilfst bereits als Steuerperson aus!")
 | |
|         }
 | |
|         Err(UserTripError::CantRegisterAtOwnEvent) => Flash::error(
 | |
|             Redirect::to("/planned"),
 | |
|             "Du kannst bei einer selbst ausgeschriebenen Fahrt nicht mitrudern ;)",
 | |
|         ),
 | |
|         Err(UserTripError::GuestNotAllowedForThisEvent) => Flash::error(
 | |
|             Redirect::to("/planned"),
 | |
|             "Bei dieser Ausfahrt können leider keine Gäste mitfahren.",
 | |
|         ),
 | |
|         Err(UserTripError::NotAllowedToAddGuest) => Flash::error(
 | |
|             Redirect::to("/planned"),
 | |
|             "Du darfst keine Gäste hinzufügen.",
 | |
|         ),
 | |
|         Err(UserTripError::NotVisibleToUser) => Flash::error(
 | |
|             Redirect::to("/planned"),
 | |
|             "Du kannst dich nicht registrieren, weil du die Ausfahrt gar nicht sehen solltest.",
 | |
|         ),
 | |
|         Err(UserTripError::DetailsLocked) => Flash::error(
 | |
|             Redirect::to("/planned"),
 | |
|             "Die Bootseinteilung wurde bereits gemacht. Wenn du noch mitrudern möchtest, frag bitte bei einer angemeldeten Steuerperson nach, ob das noch möglich ist.",
 | |
|         ),
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[get("/remove/<trip_details_id>/<name>")]
 | |
| async fn remove_guest(
 | |
|     db: &State<SqlitePool>,
 | |
|     trip_details_id: i64,
 | |
|     user: AllowedForPlannedTripsUser,
 | |
|     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");
 | |
|     };
 | |
| 
 | |
|     match UserTrip::delete(db, &user, &trip_details, Some(name)).await {
 | |
|         Ok(_) => {
 | |
|             Log::create(
 | |
|                 db,
 | |
|                 format!(
 | |
|                     "User {} unregistered for trip_details.id={}",
 | |
|                     user.name, trip_details_id
 | |
|                 ),
 | |
|             )
 | |
|             .await;
 | |
| 
 | |
|             Flash::success(Redirect::to("/planned"), "Erfolgreich abgemeldet!")
 | |
|         }
 | |
|         Err(UserTripDeleteError::DetailsLocked) => {
 | |
|             Log::create(
 | |
|                 db,
 | |
|                 format!(
 | |
|                     "User {} tried to unregister for locked trip_details.id={}",
 | |
|                     user.name, trip_details_id
 | |
|                 ),
 | |
|             )
 | |
|             .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!")
 | |
|         }
 | |
|         Err(UserTripDeleteError::GuestNotParticipating) => {
 | |
|             Flash::error(Redirect::to("/planned"), "Gast nicht angemeldet.")
 | |
|         }
 | |
|         Err(UserTripDeleteError::NotVisibleToUser) => Flash::error(
 | |
|             Redirect::to("/planned"),
 | |
|             "Du kannst dich nicht abmelden, weil du die Ausfahrt gar nicht sehen solltest.",
 | |
|         ),
 | |
|         Err(UserTripDeleteError::NotAllowedToDeleteGuest) => Flash::error(
 | |
|             Redirect::to("/planned"),
 | |
|             "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();
 | |
| 
 | |
|     let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
 | |
|         return Flash::error(Redirect::to("/planned"), "TripDetailsId does not exist");
 | |
|     };
 | |
| 
 | |
|     match UserTrip::delete(db, &user, &trip_details, None).await {
 | |
|         Ok(_) => {
 | |
|             Log::create(
 | |
|                 db,
 | |
|                 format!(
 | |
|                     "User {} unregistered for trip_details.id={}",
 | |
|                     user.name, trip_details_id
 | |
|                 ),
 | |
|             )
 | |
|             .await;
 | |
| 
 | |
|             Flash::success(Redirect::to("/planned"), "Erfolgreich abgemeldet!")
 | |
|         }
 | |
|         Err(UserTripDeleteError::DetailsLocked) => {
 | |
|             Log::create(
 | |
|                 db,
 | |
|                 format!(
 | |
|                     "User {} tried to unregister for locked trip_details.id={}",
 | |
|                     user.name, trip_details_id
 | |
|                 ),
 | |
|             )
 | |
|             .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.")
 | |
|         }
 | |
|         Err(UserTripDeleteError::NotVisibleToUser) => {
 | |
|             Log::create(
 | |
|                 db,
 | |
|                 format!(
 | |
|                     "User {} tried to unregister for not-yet-seeable trip_details.id={}",
 | |
|                     user.name, trip_details_id
 | |
|                 ),
 | |
|             )
 | |
|             .await;
 | |
| 
 | |
|             Flash::error(Redirect::to("/planned"), "Abmeldung nicht möglich, da du dieses Event eigentlich gar nicht sehen solltest...")
 | |
|         }
 | |
|         Err(_) => {
 | |
|             panic!("Not possible to be here");
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub fn routes() -> Vec<Route> {
 | |
|     routes![index, join, remove, remove_guest]
 | |
| }
 | |
| 
 | |
| #[cfg(test)]
 | |
| mod test {
 | |
|     use rocket::{
 | |
|         http::{ContentType, Status},
 | |
|         local::asynchronous::Client,
 | |
|     };
 | |
|     use sqlx::SqlitePool;
 | |
| 
 | |
|     use crate::testdb;
 | |
| 
 | |
|     #[sqlx::test]
 | |
|     fn test_join_and_remove() {
 | |
|         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=rower&password=rower"); // Add the form data to the request body;
 | |
|         login.dispatch().await;
 | |
| 
 | |
|         let req = client.get("/planned/join/1");
 | |
|         let response = req.dispatch().await;
 | |
| 
 | |
|         assert_eq!(response.status(), Status::SeeOther);
 | |
|         assert_eq!(response.headers().get("Location").next(), Some("/planned"));
 | |
| 
 | |
|         let flash_cookie = response
 | |
|             .cookies()
 | |
|             .get("_flash")
 | |
|             .expect("Expected flash cookie");
 | |
| 
 | |
|         assert_eq!(flash_cookie.value(), "7:successErfolgreich angemeldet!");
 | |
| 
 | |
|         let req = client.get("/planned/remove/1");
 | |
|         let response = req.dispatch().await;
 | |
| 
 | |
|         assert_eq!(response.status(), Status::SeeOther);
 | |
|         assert_eq!(response.headers().get("Location").next(), Some("/planned"));
 | |
| 
 | |
|         let flash_cookie = response
 | |
|             .cookies()
 | |
|             .get("_flash")
 | |
|             .expect("Expected flash cookie");
 | |
| 
 | |
|         assert_eq!(flash_cookie.value(), "7:successErfolgreich abgemeldet!");
 | |
|     }
 | |
| 
 | |
|     #[sqlx::test]
 | |
|     fn test_join_invalid_event() {
 | |
|         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=rower&password=rower"); // Add the form data to the request body;
 | |
|         login.dispatch().await;
 | |
| 
 | |
|         let req = client.get("/planned/join/9999");
 | |
|         let response = req.dispatch().await;
 | |
| 
 | |
|         assert_eq!(response.status(), Status::SeeOther);
 | |
|         assert_eq!(response.headers().get("Location").next(), Some("/"));
 | |
| 
 | |
|         let flash_cookie = response
 | |
|             .cookies()
 | |
|             .get("_flash")
 | |
|             .expect("Expected flash cookie");
 | |
| 
 | |
|         assert_eq!(flash_cookie.value(), "5:errorTrip_details do not exist.");
 | |
|     }
 | |
| }
 |