660 lines
No EOL
25 KiB
Rust
660 lines
No EOL
25 KiB
Rust
/*
|
|
* asklyphe-frontend routes/admin.rs
|
|
* - http routes for the admin panel
|
|
*
|
|
* 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 askama::Template;
|
|
use asklyphe_common::nats::authservice::{AuthServiceQuery, AuthServiceRequest, AuthServiceResponse};
|
|
use asklyphe_common::nats::authservice::admin::{AdminCreateAnnouncementRequest, AdminCreateAnnouncementResponse, AdminInviteCodeGenerateRequest, AdminInviteCodeGenerateResponse, AdminListAllUsersRequest, AdminListAllUsersResponse, AdminListInviteCodesRequest, AdminListInviteCodesResponse, InviteCodeSerialized, Stats, StatsRequest, StatsResponse, UserSerialized};
|
|
use asklyphe_common::nats::authservice::profile::{UserInfoRequest, UserInfoResponse};
|
|
use asklyphe_common::nats::comms;
|
|
use asklyphe_common::nats::comms::ServiceResponse;
|
|
use async_nats::jetstream;
|
|
use axum::{Extension, Form};
|
|
use axum::response::{IntoResponse, Redirect};
|
|
use axum_extra::extract::CookieJar;
|
|
use rand::Rng;
|
|
use serde::Deserialize;
|
|
use tracing::{debug, error};
|
|
use crate::Opts;
|
|
use crate::routes::{authenticate_user, UserInfo};
|
|
|
|
const MOTD: &[&str] = &[
|
|
"be evil :3c",
|
|
"pong",
|
|
">:o",
|
|
"\" UNION SELECT username, password FROM users; -- ",
|
|
];
|
|
|
|
#[derive(Template)]
|
|
#[template(path = "admin/error.html")]
|
|
pub struct AdminErrorTemplate {
|
|
motd: &'static str,
|
|
|
|
error: String,
|
|
}
|
|
|
|
#[derive(Template)]
|
|
#[template(path = "admin/home.html")]
|
|
pub struct AdminHomeTemplate {
|
|
motd: &'static str,
|
|
|
|
last_ten_users: Vec<String>,
|
|
user_count: usize,
|
|
admin_count: usize,
|
|
active_verification_requests: usize,
|
|
}
|
|
|
|
fn motd() -> &'static str {
|
|
let mut rng = rand::thread_rng();
|
|
MOTD[rng.gen_range(0..MOTD.len())]
|
|
}
|
|
|
|
fn adminerror(message: String) -> impl IntoResponse {
|
|
AdminErrorTemplate {
|
|
motd: motd(),
|
|
error: message,
|
|
}
|
|
}
|
|
|
|
async fn stats(nats: Arc<jetstream::Context>, token: String) -> Result<Stats, impl IntoResponse> {
|
|
let response = comms::query_service(
|
|
comms::Query::AuthService(AuthServiceQuery {
|
|
request: AuthServiceRequest::StatsRequest(StatsRequest {
|
|
token,
|
|
}),
|
|
replyto: "".to_string(),
|
|
}),
|
|
&nats,
|
|
false,
|
|
).await;
|
|
if let Err(e) = response {
|
|
error!("nats error: {:?}", e);
|
|
Err(adminerror(format!("nats error: {:?}", e)).into_response())
|
|
} else {
|
|
let response = response.unwrap();
|
|
match response {
|
|
ServiceResponse::SearchService(_) => {
|
|
error!("sent search service response when asking for auth service!! investigate ASAP!!!");
|
|
Err(adminerror("sent search service response when asking auth service!".to_string()).into_response())
|
|
}
|
|
ServiceResponse::BingService(_) => {
|
|
error!("sent bing service response when asking for auth service!! investigate ASAP!!!");
|
|
Err(adminerror("sent bing service response when asking auth service!".to_string()).into_response())
|
|
}
|
|
ServiceResponse::AuthService(r) => match r {
|
|
AuthServiceResponse::StatsResponse(stats) => {
|
|
match stats {
|
|
StatsResponse::Success(stats) => {
|
|
Ok(stats)
|
|
}
|
|
StatsResponse::InternalServerError(e) => {
|
|
Err(adminerror(format!("internal server error: {e}")).into_response())
|
|
}
|
|
StatsResponse::Logout => {
|
|
Err(Redirect::to("/").into_response())
|
|
}
|
|
StatsResponse::AccessDenied => {
|
|
Err(Redirect::to("/").into_response())
|
|
}
|
|
}
|
|
}
|
|
x => {
|
|
error!("auth service gave {} to our user info request!", x);
|
|
Err(adminerror(format!("auth service gave {} to our user info request!", x)).into_response())
|
|
}
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn list_invite_codes(nats: Arc<jetstream::Context>, token: String, show_used: bool) -> Result<Vec<InviteCodeSerialized>, impl IntoResponse> {
|
|
let response = comms::query_service(
|
|
comms::Query::AuthService(AuthServiceQuery {
|
|
request: AuthServiceRequest::AdminListInviteCodesRequest(AdminListInviteCodesRequest {
|
|
token,
|
|
show_used,
|
|
}),
|
|
replyto: "".to_string(),
|
|
}),
|
|
&nats,
|
|
false,
|
|
).await;
|
|
if let Err(e) = response {
|
|
error!("nats error: {:?}", e);
|
|
Err(adminerror(format!("nats error: {:?}", e)).into_response())
|
|
} else {
|
|
let response = response.unwrap();
|
|
match response {
|
|
ServiceResponse::SearchService(_) => {
|
|
error!("sent search service response when asking for auth service!! investigate ASAP!!!");
|
|
Err(adminerror("sent search service response when asking auth service!".to_string()).into_response())
|
|
}
|
|
ServiceResponse::BingService(_) => {
|
|
error!("sent bing service response when asking for auth service!! investigate ASAP!!!");
|
|
Err(adminerror("sent bing service response when asking auth service!".to_string()).into_response())
|
|
}
|
|
ServiceResponse::AuthService(r) => match r {
|
|
AuthServiceResponse::AdminListInviteCodesResponse(v) => {
|
|
match v {
|
|
AdminListInviteCodesResponse::Success(v) => {
|
|
Ok(v)
|
|
}
|
|
AdminListInviteCodesResponse::InternalServerError(e) => {
|
|
Err(adminerror(format!("internal server error: {e}")).into_response())
|
|
}
|
|
AdminListInviteCodesResponse::Logout => {
|
|
Err(Redirect::to("/").into_response())
|
|
}
|
|
AdminListInviteCodesResponse::AccessDenied => {
|
|
Err(Redirect::to("/").into_response())
|
|
}
|
|
}
|
|
}
|
|
x => {
|
|
error!("auth service gave {} to our list invite codes request!", x);
|
|
Err(adminerror(format!("auth service gave {} to our list invite codes request!", x)).into_response())
|
|
}
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn list_all_users(nats: Arc<jetstream::Context>, token: String) -> Result<Vec<UserSerialized>, impl IntoResponse> {
|
|
let response = comms::query_service(
|
|
comms::Query::AuthService(AuthServiceQuery {
|
|
request: AuthServiceRequest::AdminListAllUsersRequest(AdminListAllUsersRequest {
|
|
token,
|
|
}),
|
|
replyto: "".to_string(),
|
|
}),
|
|
&nats,
|
|
false,
|
|
).await;
|
|
if let Err(e) = response {
|
|
error!("nats error: {:?}", e);
|
|
Err(adminerror(format!("nats error: {:?}", e)).into_response())
|
|
} else {
|
|
let response = response.unwrap();
|
|
match response {
|
|
ServiceResponse::SearchService(_) => {
|
|
error!("sent search service response when asking for auth service!! investigate ASAP!!!");
|
|
Err(adminerror("sent search service response when asking auth service!".to_string()).into_response())
|
|
}
|
|
ServiceResponse::BingService(_) => {
|
|
error!("sent bing service response when asking for auth service!! investigate ASAP!!!");
|
|
Err(adminerror("sent bing service response when asking auth service!".to_string()).into_response())
|
|
}
|
|
ServiceResponse::AuthService(r) => match r {
|
|
AuthServiceResponse::AdminListAllUsersResponse(v) => {
|
|
match v {
|
|
AdminListAllUsersResponse::Success(v) => {
|
|
Ok(v)
|
|
}
|
|
AdminListAllUsersResponse::InternalServerError(e) => {
|
|
Err(adminerror(format!("internal server error: {e}")).into_response())
|
|
}
|
|
AdminListAllUsersResponse::Logout => {
|
|
Err(Redirect::to("/").into_response())
|
|
}
|
|
AdminListAllUsersResponse::AccessDenied => {
|
|
Err(Redirect::to("/").into_response())
|
|
}
|
|
}
|
|
}
|
|
x => {
|
|
error!("auth service gave {} to our list users request!", x);
|
|
Err(adminerror(format!("auth service gave {} to our list users request!", x)).into_response())
|
|
}
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn gen_invite_code(nats: Arc<jetstream::Context>, token: String, code: String, comment: String) -> Result<(), impl IntoResponse> {
|
|
let response = comms::query_service(
|
|
comms::Query::AuthService(AuthServiceQuery {
|
|
request: AuthServiceRequest::AdminInviteCodeGenerateRequest(AdminInviteCodeGenerateRequest {
|
|
token,
|
|
invite_code: code,
|
|
comment,
|
|
}),
|
|
replyto: "".to_string(),
|
|
}),
|
|
&nats,
|
|
false,
|
|
).await;
|
|
if let Err(e) = response {
|
|
error!("nats error: {:?}", e);
|
|
Err(adminerror(format!("nats error: {:?}", e)).into_response())
|
|
} else {
|
|
let response = response.unwrap();
|
|
match response {
|
|
ServiceResponse::SearchService(_) => {
|
|
error!("sent search service response when asking for auth service!! investigate ASAP!!!");
|
|
Err(adminerror("sent search service response when asking auth service!".to_string()).into_response())
|
|
}
|
|
ServiceResponse::BingService(_) => {
|
|
error!("sent bing service response when asking for auth service!! investigate ASAP!!!");
|
|
Err(adminerror("sent bing service response when asking auth service!".to_string()).into_response())
|
|
}
|
|
ServiceResponse::AuthService(r) => match r {
|
|
AuthServiceResponse::AdminInviteCodeGenerateResponse(res) => {
|
|
match res {
|
|
AdminInviteCodeGenerateResponse::Success => {
|
|
Ok(())
|
|
}
|
|
AdminInviteCodeGenerateResponse::InternalServerError(e) => {
|
|
Err(adminerror(format!("internal server error: {e}")).into_response())
|
|
}
|
|
AdminInviteCodeGenerateResponse::Logout => {
|
|
Err(Redirect::to("/").into_response())
|
|
}
|
|
AdminInviteCodeGenerateResponse::AccessDenied => {
|
|
Err(Redirect::to("/").into_response())
|
|
}
|
|
AdminInviteCodeGenerateResponse::CodeAlreadyExists => {
|
|
Err(adminerror("code already exists".to_string()).into_response())
|
|
}
|
|
}
|
|
}
|
|
x => {
|
|
error!("auth service gave {} to our user info request!", x);
|
|
Err(adminerror(format!("auth service gave {} to our geninvitecode request!", x)).into_response())
|
|
}
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn admin_home(
|
|
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.clone()).await {
|
|
Ok(i) => i,
|
|
Err(e) => {
|
|
return Redirect::to("/").into_response();
|
|
}
|
|
};
|
|
|
|
if !info.administrator {
|
|
return Redirect::to("/").into_response();
|
|
}
|
|
|
|
let stats = match stats(nats.clone(), token).await {
|
|
Ok(s) => s,
|
|
Err(e) => {
|
|
return e.into_response();
|
|
}
|
|
};
|
|
|
|
AdminHomeTemplate {
|
|
motd: motd(),
|
|
last_ten_users: stats.last_ten_users,
|
|
user_count: stats.user_count,
|
|
admin_count: stats.admin_count,
|
|
active_verification_requests: stats.active_verification_requests,
|
|
}.into_response()
|
|
} else {
|
|
Redirect::to("/").into_response()
|
|
}
|
|
}
|
|
|
|
|
|
#[derive(Template)]
|
|
#[template(path = "admin/invitecode.html")]
|
|
pub struct AdminInviteCodeTemplate {
|
|
motd: &'static str,
|
|
|
|
username: String,
|
|
active_codes: Vec<InviteCodeSerialized>,
|
|
used_codes: Vec<InviteCodeSerialized>,
|
|
}
|
|
|
|
pub async fn admin_invitecode(
|
|
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.clone()).await {
|
|
Ok(i) => i,
|
|
Err(e) => {
|
|
return Redirect::to("/").into_response();
|
|
}
|
|
};
|
|
|
|
if !info.administrator {
|
|
return Redirect::to("/").into_response();
|
|
}
|
|
|
|
let active_codes = match list_invite_codes(nats.clone(), token.clone(), false).await {
|
|
Ok(mut v) => {
|
|
for v in &mut v {
|
|
if let Some(used_by) = &v.used_by {
|
|
if used_by.len() > 32 {
|
|
v.used_by = Some(format!("{}...", &used_by[0..32]));
|
|
}
|
|
}
|
|
|
|
if v.creator.len() > 32 {
|
|
v.creator = format!("{}...", &v.creator[0..32]);
|
|
}
|
|
}
|
|
|
|
v
|
|
},
|
|
Err(e) => {
|
|
return e.into_response();
|
|
}
|
|
};
|
|
|
|
let used_codes = match list_invite_codes(nats.clone(), token.clone(), true).await {
|
|
Ok(v) => v.into_iter().map(|mut v| {
|
|
if let Some(used_by) = &v.used_by {
|
|
if used_by.len() > 32 {
|
|
v.used_by = Some(format!("{}...", &used_by[0..32]));
|
|
}
|
|
}
|
|
|
|
if v.creator.len() > 32 {
|
|
v.creator = format!("{}...", &v.creator[0..32]);
|
|
}
|
|
|
|
if v.used_at.is_none() {
|
|
v.used_at = Some(String::from("unset"));
|
|
v
|
|
} else {
|
|
v
|
|
}
|
|
}).collect(),
|
|
Err(e) => {
|
|
return e.into_response();
|
|
}
|
|
};
|
|
|
|
AdminInviteCodeTemplate {
|
|
motd: motd(),
|
|
username: info.username,
|
|
active_codes,
|
|
used_codes,
|
|
}.into_response()
|
|
} else {
|
|
Redirect::to("/").into_response()
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
pub struct InviteCodeForm {
|
|
code: Option<String>,
|
|
comment: Option<String>,
|
|
}
|
|
|
|
pub async fn admin_invitecode_post(
|
|
jar: CookieJar,
|
|
Extension(nats): Extension<Arc<jetstream::Context>>,
|
|
Extension(opts): Extension<Opts>,
|
|
Form(input): Form<InviteCodeForm>,
|
|
) -> impl IntoResponse {
|
|
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 Redirect::to("/").into_response();
|
|
}
|
|
};
|
|
|
|
if !info.administrator {
|
|
return Redirect::to("/").into_response();
|
|
}
|
|
|
|
if input.code.is_none() || input.comment.is_none() {
|
|
return adminerror("code and comment required".to_string()).into_response();
|
|
}
|
|
if input.code.as_ref().unwrap().is_empty() || input.comment.as_ref().unwrap().is_empty() {
|
|
return adminerror("code and comment required".to_string()).into_response();
|
|
}
|
|
|
|
match gen_invite_code(nats.clone(), token, input.code.unwrap(), input.comment.unwrap()).await {
|
|
Ok(_) => {
|
|
Redirect::to("/admin/invitecode").into_response()
|
|
}
|
|
Err(e) => {
|
|
e.into_response()
|
|
}
|
|
}
|
|
} else {
|
|
Redirect::to("/").into_response()
|
|
}
|
|
}
|
|
|
|
#[derive(Template)]
|
|
#[template(path = "admin/announcement.html")]
|
|
pub struct AdminAnnouncementTemplate {
|
|
motd: &'static str,
|
|
|
|
error: Option<String>,
|
|
|
|
username: String,
|
|
|
|
slug: String,
|
|
title: String,
|
|
short_content: String,
|
|
full_content: String,
|
|
}
|
|
|
|
pub async fn admin_announcement(
|
|
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.clone()).await {
|
|
Ok(i) => i,
|
|
Err(e) => {
|
|
return Redirect::to("/").into_response();
|
|
}
|
|
};
|
|
|
|
if !info.administrator {
|
|
return Redirect::to("/").into_response();
|
|
}
|
|
|
|
AdminAnnouncementTemplate {
|
|
motd: motd(),
|
|
error: None,
|
|
username: info.username,
|
|
slug: "the-post-like-this".to_string(),
|
|
title: "Changes Happened!".to_string(),
|
|
short_content: "we made some changes!".to_string(),
|
|
full_content: "we made some changes! here are the details!".to_string(),
|
|
}.into_response()
|
|
} else {
|
|
Redirect::to("/").into_response()
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Debug, Clone)]
|
|
pub struct AnnouncementForm {
|
|
slug: Option<String>,
|
|
title: Option<String>,
|
|
short_content: Option<String>,
|
|
full_content: Option<String>,
|
|
send_emails: Option<String>,
|
|
}
|
|
|
|
pub async fn admin_announcement_post(
|
|
jar: CookieJar,
|
|
Extension(nats): Extension<Arc<jetstream::Context>>,
|
|
Extension(opts): Extension<Opts>,
|
|
Form(input): Form<AnnouncementForm>,
|
|
) -> impl IntoResponse {
|
|
fn announcement_error(error: &str, username: String, form: AnnouncementForm) -> impl IntoResponse {
|
|
AdminAnnouncementTemplate {
|
|
motd: motd(),
|
|
error: Some(error.to_string()),
|
|
username,
|
|
slug: form.slug.unwrap_or_default(),
|
|
title: form.title.unwrap_or_default(),
|
|
short_content: form.short_content.unwrap_or_default(),
|
|
full_content: form.full_content.unwrap_or_default(),
|
|
}
|
|
}
|
|
|
|
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 Redirect::to("/").into_response();
|
|
}
|
|
};
|
|
|
|
if !info.administrator {
|
|
return Redirect::to("/").into_response();
|
|
}
|
|
|
|
if input.slug.is_none() || input.slug.as_ref().unwrap().is_empty() {
|
|
return announcement_error("slug required", info.username, input).into_response();
|
|
}
|
|
if input.title.is_none() || input.title.as_ref().unwrap().is_empty() {
|
|
return announcement_error("title required", info.username, input).into_response();
|
|
}
|
|
if input.short_content.is_none() || input.short_content.as_ref().unwrap().is_empty() {
|
|
return announcement_error("short content required", info.username, input).into_response();
|
|
}
|
|
if input.full_content.is_none() || input.full_content.as_ref().unwrap().is_empty() {
|
|
return announcement_error("full content required", info.username, input).into_response();
|
|
}
|
|
|
|
debug!("sendemails: {:?}", input.send_emails);
|
|
|
|
let input_og = input.clone();
|
|
let response = comms::query_service(comms::Query::AuthService(AuthServiceQuery {
|
|
request: AuthServiceRequest::AdminCreateAnnouncementRequest(AdminCreateAnnouncementRequest {
|
|
token: token.clone(),
|
|
title: input.title.unwrap(),
|
|
slug: input.slug.unwrap(),
|
|
short_text: input.short_content.unwrap(),
|
|
full_markdown_text: input.full_content.unwrap(),
|
|
send_emails: !input.send_emails.unwrap_or("".to_string()).is_empty(),
|
|
}),
|
|
replyto: "".to_string(),
|
|
}), &nats, false).await;
|
|
|
|
if let Err(e) = response {
|
|
error!("nats error: {:?}", e);
|
|
announcement_error(&format!("nats error: {:?}", e), info.username, input_og).into_response()
|
|
} else {
|
|
let response = response.unwrap();
|
|
match response {
|
|
ServiceResponse::SearchService(_) => {
|
|
error!("sent search service response when asking for auth service!! investigate ASAP!!!");
|
|
announcement_error("auth service error", info.username, input_og).into_response()
|
|
}
|
|
ServiceResponse::BingService(_) => {
|
|
error!("sent bing service response when asking for auth service!! investigate ASAP!!!");
|
|
announcement_error("auth service error", info.username, input_og).into_response()
|
|
}
|
|
ServiceResponse::AuthService(r) => match r {
|
|
AuthServiceResponse::AdminCreateAnnouncementResponse(r) => {
|
|
match r {
|
|
AdminCreateAnnouncementResponse::Success => {
|
|
AdminAnnouncementTemplate {
|
|
motd: motd(),
|
|
error: Some("success!".to_string()),
|
|
username: info.username,
|
|
slug: "".to_string(),
|
|
title: "".to_string(),
|
|
short_content: "".to_string(),
|
|
full_content: "".to_string(),
|
|
}.into_response()
|
|
}
|
|
AdminCreateAnnouncementResponse::SlugTaken => {
|
|
announcement_error("slug taken!", info.username, input_og).into_response()
|
|
}
|
|
AdminCreateAnnouncementResponse::InternalServerError(e) => {
|
|
announcement_error(&format!("internal server error: {}", e), info.username, input_og).into_response()
|
|
}
|
|
AdminCreateAnnouncementResponse::Logout => {
|
|
Redirect::to("/").into_response()
|
|
}
|
|
AdminCreateAnnouncementResponse::AccessDenied => {
|
|
Redirect::to("/").into_response()
|
|
}
|
|
}
|
|
}
|
|
x => {
|
|
error!("auth survace gave {} to our create announcement request!", x);
|
|
announcement_error(&format!("auth service gave {x} to our create announcement request!"), info.username, input_og).into_response()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
Redirect::to("/").into_response()
|
|
}
|
|
}
|
|
|
|
#[derive(Template)]
|
|
#[template(path = "admin/allusers.html")]
|
|
pub struct AdminUserListTemplate {
|
|
motd: &'static str,
|
|
|
|
users: Vec<UserSerialized>
|
|
}
|
|
|
|
pub async fn admin_user_list(
|
|
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.clone()).await {
|
|
Ok(i) => i,
|
|
Err(e) => {
|
|
return Redirect::to("/").into_response();
|
|
}
|
|
};
|
|
|
|
if !info.administrator {
|
|
return Redirect::to("/").into_response();
|
|
}
|
|
|
|
match list_all_users(nats.clone(), token).await {
|
|
Ok(mut v) => {
|
|
for v in &mut v {
|
|
if v.username.len() > 32 {
|
|
v.username = format!("{}...", &v.username[0..32]);
|
|
}
|
|
}
|
|
AdminUserListTemplate {
|
|
motd: motd(),
|
|
users: v,
|
|
}.into_response()
|
|
}
|
|
Err(e) => {
|
|
e.into_response()
|
|
}
|
|
}
|
|
} else {
|
|
Redirect::to("/").into_response()
|
|
}
|
|
} |