aboutsummaryrefslogtreecommitdiff
path: root/server/src/routes/ui/admin/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/routes/ui/admin/mod.rs')
-rw-r--r--server/src/routes/ui/admin/mod.rs154
1 files changed, 154 insertions, 0 deletions
diff --git a/server/src/routes/ui/admin/mod.rs b/server/src/routes/ui/admin/mod.rs
new file mode 100644
index 0000000..0775423
--- /dev/null
+++ b/server/src/routes/ui/admin/mod.rs
@@ -0,0 +1,154 @@
+/*
+ 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) 2023 metamuffin <metamuffin.org>
+*/
+use crate::{
+ database::Database,
+ federation::Federation,
+ import::import,
+ routes::ui::{
+ account::session::Session,
+ error::MyResult,
+ layout::{DynLayoutPage, FlashDisplay, LayoutPage},
+ },
+ uri,
+};
+use anyhow::anyhow;
+use rand::Rng;
+use rocket::{form::Form, get, post, FromForm, State};
+use std::time::Instant;
+
+#[get("/admin/dashboard")]
+pub fn r_admin_dashboard(
+ session: Session,
+ database: &State<Database>,
+) -> MyResult<DynLayoutPage<'static>> {
+ if !session.user.admin {
+ Err(anyhow!("you not admin"))?
+ }
+ admin_dashboard(database, None)
+}
+
+pub fn admin_dashboard<'a>(
+ database: &Database,
+ flash: Option<MyResult<String>>,
+) -> MyResult<DynLayoutPage<'a>> {
+ // TODO this doesnt scale, pagination!
+ let users = database.user.iter().collect::<Result<Vec<_>, _>>()?;
+ let invites = database.invite.iter().collect::<Result<Vec<_>, _>>()?;
+ let flash = flash.map(|f| f.map_err(|e| format!("{e:?}")));
+
+ Ok(LayoutPage {
+ title: "Admin Dashboard".to_string(),
+ content: markup::new! {
+ h1 { "Admin Panel" }
+ @FlashDisplay { flash: flash.clone() }
+ h2 { "Library" }
+ form[method="POST", action=uri!(r_admin_import())] {
+ input[type="submit", value="(Re-)Import Library"];
+ }
+ h2 { "Invitations" }
+ form[method="POST", action=uri!(r_admin_invite())] {
+ input[type="submit", value="Generate new invite code"];
+ }
+ ul { @for t in &invites {
+ li {
+ form[method="POST", action=uri!(r_admin_remove_invite())] {
+ span { @t.0 }
+ input[type="text", name="invite", value=&t.0, hidden];
+ input[type="submit", value="Invalidate"];
+ }
+ }
+ }}
+ h2 { "Users" }
+ ul { @for (_, u) in &users {
+ li { form[method="POST", action=uri!(r_admin_remove_user())] {
+ span { @format!("{:?}", u.display_name) " (" @u.name ")" }
+ input[type="text", name="name", value=&u.name, hidden];
+ input[type="submit", value="Remove(!)"];
+ }}
+ }}
+ },
+ ..Default::default()
+ })
+}
+
+#[post("/admin/generate_invite")]
+pub fn r_admin_invite(
+ session: Session,
+ database: &State<Database>,
+) -> MyResult<DynLayoutPage<'static>> {
+ if !session.user.admin {
+ Err(anyhow!("you not admin"))?
+ }
+
+ let i = format!("{}", rand::thread_rng().gen::<u128>());
+ database.invite.insert(&i, &())?;
+
+ admin_dashboard(database, Some(Ok(format!("Invite: {}", i))))
+}
+
+#[derive(FromForm)]
+pub struct DeleteUser {
+ name: String,
+}
+
+#[post("/admin/remove_user", data = "<form>")]
+pub fn r_admin_remove_user(
+ session: Session,
+ database: &State<Database>,
+ form: Form<DeleteUser>,
+) -> MyResult<DynLayoutPage<'static>> {
+ if !session.user.admin {
+ Err(anyhow!("you not admin"))?
+ }
+ database
+ .user
+ .remove(&form.name)?
+ .ok_or(anyhow!("user did not exist"))?;
+
+ admin_dashboard(database, Some(Ok("User removed".into())))
+}
+
+#[derive(FromForm)]
+pub struct DeleteInvite {
+ invite: String,
+}
+
+#[post("/admin/remove_invite", data = "<form>")]
+pub fn r_admin_remove_invite(
+ session: Session,
+ database: &State<Database>,
+ form: Form<DeleteInvite>,
+) -> MyResult<DynLayoutPage<'static>> {
+ if !session.user.admin {
+ Err(anyhow!("you not admin"))?
+ }
+ database
+ .invite
+ .remove(&form.invite)?
+ .ok_or(anyhow!("invite did not exist"))?;
+
+ admin_dashboard(database, Some(Ok("Invite invalidated".into())))
+}
+
+#[post("/admin/import")]
+pub async fn r_admin_import(
+ session: Session,
+ database: &State<Database>,
+ federation: &State<Federation>,
+) -> MyResult<DynLayoutPage<'static>> {
+ if !session.user.admin {
+ Err(anyhow!("you not admin"))?
+ }
+ let t = Instant::now();
+ let r = import(&database, &federation).await;
+ admin_dashboard(
+ &database,
+ Some(
+ r.map_err(|e| e.into())
+ .map(|_| format!("Import successful; took {:?}", t.elapsed())),
+ ),
+ )
+}