asklyphe/asklyphe-frontend/src/routes/user_settings.rs

203 lines
No EOL
7.4 KiB
Rust

/*
* 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 <https://www.gnu.org/licenses/>.
*/
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, UserInfo};
pub struct Theme<'a> {
pub value: &'a str,
pub name: &'a str,
}
pub static THEMES: &[Theme] = &[
Theme {
value: "default",
name: "default theme",
},
Theme {
value: "dark",
name: "dark theme",
},
Theme {
value: "oled",
name: "lights out",
},
Theme {
value: "classic",
name: "classic",
},
Theme {
value: "freaky",
name: "freaky",
},
Theme {
value: "water",
name: "water",
},
Theme {
value: "gloss",
name: "gloss",
},
];
#[derive(Template)]
#[template(path = "user_settings.html")]
pub struct SettingsTemplate {
themes: &'static [Theme<'static>],
error: Option<String>,
info: UserInfo,
search_query: String,
version: String,
git_commit: String,
built_on: String,
year: String,
alpha: bool,
count: u64,
theme: String,
}
pub async fn user_settings(
jar: CookieJar,
Extension(nats): Extension<Arc<jetstream::Context>>,
Extension(opts): Extension<Opts>,
) -> 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.theme.clone();
SettingsTemplate {
themes: THEMES,
error: None,
info,
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,
}.into_response()
} else {
Redirect::temporary("/").into_response()
}
}
#[derive(Deserialize, Debug)]
pub struct ThemeChangeForm {
pub theme: Option<String>,
}
pub async fn theme_change_post(
jar: CookieJar,
Extension(nats): Extension<Arc<jetstream::Context>>,
Extension(opts): Extension<Opts>,
Form(input): Form<ThemeChangeForm>,
) -> impl IntoResponse {
fn settings_error(info: UserInfo, theme: String, error: String) -> impl IntoResponse {
SettingsTemplate {
themes: THEMES,
error: Some(error),
info,
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,
}.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.theme.clone();
if !THEMES.iter().map(|v| v.value.to_string()).collect::<Vec<String>>().contains(&input.theme.clone().unwrap_or("default".to_string())) {
return settings_error(info, theme, "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()
}
}