diff options
Diffstat (limited to 'ui/src/old/admin')
| -rw-r--r-- | ui/src/old/admin/import.rs | 46 | ||||
| -rw-r--r-- | ui/src/old/admin/log.rs | 127 | ||||
| -rw-r--r-- | ui/src/old/admin/mod.rs | 64 | ||||
| -rw-r--r-- | ui/src/old/admin/user.rs | 81 |
4 files changed, 318 insertions, 0 deletions
diff --git a/ui/src/old/admin/import.rs b/ui/src/old/admin/import.rs new file mode 100644 index 0000000..805d787 --- /dev/null +++ b/ui/src/old/admin/import.rs @@ -0,0 +1,46 @@ +/* + 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 crate::{FlashM, Page, locale::tr, scaffold::{FlashDisplay, RenderInfo}}; +use jellycommon::routes::u_admin_import_post; + +impl Page for AdminImportPage<'_> { + fn title(&self) -> String { + "Import".to_string() + } + fn to_render(&self) -> markup::DynRender<'_> { + markup::new!(@self) + } +} + +markup::define!( + AdminImportPage<'a>(ri: &'a RenderInfo<'a>, busy: bool, last_import_err: &'a [String], flash: &'a FlashM) { + @FlashDisplay { flash } + @if *busy { + h1 { @tr(ri.lang, "admin.import.running") } + noscript { "Live import progress needs javascript." } + div[id="admin_import"] {} + } else { + h1 { @tr(ri.lang, "admin.import.title") } + @if !last_import_err.is_empty() { + section.message.error { + details { + summary { p.error { @tr(ri.lang, "admin.import_errors").replace("{n}", &last_import_err.len().to_string()) } } + ol { @for e in *last_import_err { + li.error { pre.error { @e } } + }} + } + } + } + form[method="POST", action=u_admin_import_post(true)] { + input[type="submit", value=tr(ri.lang, "admin.dashboard.import.inc").to_string()]; + } + form[method="POST", action=u_admin_import_post(false)] { + input[type="submit", value=tr(ri.lang, "admin.dashboard.import.full").to_string()]; + } + } + } +); diff --git a/ui/src/old/admin/log.rs b/ui/src/old/admin/log.rs new file mode 100644 index 0000000..637158f --- /dev/null +++ b/ui/src/old/admin/log.rs @@ -0,0 +1,127 @@ +/* + 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 crate::Page; +use jellycommon::routes::u_admin_log; +use markup::raw; +use std::fmt::Write; + +impl Page for ServerLogPage<'_> { + fn title(&self) -> String { + "Server Log".to_string() + } + fn class(&self) -> Option<&'static str> { + Some("admin_log") + } + fn to_render(&self) -> markup::DynRender<'_> { + markup::new!(@self) + } +} + +markup::define! { + ServerLogPage<'a>(warnonly: bool, messages: &'a [String]) { + h1 { "Server Log" } + a[href=u_admin_log(!warnonly)] { @if *warnonly { "Show everything" } else { "Show only warnings" }} + code.log[id="log"] { + table { @for e in *messages { + @raw(e) + }} + } + } + ServerLogLine<'a>(e: &'a LogLine) { + tr[class=format!("level-{}", e.level).to_ascii_lowercase()] { + td.time { @e.time.to_rfc3339() } + td.loglevel { @format_level(e.level) } + td.module { @e.module } + td { @markup::raw(vt100_to_html(&e.message)) } + } + } +} + +pub fn render_log_line(line: &LogLine) -> String { + ServerLogLine { e: line }.to_string() +} + +fn vt100_to_html(s: &str) -> String { + let mut out = HtmlOut::default(); + let mut st = vte::Parser::new(); + st.advance(&mut out, s.as_bytes()); + out.s +} + +fn format_level(level: LogLevel) -> impl markup::Render { + let (s, c) = match level { + LogLevel::Debug => ("DEBUG", "blue"), + LogLevel::Error => ("ERROR", "red"), + LogLevel::Warn => ("WARN", "yellow"), + LogLevel::Info => ("INFO", "green"), + LogLevel::Trace => ("TRACE", "lightblue"), + }; + markup::new! { span[style=format!("color:{c}")] {@s} } +} + +#[derive(Default)] +pub struct HtmlOut { + s: String, + color: bool, +} +impl HtmlOut { + pub fn set_color(&mut self, [r, g, b]: [u8; 3]) { + self.reset_color(); + self.color = true; + write!(self.s, "<span style=color:#{:02x}{:02x}{:02x}>", r, g, b).unwrap() + } + pub fn reset_color(&mut self) { + if self.color { + write!(self.s, "</span>").unwrap(); + self.color = false; + } + } +} +impl vte::Perform for HtmlOut { + fn print(&mut self, c: char) { + match c { + 'a'..='z' | 'A'..='Z' | '0'..='9' | ' ' => self.s.push(c), + x => write!(self.s, "&#{};", x as u32).unwrap(), + } + } + fn execute(&mut self, _byte: u8) {} + fn hook(&mut self, _params: &vte::Params, _i: &[u8], _ignore: bool, _a: char) {} + fn put(&mut self, _byte: u8) {} + fn unhook(&mut self) {} + fn osc_dispatch(&mut self, _params: &[&[u8]], _bell_terminated: bool) {} + fn esc_dispatch(&mut self, _intermediates: &[u8], _ignore: bool, _byte: u8) {} + fn csi_dispatch( + &mut self, + params: &vte::Params, + _intermediates: &[u8], + _ignore: bool, + action: char, + ) { + let mut k = params.iter(); + #[allow(clippy::single_match)] + match action { + 'm' => match k.next().unwrap_or(&[0]).first().unwrap_or(&0) { + c @ (30..=37 | 40..=47) => { + let c = if *c >= 40 { *c - 10 } else { *c }; + self.set_color(match c { + 30 => [0, 0, 0], + 31 => [255, 0, 0], + 32 => [0, 255, 0], + 33 => [255, 255, 0], + 34 => [0, 0, 255], + 35 => [255, 0, 255], + 36 => [0, 255, 255], + 37 => [255, 255, 255], + _ => unreachable!(), + }); + } + _ => (), + }, + _ => (), + } + } +} diff --git a/ui/src/old/admin/mod.rs b/ui/src/old/admin/mod.rs new file mode 100644 index 0000000..f42ba76 --- /dev/null +++ b/ui/src/old/admin/mod.rs @@ -0,0 +1,64 @@ +/* + 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> +*/ + +pub mod import; +pub mod log; +pub mod user; + +use crate::{FlashM, Page, locale::tr, scaffold::{FlashDisplay, RenderInfo}}; +use jellycommon::routes::{ + u_admin_import, u_admin_invite_create, u_admin_invite_remove, u_admin_log, + u_admin_update_search, u_admin_users, +}; + +impl Page for AdminDashboardPage<'_> { + fn title(&self) -> String { + "Admin Dashboard".to_string() + } + fn to_render(&self) -> markup::DynRender<'_> { + markup::new!(@self) + } +} + +markup::define!( + AdminDashboardPage<'a>(ri: &'a RenderInfo<'a>, busy: Option<&'static str>, flash: &'a FlashM, invites: &'a [String]) { + h1 { @tr(ri.lang, "admin.dashboard.title") } + @FlashDisplay { flash } + ul { + li{a[href=u_admin_log(true)] { @tr(ri.lang, "admin.log.warnonly") }} + li{a[href=u_admin_log(false)] { @tr(ri.lang, "admin.log.full") }} + } + + a[href=u_admin_import()] { h2 { @tr(ri.lang, "admin.import.title") }} + @if let Some(text) = busy { + section.message { p.warn { @text } } + } + form[method="POST", action=u_admin_update_search()] { + input[type="submit", value=tr(ri.lang, "admin.dashboard.update_search").to_string()]; + } + h2 { @tr(ri.lang, "admin.dashboard.users") } + p { a[href=u_admin_users()] { @tr(ri.lang, "admin.dashboard.manage_users") } } + h2 { @tr(ri.lang, "admin.dashboard.invites") } + form[method="POST", action=u_admin_invite_create()] { + input[type="submit", value=tr(ri.lang, "admin.dashboard.create_invite").to_string()]; + } + ul { @for t in *invites { + li { + form[method="POST", action=u_admin_invite_remove()] { + span { @t } + input[type="text", name="invite", value=&t, hidden]; + input[type="submit", value=tr(ri.lang, "admin.dashboard.create_invite").to_string()]; + } + } + }} + + // h2 { "Database" } + // @match db_stats(&database) { + // Ok(s) => { @s } + // Err(e) => { pre.error { @format!("{e:?}") } } + // } + } +); diff --git a/ui/src/old/admin/user.rs b/ui/src/old/admin/user.rs new file mode 100644 index 0000000..e4a8975 --- /dev/null +++ b/ui/src/old/admin/user.rs @@ -0,0 +1,81 @@ +/* + 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 crate::{FlashM, Page, scaffold::FlashDisplay}; +use jellycommon::routes::{ + u_admin_user, u_admin_user_permission, u_admin_user_remove, u_admin_users, +}; + +impl Page for AdminUserPage<'_> { + fn title(&self) -> String { + "User Management".to_string() + } + fn to_render(&self) -> markup::DynRender<'_> { + markup::new!(@self) + } +} +impl Page for AdminUsersPage<'_> { + fn title(&self) -> String { + "User Management".to_string() + } + fn to_render(&self) -> markup::DynRender<'_> { + markup::new!(@self) + } +} + +markup::define! { + AdminUsersPage<'a>(lang: &'a Language, users: &'a [User], flash: &'a FlashM) { + h1 { @trs(lang, "admin.users.title") } + @FlashDisplay { flash } + h2 { @trs(lang, "admin.users.user_list") } + ul { @for u in *users { + li { + a[href=u_admin_user(&u.name)] { @format!("{:?}", u.display_name) " (" @u.name ")" } + } + }} + } + AdminUserPage<'a>(lang: &'a Language, user: &'a User, flash: &'a FlashM) { + h1 { @format!("{:?}", user.display_name) " (" @user.name ")" } + a[href=u_admin_users()] { @trs(lang, "admin.users.return_to_list") } + @FlashDisplay { flash } + form[method="POST", action=u_admin_user_remove(&user.name)] { + // input[type="text", name="name", value=&user.name, hidden]; + input.danger[type="submit", value="Remove user(!)"]; + } + + h2 { "Permissions" } + @PermissionDisplay { perms: &user.permissions } + + form[method="POST", action=u_admin_user_permission(&user.name)] { + // input[type="text", name="name", value=&user.name, hidden]; + fieldset.perms { + legend { "Permission" } + @for p in UserPermission::ALL_ENUMERABLE { + label { + input[type="radio", name="permission", value=serde_json::to_string(p).unwrap()]; + @format!("{p}") + } br; + } + } + fieldset.perms { + legend { "State" } + label { input[type="radio", name="action", value="unset"]; "Unset" } br; + label { input[type="radio", name="action", value="grant"]; "Grant" } br; + label { input[type="radio", name="action", value="revoke"]; "Revoke" } br; + } + input[type="submit", value="Update"]; + } + } + PermissionDisplay<'a>(perms: &'a PermissionSet) { + ul { @for (perm,grant) in &perms.0 { + @if *grant { + li[class="perm-grant"] { @format!("Allow {}", perm) } + } else { + li[class="perm-revoke"] { @format!("Deny {}", perm) } + } + }} + } +} |