1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
/*
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 <metamuffin.org>
*/
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<UiResponse> {
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::<Vec<_>>();
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 = "<form>")]
pub fn r_admin_new_user(ri: RequestInfo, form: Form<NewUser>) -> MyResult<Flash<Redirect>> {
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/<name>")]
pub fn r_admin_user(ri: RequestInfo<'_>, name: &str) -> MyResult<UiResponse> {
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/<name>/remove")]
pub fn r_admin_user_remove(ri: RequestInfo<'_>, name: &str) -> MyResult<Flash<Redirect>> {
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"),
))
}
|