use axum::{ extract::{Form, State}, http::StatusCode, response::{IntoResponse, Redirect, Response}, routing::post, Router, }; use crate::{app::App, clock::RequestedAt, error::InternalError}; use super::extract::IdentityToken; pub fn router() -> Router { Router::new() .route("/login", post(on_login)) .route("/logout", post(on_logout)) } #[derive(serde::Deserialize)] struct LoginRequest { name: String, password: String, } async fn on_login( State(app): State, RequestedAt(now): RequestedAt, identity: IdentityToken, Form(form): Form, ) -> Result { let token = app.logins().login(&form.name, &form.password, now).await?; let resp = if let Some(token) = token { let identity = identity.set(&token); (identity, LoginResponse::Successful) } else { (identity, LoginResponse::Rejected) }; Ok(resp) } enum LoginResponse { Rejected, Successful, } impl IntoResponse for LoginResponse { fn into_response(self) -> Response { match self { Self::Rejected => { (StatusCode::UNAUTHORIZED, "invalid name or password").into_response() } Self::Successful => Redirect::to("/").into_response(), } } } async fn on_logout( State(app): State, identity: IdentityToken, ) -> Result { if let Some(secret) = identity.secret() { app.logins().logout(secret).await?; } let identity = identity.clear(); Ok((identity, Redirect::to("/"))) }