/* This file is part of jellything (https://codeberg.org/metamuffin/jellything) which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2026 metamuffin */ use std::str::FromStr; use base64::{Engine, prelude::BASE64_URL_SAFE}; use jellycommon::{ jellyobject::{OBB, ObjectBufferBuilder, Path}, routes::u_admin_users, *, }; use jellydb::{Filter, Query, Sort}; use jellyui::tr; use rand::random; use rocket::{ FromForm, form::Form, get, post, response::{Flash, Redirect}, }; use crate::{ auth::hash_password, request_info::RequestInfo, ui::error::MyResult, ui_responder::UiResponse, }; #[get("/admin/users")] pub fn r_admin_users(ri: RequestInfo) -> MyResult { ri.require_admin()?; let mut users = Vec::new(); ri.state.database.transaction(&mut |txn| { users.clear(); let rows = txn .query(Query::from_str("FILTER Ulgn")?)? .collect::>(); for row in rows { let (row, _) = row?; users.push(txn.get(row)?.unwrap()); } Ok(()) })?; let mut list = ObjectBufferBuilder::default(); for u in users { list.push(ADMIN_USER_LIST_ITEM, u.as_object()); } let mut page = ObjectBufferBuilder::default(); page.push(VIEW_TITLE, &*tr(ri.lang, "admin.users")); page.push(VIEW_ADMIN_USER_LIST, list.finish().as_object()); Ok(ri.respond_ui(page)) } #[derive(FromForm)] pub struct NewUser { login: String, } #[post("/admin/new_user", data = "
")] pub fn r_admin_new_user(ri: RequestInfo, form: Form) -> MyResult> { ri.require_admin()?; let password = BASE64_URL_SAFE.encode([(); 12].map(|()| random())); let password_hashed = hash_password(&form.login, &password); ri.state.database.transaction(&mut |txn| { let mut user = ObjectBufferBuilder::default(); user.push(USER_LOGIN, &form.login); user.push(USER_PASSWORD, &password_hashed); user.push(USER_PASSWORD_REQUIRE_CHANGE, ()); txn.insert(user.finish())?; Ok(()) })?; Ok(Flash::new( Redirect::to(u_admin_users()), "success", format!("User created; password: {password}"), )) } #[get("/admin/user/")] pub fn r_admin_user(ri: RequestInfo<'_>, name: &str) -> MyResult { ri.require_admin()?; let mut page = OBB::new(); ri.state.database.transaction(&mut |txn| { if let Some(row) = txn.query_single(Query { sort: Sort::None, filter: Filter::Match(Path(vec![USER_LOGIN.0]), name.into()), })? { let user = txn.get(row)?.unwrap(); page = OBB::new(); page.push(VIEW_ADMIN_USER, user.as_object()); } Ok(()) })?; Ok(ri.respond_ui(page)) } #[post("/admin/user//remove")] pub fn r_admin_user_remove(ri: RequestInfo<'_>, name: &str) -> MyResult> { ri.require_admin()?; ri.state.database.transaction(&mut |txn| { if let Some(row) = txn.query_single(Query { sort: Sort::None, filter: Filter::Match(Path(vec![USER_LOGIN.0]), name.into()), })? { txn.remove(row)?; } Ok(()) })?; Ok(Flash::success( Redirect::to(u_admin_users()), tr(ri.lang, "admin.users.remove_success"), )) }