aboutsummaryrefslogtreecommitdiff
path: root/ui/src/old/admin
diff options
context:
space:
mode:
Diffstat (limited to 'ui/src/old/admin')
-rw-r--r--ui/src/old/admin/import.rs46
-rw-r--r--ui/src/old/admin/log.rs127
-rw-r--r--ui/src/old/admin/mod.rs64
-rw-r--r--ui/src/old/admin/user.rs81
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) }
+ }
+ }}
+ }
+}