Extend user reminder API endpoints

This commit is contained in:
jude
2024-03-09 16:17:55 +00:00
parent 8f4810b532
commit 3190738fc5
11 changed files with 342 additions and 75 deletions

View File

@ -131,6 +131,7 @@ pub async fn initialize(
routes![
routes::dashboard::dashboard,
routes::dashboard::dashboard_home,
routes::dashboard::api::delete_reminder,
routes::dashboard::api::user::get_user_info,
routes::dashboard::api::user::update_user_info,
routes::dashboard::api::user::get_user_guilds,
@ -145,7 +146,6 @@ pub async fn initialize(
routes::dashboard::api::guild::create_guild_reminder,
routes::dashboard::api::guild::get_reminders,
routes::dashboard::api::guild::edit_reminder,
routes::dashboard::api::guild::delete_reminder,
routes::dashboard::export::export_reminders,
routes::dashboard::export::export_reminder_templates,
routes::dashboard::export::export_todos,

View File

@ -14,9 +14,7 @@ use crate::{
consts::MIN_INTERVAL,
guards::transaction::Transaction,
routes::{
dashboard::{
create_database_channel, create_reminder, DeleteReminder, PatchReminder, Reminder,
},
dashboard::{create_database_channel, create_reminder, PatchReminder, Reminder},
JsonResult,
},
Database,
@ -364,27 +362,3 @@ pub async fn edit_reminder(
}
}
}
#[delete("/api/guild/<id>/reminders", data = "<reminder>")]
pub async fn delete_reminder(
cookies: &CookieJar<'_>,
id: u64,
reminder: Json<DeleteReminder>,
ctx: &State<Context>,
pool: &State<Pool<MySql>>,
) -> JsonResult {
check_authorization(cookies, ctx.inner(), id).await?;
match sqlx::query!("UPDATE reminders SET `status` = 'deleted' WHERE uid = ?", reminder.uid)
.execute(pool.inner())
.await
{
Ok(_) => Ok(json!({})),
Err(e) => {
warn!("Error in `delete_reminder`: {:?}", e);
Err(json!({"error": "Could not delete reminder"}))
}
}
}

View File

@ -1,2 +1,41 @@
pub mod guild;
pub mod user;
use rocket::{
http::CookieJar,
serde::json::{json, Json},
State,
};
use serenity::client::Context;
use sqlx::{MySql, Pool};
use crate::routes::{dashboard::DeleteReminder, JsonResult};
#[delete("/api/reminders", data = "<reminder>")]
pub async fn delete_reminder(
cookies: &CookieJar<'_>,
reminder: Json<DeleteReminder>,
pool: &State<Pool<MySql>>,
) -> JsonResult {
match cookies.get_private("userid").map(|c| c.value().parse::<u64>().ok()).flatten() {
Some(_) => {
match sqlx::query!(
"UPDATE reminders SET `status` = 'deleted' WHERE uid = ?",
reminder.uid
)
.execute(pool.inner())
.await
{
Ok(_) => Ok(json!({})),
Err(e) => {
warn!("Error in `delete_reminder`: {:?}", e);
Err(json!({"error": "Could not delete reminder"}))
}
}
}
None => Err(json!({"error": "User not authorized"})),
}
}

View File

@ -15,7 +15,10 @@ use crate::{
},
guards::transaction::Transaction,
routes::{
dashboard::{create_database_channel, generate_uid, name_default, Attachment, EmbedField},
dashboard::{
create_database_channel, deserialize_optional_field, generate_uid, interval_default,
name_default, Attachment, EmbedField, Unset,
},
JsonResult,
},
Error,
@ -228,3 +231,60 @@ pub async fn create_reminder(
}
}
}
#[derive(Deserialize)]
pub struct PatchReminder {
pub uid: String,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
pub attachment: Unset<Option<Attachment>>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
pub attachment_name: Unset<Option<String>>,
#[serde(default)]
pub content: Unset<String>,
#[serde(default)]
pub embed_author: Unset<String>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
pub embed_author_url: Unset<Option<String>>,
#[serde(default)]
pub embed_color: Unset<u32>,
#[serde(default)]
pub embed_description: Unset<String>,
#[serde(default)]
pub embed_footer: Unset<String>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
pub embed_footer_url: Unset<Option<String>>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
pub embed_image_url: Unset<Option<String>>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
pub embed_thumbnail_url: Unset<Option<String>>,
#[serde(default)]
pub embed_title: Unset<String>,
#[serde(default)]
pub embed_fields: Unset<Json<Vec<EmbedField>>>,
#[serde(default)]
pub enabled: Unset<bool>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
pub expires: Unset<Option<NaiveDateTime>>,
#[serde(default = "interval_default")]
#[serde(deserialize_with = "deserialize_optional_field")]
pub interval_seconds: Unset<Option<u32>>,
#[serde(default = "interval_default")]
#[serde(deserialize_with = "deserialize_optional_field")]
pub interval_days: Unset<Option<u32>>,
#[serde(default = "interval_default")]
#[serde(deserialize_with = "deserialize_optional_field")]
pub interval_months: Unset<Option<u32>>,
#[serde(default)]
pub name: Unset<String>,
#[serde(default)]
pub tts: Unset<bool>,
#[serde(default)]
pub utc_time: Unset<NaiveDateTime>,
}

View File

@ -7,11 +7,16 @@ use serenity::{client::Context, model::id::UserId};
use sqlx::{MySql, Pool};
use crate::{
check_subscription,
guards::transaction::Transaction,
routes::{
dashboard::api::user::models::{create_reminder, Reminder},
dashboard::{
api::user::models::{create_reminder, Reminder},
PatchReminder, MIN_INTERVAL,
},
JsonResult,
},
Database,
};
#[post("/api/user/reminders", data = "<reminder>")]
@ -103,3 +108,174 @@ pub async fn get_reminders(
}
}
}
#[patch("/api/user/reminders", data = "<reminder>")]
pub async fn edit_reminder(
reminder: Json<PatchReminder>,
ctx: &State<Context>,
mut transaction: Transaction<'_>,
pool: &State<Pool<Database>>,
cookies: &CookieJar<'_>,
) -> JsonResult {
let user_id_cookie =
cookies.get_private("userid").map(|c| c.value().parse::<u64>().ok()).flatten();
if user_id_cookie.is_none() {
return Err(json!({"error": "User not authorized"}));
}
let mut error = vec![];
let user_id = user_id_cookie.unwrap();
if reminder.message_ok() {
update_field!(transaction.executor(), error, reminder.[
content,
embed_author,
embed_description,
embed_footer,
embed_title,
embed_fields
]);
} else {
error.push("Message exceeds limits.".to_string());
}
update_field!(transaction.executor(), error, reminder.[
attachment,
attachment_name,
embed_author_url,
embed_color,
embed_footer_url,
embed_image_url,
embed_thumbnail_url,
enabled,
expires,
name,
tts,
utc_time
]);
if reminder.interval_days.flatten().is_some()
|| reminder.interval_months.flatten().is_some()
|| reminder.interval_seconds.flatten().is_some()
{
if check_subscription(&ctx.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(transaction.executor())
.await
.map_err(|e| {
warn!("Error updating reminder interval: {:?}", e);
json!({ "reminder": Option::<Reminder>::None, "errors": vec!["Unknown error"] })
})?
.days
.unwrap_or(0),
} * 86400 + 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(transaction.executor())
.await
.map_err(|e| {
warn!("Error updating reminder interval: {:?}", e);
json!({ "reminder": Option::<Reminder>::None, "errors": vec!["Unknown error"] })
})?
.months
.unwrap_or(0),
} * 2592000 + 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(transaction.executor())
.await
.map_err(|e| {
warn!("Error updating reminder interval: {:?}", e);
json!({ "reminder": Option::<Reminder>::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!(transaction.executor(), error, reminder.[
interval_days,
interval_months,
interval_seconds
]);
}
}
} else {
sqlx::query!(
"
UPDATE reminders
SET interval_seconds = NULL, interval_days = NULL, interval_months = NULL
WHERE uid = ?
",
reminder.uid
)
.execute(transaction.executor())
.await
.map_err(|e| {
warn!("Error updating reminder interval: {:?}", e);
json!({ "reminder": Option::<Reminder>::None, "errors": vec!["Unknown error"] })
})?;
}
if let Err(e) = transaction.commit().await {
warn!("Couldn't commit transaction: {:?}", e);
return json_err!("Couldn't commit transaction");
}
match sqlx::query_as_unchecked!(
Reminder,
"
SELECT reminders.attachment,
reminders.attachment_name,
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,
reminders.embed_fields,
reminders.enabled,
reminders.expires,
reminders.interval_seconds,
reminders.interval_days,
reminders.interval_months,
reminders.name,
reminders.tts,
reminders.uid,
reminders.utc_time
FROM reminders
LEFT JOIN channels ON channels.id = reminders.channel_id
WHERE uid = ?
",
reminder.uid
)
.fetch_one(pool.inner())
.await
{
Ok(reminder) => Ok(json!({"reminder": reminder, "errors": error})),
Err(e) => {
warn!("Error exiting `edit_reminder': {:?}", e);
Err(json!({"reminder": Option::<Reminder>::None, "errors": vec!["Unknown error"]}))
}
}
}