major features update
This commit is contained in:
parent
519fb49901
commit
4e2fab67c5
48 changed files with 3925 additions and 208 deletions
114
src/web/auth.rs
Normal file
114
src/web/auth.rs
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
use axum::extract::State;
|
||||
use axum::http::HeaderMap;
|
||||
use axum::{
|
||||
Form, Router,
|
||||
extract::Query,
|
||||
response::{IntoResponse, Redirect},
|
||||
routing::{get, post},
|
||||
};
|
||||
use maud::{DOCTYPE, html};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::models::user::{AuthSession, Credentials};
|
||||
use crate::{AppError, AppState};
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct NextUrl {
|
||||
next: Option<String>,
|
||||
}
|
||||
|
||||
pub fn router() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/login", post(self::post::login))
|
||||
.route("/login", get(self::get::login))
|
||||
.route("/logout", get(self::get::logout))
|
||||
}
|
||||
|
||||
mod post {
|
||||
use super::*;
|
||||
|
||||
#[axum::debug_handler]
|
||||
pub async fn login(
|
||||
mut auth_session: AuthSession,
|
||||
State(mut state): State<AppState>,
|
||||
Query(NextUrl { next }): Query<NextUrl>,
|
||||
Form(creds): Form<Credentials>,
|
||||
) -> Result<impl IntoResponse, AppError> {
|
||||
let mut headers = HeaderMap::new();
|
||||
|
||||
let user = match auth_session.authenticate(creds.clone()).await {
|
||||
Ok(Some(user)) => user,
|
||||
Ok(None) => {
|
||||
return Err(AppError(anyhow::Error::msg(
|
||||
"Username and password do not match",
|
||||
)));
|
||||
}
|
||||
Err(_) => return Err(AppError(anyhow::Error::msg("Internal server error"))),
|
||||
};
|
||||
|
||||
if auth_session.login(&user).await.is_err() {
|
||||
return Err(AppError(anyhow::Error::msg("Server error during login")));
|
||||
}
|
||||
|
||||
state.init(&user).await?;
|
||||
|
||||
if let Some(url) = next {
|
||||
headers.insert("HX-Redirect", url.parse()?);
|
||||
} else {
|
||||
headers.insert("HX-Redirect", "/".parse()?);
|
||||
}
|
||||
|
||||
Ok((headers, "ok"))
|
||||
}
|
||||
}
|
||||
|
||||
mod get {
|
||||
use super::*;
|
||||
|
||||
pub async fn login(Query(NextUrl { next }): Query<NextUrl>) -> impl IntoResponse {
|
||||
let post_url = format!(
|
||||
"/login{}",
|
||||
next.map_or("".to_string(), |n| format!("?next={}", n))
|
||||
);
|
||||
html! {
|
||||
(DOCTYPE)
|
||||
html {
|
||||
head {
|
||||
meta name="viewport" content="width=device-width";
|
||||
script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.7/dist/htmx.min.js" {}
|
||||
script src="https://cdn.jsdelivr.net/npm/htmx-ext-response-targets@2.0.4" integrity="sha384-T41oglUPvXLGBVyRdZsVRxNWnOOqCynaPubjUVjxhsjFTKrFJGEMm3/0KGmNQ+Pg" crossorigin="anonymous" {}
|
||||
script src="https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/dist/cdn.min.js" defer {}
|
||||
link rel="stylesheet" type="text/css" href="/static/index.css";
|
||||
link rel="stylesheet" type="text/css" href="/static/login.css";
|
||||
title { "Mascarpone" }
|
||||
}
|
||||
body hx-ext="response-targets" {
|
||||
h1 { "Mascarpone" }
|
||||
form hx-post=(post_url) hx-target-error="#error" x-data="{ user: '', pass: '' }" {
|
||||
label for="username" { "Username" }
|
||||
input name="username" #username autofocus x-model="user";
|
||||
label for="password" { "Password" }
|
||||
input name="password" #password type="password" x-model="pass";
|
||||
|
||||
input type="submit" value="login" x-bind:disabled="!(user.length && pass.length)";
|
||||
#error {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn logout(
|
||||
mut auth_session: AuthSession,
|
||||
State(mut state): State<AppState>,
|
||||
) -> Result<impl IntoResponse, AppError> {
|
||||
let user = auth_session.user.clone();
|
||||
auth_session.logout().await?;
|
||||
|
||||
if let Some(user) = user {
|
||||
state.remove(&user);
|
||||
}
|
||||
|
||||
Ok(Redirect::to("/login").into_response())
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue