Files
star/src/route.rs
2025-04-07 20:33:55 +02:00

202 lines
5.4 KiB
Rust

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<Self> {
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<Self> {
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<Arc<SqlitePool>>, 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<Arc<SqlitePool>>,
session: Session,
Form(form): Form<CreateForm>,
) -> 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<Arc<SqlitePool>>,
session: Session,
axum::extract::Path(id): axum::extract::Path<i64>,
) -> 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<Arc<SqlitePool>>,
session: Session,
axum::extract::Path(id): axum::extract::Path<i64>,
) -> Result<Markup, 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 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<Arc<SqlitePool>>,
session: Session,
axum::extract::Path(id): axum::extract::Path<i64>,
Form(form): Form<UpdateNameForm>,
) -> 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<Arc<SqlitePool>> {
Router::new()
.route("/", get(index))
.route("/", post(create))
.route("/{id}/delete", get(delete))
.route("/{id}", get(view))
.route("/{id}/name", post(update_name))
}