major features update
This commit is contained in:
parent
519fb49901
commit
4e2fab67c5
48 changed files with 3925 additions and 208 deletions
167
src/web/settings.rs
Normal file
167
src/web/settings.rs
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
use axum::{
|
||||
Router,
|
||||
extract::State,
|
||||
routing::{delete, get, post, put},
|
||||
};
|
||||
use axum_extra::extract::Form;
|
||||
use maud::{Markup, html};
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
use short_uuid::ShortUuid;
|
||||
|
||||
use super::Layout;
|
||||
use crate::models::user::{AuthSession, Credentials};
|
||||
use crate::{AppError, AppState};
|
||||
|
||||
pub fn router() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/settings", get(self::get::settings))
|
||||
.route("/settings/ics_path", post(self::post::ics_path))
|
||||
.route("/settings/ics_path", delete(self::delete::ics_path))
|
||||
.route("/password", put(self::put::password))
|
||||
}
|
||||
|
||||
fn calendar_link(path: Option<String>) -> Markup {
|
||||
if let Some(path) = path {
|
||||
html! {
|
||||
#cal-link x-data=(json!({ "path": path })) hx-target="this" hx-swap="outerHTML" {
|
||||
a x-bind:href="window.location.origin + '/cal/' + path" {
|
||||
span x-text="window.location.origin + '/cal/'" {}
|
||||
span { (path) }
|
||||
}
|
||||
p {
|
||||
"Warning: These actions unrecoverably change your calendar's URL."
|
||||
}
|
||||
button hx-post="/settings/ics_path" { "Regenerate path" }
|
||||
button hx-delete="/settings/ics_path" { "Destroy calendar" }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
html! {
|
||||
#cal-link hx-target="this" hx-swap="outerHTML" {
|
||||
div { "Birthdays calendar is disabled." }
|
||||
button hx-post="/settings/ics_path" { "Enable calendar" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod get {
|
||||
use super::*;
|
||||
|
||||
pub async fn settings(
|
||||
auth_session: AuthSession,
|
||||
State(state): State<AppState>,
|
||||
layout: Layout,
|
||||
) -> Result<Markup, AppError> {
|
||||
let pool = &state.db(&auth_session.user.unwrap()).pool;
|
||||
let ics_path: (Option<String>,) = sqlx::query_as("select ics_path from settings")
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
let ics_path: Option<String> = ics_path.0;
|
||||
|
||||
Ok(layout.render(
|
||||
Some(vec!["static/settings.css"]),
|
||||
html! {
|
||||
h2 { "Birthdays Calendar URL" }
|
||||
(calendar_link(ics_path))
|
||||
|
||||
h2 { "Change Password" }
|
||||
form x-data="{ old_p: '', new_p: '', confirm: '' }" hx-put="/password"
|
||||
hx-on::after-request="if(event.detail.successful) { this.reset(); setTimeout(() => window.location.reload(), 5000); }"
|
||||
hx-target="this" hx-target-error="this" hx-swap="beforeend" {
|
||||
label for="old" { "Current password:" }
|
||||
input id="old" name="current" x-model="old_p" type="password";
|
||||
|
||||
label for="new" { "New password:" }
|
||||
input id="new" name="new_password" x-model="new_p" type="password";
|
||||
|
||||
label for="confirm" { "Confirm:" }
|
||||
input id="confirm" x-model="confirm" type="password";
|
||||
|
||||
button type="submit" x-bind:disabled="!(new_p.length && new_p === confirm)" { "Submit" }
|
||||
.error x-show="new_p.length && confirm.length && new_p !== confirm" {
|
||||
"Passwords do not match"
|
||||
}
|
||||
}
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
mod post {
|
||||
use super::*;
|
||||
|
||||
pub async fn ics_path(
|
||||
auth_session: AuthSession,
|
||||
State(state): State<AppState>,
|
||||
) -> Result<Markup, AppError> {
|
||||
let user = auth_session.user.unwrap();
|
||||
let pool = &state.db(&user).pool;
|
||||
|
||||
let ics_path = format!("{}-{}.ics", &user.username, ShortUuid::generate());
|
||||
|
||||
sqlx::query!("update settings set ics_path=$1", ics_path)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
Ok(calendar_link(Some(ics_path)))
|
||||
}
|
||||
}
|
||||
|
||||
mod delete {
|
||||
use super::*;
|
||||
|
||||
pub async fn ics_path(
|
||||
auth_session: AuthSession,
|
||||
State(state): State<AppState>,
|
||||
) -> Result<Markup, AppError> {
|
||||
let pool = &state.db(&auth_session.user.unwrap()).pool;
|
||||
|
||||
sqlx::query!("update settings set ics_path=null")
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
Ok(calendar_link(None))
|
||||
}
|
||||
}
|
||||
|
||||
mod put {
|
||||
use super::*;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct PassChange {
|
||||
current: String,
|
||||
new_password: String,
|
||||
}
|
||||
|
||||
pub async fn password(
|
||||
auth_session: AuthSession,
|
||||
Form(payload): Form<PassChange>,
|
||||
) -> Result<Markup, AppError> {
|
||||
let username = auth_session.user.as_ref().unwrap().username.clone();
|
||||
|
||||
tracing::debug!("Resetting password for {}...", username);
|
||||
|
||||
let current_creds = Credentials {
|
||||
username: username.clone(),
|
||||
password: payload.current,
|
||||
};
|
||||
|
||||
let new_creds = Credentials {
|
||||
username: username,
|
||||
password: payload.new_password,
|
||||
};
|
||||
|
||||
match auth_session.authenticate(current_creds).await {
|
||||
Err(_) => Ok(html! { .error { "Server error; could not verify authentication." } }),
|
||||
Ok(None) => Ok(html! { .error { "Current password is incorrect." } }),
|
||||
Ok(Some(_)) => {
|
||||
auth_session.backend.set_password(new_creds).await?;
|
||||
Ok(html! { .msg {
|
||||
"Password changed successfully. Redirecting to login page after 5 seconds..."
|
||||
} })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue