upload attachments

This commit is contained in:
jude 2022-03-26 20:03:58 +00:00
parent 08fd88ce54
commit f8547bba70
6 changed files with 161 additions and 74 deletions

1
Cargo.lock generated
View File

@ -2142,6 +2142,7 @@ dependencies = [
name = "reminder_web" name = "reminder_web"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"base64",
"chrono", "chrono",
"chrono-tz 0.5.3", "chrono-tz 0.5.3",
"lazy_static", "lazy_static",

View File

@ -17,3 +17,4 @@ chrono = "0.4"
chrono-tz = "0.5" chrono-tz = "0.5"
lazy_static = "1.4.0" lazy_static = "1.4.0"
rand = "0.7" rand = "0.7"
base64 = "0.13"

View File

@ -1,3 +1,4 @@
use base64;
use chrono::Utc; use chrono::Utc;
use rocket::{ use rocket::{
http::CookieJar, http::CookieJar,
@ -7,7 +8,10 @@ use rocket::{
use serde::Serialize; use serde::Serialize;
use serenity::{ use serenity::{
client::Context, client::Context,
model::id::{ChannelId, GuildId}, model::{
channel::GuildChannel,
id::{ChannelId, GuildId},
},
}; };
use sqlx::{MySql, Pool}; use sqlx::{MySql, Pool};
@ -19,8 +23,8 @@ use crate::{
MAX_URL_LENGTH, MAX_USERNAME_LENGTH, MIN_INTERVAL, MAX_URL_LENGTH, MAX_USERNAME_LENGTH, MIN_INTERVAL,
}, },
routes::dashboard::{ routes::dashboard::{
create_database_channel, generate_uid, name_default, DeleteReminder, PatchReminder, create_database_channel, generate_uid, name_default, DeleteReminder, JsonReminder,
Reminder, PatchReminder, Reminder,
}, },
}; };
@ -45,12 +49,16 @@ pub async fn get_guild_channels(
Some(guild) => { Some(guild) => {
let mut channel_info = vec![]; let mut channel_info = vec![];
for (channel_id, channel) in guild let mut channels = guild
.channels .channels
.iter() .iter()
.filter_map(|(id, channel)| channel.to_owned().guild().map(|c| (id, c))) .filter_map(|(id, channel)| channel.to_owned().guild().map(|c| (id.to_owned(), c)))
.filter(|(_, channel)| channel.is_text_based()) .filter(|(_, channel)| channel.is_text_based())
{ .collect::<Vec<(ChannelId, GuildChannel)>>();
channels.sort_by(|(_, c1), (_, c2)| c1.position.cmp(&c2.position));
for (channel_id, channel) in channels {
let mut ch = ChannelInfo { let mut ch = ChannelInfo {
name: channel.name.to_string(), name: channel.name.to_string(),
id: channel_id.to_string(), id: channel_id.to_string(),
@ -125,7 +133,7 @@ pub async fn get_guild_roles(id: u64, cookies: &CookieJar<'_>, ctx: &State<Conte
#[post("/api/guild/<id>/reminders", data = "<reminder>")] #[post("/api/guild/<id>/reminders", data = "<reminder>")]
pub async fn create_reminder( pub async fn create_reminder(
id: u64, id: u64,
reminder: Json<Reminder>, reminder: Json<JsonReminder>,
cookies: &CookieJar<'_>, cookies: &CookieJar<'_>,
serenity_context: &State<Context>, serenity_context: &State<Context>,
pool: &State<Pool<MySql>>, pool: &State<Pool<MySql>>,
@ -213,12 +221,16 @@ pub async fn create_reminder(
} }
} }
// base64 decode error dropped here
let attachment_data = reminder.attachment.as_ref().map(|s| base64::decode(s).ok()).flatten();
let name = if reminder.name.is_empty() { name_default() } else { reminder.name.clone() }; let name = if reminder.name.is_empty() { name_default() } else { reminder.name.clone() };
// write to db // write to db
match sqlx::query!( match sqlx::query!(
"INSERT INTO reminders ( "INSERT INTO reminders (
uid, uid,
attachment,
attachment_name,
channel_id, channel_id,
avatar, avatar,
content, content,
@ -241,8 +253,10 @@ pub async fn create_reminder(
tts, tts,
username, username,
`utc_time` `utc_time`
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
generate_uid(), generate_uid(),
attachment_data,
reminder.attachment_name,
channel, channel,
reminder.avatar, reminder.avatar,
reminder.content, reminder.content,

View File

@ -26,6 +26,45 @@ fn channel_default() -> u64 {
0 0
} }
#[derive(Serialize, Deserialize)]
pub struct EmbedField {
title: String,
value: String,
}
#[derive(Serialize, Deserialize)]
pub struct JsonReminder {
attachment: Option<String>,
attachment_name: Option<String>,
avatar: Option<String>,
#[serde(with = "string")]
channel: u64,
content: String,
embed_author: String,
embed_author_url: Option<String>,
embed_color: u32,
embed_description: String,
embed_footer: String,
embed_footer_url: Option<String>,
embed_image_url: Option<String>,
embed_thumbnail_url: Option<String>,
embed_title: String,
embed_fields: Vec<EmbedField>,
enabled: bool,
expires: Option<NaiveDateTime>,
interval_seconds: Option<u32>,
interval_months: Option<u32>,
#[serde(default = "name_default")]
name: String,
pin: bool,
restartable: bool,
tts: bool,
#[serde(default)]
uid: String,
username: Option<String>,
utc_time: NaiveDateTime,
}
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Reminder { pub struct Reminder {
attachment: Option<Vec<u8>>, attachment: Option<Vec<u8>>,
@ -62,7 +101,7 @@ pub struct Reminder {
pub struct PatchReminder { pub struct PatchReminder {
uid: String, uid: String,
#[serde(default)] #[serde(default)]
attachment: Unset<Option<Vec<u8>>>, attachment: Unset<Option<String>>,
#[serde(default)] #[serde(default)]
attachment_name: Unset<Option<String>>, attachment_name: Unset<Option<String>>,
#[serde(default)] #[serde(default)]
@ -91,6 +130,8 @@ pub struct PatchReminder {
#[serde(default)] #[serde(default)]
embed_title: Unset<String>, embed_title: Unset<String>,
#[serde(default)] #[serde(default)]
embed_fields: Unset<EmbedField>,
#[serde(default)]
enabled: Unset<bool>, enabled: Unset<bool>,
#[serde(default)] #[serde(default)]
expires: Unset<Option<NaiveDateTime>>, expires: Unset<Option<NaiveDateTime>>,

View File

@ -262,6 +262,13 @@ document.addEventListener("remindersLoaded", (event) => {
node.querySelector('input[name="time"]').value node.querySelector('input[name="time"]').value
).setZone("UTC"); ).setZone("UTC");
let fields = node.querySelectorAll(".embed-field-box", (el) => {
return {
title: el.querySelector('input[name="embed_field_title[]"]').value,
value: el.querySelector('input[name="embed_field_value[]"]').value,
};
});
let reminder = { let reminder = {
uid: node.closest(".reminderContent").dataset["uid"], uid: node.closest(".reminderContent").dataset["uid"],
avatar: has_source(node.querySelector("img.discord-avatar").src), avatar: has_source(node.querySelector("img.discord-avatar").src),
@ -286,6 +293,7 @@ document.addEventListener("remindersLoaded", (event) => {
node.querySelector("img.embed_thumbnail_url").src node.querySelector("img.embed_thumbnail_url").src
), ),
embed_title: node.querySelector('textarea[name="embed_title"]').value, embed_title: node.querySelector('textarea[name="embed_title"]').value,
embed_fields: fields,
expires: null, expires: null,
interval_seconds: seconds, interval_seconds: seconds,
interval_months: months, interval_months: months,
@ -493,77 +501,99 @@ function has_source(string) {
let $createReminder = document.querySelector("#reminderCreator"); let $createReminder = document.querySelector("#reminderCreator");
$createReminder.querySelector("button#createReminder").addEventListener("click", () => { $createReminder
// create reminder object .querySelector("button#createReminder")
let seconds = .addEventListener("click", async () => {
parseInt($createReminder.querySelector('input[name="interval_seconds"]').value) || // create reminder object
null; let seconds =
let months = parseInt(
parseInt($createReminder.querySelector('input[name="interval_months"]').value) || $createReminder.querySelector('input[name="interval_seconds"]').value
null; ) || null;
let months =
parseInt(
$createReminder.querySelector('input[name="interval_months"]').value
) || null;
let rgb_color = window.getComputedStyle( let rgb_color = window.getComputedStyle(
$createReminder.querySelector("div.discord-embed") $createReminder.querySelector("div.discord-embed")
).borderLeftColor; ).borderLeftColor;
let rgb = rgb_color.match(/\d+/g); let rgb = rgb_color.match(/\d+/g);
let color = colorToInt(parseInt(rgb[0]), parseInt(rgb[1]), parseInt(rgb[2])); let color = colorToInt(parseInt(rgb[0]), parseInt(rgb[1]), parseInt(rgb[2]));
let utc_time = luxon.DateTime.fromISO( let utc_time = luxon.DateTime.fromISO(
$createReminder.querySelector('input[name="time"]').value $createReminder.querySelector('input[name="time"]').value
).setZone("UTC"); ).setZone("UTC");
let reminder = { let attachment = null;
avatar: has_source($createReminder.querySelector("img.discord-avatar").src), let attachment_name = null;
channel: $createReminder.querySelector("select.channel-selector").value,
content: $createReminder.querySelector("textarea#messageContent").value,
embed_author_url: has_source(
$createReminder.querySelector("img.embed_author_url").src
),
embed_author: $createReminder.querySelector("textarea#embedAuthor").value,
embed_color: color,
embed_description: $createReminder.querySelector("textarea#embedDescription")
.value,
embed_footer: $createReminder.querySelector("textarea#embedFooter").value,
embed_footer_url: has_source(
$createReminder.querySelector("img.embed_footer_url").src
),
embed_image_url: has_source(
$createReminder.querySelector("img.embed_image_url").src
),
embed_thumbnail_url: has_source(
$createReminder.querySelector("img.embed_thumbnail_url").src
),
embed_title: $createReminder.querySelector("textarea#embedTitle").value,
enabled: true,
expires: null,
interval_seconds: seconds,
interval_months: months,
name: $createReminder.querySelector('input[name="name"]').value,
pin: $createReminder.querySelector('input[name="pin"]').checked,
restartable: false,
tts: $createReminder.querySelector('input[name="tts"]').checked,
username: $createReminder.querySelector("input#reminderUsername").value,
utc_time: utc_time.toFormat("yyyy-LL-dd'T'HH:mm:ss"),
};
// send to server if ($createReminder.querySelector('input[name="attachment"]').files.length > 0) {
let guild = document.querySelector(".guildList a.is-active").dataset["guild"]; let file = $createReminder.querySelector('input[name="attachment"]').files[0];
fetch(`/dashboard/api/guild/${guild}/reminders`, { attachment = await new Promise((resolve) => {
method: "POST", let fileReader = new FileReader();
headers: { fileReader.onload = (e) => resolve(fileReader.result);
"Content-Type": "application/json", fileReader.readAsDataURL(file);
}, });
body: JSON.stringify(reminder), attachment = attachment.split(",")[1];
}) attachment_name = file.name;
.then((response) => response.json()) }
.then((data) => console.log(data));
// process response let reminder = {
fetch_reminders(guild); attachment: attachment,
attachment_name: attachment_name,
avatar: has_source($createReminder.querySelector("img.discord-avatar").src),
channel: $createReminder.querySelector("select.channel-selector").value,
content: $createReminder.querySelector("textarea#messageContent").value,
embed_author_url: has_source(
$createReminder.querySelector("img.embed_author_url").src
),
embed_author: $createReminder.querySelector("textarea#embedAuthor").value,
embed_color: color,
embed_description: $createReminder.querySelector("textarea#embedDescription")
.value,
embed_footer: $createReminder.querySelector("textarea#embedFooter").value,
embed_footer_url: has_source(
$createReminder.querySelector("img.embed_footer_url").src
),
embed_image_url: has_source(
$createReminder.querySelector("img.embed_image_url").src
),
embed_thumbnail_url: has_source(
$createReminder.querySelector("img.embed_thumbnail_url").src
),
embed_title: $createReminder.querySelector("textarea#embedTitle").value,
embed_fields: [],
enabled: true,
expires: null,
interval_seconds: seconds,
interval_months: months,
name: $createReminder.querySelector('input[name="name"]').value,
pin: $createReminder.querySelector('input[name="pin"]').checked,
restartable: false,
tts: $createReminder.querySelector('input[name="tts"]').checked,
username: $createReminder.querySelector("input#reminderUsername").value,
utc_time: utc_time.toFormat("yyyy-LL-dd'T'HH:mm:ss"),
};
// reset inputs // send to server
}); let guild = document.querySelector(".guildList a.is-active").dataset["guild"];
fetch(`/dashboard/api/guild/${guild}/reminders`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(reminder),
})
.then((response) => response.json())
.then((data) => console.log(data));
// process response
fetch_reminders(guild);
// reset inputs
});
document.querySelectorAll("textarea.autoresize").forEach((element) => { document.querySelectorAll("textarea.autoresize").forEach((element) => {
element.addEventListener("input", () => { element.addEventListener("input", () => {

View File

@ -178,7 +178,7 @@
<div class="column has-text-centered"> <div class="column has-text-centered">
<div class="file is-boxed"> <div class="file is-boxed">
<label class="file-label"> <label class="file-label">
<input class="file-input" type="file"> <input class="file-input" type="file" name="attachment">
<span class="file-cta"> <span class="file-cta">
<span class="file-label"> <span class="file-label">
Add Attachment Add Attachment