From 676fe5f1d77b1e9b0109a6381c7fb78af9128074 Mon Sep 17 00:00:00 2001 From: G2-Games Date: Tue, 22 Oct 2024 03:07:28 -0500 Subject: [PATCH] Database works, uploading now shows the resulting link to the client --- .gitignore | 1 + Cargo.lock | 14 +++---- Cargo.toml | 3 +- Rocket.toml | 8 +--- src/database.rs | 15 ++++++- src/main.rs | 52 ++++++++++++++++------- src/static/form_handler.js | 41 ------------------- src/static/main.css | 13 ++++-- src/static/request.js | 84 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 154 insertions(+), 77 deletions(-) delete mode 100644 src/static/form_handler.js create mode 100644 src/static/request.js diff --git a/.gitignore b/.gitignore index 41614f3..355dd8d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target /tmp /src/files +*.mochi diff --git a/Cargo.lock b/Cargo.lock index 5aadeee..5cc2997 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -187,9 +187,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "cc" @@ -740,7 +740,6 @@ dependencies = [ "log", "maud", "rocket", - "serde", "uuid", ] @@ -1081,6 +1080,7 @@ dependencies = [ "rocket_codegen", "rocket_http", "serde", + "serde_json", "state", "tempfile", "time", @@ -1181,18 +1181,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.211" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "1ac55e59090389fb9f0dd9e0f3c09615afed1d19094284d0b200441f13550793" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.211" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "54be4f245ce16bc58d57ef2716271d0d4519e0f6defa147f6e081005bcb278ff" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 7541250..132eaa6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,7 @@ blake3 = { version = "1.5.4", features = ["serde"] } chrono = { version = "0.4.38", features = ["serde"] } log = "0.4" maud = { version = "0.26", features = ["rocket"] } -rocket = "0.5" -serde = { version = "1.0.210", features = ["derive"] } +rocket = { version = "0.5", features = ["json"] } uuid = { version = "1.11.0", features = ["v4"] } [profile.production] diff --git a/Rocket.toml b/Rocket.toml index 8fbf7c8..4530c68 100644 --- a/Rocket.toml +++ b/Rocket.toml @@ -4,9 +4,5 @@ port = 8950 temp_dir = "tmp" [default.limits] -form = "1 GiB" -data-form = "1 GiB" -file = "1 GiB" -bytes = "1 GiB" -json = "1 GiB" -msgpack = "1 GiB" +data-form = "10 GB" +file = "10 GB" diff --git a/src/database.rs b/src/database.rs index dc3abc2..68e8c8c 100644 --- a/src/database.rs +++ b/src/database.rs @@ -3,6 +3,7 @@ use std::{collections::HashMap, fs::{self, File}, path::{Path, PathBuf}}; use bincode::{config::Configuration, decode_from_std_read, encode_into_std_write, Decode, Encode}; use chrono::{DateTime, TimeDelta, Utc}; use blake3::Hash; +use rocket::serde::{Deserialize, Serialize}; const BINCODE_CFG: Configuration = bincode::config::standard(); @@ -49,7 +50,8 @@ impl Database { #[derive(Debug, Clone)] #[derive(Decode, Encode)] -#[derive(serde::Deserialize, serde::Serialize)] +#[derive(Deserialize, Serialize)] +#[serde(crate = "rocket::serde")] pub struct MochiFile { /// The original name of the file name: String, @@ -95,17 +97,26 @@ impl MochiFile { } } + pub fn name(&self) -> &String { + &self.name + } + pub fn get_key(&self) -> MochiKey { MochiKey { name: self.name.clone(), hash: self.hash } } + + pub fn get_expiry(&self) -> DateTime { + self.expiry_datetime + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Decode, Encode)] -#[derive(serde::Deserialize, serde::Serialize)] +#[derive(Deserialize, Serialize)] +#[serde(crate = "rocket::serde")] pub struct MochiKey { name: String, #[bincode(with_serde)] diff --git a/src/main.rs b/src/main.rs index 1ecd125..f5b8842 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,12 +2,12 @@ mod database; use std::{path::{Path, PathBuf}, sync::{Arc, RwLock}}; use blake3::Hash; -use chrono::TimeDelta; +use chrono::{DateTime, TimeDelta, Utc}; use database::{Database, MochiFile}; use log::info; use maud::{html, Markup, DOCTYPE, PreEscaped}; use rocket::{ - form::Form, fs::{FileServer, Options, TempFile}, get, post, routes, tokio::{fs::File, io::AsyncReadExt}, FromForm, State + form::Form, fs::{FileServer, Options, TempFile}, get, post, routes, serde::{json::Json, Serialize}, tokio::{fs::File, io::AsyncReadExt}, FromForm, State }; use uuid::Uuid; @@ -18,7 +18,7 @@ fn head(page_title: &str) -> Markup { meta name="viewport" content="width=device-width, initial-scale=1"; title { (page_title) } // Javascript stuff for client side handling - script { (PreEscaped(include_str!("static/form_handler.js"))) } + script { (PreEscaped(include_str!("static/request.js"))) } // CSS for styling the sheets style { (PreEscaped(include_str!("static/main.css"))) } } @@ -29,16 +29,22 @@ fn home() -> Markup { html! { (head("Mochi")) body { - div class="main-wrapper" { - form id="uploadForm" { - label for="fileUpload" class="file-upload" onclick="document.getElementById('fileInput').click()" { - "Upload File" + main { + section class="centered" { + form id="uploadForm" { + label for="fileUpload" class="file-upload" onclick="document.getElementById('fileInput').click()" { + "Upload File" + } + input id="fileInput" type="file" name="fileUpload" onchange="formSubmit(this.parentNode)" style="display:none;"; + } + div class="progress-box" { + progress id="uploadProgress" value="0" max="100" {} + p id="uploadProgressValue" class="progress-value" { "0%" } } - input id="fileInput" type="file" name="fileUpload" onchange="formSubmit(this.parentNode)" style="display:none;"; } - div class="progress-box" { - progress id="uploadProgress" value="0" max="100" {} - p id="uploadProgressValue" class="progress-value" { "0%" } + + section class="centered" id="uploadedFilesDisplay" { + h2 class="sep center" { "Uploaded Files" } } } } @@ -56,7 +62,7 @@ struct Upload<'r> { async fn handle_upload( mut file_data: Form>, db: &State>> -) -> Result<(), std::io::Error> { +) -> Result, std::io::Error> { let mut out_path = PathBuf::from("files/"); // Get temp path and hash it @@ -69,7 +75,7 @@ async fn handle_upload( file_data.file.raw_name().unwrap().dangerous_unsafe_unsanitized_raw().as_str(), hash.0 ); - out_path.push(filename); + out_path.push(filename.clone()); let constructed_file = MochiFile::new_with_expiry( file_data.file.raw_name().unwrap().dangerous_unsafe_unsanitized_raw().as_str(), @@ -82,10 +88,26 @@ async fn handle_upload( // Move it to the new proper place std::fs::rename(temp_filename, out_path)?; - db.write().unwrap().files.insert(constructed_file.get_key(), constructed_file); + db.write().unwrap().files.insert(constructed_file.get_key(), constructed_file.clone()); db.write().unwrap().save(); - Ok(()) + let location = FileLocation { + name: constructed_file.name().clone(), + status: true, + url: Some("files/".to_string() + &filename), + expires: constructed_file.get_expiry(), + }; + + Ok(Json(location)) +} + +#[derive(Serialize)] +#[serde(crate = "rocket::serde")] +struct FileLocation { + pub name: String, + pub status: bool, + pub url: Option, + pub expires: DateTime, } /// Get the Blake3 hash of a file, without reading it all into memory, and also get the size diff --git a/src/static/form_handler.js b/src/static/form_handler.js deleted file mode 100644 index dca807c..0000000 --- a/src/static/form_handler.js +++ /dev/null @@ -1,41 +0,0 @@ -let progressBar = null; -let progressValue = null; - -function formSubmit(form) { - let url = "/upload"; - let request = new XMLHttpRequest(); - request.open('POST', url, true); - request.onload = function() { // request successful - console.log(request.responseText); - }; - - request.upload.onprogress = uploadProgress; - - request.onerror = function() { - console.log(request.responseText); - }; - - // Create and send FormData - request.send(new FormData(form)); - - // Reset the form data since we've successfully submitted it - form.reset(); -} - -function uploadProgress(progress) { - if (progress.lengthComputable) { - const progressPercent = Math.floor((progress.loaded / progress.total) * 100); - progressBar.value = progressPercent; - progressValue.textContent = progressPercent + "%"; - } -} - -function attachFormSubmitEvent(formId) { - document.getElementById(formId).addEventListener("submit", formSubmit); -} - -document.addEventListener("DOMContentLoaded", function(_event){ - attachFormSubmitEvent("uploadForm"); - progressBar = document.getElementById("uploadProgress"); - progressValue = document.getElementById("uploadProgressValue"); -}) diff --git a/src/static/main.css b/src/static/main.css index 32cae77..9d97277 100644 --- a/src/static/main.css +++ b/src/static/main.css @@ -26,17 +26,22 @@ progress[value] { border-radius: 5px; -webkit-appearance: none; appearance: none; - + background-color: #fffde6; width: 100%; height: 100%; } -progress[value]::-webkit-progress-bar, progress[value]::-moz-progress-bar { - background-color: #a6e3a1; +::-webkit-progress-bar { border-radius: 5px; + background-color: #fffde6; } -progress[value]::-webkit-progress-value { +progress[value]::-moz-progress-bar { + border-radius: 5px; + background-color: #a6e3a1; +} + +::-webkit-progress-value { background-color: #a6e3a1; border-radius: 5px; } diff --git a/src/static/request.js b/src/static/request.js new file mode 100644 index 0000000..03ba5f1 --- /dev/null +++ b/src/static/request.js @@ -0,0 +1,84 @@ +let progressBar = null; +let progressValue = null; +let statusNotifier = null; + +let uploadInProgress = false; +let uploadedFilesDisplay = null; + +async function formSubmit(form) { + let url = "/upload"; + let request = new XMLHttpRequest(); + request.open('POST', url, true); + + request.addEventListener('load', uploadComplete, false); + request.addEventListener('error', networkErrorHandler, false); + request.upload.addEventListener('progress', uploadProgress, false); + + uploadInProgress = true; + // Create and send FormData + try { + request.send(new FormData(form)); + } catch (e) { + console.log(e); + } + + // Reset the form data since we've successfully submitted it + form.reset(); +} + +function networkErrorHandler(_err) { + uploadInProgress = false; + console.log("An error occured while uploading"); + progressValue.textContent = "A network error occured!"; +} + +function uploadComplete(response) { + let target = response.target; + + console.log(target); + if (target.status === 200) { + const response = JSON.parse(target.responseText); + + console.log(response); + if (response.status) { + progressValue.textContent = "Success"; + addToList(response.name, response.url); + } + } else if (target.status === 413) { + progressValue.textContent = "File too large!"; + } + + uploadInProgress = false; +} + +function addToList(filename, link) { + const link_row = uploadedFilesDisplay.appendChild(document.createElement("p")); + const new_link = link_row.appendChild(document.createElement("a")); + + new_link.href = link; + new_link.textContent = filename; +} + +function uploadProgress(progress) { + if (progress.lengthComputable) { + const progressPercent = Math.floor((progress.loaded / progress.total) * 100); + progressBar.value = progressPercent; + progressValue.textContent = progressPercent + "%"; + } +} + +function attachFormSubmitEvent(formId) { + if (uploadInProgress) { + return; + } + + document.getElementById(formId).addEventListener("submit", formSubmit); +} + +document.addEventListener("DOMContentLoaded", function(_event){ + attachFormSubmitEvent("uploadForm"); + progressBar = document.getElementById("uploadProgress"); + progressValue = document.getElementById("uploadProgressValue"); + statusNotifier = document.getElementById("uploadStatus"); + uploadedFilesDisplay = document.getElementById("uploadedFilesDisplay"); +})