From e3d3418f99ec2fc2423575c2fb6ade165b481bae Mon Sep 17 00:00:00 2001 From: jude Date: Thu, 5 Oct 2023 18:54:53 +0100 Subject: [PATCH] Change routing. Remove a macro --- web/src/guards/transaction.rs | 8 ++-- web/src/lib.rs | 72 +++++++++++++++++++++++++++++- web/src/macros.rs | 50 --------------------- web/src/routes/dashboard/export.rs | 13 +++--- web/src/routes/dashboard/guild.rs | 46 ++++++++++--------- web/src/routes/dashboard/mod.rs | 2 +- web/static/js/main.js | 32 +++++++++---- web/templates/dashboard.html.tera | 2 +- 8 files changed, 132 insertions(+), 93 deletions(-) diff --git a/web/src/guards/transaction.rs b/web/src/guards/transaction.rs index 91061e9..e50890f 100644 --- a/web/src/guards/transaction.rs +++ b/web/src/guards/transaction.rs @@ -7,20 +7,20 @@ use sqlx::Pool; use crate::Database; -pub(crate) struct Transaction<'a>(sqlx::Transaction<'a, Database>); +pub struct Transaction<'a>(sqlx::Transaction<'a, Database>); impl Transaction<'_> { - pub(crate) fn executor(&mut self) -> impl sqlx::Executor<'_, Database = Database> { + pub fn executor(&mut self) -> impl sqlx::Executor<'_, Database = Database> { &mut *(self.0) } - pub(crate) async fn commit(self) -> Result<(), sqlx::Error> { + pub async fn commit(self) -> Result<(), sqlx::Error> { self.0.commit().await } } #[derive(Debug)] -pub(crate) enum TransactionError { +pub enum TransactionError { Error(sqlx::Error), Missing, } diff --git a/web/src/lib.rs b/web/src/lib.rs index 85ac791..c424f07 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -11,7 +11,12 @@ mod routes; use std::{env, path::Path}; use oauth2::{basic::BasicClient, AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl}; -use rocket::{fs::FileServer, serde::json::Value as JsonValue, tokio::sync::broadcast::Sender}; +use rocket::{ + fs::FileServer, + http::CookieJar, + serde::json::{json, Value as JsonValue}, + tokio::sync::broadcast::Sender, +}; use rocket_dyn_templates::Template; use serenity::{ client::Context, @@ -186,3 +191,68 @@ pub async fn check_guild_subscription( false } } + +pub async fn check_authorization( + cookies: &CookieJar<'_>, + ctx: &Context, + guild: u64, +) -> Result<(), JsonValue> { + let user_id = cookies.get_private("userid").map(|c| c.value().parse::().ok()).flatten(); + + if std::env::var("OFFLINE").map_or(true, |v| v != "1") { + match user_id { + Some(user_id) => { + println!("{:?}", std::env::var("ADMIN_ID")); + println!("{:?}", user_id); + + let admin_id = std::env::var("ADMIN_ID") + .map_or(false, |u| u.parse::().map_or(false, |u| u == user_id)); + + if admin_id { + return Ok(()); + } + + match GuildId(guild).to_guild_cached(ctx) { + Some(guild) => { + let member_res = guild.member(ctx, UserId(user_id)).await; + + match member_res { + Err(_) => { + return Err(json!({"error": "User not in guild"})); + } + + Ok(member) => { + let permissions_res = member.permissions(ctx); + + match permissions_res { + Err(_) => { + return Err(json!({"error": "Couldn't fetch permissions"})); + } + + Ok(permissions) => { + if !(permissions.manage_messages() + || permissions.manage_guild() + || permissions.administrator()) + { + return Err(json!({"error": "Incorrect permissions"})); + } + } + } + } + } + } + + None => { + return Err(json!({"error": "Bot not in guild"})); + } + } + } + + None => { + return Err(json!({"error": "User not authorized"})); + } + } + } + + Ok(()) +} diff --git a/web/src/macros.rs b/web/src/macros.rs index aed2dcf..8c91f30 100644 --- a/web/src/macros.rs +++ b/web/src/macros.rs @@ -54,56 +54,6 @@ macro_rules! check_url_opt { }; } -macro_rules! check_authorization { - ($cookies:expr, $ctx:expr, $guild:expr) => { - use serenity::model::id::UserId; - - let user_id = $cookies.get_private("userid").map(|c| c.value().parse::().ok()).flatten(); - - if std::env::var("OFFLINE").map_or(true, |v| v != "1") { - match user_id { - Some(user_id) => { - match GuildId($guild).to_guild_cached($ctx) { - Some(guild) => { - let member_res = guild.member($ctx, UserId(user_id)).await; - - match member_res { - Err(_) => { - return Err(json!({"error": "User not in guild"})); - } - - Ok(member) => { - let permissions_res = member.permissions($ctx); - - match permissions_res { - Err(_) => { - return Err(json!({"error": "Couldn't fetch permissions"})); - } - - Ok(permissions) => { - if !(permissions.manage_messages() || permissions.manage_guild() || permissions.administrator()) { - return Err(json!({"error": "Incorrect permissions"})); - } - } - } - } - } - } - - None => { - return Err(json!({"error": "Bot not in guild"})); - } - } - } - - None => { - return Err(json!({"error": "User not authorized"})); - } - } - } - } -} - macro_rules! update_field { ($pool:expr, $error:ident, $reminder:ident.[$field:ident]) => { if let Some(value) = &$reminder.$field { diff --git a/web/src/routes/dashboard/export.rs b/web/src/routes/dashboard/export.rs index 44a4cd3..3a9a7cf 100644 --- a/web/src/routes/dashboard/export.rs +++ b/web/src/routes/dashboard/export.rs @@ -6,11 +6,12 @@ use rocket::{ }; use serenity::{ client::Context, - model::id::{ChannelId, GuildId}, + model::id::{ChannelId, GuildId, UserId}, }; use sqlx::{MySql, Pool}; use crate::{ + check_authorization, guards::transaction::Transaction, routes::{ dashboard::{ @@ -28,7 +29,7 @@ pub async fn export_reminders( ctx: &State, pool: &State>, ) -> JsonResult { - check_authorization!(cookies, ctx.inner(), id); + check_authorization(cookies, ctx.inner(), id).await?; let mut csv_writer = WriterBuilder::new().quote_style(QuoteStyle::Always).from_writer(vec![]); @@ -128,7 +129,7 @@ pub(crate) async fn import_reminders( ctx: &State, mut transaction: Transaction<'_>, ) -> JsonResult { - check_authorization!(cookies, ctx.inner(), id); + check_authorization(cookies, ctx.inner(), id).await?; let user_id = cookies.get_private("userid").map(|c| c.value().parse::().ok()).flatten().unwrap(); @@ -231,7 +232,7 @@ pub async fn export_todos( ctx: &State, pool: &State>, ) -> JsonResult { - check_authorization!(cookies, ctx.inner(), id); + check_authorization(cookies, ctx.inner(), id).await?; let mut csv_writer = WriterBuilder::new().quote_style(QuoteStyle::Always).from_writer(vec![]); @@ -286,7 +287,7 @@ pub async fn import_todos( ctx: &State, pool: &State>, ) -> JsonResult { - check_authorization!(cookies, ctx.inner(), id); + check_authorization(cookies, ctx.inner(), id).await?; let channels_res = GuildId(id).channels(&ctx.inner()).await; @@ -381,7 +382,7 @@ pub async fn export_reminder_templates( ctx: &State, pool: &State>, ) -> JsonResult { - check_authorization!(cookies, ctx.inner(), id); + check_authorization(cookies, ctx.inner(), id).await?; let mut csv_writer = WriterBuilder::new().quote_style(QuoteStyle::Always).from_writer(vec![]); diff --git a/web/src/routes/dashboard/guild.rs b/web/src/routes/dashboard/guild.rs index 776c6bc..fe640ae 100644 --- a/web/src/routes/dashboard/guild.rs +++ b/web/src/routes/dashboard/guild.rs @@ -10,13 +10,13 @@ use serenity::{ client::Context, model::{ channel::GuildChannel, - id::{ChannelId, GuildId, RoleId}, + id::{ChannelId, GuildId, RoleId, UserId}, }, }; use sqlx::{MySql, Pool}; use crate::{ - check_guild_subscription, check_subscription, + check_authorization, check_guild_subscription, check_subscription, consts::{ MAX_CONTENT_LENGTH, MAX_EMBED_AUTHOR_LENGTH, MAX_EMBED_DESCRIPTION_LENGTH, MAX_EMBED_FIELDS, MAX_EMBED_FIELD_TITLE_LENGTH, MAX_EMBED_FIELD_VALUE_LENGTH, @@ -49,7 +49,7 @@ pub async fn get_guild_patreon( ctx: &State, ) -> JsonResult { offline!(Ok(json!({ "patreon": true }))); - check_authorization!(cookies, ctx.inner(), id); + check_authorization(cookies, ctx.inner(), id).await?; match GuildId(id).to_guild_cached(ctx.inner()) { Some(guild) => { @@ -82,7 +82,7 @@ pub async fn get_guild_channels( webhook_avatar: None, webhook_name: None, }]))); - check_authorization!(cookies, ctx.inner(), id); + check_authorization(cookies, ctx.inner(), id).await?; match GuildId(id).to_guild_cached(ctx.inner()) { Some(guild) => { @@ -121,7 +121,7 @@ struct RoleInfo { #[get("/api/guild//roles")] pub async fn get_guild_roles(id: u64, cookies: &CookieJar<'_>, ctx: &State) -> JsonResult { offline!(Ok(json!(vec![RoleInfo { name: "@everyone".to_string(), id: "1".to_string() }]))); - check_authorization!(cookies, ctx.inner(), id); + check_authorization(cookies, ctx.inner(), id).await?; let roles_res = ctx.cache.guild_roles(id); @@ -149,7 +149,7 @@ pub async fn get_reminder_templates( ctx: &State, pool: &State>, ) -> JsonResult { - check_authorization!(cookies, ctx.inner(), id); + check_authorization(cookies, ctx.inner(), id).await?; match sqlx::query_as_unchecked!( ReminderTemplate, @@ -176,7 +176,7 @@ pub async fn create_reminder_template( ctx: &State, pool: &State>, ) -> JsonResult { - check_authorization!(cookies, ctx.inner(), id); + check_authorization(cookies, ctx.inner(), id).await?; // validate lengths check_length!(MAX_CONTENT_LENGTH, reminder_template.content); @@ -283,7 +283,7 @@ pub async fn delete_reminder_template( ctx: &State, pool: &State>, ) -> JsonResult { - check_authorization!(cookies, ctx.inner(), id); + check_authorization(cookies, ctx.inner(), id).await?; match sqlx::query!( "DELETE FROM reminder_template WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?) AND id = ?", @@ -304,20 +304,20 @@ pub async fn delete_reminder_template( } #[post("/api/guild//reminders", data = "")] -pub(crate) async fn create_guild_reminder( +pub async fn create_guild_reminder( id: u64, reminder: Json, cookies: &CookieJar<'_>, - serenity_context: &State, + ctx: &State, mut transaction: Transaction<'_>, ) -> JsonResult { - check_authorization!(cookies, serenity_context.inner(), id); + check_authorization(cookies, ctx.inner(), id).await?; let user_id = cookies.get_private("userid").map(|c| c.value().parse::().ok()).flatten().unwrap(); match create_reminder( - serenity_context.inner(), + ctx.inner(), &mut transaction, GuildId(id), UserId(user_id), @@ -342,10 +342,9 @@ pub async fn get_reminders( id: u64, cookies: &CookieJar<'_>, ctx: &State, - serenity_context: &State, pool: &State>, ) -> JsonResult { - check_authorization!(cookies, serenity_context.inner(), id); + check_authorization(cookies, ctx.inner(), id).await?; let channels_res = GuildId(id).channels(&ctx.inner()).await; @@ -413,12 +412,12 @@ pub async fn get_reminders( pub(crate) async fn edit_reminder( id: u64, reminder: Json, - serenity_context: &State, + ctx: &State, mut transaction: Transaction<'_>, pool: &State>, cookies: &CookieJar<'_>, ) -> JsonResult { - check_authorization!(cookies, serenity_context.inner(), id); + check_authorization(cookies, ctx.inner(), id).await?; let mut error = vec![]; @@ -460,8 +459,8 @@ pub(crate) async fn edit_reminder( || reminder.interval_months.flatten().is_some() || reminder.interval_seconds.flatten().is_some() { - if check_guild_subscription(&serenity_context.inner(), id).await - || check_subscription(&serenity_context.inner(), user_id).await + if check_guild_subscription(&ctx.inner(), id).await + || check_subscription(&ctx.inner(), user_id).await { let new_interval_length = match reminder.interval_days { Some(interval) => interval.unwrap_or(0), @@ -520,7 +519,7 @@ pub(crate) async fn edit_reminder( } if reminder.channel > 0 { - let channel = ChannelId(reminder.channel).to_channel_cached(&serenity_context.inner()); + let channel = ChannelId(reminder.channel).to_channel_cached(&ctx.inner()); match channel { Some(channel) => { let channel_matches_guild = channel.guild().map_or(false, |c| c.guild_id.0 == id); @@ -535,7 +534,7 @@ pub(crate) async fn edit_reminder( } let channel = create_database_channel( - serenity_context.inner(), + ctx.inner(), ChannelId(reminder.channel), &mut transaction, ) @@ -630,11 +629,16 @@ pub(crate) async fn edit_reminder( } } -#[delete("/api/guild/<_>/reminders", data = "")] +#[delete("/api/guild//reminders", data = "")] pub async fn delete_reminder( + cookies: &CookieJar<'_>, + id: u64, reminder: Json, + ctx: &State, pool: &State>, ) -> JsonResult { + check_authorization(cookies, ctx.inner(), id).await?; + match sqlx::query!("UPDATE reminders SET `status` = 'deleted' WHERE uid = ?", reminder.uid) .execute(pool.inner()) .await diff --git a/web/src/routes/dashboard/mod.rs b/web/src/routes/dashboard/mod.rs index 46ff4e0..f0add8a 100644 --- a/web/src/routes/dashboard/mod.rs +++ b/web/src/routes/dashboard/mod.rs @@ -668,7 +668,7 @@ pub async fn dashboard_home(cookies: &CookieJar<'_>) -> Result")] +#[get("/<_..>")] pub async fn dashboard(cookies: &CookieJar<'_>) -> Result { if cookies.get_private("userid").is_some() { let map: HashMap<&str, String> = HashMap::new(); diff --git a/web/static/js/main.js b/web/static/js/main.js index 3238fbb..ce1410c 100644 --- a/web/static/js/main.js +++ b/web/static/js/main.js @@ -33,7 +33,16 @@ let globalPatreon = false; let guildPatreon = false; function guildId() { - return document.querySelector(".guildList a.is-active").dataset["guild"]; + return window.location.pathname.match(/dashboard\/(\d+)/)[1]; +} + +function pane() { + const match = window.location.pathname.match(/dashboard\/\d+\/(.+)/); + if (match === null) { + return null; + } else { + return match[1]; + } } function colorToInt(r, g, b) { @@ -454,15 +463,16 @@ document.addEventListener("guildSwitched", async (e) => { let hasError = false; - if ($anchor === null) { - switch_pane("user-error"); - hasError = true; - return; + if ($anchor !== null) { + $anchor.classList.add("is-active"); } - switch_pane($anchor.dataset["pane"]); + if (pane() === null) { + window.history.replaceState({}, "", `/dashboard/${guildId()}/reminders`); + } + + switch_pane(pane()); reset_guild_pane(); - $anchor.classList.add("is-active"); if (globalPatreon || (await fetch_patreon(e.detail.guild_id))) { document @@ -695,11 +705,15 @@ document.addEventListener("DOMContentLoaded", async () => { ); $anchor.dataset["guild"] = guild.id; $anchor.dataset["name"] = guild.name; - $anchor.href = `/dashboard/${guild.id}?name=${guild.name}`; + $anchor.href = `/dashboard/${guild.id}/reminders`; $anchor.addEventListener("click", async (e) => { e.preventDefault(); - window.history.pushState({}, "", `/dashboard/${guild.id}`); + window.history.pushState( + {}, + "", + `/dashboard/${guild.id}/reminders` + ); const event = new CustomEvent("guildSwitched", { detail: { guild_name: guild.name, diff --git a/web/templates/dashboard.html.tera b/web/templates/dashboard.html.tera index ee35c0d..6d078cb 100644 --- a/web/templates/dashboard.html.tera +++ b/web/templates/dashboard.html.tera @@ -325,7 +325,7 @@

Press the to get started

-