diff --git a/migrations/20230812111348_orphan_reminders.sql b/migrations/20230812111348_orphan_reminders.sql index a2f7c19..00bf1a7 100644 --- a/migrations/20230812111348_orphan_reminders.sql +++ b/migrations/20230812111348_orphan_reminders.sql @@ -1,9 +1,8 @@ -- Drop existing constraint --- TODO -ALTER TABLE `reminders` DROP CONSTRAINT `channel_id_` +ALTER TABLE `reminders` DROP CONSTRAINT `reminders_ibfk_1`; -ALTER TABLE `reminders` MODIFY COLUMN `channel_id` BIGINT ; -ALTER TABLE `reminders` ADD COLUMN `guild_id` BIGINT; +ALTER TABLE `reminders` MODIFY COLUMN `channel_id` INT UNSIGNED; +ALTER TABLE `reminders` ADD COLUMN `guild_id` INT UNSIGNED; ALTER TABLE `reminders` ADD CONSTRAINT `guild_id_fk` @@ -17,5 +16,4 @@ ALTER TABLE `reminders` REFERENCES `channels`(`id`) ON DELETE SET NULL; --- TODO -UPDATE `reminders` SET `guild_id` = (SELECT guilds.`id` FROM `channels` INNER JOIN `guilds` ON channels.guild_id = guilds.id WHERE ) +UPDATE `reminders` SET `guild_id` = (SELECT guilds.`id` FROM `channels` INNER JOIN `guilds` ON channels.guild_id = guilds.id WHERE reminders.channel_id = channels.id); diff --git a/src/models/channel_data.rs b/src/models/channel_data.rs index 9983056..e36f31c 100644 --- a/src/models/channel_data.rs +++ b/src/models/channel_data.rs @@ -10,6 +10,7 @@ pub struct ChannelData { pub webhook_id: Option, pub webhook_token: Option, pub paused: bool, + pub db_guild_id: Option, pub paused_until: Option, } @@ -22,7 +23,7 @@ impl ChannelData { if let Ok(c) = sqlx::query_as_unchecked!( Self, - "SELECT id, name, nudge, blacklisted, webhook_id, webhook_token, paused, paused_until FROM channels WHERE channel = ?", + "SELECT id, name, nudge, blacklisted, webhook_id, webhook_token, paused, paused_until, guild_id AS db_guild_id FROM channels WHERE channel = ?", channel_id ) .fetch_one(pool) @@ -46,7 +47,7 @@ impl ChannelData { Ok(sqlx::query_as_unchecked!( Self, " -SELECT id, name, nudge, blacklisted, webhook_id, webhook_token, paused, paused_until FROM channels WHERE channel = ? +SELECT id, name, nudge, blacklisted, webhook_id, webhook_token, paused, paused_until, guild_id AS db_guild_id FROM channels WHERE channel = ? ", channel_id ) diff --git a/src/models/reminder/builder.rs b/src/models/reminder/builder.rs index 0d2e6d8..8e3cf42 100644 --- a/src/models/reminder/builder.rs +++ b/src/models/reminder/builder.rs @@ -51,6 +51,7 @@ pub struct ReminderBuilder { pool: MySqlPool, uid: String, channel: u32, + guild: Option, thread_id: Option, utc_time: NaiveDateTime, timezone: String, @@ -86,6 +87,7 @@ impl ReminderBuilder { INSERT INTO reminders ( `uid`, `channel_id`, + `guild_id`, `utc_time`, `timezone`, `interval_seconds`, @@ -110,11 +112,13 @@ INSERT INTO reminders ( ?, ?, ?, + ?, ? ) ", self.uid, self.channel, + self.guild, utc_time, self.timezone, self.interval_seconds, @@ -247,10 +251,10 @@ impl<'a> MultiReminderBuilder<'a> { { Err(ReminderError::UserBlockedDm) } else { - Ok(user_data.dm_channel) + Ok((user_data.dm_channel, None)) } } else { - Ok(user_data.dm_channel) + Ok((user_data.dm_channel, None)) } } else { Err(ReminderError::InvalidTag) @@ -297,13 +301,13 @@ impl<'a> MultiReminderBuilder<'a> { .commit_changes(&self.ctx.data().database) .await; - Ok(channel_data.id) + Ok((channel_data.id, channel_data.db_guild_id)) } Err(e) => Err(ReminderError::DiscordError(e.to_string())), } } else { - Ok(channel_data.id) + Ok((channel_data.id, channel_data.db_guild_id)) } } } else { @@ -317,7 +321,8 @@ impl<'a> MultiReminderBuilder<'a> { let builder = ReminderBuilder { pool: self.ctx.data().database.clone(), uid: generate_uid(), - channel: c, + channel: c.0, + guild: c.1, thread_id, utc_time: self.utc_time, timezone: self.timezone.to_string(), diff --git a/web/src/lib.rs b/web/src/lib.rs index 54631cd..7e9fb03 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -165,6 +165,7 @@ pub async fn initialize( routes::dashboard::guild::get_reminders, routes::dashboard::guild::edit_reminder, routes::dashboard::guild::delete_reminder, + routes::dashboard::guild::get_reminder_errors, routes::dashboard::export::export_reminders, routes::dashboard::export::export_reminder_templates, routes::dashboard::export::export_todos, diff --git a/web/src/routes/dashboard/guild.rs b/web/src/routes/dashboard/guild.rs index 1f89aca..019e99d 100644 --- a/web/src/routes/dashboard/guild.rs +++ b/web/src/routes/dashboard/guild.rs @@ -26,7 +26,7 @@ use crate::{ routes::{ dashboard::{ create_database_channel, create_reminder, template_name_default, DeleteReminder, - DeleteReminderTemplate, PatchReminder, Reminder, ReminderTemplate, + DeleteReminderTemplate, PatchReminder, Reminder, ReminderError, ReminderTemplate, }, JsonResult, }, @@ -322,72 +322,53 @@ pub async fn create_guild_reminder( pub async fn get_reminders( id: u64, cookies: &CookieJar<'_>, - ctx: &State, serenity_context: &State, pool: &State>, ) -> JsonResult { check_authorization!(cookies, serenity_context.inner(), id); - let channels_res = GuildId(id).channels(&ctx.inner()).await; + sqlx::query_as_unchecked!( + Reminder, + "SELECT + reminders.attachment, + reminders.attachment_name, + reminders.avatar, + channels.channel, + reminders.content, + reminders.embed_author, + reminders.embed_author_url, + reminders.embed_color, + reminders.embed_description, + reminders.embed_footer, + reminders.embed_footer_url, + reminders.embed_image_url, + reminders.embed_thumbnail_url, + reminders.embed_title, + IFNULL(reminders.embed_fields, '[]') AS embed_fields, + reminders.enabled, + reminders.expires, + reminders.interval_seconds, + reminders.interval_days, + reminders.interval_months, + reminders.name, + reminders.restartable, + reminders.tts, + reminders.uid, + reminders.username, + reminders.utc_time + FROM reminders + LEFT JOIN channels ON channels.id = reminders.channel_id + WHERE `status` = 'pending' AND reminders.guild_id = (SELECT id FROM guilds WHERE guild = ?)", + id + ) + .fetch_all(pool.inner()) + .await + .map(|r| Ok(json!(r))) + .unwrap_or_else(|e| { + warn!("Failed to complete SQL query: {:?}", e); - match channels_res { - Ok(channels) => { - let channels = channels - .keys() - .into_iter() - .map(|k| k.as_u64().to_string()) - .collect::>() - .join(","); - - sqlx::query_as_unchecked!( - Reminder, - "SELECT - reminders.attachment, - reminders.attachment_name, - reminders.avatar, - channels.channel, - reminders.content, - reminders.embed_author, - reminders.embed_author_url, - reminders.embed_color, - reminders.embed_description, - reminders.embed_footer, - reminders.embed_footer_url, - reminders.embed_image_url, - reminders.embed_thumbnail_url, - reminders.embed_title, - IFNULL(reminders.embed_fields, '[]') AS embed_fields, - reminders.enabled, - reminders.expires, - reminders.interval_seconds, - reminders.interval_days, - reminders.interval_months, - reminders.name, - reminders.restartable, - reminders.tts, - reminders.uid, - reminders.username, - reminders.utc_time - FROM reminders - LEFT JOIN channels ON channels.id = reminders.channel_id - WHERE `status` = 'pending' AND FIND_IN_SET(channels.channel, ?)", - channels - ) - .fetch_all(pool.inner()) - .await - .map(|r| Ok(json!(r))) - .unwrap_or_else(|e| { - warn!("Failed to complete SQL query: {:?}", e); - - json_err!("Could not load reminders") - }) - } - Err(e) => { - warn!("Could not fetch channels from {}: {:?}", id, e); - - Ok(json!([])) - } - } + json_err!("Could not load reminders") + }) } #[patch("/api/guild//reminders", data = "")] @@ -623,3 +604,35 @@ pub async fn delete_reminder( } } } + +#[get("/api/guild//errors")] +pub async fn get_reminder_errors( + id: u64, + cookies: &CookieJar<'_>, + serenity_context: &State, + pool: &State>, +) -> JsonResult { + check_authorization!(cookies, serenity_context.inner(), id); + + sqlx::query_as_unchecked!( + ReminderError, + "SELECT + reminders.status, + reminders.utc_time, + reminders.name, + reminders.uid, + reminders.channel_id AS channel + FROM reminders + LEFT JOIN channels ON channels.id = reminders.channel_id + WHERE (`status` != 'pending' OR reminders.channel_id IS NULL) AND reminders.guild_id = (SELECT id FROM guilds WHERE guild = ?)", + id + ) + .fetch_all(pool.inner()) + .await + .map(|r| Ok(json!(r))) + .unwrap_or_else(|e| { + warn!("Failed to complete SQL query: {:?}", e); + + json_err!("Could not load reminders") + }) +} diff --git a/web/src/routes/dashboard/mod.rs b/web/src/routes/dashboard/mod.rs index 4168a4a..37f2f89 100644 --- a/web/src/routes/dashboard/mod.rs +++ b/web/src/routes/dashboard/mod.rs @@ -152,6 +152,18 @@ pub struct Reminder { utc_time: NaiveDateTime, } +#[derive(Serialize)] +pub struct ReminderError { + #[serde(with = "string")] + channel: u64, + status: String, + #[serde(default = "name_default")] + name: String, + #[serde(default)] + uid: String, + utc_time: NaiveDateTime, +} + #[derive(Serialize, Deserialize)] pub struct ReminderCsv { #[serde(with = "base64s")] @@ -479,6 +491,7 @@ pub async fn create_reminder( attachment, attachment_name, channel_id, + guild_id, avatar, content, embed_author, @@ -501,11 +514,12 @@ pub async fn create_reminder( tts, username, `utc_time` - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", new_uid, attachment_data, reminder.attachment_name, channel, + guild_id.0, reminder.avatar, reminder.content, reminder.embed_author, diff --git a/web/static/js/reminder_errors.js b/web/static/js/reminder_errors.js index 6fa4798..42246bc 100644 --- a/web/static/js/reminder_errors.js +++ b/web/static/js/reminder_errors.js @@ -5,7 +5,7 @@ const reminderErrors = () => { } const guildId = () => { - let selected: HTMLElement = document.querySelector(".guildList a.is-active"); + let selected = document.querySelector(".guildList a.is-active"); return selected.dataset["guild"]; }