fix attachments. remove webhook sending for speedup

This commit is contained in:
2022-04-09 12:21:28 +01:00
parent 0f05018cab
commit 4c4f0927f1
11 changed files with 370 additions and 231 deletions

View File

@ -12,7 +12,6 @@ use oauth2::{basic::BasicClient, AuthUrl, ClientId, ClientSecret, RedirectUrl, T
use rocket::{
fs::FileServer,
serde::json::{json, Value as JsonValue},
shield::Shield,
tokio::sync::broadcast::Sender,
};
use rocket_dyn_templates::Template;
@ -51,6 +50,11 @@ async fn not_found() -> Template {
Template::render("errors/404", &map)
}
#[catch(413)]
async fn payload_too_large() -> JsonValue {
json!({"error": "Data too large.", "errors": ["Data too large."]})
}
#[catch(422)]
async fn unprocessable_entity() -> JsonValue {
json!({"error": "Invalid request.", "errors": ["Invalid request."]})
@ -67,6 +71,13 @@ pub async fn initialize(
serenity_context: Context,
db_pool: Pool<Database>,
) -> Result<(), Box<dyn std::error::Error>> {
info!("Checking environment variables...");
env::var("OAUTH2_CLIENT_ID").expect("`OAUTH2_CLIENT_ID' not supplied");
env::var("OAUTH2_CLIENT_SECRET").expect("`OAUTH2_CLIENT_SECRET' not supplied");
env::var("OAUTH2_DISCORD_CALLBACK").expect("`OAUTH2_DISCORD_CALLBACK' not supplied");
env::var("PATREON_GUILD_ID").expect("`PATREON_GUILD' not supplied");
info!("Done!");
let oauth2_client = BasicClient::new(
ClientId::new(env::var("OAUTH2_CLIENT_ID")?),
Some(ClientSecret::new(env::var("OAUTH2_CLIENT_SECRET")?)),
@ -86,7 +97,8 @@ pub async fn initialize(
forbidden,
not_found,
internal_server_error,
unprocessable_entity
unprocessable_entity,
payload_too_large,
],
)
.manage(oauth2_client)
@ -129,6 +141,7 @@ pub async fn initialize(
routes::dashboard::guild::get_guild_roles,
routes::dashboard::guild::get_reminder_templates,
routes::dashboard::guild::create_reminder_template,
routes::dashboard::guild::delete_reminder_template,
routes::dashboard::guild::create_reminder,
routes::dashboard::guild::get_reminders,
routes::dashboard::guild::edit_reminder,

View File

@ -25,7 +25,7 @@ use crate::{
},
routes::dashboard::{
create_database_channel, generate_uid, name_default, template_name_default, DeleteReminder,
PatchReminder, Reminder, ReminderTemplate,
DeleteReminderTemplate, PatchReminder, Reminder, ReminderTemplate,
},
};
@ -59,6 +59,7 @@ pub async fn get_guild_channels(
channels.sort_by(|(_, c1), (_, c2)| c1.position.cmp(&c2.position));
// todo change to map
for (channel_id, channel) in channels {
let mut ch = ChannelInfo {
name: channel.name.to_string(),
@ -67,29 +68,6 @@ pub async fn get_guild_channels(
webhook_name: None,
};
if let Ok(webhook_details) = sqlx::query!(
"SELECT webhook_id, webhook_token FROM channels WHERE channel = ?",
channel.id.as_u64()
)
.fetch_one(pool.inner())
.await
{
if let (Some(webhook_id), Some(webhook_token)) =
(webhook_details.webhook_id, webhook_details.webhook_token)
{
let webhook_res =
ctx.http.get_webhook_with_token(webhook_id, &webhook_token).await;
if let Ok(webhook) = webhook_res {
ch.webhook_avatar = webhook.avatar.map(|a| {
format!("{}/{}/{}.webp?size=128", DISCORD_CDN, webhook_id, a)
});
ch.webhook_name = webhook.name;
}
}
}
channel_info.push(ch);
}
@ -260,6 +238,34 @@ pub async fn create_reminder_template(
}
}
#[delete("/api/guild/<id>/templates", data = "<delete_reminder_template>")]
pub async fn delete_reminder_template(
id: u64,
delete_reminder_template: Json<DeleteReminderTemplate>,
cookies: &CookieJar<'_>,
ctx: &State<Context>,
pool: &State<Pool<MySql>>,
) -> JsonValue {
check_authorization!(cookies, ctx.inner(), id);
match sqlx::query!(
"DELETE FROM reminder_template WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?) AND id = ?",
id, delete_reminder_template.id
)
.fetch_all(pool.inner())
.await
{
Ok(_) => {
json!({})
}
Err(e) => {
warn!("Could not delete template from {}: {:?}", id, e);
json!({"error": "Could not delete template"})
}
}
}
#[post("/api/guild/<id>/reminders", data = "<reminder>")]
pub async fn create_reminder(
id: u64,

View File

@ -60,6 +60,11 @@ pub struct ReminderTemplate {
username: Option<String>,
}
#[derive(Deserialize)]
pub struct DeleteReminderTemplate {
id: u32,
}
#[derive(Serialize, Deserialize)]
pub struct EmbedField {
title: String,
@ -69,6 +74,7 @@ pub struct EmbedField {
#[derive(Serialize, Deserialize)]
pub struct Reminder {
#[serde(with = "base64s")]
attachment: Option<Vec<u8>>,
attachment_name: Option<String>,
avatar: Option<String>,
@ -189,6 +195,29 @@ mod string {
}
}
mod base64s {
use serde::{de, Deserialize, Deserializer, Serializer};
pub fn serialize<S>(value: &Option<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if let Some(opt) = value {
serializer.collect_str(&base64::encode(opt))
} else {
serializer.serialize_none()
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
where
D: Deserializer<'de>,
{
let string = String::deserialize(deserializer)?;
Some(base64::decode(string).map_err(de::Error::custom)).transpose()
}
}
#[derive(Deserialize)]
pub struct DeleteReminder {
uid: String,
@ -199,6 +228,8 @@ async fn create_database_channel(
channel: ChannelId,
pool: impl Executor<'_, Database = Database> + Copy,
) -> Result<u32, crate::Error> {
println!("{:?}", channel);
let row =
sqlx::query!("SELECT webhook_token, webhook_id FROM channels WHERE channel = ?", channel.0)
.fetch_one(pool)
@ -239,11 +270,7 @@ async fn create_database_channel(
webhook_id,
webhook_token,
channel
) VALUES (
webhook_id = ?,
webhook_token = ?,
channel = ?
)",
) VALUES (?, ?, ?)",
webhook.id.0,
webhook.token,
channel.0

View File

@ -34,7 +34,7 @@ div.reminderContent.is-collapsed .channel-field {
order: 1;
}
div.reminderContent.is-collapsed .columns {
div.reminderContent.is-collapsed .reminder-topbar {
display: inline-flex;
margin-bottom: 0px;
flex-grow: 1;
@ -555,3 +555,11 @@ textarea, input {
.create-reminder {
margin: 0 12px 12px 12px;
}
.button.is-success:not(.is-outlined) {
color: white;
}
.button.is-outlined.is-success {
background-color: white;
}

View File

@ -10,6 +10,7 @@ const $createReminder = document.querySelector("#reminderCreator");
const $createReminderBtn = $createReminder.querySelector("button#createReminder");
const $createTemplateBtn = $createReminder.querySelector("button#createTemplate");
const $loadTemplateBtn = document.querySelector("button#load-template");
const $deleteTemplateBtn = document.querySelector("button#delete-template");
const $templateSelect = document.querySelector("select#templateSelect");
let channels = [];
@ -172,7 +173,7 @@ async function fetch_reminders(guild_id) {
}
async function serialize_reminder(node, mode) {
let interval, utc_time;
let interval, utc_time, expiration_time;
if (mode !== "template") {
interval = get_interval(node);
@ -185,6 +186,15 @@ async function serialize_reminder(node, mode) {
} else {
utc_time = utc_time.toFormat("yyyy-LL-dd'T'HH:mm:ss");
}
expiration_time = luxon.DateTime.fromISO(
node.querySelector('input[name="time"]').value
).setZone("UTC");
if (expiration_time.invalid) {
return { error: "Expiration provided invalid." };
} else {
expiration_time = expiration_time.toFormat("yyyy-LL-dd'T'HH:mm:ss");
}
}
let rgb_color = window.getComputedStyle(
@ -253,7 +263,7 @@ async function serialize_reminder(node, mode) {
),
embed_title: node.querySelector('textarea[name="embed_title"]').value,
embed_fields: fields,
expires: null,
expires: expiration_time,
interval_seconds: mode !== "template" ? interval.seconds : null,
interval_months: mode !== "template" ? interval.months : null,
name: node.querySelector('input[name="name"]').value,
@ -314,6 +324,14 @@ function deserialize_reminder(reminder, frame, mode) {
zone: "UTC",
}).setZone(timezone);
timeInput.value = localTime.toFormat("yyyy-LL-dd'T'HH:mm:ss");
if (reminder['expires']) {
let expiresInput = frame.querySelector('input[name="time"]');
let expiresTime = luxon.DateTime.fromISO(reminder["expires"], {
zone: "UTC",
}).setZone(timezone);
expiresInput.value = expiresTime.toFormat("yyyy-LL-dd'T'HH:mm:ss");
}
}
}
@ -578,12 +596,6 @@ function set_channels(element) {
newOption.value = channel.id;
newOption.textContent = channel.name;
if (channel.webhook_avatar !== null) {
newOption.dataset["webhookAvatar"] = channel.webhook_avatar;
}
if (channel.webhook_name !== null) {
newOption.dataset["webhookName"] = channel.webhook_name;
}
element.appendChild(newOption);
}
@ -659,6 +671,10 @@ $createReminderBtn.addEventListener("click", async () => {
});
$createTemplateBtn.addEventListener("click", async () => {
$createTemplateBtn.querySelector("span.icon > i").classList = [
"fas fa-spinner fa-spin",
];
let reminder = await serialize_reminder($createReminder, "template");
let guild = guildId();
@ -700,6 +716,25 @@ $loadTemplateBtn.addEventListener("click", (ev) => {
);
});
$deleteTemplateBtn.addEventListener("click", (ev) => {
fetch(`/dashboard/api/guild/${guildId()}/templates`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({id: parseInt($templateSelect.value)}),
})
.then((response) => response.json())
.then((data) => {
if (data.error) {
show_error(data.error)
} else {
$templateSelect.querySelector(`option[value="${$templateSelect.value}"]`).remove();
}
})
});
document.querySelectorAll("textarea.autoresize").forEach((element) => {
element.addEventListener("input", () => {
element.style.height = "";

View File

@ -6,7 +6,7 @@
<meta charset="UTF-8">
<meta name="yandex-verification" content="bb77b8681eb64a90"/>
<meta name="google-site-verification" content="7h7UVTeEe0AOzHiH3cFtsqMULYGN-zCZdMT_YCkW1Ho"/>
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src *; font-src fonts.gstatic.com 'self'">
<!-- <meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src *; font-src fonts.gstatic.com 'self'"> -->
<!-- favicon -->
<link rel="apple-touch-icon" sizes="180x180"

View File

@ -112,7 +112,7 @@
</article>
</div>
<div class="column settings">
<div class="columns is-mobile">
<div class="columns is-mobile reminder-topbar">
<div class="column">
<div class="field">
<div class="control">
@ -128,33 +128,39 @@
</div>
</div>
<div class="field channel-field">
<div class="collapses">
<label class="label" for="channelOption">Channel</label>
</div>
<div class="control has-icons-left">
<div class="select">
<select name="channel" class="channel-selector">
</select>
</div>
<div class="icon is-small is-left">
<i class="fas fa-hashtag"></i>
<div class="columns">
<div class="column">
<div class="field channel-field">
<div class="collapses">
<label class="label" for="channelOption">Channel*</label>
</div>
<div class="control has-icons-left">
<div class="select">
<select name="channel" class="channel-selector">
</select>
</div>
<div class="icon is-small is-left">
<i class="fas fa-hashtag"></i>
</div>
</div>
</div>
</div>
<div class="column">
<div class="field">
<div class="control">
<label class="label collapses">
Time*
<input class="input" type="datetime-local" step="1" name="time">
</label>
</div>
</div>
</div>
</div>
<div class="collapses">
<div class="field">
<div class="control">
<label class="label">
Time
<input class="input" type="datetime-local" step="1" name="time">
</label>
</div>
</div>
<div class="field">
<a class="intervalLabel"><label class="label">Interval <i class="fas fa-chevron-down"></i></label></a>
<div class="control intervalSelector">
<div class="control intervalSelector" style="min-width: 400px;" >
<div class="input interval-group">
<div class="interval-group-left">
<label>
@ -183,6 +189,15 @@
</div>
</div>
<div class="field">
<div class="control">
<label class="label">
Expiration
<input class="input" type="datetime-local" step="1" name="expiration">
</label>
</div>
</div>
<div class="columns">
<div class="column has-text-centered">
<div class="is-boxed">
@ -213,21 +228,20 @@
<span class="pad-left"></span>
{% if creating %}
<button class="button is-outlined show-modal" data-modal="chooseTemplateModal">
Load Template
<button class="button is-success" id="createReminder">
<span>Create Reminder</span> <span class="icon"><i class="fas fa-sparkles"></i></span>
</button>
<button class="button is-success is-outlined" id="createTemplate">
<span>Create Template</span> <span class="icon"><i class="fas fa-file-spreadsheet"></i></span>
</button>
<button class="button is-success" id="createReminder">
<span>Create Reminder</span> <span class="icon"><i class="fas fa-sparkles"></i></span>
<button class="button is-outlined show-modal is-pulled-right" data-modal="chooseTemplateModal">
Load Template
</button>
{% else %}
<button class="button is-primary save-btn">
<button class="button is-success save-btn">
<span>Save</span> <span class="icon"><i class="fas fa-save"></i></span>
</button>
<button class="button is-warning disable-enable">
<span class="is-sr-only">Text content filled by CSS</span>
</button>
<button class="button is-danger delete-reminder">
Delete