From 1519474f93e53fe0f55df953fb318e5210f45b5c Mon Sep 17 00:00:00 2001 From: jude Date: Tue, 20 Jun 2023 13:13:26 +0100 Subject: [PATCH] Report errors to server --- src/component_models/mod.rs | 6 ++-- web/src/lib.rs | 3 +- web/src/routes/dashboard/export.rs | 9 ++++-- web/src/routes/dashboard/guild.rs | 9 ++++-- web/src/routes/dashboard/mod.rs | 8 ++--- web/src/routes/mod.rs | 5 ++- web/src/routes/report.rs | 48 +++++++++++++++++++++++++++++ web/static/favicon/site.webmanifest | 4 +-- web/static/js/reporter.js | 16 ++++++++++ web/templates/dashboard.html.tera | 2 ++ 10 files changed, 92 insertions(+), 18 deletions(-) create mode 100644 web/src/routes/report.rs create mode 100644 web/static/js/reporter.js diff --git a/src/component_models/mod.rs b/src/component_models/mod.rs index ff0fc96..11d6cbf 100644 --- a/src/component_models/mod.rs +++ b/src/component_models/mod.rs @@ -2,6 +2,7 @@ pub(crate) mod pager; use std::io::Cursor; +use base64::{engine::general_purpose, Engine}; use chrono_tz::Tz; use log::warn; use poise::{ @@ -51,11 +52,12 @@ impl ComponentDataModel { pub fn to_custom_id(&self) -> String { let mut buf = Vec::new(); self.serialize(&mut Serializer::new(&mut buf)).unwrap(); - base64::encode(buf) + general_purpose::STANDARD.encode(buf) } pub fn from_custom_id(data: &String) -> Self { - let buf = base64::decode(data) + let buf = general_purpose::STANDARD + .decode(data) .map_err(|e| format!("Could not decode `custom_id' {}: {:?}", data, e)) .unwrap(); let cur = Cursor::new(buf); diff --git a/web/src/lib.rs b/web/src/lib.rs index f0f7904..c3df67a 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -116,7 +116,8 @@ pub async fn initialize( routes::cookies, routes::privacy, routes::terms, - routes::return_to_same_site + routes::return_to_same_site, + routes::report::report_error, ], ) .mount( diff --git a/web/src/routes/dashboard/export.rs b/web/src/routes/dashboard/export.rs index 255a05e..d1f33c1 100644 --- a/web/src/routes/dashboard/export.rs +++ b/web/src/routes/dashboard/export.rs @@ -10,9 +10,12 @@ use serenity::{ }; use sqlx::{MySql, Pool}; -use crate::routes::dashboard::{ - create_reminder, generate_uid, ImportBody, JsonResult, Reminder, ReminderCsv, - ReminderTemplateCsv, TodoCsv, +use crate::routes::{ + dashboard::{ + create_reminder, generate_uid, ImportBody, Reminder, ReminderCsv, ReminderTemplateCsv, + TodoCsv, + }, + JsonResult, }; #[get("/api/guild//export/reminders")] diff --git a/web/src/routes/dashboard/guild.rs b/web/src/routes/dashboard/guild.rs index 9fc66d3..7af714e 100644 --- a/web/src/routes/dashboard/guild.rs +++ b/web/src/routes/dashboard/guild.rs @@ -23,9 +23,12 @@ use crate::{ MAX_EMBED_FOOTER_LENGTH, MAX_EMBED_TITLE_LENGTH, MAX_URL_LENGTH, MAX_USERNAME_LENGTH, MIN_INTERVAL, }, - routes::dashboard::{ - create_database_channel, create_reminder, template_name_default, DeleteReminder, - DeleteReminderTemplate, JsonResult, PatchReminder, Reminder, ReminderTemplate, + routes::{ + dashboard::{ + create_database_channel, create_reminder, template_name_default, DeleteReminder, + DeleteReminderTemplate, PatchReminder, Reminder, ReminderTemplate, + }, + JsonResult, }, }; diff --git a/web/src/routes/dashboard/mod.rs b/web/src/routes/dashboard/mod.rs index dda81d2..fdfd337 100644 --- a/web/src/routes/dashboard/mod.rs +++ b/web/src/routes/dashboard/mod.rs @@ -2,11 +2,7 @@ use std::collections::HashMap; use chrono::{naive::NaiveDateTime, Utc}; use rand::{rngs::OsRng, seq::IteratorRandom}; -use rocket::{ - http::CookieJar, - response::Redirect, - serde::json::{json, Value as JsonValue}, -}; +use rocket::{http::CookieJar, response::Redirect, serde::json::json}; use rocket_dyn_templates::Template; use serde::{Deserialize, Deserializer, Serialize}; use serenity::{ @@ -24,6 +20,7 @@ use crate::{ MAX_EMBED_FIELD_VALUE_LENGTH, MAX_EMBED_FOOTER_LENGTH, MAX_EMBED_TITLE_LENGTH, MAX_URL_LENGTH, MAX_USERNAME_LENGTH, MIN_INTERVAL, }, + routes::JsonResult, Database, Error, }; @@ -31,7 +28,6 @@ pub mod export; pub mod guild; pub mod user; -pub type JsonResult = Result; type Unset = Option; fn name_default() -> String { diff --git a/web/src/routes/mod.rs b/web/src/routes/mod.rs index 29c9352..895be9e 100644 --- a/web/src/routes/mod.rs +++ b/web/src/routes/mod.rs @@ -1,11 +1,14 @@ pub mod dashboard; pub mod login; +pub mod report; use std::collections::HashMap; -use rocket::request::FlashMessage; +use rocket::{request::FlashMessage, serde::json::Value as JsonValue}; use rocket_dyn_templates::Template; +pub type JsonResult = Result; + #[get("/")] pub async fn index(flash: Option>) -> Template { let mut map: HashMap<&str, String> = HashMap::new(); diff --git a/web/src/routes/report.rs b/web/src/routes/report.rs new file mode 100644 index 0000000..8bc20b3 --- /dev/null +++ b/web/src/routes/report.rs @@ -0,0 +1,48 @@ +use rocket::{ + http::CookieJar, + serde::{ + json::{json, Json}, + Deserialize, + }, +}; + +use crate::routes::JsonResult; + +#[derive(Deserialize)] +pub struct ClientError { + #[serde(rename = "reporterId")] + reporter_id: String, + url: String, + #[serde(rename = "relativeTimestamp")] + relative_timestamp: i64, + #[serde(rename = "errorMessage")] + error_message: String, + #[serde(rename = "errorLine")] + error_line: u64, + #[serde(rename = "errorFile")] + error_file: String, + #[serde(rename = "errorType")] + error_type: String, +} + +#[post("/report", data = "")] +pub async fn report_error(cookies: &CookieJar<'_>, client_error: Json) -> JsonResult { + if let Some(user_id) = cookies.get_private("userid") { + error!( + "User {} reports a client-side error. +{}, {}:{} at {}ms +{}: {} +Chain: {}", + user_id, + client_error.url, + client_error.error_file, + client_error.error_line, + client_error.relative_timestamp, + client_error.error_type, + client_error.error_message, + client_error.reporter_id + ); + } + + Ok(json!({})) +} diff --git a/web/static/favicon/site.webmanifest b/web/static/favicon/site.webmanifest index b20abb7..78fcc75 100644 --- a/web/static/favicon/site.webmanifest +++ b/web/static/favicon/site.webmanifest @@ -3,12 +3,12 @@ "short_name": "", "icons": [ { - "src": "/android-chrome-192x192.png", + "src": "/static/favicon/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, { - "src": "/android-chrome-512x512.png", + "src": "/static/favicon/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" } diff --git a/web/static/js/reporter.js b/web/static/js/reporter.js new file mode 100644 index 0000000..91ba35d --- /dev/null +++ b/web/static/js/reporter.js @@ -0,0 +1,16 @@ +const REPORTER_ID = crypto.randomUUID(); + +window.addEventListener("error", async (ev) => { + await fetch("/report", { + method: "POST", + body: JSON.stringify({ + reporterId: REPORTER_ID, + url: window.location.href, + relativeTimestamp: ev.timeStamp, + errorMessage: ev.message, + errorLine: ev.lineno, + errorFile: ev.filename, + errorType: ev.type, + }), + }); +}); diff --git a/web/templates/dashboard.html.tera b/web/templates/dashboard.html.tera index b137b5f..c51b5fc 100644 --- a/web/templates/dashboard.html.tera +++ b/web/templates/dashboard.html.tera @@ -1,6 +1,8 @@ + +