use askama::Template; use axum::{ Router, extract::{Query, State}, http::StatusCode, response::{Html, IntoResponse, Response}, routing::get, }; use conduwuit_service::password_reset::PASSWORD_RESET_PATH; use serde::Deserialize; use validator::Validate; use crate::{ WebError, form, pages::components::{UserCard, form::Form}, }; #[derive(Deserialize)] struct PasswordResetQuery { token: String, } #[derive(Debug, Template)] #[template(path = "password_reset.html.j2")] struct PasswordReset<'a> { user_card: UserCard<'a>, body: PasswordResetBody, } #[derive(Debug)] enum PasswordResetBody { Form(Form<'static>), Success, } form! { struct PasswordResetForm { #[validate(length(min = 1, message = "Password cannot be empty"))] new_password: String where { input_type: "password", label: "New password", autocomplete: "new-password" }, #[validate(must_match(other = "new_password", message = "Passwords must match"))] confirm_new_password: String where { input_type: "password", label: "Confirm new password", autocomplete: "new-password" } submit: "Reset Password" } } pub(crate) fn build() -> Router { Router::new().route(PASSWORD_RESET_PATH, get(get_password_reset).post(post_password_reset)) } async fn password_reset_form( services: crate::State, query: PasswordResetQuery, reset_form: Form<'static>, ) -> Result { let Some(token) = services.password_reset.check_token(&query.token).await else { return Err(WebError::BadRequest("Invalid reset token".to_owned())); }; let user_card = UserCard::for_local_user(&services, &token.info.user).await; let template = PasswordReset { user_card, body: PasswordResetBody::Form(reset_form), }; Ok(Html(template.render()?)) } async fn get_password_reset( State(services): State, Query(query): Query, ) -> Result { password_reset_form(services, query, PasswordResetForm::build(None)).await } async fn post_password_reset( State(services): State, Query(query): Query, axum::Form(form): axum::Form, ) -> Result { match form.validate() { | Ok(()) => { let Some(token) = services.password_reset.check_token(&query.token).await else { return Err(WebError::BadRequest("Invalid reset token".to_owned())); }; let user_id = token.info.user.clone(); services .password_reset .consume_token(token, &form.new_password) .await?; let user_card = UserCard::for_local_user(&services, &user_id).await; let template = PasswordReset { user_card, body: PasswordResetBody::Success, }; Ok(Html(template.render()?).into_response()) }, | Err(err) => Ok(( StatusCode::BAD_REQUEST, password_reset_form(services, query, PasswordResetForm::build(Some(err))).await, ) .into_response()), } }