/* * asklyphe-frontend routes/user_settings.rs * - http routes for the settings page * * Copyright (C) 2025 Real Microsoft, LLC * * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ use std::sync::Arc; use std::sync::atomic::Ordering; use askama::Template; use askama_axum::IntoResponse; use asklyphe_common::nats::authservice::{AuthError, AuthRequest, AuthResponse, AuthServiceQuery, AuthServiceRequest, AuthServiceResponse, LogoutRequest, LogoutResponse}; use asklyphe_common::nats::authservice::profile::{ThemeChangeRequest, ThemeChangeResponse, UserInfoRequest, UserInfoResponse}; use asklyphe_common::nats::comms; use asklyphe_common::nats::comms::{ServiceError, ServiceResponse}; use async_nats::jetstream; use axum::{Extension, Form}; use axum::response::Redirect; use axum_extra::extract::CookieJar; use serde::Deserialize; use tokio::sync::Mutex; use tracing::error; use crate::{BUILT_ON, GIT_COMMIT, Opts, ALPHA, VERSION, WEBSITE_COUNT, YEAR}; use crate::routes::{authenticate_user, Themes, UserInfo}; #[derive(Template)] #[template(path = "user_settings.html")] pub struct SettingsTemplate { themes: Vec, error: Option, info: UserInfo, search_query: String, version: String, git_commit: String, built_on: String, year: String, alpha: bool, count: u64, theme: Themes, true_theme: Themes, } pub async fn user_settings( jar: CookieJar, Extension(nats): Extension>, Extension(opts): Extension, ) -> impl IntoResponse { if let Some(token) = jar.get("token") { let token = token.value().to_string(); let info = match authenticate_user(nats.clone(), token).await { Ok(i) => i, Err(e) => { return (jar.remove("token"), crate::routes::index::frontpage_error(e.as_str(), opts.auth_url.clone())).into_response(); } }; let theme = info.get_theme(); SettingsTemplate { themes: Themes::get_all_themes(), error: None, info: info.clone(), search_query: "".to_string(), version: VERSION.to_string(), git_commit: GIT_COMMIT.to_string(), built_on: BUILT_ON.to_string(), year: YEAR.to_string(), alpha: ALPHA, count: WEBSITE_COUNT.load(Ordering::Relaxed), theme, true_theme: info.get_true_theme(), }.into_response() } else { Redirect::temporary("/").into_response() } } #[derive(Deserialize, Debug)] pub struct ThemeChangeForm { pub theme: Option, } pub async fn theme_change_post( jar: CookieJar, Extension(nats): Extension>, Extension(opts): Extension, Form(input): Form, ) -> impl IntoResponse { fn settings_error(info: UserInfo, theme: Themes, error: String) -> impl IntoResponse { SettingsTemplate { themes: Themes::get_all_themes(), error: Some(error), info: info.clone(), search_query: "".to_string(), version: VERSION.to_string(), git_commit: GIT_COMMIT.to_string(), built_on: BUILT_ON.to_string(), year: YEAR.to_string(), alpha: ALPHA, count: WEBSITE_COUNT.load(Ordering::Relaxed), theme, true_theme: info.get_true_theme(), }.into_response() } if let Some(token) = jar.get("token") { let token = token.value().to_string(); let info = match authenticate_user(nats.clone(), token.clone()).await { Ok(i) => i, Err(e) => { return (jar.remove("token"), crate::routes::index::frontpage_error(e.as_str(), opts.auth_url.clone())).into_response(); } }; let theme = info.get_theme(); if let Some(theme_input) = &input.theme { if !Themes::get_all_themes().iter().map(|x| x.internal_name().to_string()).collect::>().contains(&theme_input) { return settings_error(info, theme.clone(), "invalid input, please try again!".to_string()).into_response(); } } let response = comms::query_service(comms::Query::AuthService(AuthServiceQuery { request: AuthServiceRequest::ThemeChangeRequest(ThemeChangeRequest { token, theme: input.theme.unwrap_or("default".to_string()), }), replyto: "".to_string(), }), &nats, false).await; if let Err(e) = response { error!("nats error: {:?}", e); settings_error(info, theme, "internal server error occurred! if the error persists, contact a developer!".to_string()).into_response() } else { let response = response.unwrap(); match response { ServiceResponse::AuthService(r) => { match r { AuthServiceResponse::ThemeChangeResponse(r) => { match r { ThemeChangeResponse::Success => { Redirect::to("/user_settings").into_response() } ThemeChangeResponse::Logout => { (jar.remove("token"), crate::routes::index::frontpage_error("your session token was invalid, please try logging in again", opts.auth_url.clone())).into_response() } ThemeChangeResponse::InternalServerError => { settings_error(info, theme, "internal server error occurred! if the error persists, contact a developer!".to_string()).into_response() } } } x => { error!("ERROR! RECV WRONG AUTHSERVICE RESPONSE WHEN ASKING FOR THEME CHANGE! INVESTIGATE ASAP! {x}"); settings_error(info, theme, "internal server error occurred! if the error persists, contact a developer!".to_string()).into_response() } } } x => { error!("ERROR! RECV WRONG SERVICE RESPONSE WHEN ASKING AUTHSERVICE! INVESTIGATE ASAP!"); settings_error(info, theme, "internal server error occurred! if the error persists, contact a developer!".to_string()).into_response() } } } } else { Redirect::to("/").into_response() } }