From 67a4db2e9a27cb2827bc23a139060372163511c3 Mon Sep 17 00:00:00 2001 From: jude Date: Sun, 11 Dec 2022 10:09:26 +0000 Subject: [PATCH] Ensure interval updating is performed properly Validate patreon status. Validate interval length against minimum. Update the reminder pane to reflect changes that were made. Properly deserialize. --- web/src/routes/dashboard/guild.rs | 74 +++++++++++++++++++++++++++++-- web/src/routes/dashboard/mod.rs | 22 ++++++++- web/static/js/main.js | 5 +++ 3 files changed, 97 insertions(+), 4 deletions(-) diff --git a/web/src/routes/dashboard/guild.rs b/web/src/routes/dashboard/guild.rs index c8c6660..aac433e 100644 --- a/web/src/routes/dashboard/guild.rs +++ b/web/src/routes/dashboard/guild.rs @@ -16,10 +16,12 @@ use serenity::{ use sqlx::{MySql, Pool}; use crate::{ + 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, MAX_EMBED_FOOTER_LENGTH, MAX_EMBED_TITLE_LENGTH, MAX_URL_LENGTH, MAX_USERNAME_LENGTH, + MIN_INTERVAL, }, routes::dashboard::{ create_database_channel, create_reminder, template_name_default, DeleteReminder, @@ -375,9 +377,15 @@ pub async fn edit_reminder( reminder: Json, serenity_context: &State, pool: &State>, + cookies: &CookieJar<'_>, ) -> JsonResult { + check_authorization!(cookies, serenity_context.inner(), id); + let mut error = vec![]; + let user_id = + cookies.get_private("userid").map(|c| c.value().parse::().ok()).flatten().unwrap(); + update_field!(pool.inner(), error, reminder.[ attachment, attachment_name, @@ -395,9 +403,6 @@ pub async fn edit_reminder( embed_fields, enabled, expires, - interval_seconds, - interval_days, - interval_months, name, restartable, tts, @@ -405,6 +410,69 @@ pub async fn edit_reminder( utc_time ]); + if reminder.interval_days.is_some() + || reminder.interval_months.is_some() + || reminder.interval_seconds.is_some() + { + if check_guild_subscription(&serenity_context.inner(), id).await + || check_subscription(&serenity_context.inner(), user_id).await + { + let new_interval_length = match reminder.interval_days { + Some(interval) => interval.unwrap_or(0), + None => sqlx::query!( + "SELECT interval_days AS days FROM reminders WHERE uid = ?", + reminder.uid + ) + .fetch_one(pool.inner()) + .await + .map_err(|e| { + warn!("Error updating reminder interval: {:?}", e); + json!({ "reminder": Option::::None, "errors": vec!["Unknown error"] }) + })? + .days + .unwrap_or(0), + } + match reminder.interval_months { + Some(interval) => interval.unwrap_or(0), + None => sqlx::query!( + "SELECT interval_months AS months FROM reminders WHERE uid = ?", + reminder.uid + ) + .fetch_one(pool.inner()) + .await + .map_err(|e| { + warn!("Error updating reminder interval: {:?}", e); + json!({ "reminder": Option::::None, "errors": vec!["Unknown error"] }) + })? + .months + .unwrap_or(0), + } + match reminder.interval_seconds { + Some(interval) => interval.unwrap_or(0), + None => sqlx::query!( + "SELECT interval_seconds AS seconds FROM reminders WHERE uid = ?", + reminder.uid + ) + .fetch_one(pool.inner()) + .await + .map_err(|e| { + warn!("Error updating reminder interval: {:?}", e); + json!({ "reminder": Option::::None, "errors": vec!["Unknown error"] }) + })? + .seconds + .unwrap_or(0), + }; + + if new_interval_length < *MIN_INTERVAL { + error.push(String::from("New interval is too short.")); + } else { + update_field!(pool.inner(), error, reminder.[ + interval_days, + interval_months, + interval_seconds + ]); + } + } + } + if reminder.channel > 0 { let channel = ChannelId(reminder.channel).to_channel_cached(&serenity_context.inner()); match channel { diff --git a/web/src/routes/dashboard/mod.rs b/web/src/routes/dashboard/mod.rs index c6b0f07..f0724e2 100644 --- a/web/src/routes/dashboard/mod.rs +++ b/web/src/routes/dashboard/mod.rs @@ -8,7 +8,7 @@ use rocket::{ serde::json::{json, Value as JsonValue}, }; use rocket_dyn_templates::Template; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use serenity::{ client::Context, http::Http, @@ -50,6 +50,14 @@ fn id_default() -> u32 { 0 } +fn deserialize_optional_field<'de, T, D>(deserializer: D) -> Result>, D::Error> +where + D: Deserializer<'de>, + T: Deserialize<'de>, +{ + Ok(Some(Option::deserialize(deserializer)?)) +} + #[derive(Serialize, Deserialize)] pub struct ReminderTemplate { #[serde(default = "id_default")] @@ -179,10 +187,13 @@ pub struct ReminderCsv { pub struct PatchReminder { uid: String, #[serde(default)] + #[serde(deserialize_with = "deserialize_optional_field")] attachment: Unset>, #[serde(default)] + #[serde(deserialize_with = "deserialize_optional_field")] attachment_name: Unset>, #[serde(default)] + #[serde(deserialize_with = "deserialize_optional_field")] avatar: Unset>, #[serde(default = "channel_default")] #[serde(with = "string")] @@ -192,6 +203,7 @@ pub struct PatchReminder { #[serde(default)] embed_author: Unset, #[serde(default)] + #[serde(deserialize_with = "deserialize_optional_field")] embed_author_url: Unset>, #[serde(default)] embed_color: Unset, @@ -200,10 +212,13 @@ pub struct PatchReminder { #[serde(default)] embed_footer: Unset, #[serde(default)] + #[serde(deserialize_with = "deserialize_optional_field")] embed_footer_url: Unset>, #[serde(default)] + #[serde(deserialize_with = "deserialize_optional_field")] embed_image_url: Unset>, #[serde(default)] + #[serde(deserialize_with = "deserialize_optional_field")] embed_thumbnail_url: Unset>, #[serde(default)] embed_title: Unset, @@ -212,12 +227,16 @@ pub struct PatchReminder { #[serde(default)] enabled: Unset, #[serde(default)] + #[serde(deserialize_with = "deserialize_optional_field")] expires: Unset>, #[serde(default)] + #[serde(deserialize_with = "deserialize_optional_field")] interval_seconds: Unset>, #[serde(default)] + #[serde(deserialize_with = "deserialize_optional_field")] interval_days: Unset>, #[serde(default)] + #[serde(deserialize_with = "deserialize_optional_field")] interval_months: Unset>, #[serde(default)] name: Unset, @@ -226,6 +245,7 @@ pub struct PatchReminder { #[serde(default)] tts: Unset, #[serde(default)] + #[serde(deserialize_with = "deserialize_optional_field")] username: Unset>, #[serde(default)] utc_time: Unset, diff --git a/web/static/js/main.js b/web/static/js/main.js index c403ccd..b795b3a 100644 --- a/web/static/js/main.js +++ b/web/static/js/main.js @@ -332,6 +332,9 @@ function deserialize_reminder(reminder, frame, mode) { // populate channels set_channels(frame.querySelector("select.channel-selector")); + frame.querySelector(`*[name="interval_hours"]`).value = 0; + frame.querySelector(`*[name="interval_minutes"]`).value = 0; + // populate majority of items for (let prop in reminder) { if (reminder.hasOwnProperty(prop) && reminder[prop] !== null) { @@ -498,6 +501,8 @@ document.addEventListener("remindersLoaded", (event) => { .then((response) => response.json()) .then((data) => { for (let error of data.errors) show_error(error); + + deserialize_reminder(data.reminder, node, "reload"); }); $saveBtn.querySelector("span.icon > i").classList = ["fas fa-check"];