use crate::{err, page, succ}; use axum::{ extract::State, response::{IntoResponse, Redirect}, routing::{get, post}, Form, Router, }; use maud::{html, Markup}; use serde::{Deserialize, Serialize}; use sqlx::{FromRow, SqlitePool}; use std::sync::Arc; use tower_sessions::Session; #[derive(FromRow, Debug, Serialize, Deserialize)] struct Route { id: i64, name: String, } impl Route { async fn all(db: &SqlitePool) -> Vec { sqlx::query_as::<_, Self>("SELECT id, name FROM route;") .fetch_all(db) .await .unwrap() } pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option { sqlx::query_as!(Self, "SELECT id, name FROM route WHERE id = ?", id) .fetch_one(db) .await .ok() } async fn create(db: &SqlitePool, name: &str) -> Result<(), String> { sqlx::query!("INSERT INTO route(name) VALUES (?)", name) .execute(db) .await .map_err(|e| e.to_string())?; Ok(()) } async fn update_name(&self, db: &SqlitePool, name: &str) { sqlx::query!("UPDATE route SET name = ? WHERE id = ?", name, self.id) .execute(db) .await .unwrap(); } async fn delete(&self, db: &SqlitePool) { sqlx::query!("DELETE FROM route WHERE id = ?", self.id) .execute(db) .await .unwrap(); } } async fn index(State(db): State>, session: Session) -> Markup { let routes = Route::all(&db).await; let content = html! { h1 { a href="/" { "↩️" } "Routen" } ol { @for route in &routes{ li { a href=(format!("/route/{}", route.id)){ (route.name) } a href=(format!("/route/{}/delete", route.id)) onclick="return confirm('Bist du sicher, dass die Route gelöscht werden soll? Das kann _NICHT_ mehr rückgängig gemacht werden.');" { "🗑️" } } } } h2 { "Neue Route" } form action="/route" method="post" { fieldset role="group" { input type="text" name="name" placeholder="Routenname" required; input type="submit" value="Neue Route"; } } }; page(content, session, false).await } #[derive(Deserialize)] struct CreateForm { name: String, } async fn create( State(db): State>, session: Session, Form(form): Form, ) -> impl IntoResponse { match Route::create(&db, &form.name).await { Ok(_) => succ!(session,"Route '{}' erfolgreich erstellt!", form.name), Err(e) => err!( session, "Route '{}' konnte _NICHT_ erstellt werden, da es bereits eine Route mit diesem Namen gibt ({e})!", form.name ), } Redirect::to("/route") } async fn delete( State(db): State>, session: Session, axum::extract::Path(id): axum::extract::Path, ) -> impl IntoResponse { let Some(route) = Route::find_by_id(&db, id).await else { err!( session, "Route mit ID {id} konnte nicht gelöscht werden, da sie nicht existiert" ); return Redirect::to("/route"); }; route.delete(&db).await; succ!(session, "Route '{}' erfolgreich gelöscht!", route.name); Redirect::to("/route") } async fn view( State(db): State>, session: Session, axum::extract::Path(id): axum::extract::Path, ) -> Result { let Some(route) = Route::find_by_id(&db, id).await else { err!( session, "Route mit ID {id} konnte nicht geöffnet werden, da sie nicht existiert" ); return Err(Redirect::to("/route")); }; let content = html! { h1 { a href="/route" { "↩️" } "Route " (route.name) } details { summary { "Routenname bearbeiten ✏️" } form action=(format!("/route/{}/name", route.id)) method="post" { input type="text" name="name" value=(route.name) required; input type="submit" value="Speichern"; } } }; Ok(page(content, session, false).await) } #[derive(Deserialize)] struct UpdateNameForm { name: String, } async fn update_name( State(db): State>, session: Session, axum::extract::Path(id): axum::extract::Path, Form(form): Form, ) -> impl IntoResponse { let Some(route) = Route::find_by_id(&db, id).await else { err!( session, "Route mit ID {id} konnte nicht geöffnet werden, da sie nicht existiert" ); return Redirect::to("/route"); }; route.update_name(&db, &form.name).await; succ!( session, "Name der Route '{}' wurden erfolgreich bearbeitet!", route.name ); Redirect::to(&format!("/route/{id}")) } pub(super) fn routes() -> Router> { Router::new() .route("/", get(index)) .route("/", post(create)) .route("/{id}/delete", get(delete)) .route("/{id}", get(view)) .route("/{id}/name", post(update_name)) }