jude/orphan-reminders #1

Merged
jude merged 10 commits from jude/orphan-reminders into next 2023-09-16 17:09:34 +00:00
7 changed files with 108 additions and 76 deletions
Showing only changes of commit 109cf16dbb - Show all commits

View File

@ -1,9 +1,8 @@
-- Drop existing constraint -- Drop existing constraint
-- TODO ALTER TABLE `reminders` DROP CONSTRAINT `reminders_ibfk_1`;
ALTER TABLE `reminders` DROP CONSTRAINT `channel_id_`
ALTER TABLE `reminders` MODIFY COLUMN `channel_id` BIGINT ; ALTER TABLE `reminders` MODIFY COLUMN `channel_id` INT UNSIGNED;
ALTER TABLE `reminders` ADD COLUMN `guild_id` BIGINT; ALTER TABLE `reminders` ADD COLUMN `guild_id` INT UNSIGNED;
ALTER TABLE `reminders` ALTER TABLE `reminders`
ADD CONSTRAINT `guild_id_fk` ADD CONSTRAINT `guild_id_fk`
@ -17,5 +16,4 @@ ALTER TABLE `reminders`
REFERENCES `channels`(`id`) REFERENCES `channels`(`id`)
ON DELETE SET NULL; 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 reminders.channel_id = channels.id);
UPDATE `reminders` SET `guild_id` = (SELECT guilds.`id` FROM `channels` INNER JOIN `guilds` ON channels.guild_id = guilds.id WHERE )

View File

@ -10,6 +10,7 @@ pub struct ChannelData {
pub webhook_id: Option<u64>, pub webhook_id: Option<u64>,
pub webhook_token: Option<String>, pub webhook_token: Option<String>,
pub paused: bool, pub paused: bool,
pub db_guild_id: Option<u32>,
pub paused_until: Option<NaiveDateTime>, pub paused_until: Option<NaiveDateTime>,
} }
@ -22,7 +23,7 @@ impl ChannelData {
if let Ok(c) = sqlx::query_as_unchecked!( if let Ok(c) = sqlx::query_as_unchecked!(
Self, 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 channel_id
) )
.fetch_one(pool) .fetch_one(pool)
@ -46,7 +47,7 @@ impl ChannelData {
Ok(sqlx::query_as_unchecked!( Ok(sqlx::query_as_unchecked!(
Self, 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 channel_id
) )

View File

@ -51,6 +51,7 @@ pub struct ReminderBuilder {
pool: MySqlPool, pool: MySqlPool,
uid: String, uid: String,
channel: u32, channel: u32,
guild: Option<u32>,
thread_id: Option<u64>, thread_id: Option<u64>,
utc_time: NaiveDateTime, utc_time: NaiveDateTime,
timezone: String, timezone: String,
@ -86,6 +87,7 @@ impl ReminderBuilder {
INSERT INTO reminders ( INSERT INTO reminders (
`uid`, `uid`,
`channel_id`, `channel_id`,
`guild_id`,
`utc_time`, `utc_time`,
`timezone`, `timezone`,
`interval_seconds`, `interval_seconds`,
@ -110,11 +112,13 @@ INSERT INTO reminders (
?, ?,
?, ?,
?, ?,
?,
? ?
) )
", ",
self.uid, self.uid,
self.channel, self.channel,
self.guild,
utc_time, utc_time,
self.timezone, self.timezone,
self.interval_seconds, self.interval_seconds,
@ -247,10 +251,10 @@ impl<'a> MultiReminderBuilder<'a> {
{ {
Err(ReminderError::UserBlockedDm) Err(ReminderError::UserBlockedDm)
} else { } else {
Ok(user_data.dm_channel) Ok((user_data.dm_channel, None))
} }
} else { } else {
Ok(user_data.dm_channel) Ok((user_data.dm_channel, None))
} }
} else { } else {
Err(ReminderError::InvalidTag) Err(ReminderError::InvalidTag)
@ -297,13 +301,13 @@ impl<'a> MultiReminderBuilder<'a> {
.commit_changes(&self.ctx.data().database) .commit_changes(&self.ctx.data().database)
.await; .await;
Ok(channel_data.id) Ok((channel_data.id, channel_data.db_guild_id))
} }
Err(e) => Err(ReminderError::DiscordError(e.to_string())), Err(e) => Err(ReminderError::DiscordError(e.to_string())),
} }
} else { } else {
Ok(channel_data.id) Ok((channel_data.id, channel_data.db_guild_id))
} }
} }
} else { } else {
@ -317,7 +321,8 @@ impl<'a> MultiReminderBuilder<'a> {
let builder = ReminderBuilder { let builder = ReminderBuilder {
pool: self.ctx.data().database.clone(), pool: self.ctx.data().database.clone(),
uid: generate_uid(), uid: generate_uid(),
channel: c, channel: c.0,
guild: c.1,
thread_id, thread_id,
utc_time: self.utc_time, utc_time: self.utc_time,
timezone: self.timezone.to_string(), timezone: self.timezone.to_string(),

View File

@ -165,6 +165,7 @@ pub async fn initialize(
routes::dashboard::guild::get_reminders, routes::dashboard::guild::get_reminders,
routes::dashboard::guild::edit_reminder, routes::dashboard::guild::edit_reminder,
routes::dashboard::guild::delete_reminder, routes::dashboard::guild::delete_reminder,
routes::dashboard::guild::get_reminder_errors,
routes::dashboard::export::export_reminders, routes::dashboard::export::export_reminders,
routes::dashboard::export::export_reminder_templates, routes::dashboard::export::export_reminder_templates,
routes::dashboard::export::export_todos, routes::dashboard::export::export_todos,

View File

@ -26,7 +26,7 @@ use crate::{
routes::{ routes::{
dashboard::{ dashboard::{
create_database_channel, create_reminder, template_name_default, DeleteReminder, create_database_channel, create_reminder, template_name_default, DeleteReminder,
DeleteReminderTemplate, PatchReminder, Reminder, ReminderTemplate, DeleteReminderTemplate, PatchReminder, Reminder, ReminderError, ReminderTemplate,
}, },
JsonResult, JsonResult,
}, },
@ -322,72 +322,53 @@ pub async fn create_guild_reminder(
pub async fn get_reminders( pub async fn get_reminders(
id: u64, id: u64,
cookies: &CookieJar<'_>, cookies: &CookieJar<'_>,
ctx: &State<Context>,
serenity_context: &State<Context>, serenity_context: &State<Context>,
pool: &State<Pool<MySql>>, pool: &State<Pool<MySql>>,
) -> JsonResult { ) -> JsonResult {
check_authorization!(cookies, serenity_context.inner(), id); 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 { json_err!("Could not load reminders")
Ok(channels) => { })
let channels = channels
.keys()
.into_iter()
.map(|k| k.as_u64().to_string())
.collect::<Vec<String>>()
.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!([]))
}
}
} }
#[patch("/api/guild/<id>/reminders", data = "<reminder>")] #[patch("/api/guild/<id>/reminders", data = "<reminder>")]
@ -623,3 +604,35 @@ pub async fn delete_reminder(
} }
} }
} }
#[get("/api/guild/<id>/errors")]
pub async fn get_reminder_errors(
id: u64,
cookies: &CookieJar<'_>,
serenity_context: &State<Context>,
pool: &State<Pool<MySql>>,
) -> 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")
})
}

View File

@ -152,6 +152,18 @@ pub struct Reminder {
utc_time: NaiveDateTime, 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)] #[derive(Serialize, Deserialize)]
pub struct ReminderCsv { pub struct ReminderCsv {
#[serde(with = "base64s")] #[serde(with = "base64s")]
@ -479,6 +491,7 @@ pub async fn create_reminder(
attachment, attachment,
attachment_name, attachment_name,
channel_id, channel_id,
guild_id,
avatar, avatar,
content, content,
embed_author, embed_author,
@ -501,11 +514,12 @@ pub async fn create_reminder(
tts, tts,
username, username,
`utc_time` `utc_time`
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
new_uid, new_uid,
attachment_data, attachment_data,
reminder.attachment_name, reminder.attachment_name,
channel, channel,
guild_id.0,
reminder.avatar, reminder.avatar,
reminder.content, reminder.content,
reminder.embed_author, reminder.embed_author,

View File

@ -5,7 +5,7 @@ const reminderErrors = () => {
} }
const guildId = () => { const guildId = () => {
let selected: HTMLElement = document.querySelector(".guildList a.is-active"); let selected = document.querySelector(".guildList a.is-active");
return selected.dataset["guild"]; return selected.dataset["guild"];
} }