use rocket::{ form::Form, get, http::{Cookie, CookieJar}, post, request::{self, FlashMessage, FromRequest}, response::{Flash, Redirect}, routes, time::{Duration, OffsetDateTime}, Request, Route, State, }; use rocket_dyn_templates::{context, Template}; use sqlx::SqlitePool; use tera::Context; use crate::model::{ boat::Boat, logbook::{LogToAdd, LogToFinalize, Logbook, LogbookCreateError, LogbookUpdateError}, logtype::LogType, user::{AdminUser, User}, }; pub struct KioskCookie(String); #[rocket::async_trait] impl<'r> FromRequest<'r> for KioskCookie { type Error = std::convert::Infallible; async fn from_request(request: &'r Request<'_>) -> request::Outcome { match request.cookies().get_private("kiosk") { Some(cookie) => request::Outcome::Success(KioskCookie(cookie.value().to_string())), None => request::Outcome::Forward(()), } } } #[get("/", rank = 2)] async fn index( db: &State, flash: Option>, adminuser: AdminUser, ) -> Template { let boats = Boat::all(db).await; let coxes = User::cox(db).await; let users = User::all(db).await; let logtypes = LogType::all(db).await; let distances = Logbook::distances(db).await; let on_water = Logbook::on_water(db).await; let mut context = Context::new(); if let Some(msg) = flash { context.insert("flash", &msg.into_inner()); } context.insert("boats", &boats); context.insert("coxes", &coxes); context.insert("users", &users); context.insert("logtypes", &logtypes); context.insert("loggedin_user", &adminuser.user); context.insert("on_water", &on_water); context.insert("distances", &distances); Template::render("log", context.into_json()) } #[get("/show", rank = 2)] async fn show(db: &State, user: User) -> Template { let logs = Logbook::completed(db).await; Template::render("log.completed", context!(logs, loggedin_user: &user)) } #[get("/show")] async fn show_kiosk(db: &State, _kios: KioskCookie) -> Template { let logs = Logbook::completed(db).await; Template::render("log.completed", context!(logs)) } #[get("/kiosk/ekrv2019")] fn new_kiosk(cookies: &CookieJar<'_>) -> Redirect { let mut cookie = Cookie::new("kiosk", "yes".to_string()); cookie.set_expires(OffsetDateTime::now_utc() + Duration::weeks(12)); cookies.add_private(cookie); Redirect::to("/log") } #[get("/")] async fn kiosk( db: &State, flash: Option>, _kiosk: KioskCookie, ) -> Template { let boats = Boat::all(db).await; let coxes = User::cox(db).await; let users = User::all(db).await; let logtypes = LogType::all(db).await; let distances = Logbook::distances(db).await; let on_water = Logbook::on_water(db).await; let mut context = Context::new(); if let Some(msg) = flash { context.insert("flash", &msg.into_inner()); } context.insert("boats", &boats); context.insert("coxes", &coxes); context.insert("users", &users); context.insert("logtypes", &logtypes); context.insert("on_water", &on_water); context.insert("distances", &distances); Template::render("kiosk", context.into_json()) } async fn create_logbook(db: &SqlitePool, data: Form) -> Flash { match Logbook::create( db, data.into_inner() ) .await { Ok(_) => Flash::success(Redirect::to("/log"), "Ausfahrt erfolgreich hinzugefügt"), Err(LogbookCreateError::BoatAlreadyOnWater) => Flash::error(Redirect::to("/log"), format!("Boot schon am Wasser")), Err(LogbookCreateError::ShipmasterAlreadyOnWater) => Flash::error(Redirect::to("/log"), format!("Schiffsführer schon am Wasser")), Err(LogbookCreateError::RowerAlreadyOnWater(rower)) => Flash::error(Redirect::to("/log"), format!("Ruderer {} schon am Wasser", rower.name)), Err(LogbookCreateError::BoatLocked) => Flash::error(Redirect::to("/log"), format!("Boot gesperrt")), Err(LogbookCreateError::BoatNotFound) => Flash::error(Redirect::to("/log"), format!("Boot gibt's ned")), Err(LogbookCreateError::TooManyRowers(expected, actual)) => Flash::error(Redirect::to("/log"), format!("Zu viele Ruderer (Boot fasst maximal {expected}, es wurden jedoch {actual} Ruderer ausgewählt)")), } } #[post("/", data = "", rank = 2)] async fn create( db: &State, data: Form, _adminuser: AdminUser, ) -> Flash { create_logbook(db, data).await } #[post("/", data = "")] async fn create_kiosk( db: &State, data: Form, _kiosk: KioskCookie, ) -> Flash { create_logbook(db, data).await } async fn home_logbook( db: &SqlitePool, data: Form, logbook_id: i32, user: &User, ) -> Flash { let logbook: Option = Logbook::find_by_id(db, logbook_id).await; let Some(logbook) = logbook else { return Flash::error( Redirect::to("/admin/log"), format!("Log with ID {} does not exist!", logbook_id), ) }; match logbook.home(db, user, data.into_inner()).await { Ok(_) => Flash::success(Redirect::to("/log"), "Successfully updated log"), Err(LogbookUpdateError::TooManyRowers(expected, actual)) => Flash::error(Redirect::to("/log"), format!("Zu viele Ruderer (Boot fasst maximal {expected}, es wurden jedoch {actual} Ruderer ausgewählt)")), Err(_) => Flash::error( Redirect::to("/log"), format!("Logbook with ID {} could not be updated!", logbook_id), ), } } #[post("/", data = "")] async fn home_kiosk( db: &State, data: Form, logbook_id: i32, _kiosk: KioskCookie, ) -> Flash { let logbook = Logbook::find_by_id(db, logbook_id).await.unwrap(); //TODO: fixme home_logbook( db, data, logbook_id, &User::find_by_id(db, logbook.shipmaster as i32) .await .unwrap(), ) .await } #[post("/", data = "", rank = 2)] async fn home( db: &State, data: Form, logbook_id: i32, adminuser: AdminUser, ) -> Flash { home_logbook(db, data, logbook_id, &adminuser.user).await } pub fn routes() -> Vec { routes![ index, create, create_kiosk, home, kiosk, home_kiosk, new_kiosk, show, show_kiosk ] } #[cfg(test)] mod test {}