From cb9414bc432916f175ed2edab025f91778c8af03 Mon Sep 17 00:00:00 2001 From: tomoron Date: Tue, 14 Apr 2026 16:11:04 +0200 Subject: [PATCH] file download --- Cargo.lock | 50 +++++++++++++++++++----- Cargo.toml | 6 ++- assets/tailwind.css | 36 ++---------------- src/components/upload/get.rs | 72 +++++++++++++++++++++++++++++++++++ src/components/upload/info.rs | 2 +- src/components/upload/mod.rs | 3 ++ 6 files changed, 125 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f5586f4..0bb37e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -152,9 +152,9 @@ dependencies = [ [[package]] name = "axum" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" dependencies = [ "axum-core", "axum-macros", @@ -181,7 +181,7 @@ dependencies = [ "sha1", "sync_wrapper", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.29.0", "tower", "tower-layer", "tower-service", @@ -232,9 +232,9 @@ dependencies = [ [[package]] name = "axum-macros" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" +checksum = "7aa268c23bfbbd2c4363b9cd302a4f504fb2a9dfe7e3451d66f35dd392e20aca" dependencies = [ "proc-macro2", "quote", @@ -1191,7 +1191,7 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tokio-stream", - "tokio-tungstenite", + "tokio-tungstenite 0.28.0", "tokio-util", "tower", "tower-http", @@ -1497,7 +1497,7 @@ dependencies = [ "subsecond", "thiserror 2.0.18", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.28.0", "tokio-util", "tower", "tower-http", @@ -2540,15 +2540,19 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" name = "httpserver" version = "0.1.0" dependencies = [ + "axum-core", "chrono", "diesel", "dioxus", + "dioxus-asset-resolver", "dioxus-html", "dioxus-primitives", "futures", "random-string", "reqwest 0.13.2", "serde", + "tokio", + "tokio-util", "tracing", "wasm-bindgen", "web-sys", @@ -5226,9 +5230,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.51.0" +version = "1.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd" +checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c" dependencies = [ "bytes", "libc", @@ -5284,6 +5288,18 @@ dependencies = [ "tungstenite 0.28.0", ] +[[package]] +name = "tokio-tungstenite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f72a05e828585856dacd553fba484c242c46e391fb0e58917c942ee9202915c" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.29.0", +] + [[package]] name = "tokio-util" version = "0.7.18" @@ -5561,6 +5577,22 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tungstenite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.2", + "sha1", + "thiserror 2.0.18", +] + [[package]] name = "typenum" version = "1.19.0" diff --git a/Cargo.toml b/Cargo.toml index 4cab82d..a3e59c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,15 +7,19 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +axum-core = { version = "0.5.6", optional = true } chrono = { version = "0.4.44", features = ["serde"] } diesel = { version = "2.3.7", features = ["postgres", "r2d2"], optional = true } dioxus = { version = "0.7.1", features = ["router", "fullstack"] } +dioxus-asset-resolver = "0.7.4" dioxus-html = "0.7.3" dioxus-primitives = { git = "https://github.com/DioxusLabs/components", version = "0.0.1", default-features = false } futures = "0.3.32" random-string = "1.1.0" reqwest = { version = "0.13.2", features = ["stream"] } serde = "1.0.228" +tokio = { version = "1.51.1", optional = true } +tokio-util = { version = "0.7.18", optional = true } tracing = "0.1.44" wasm-bindgen = "0.2.114" web-sys = { version = "0.3.91", features = [ "Navigator", "Clipboard" ] } @@ -29,7 +33,7 @@ desktop = ["dioxus/desktop"] # The feature that are only required for the mobile = ["dioxus/mobile"] build target should be optional and only enabled in the mobile = ["dioxus/mobile"] feature mobile = ["dioxus/mobile"] # The feature that are only required for the server = ["dioxus/server"] build target should be optional and only enabled in the server = ["dioxus/server"] feature -server = ["dioxus/server", "dep:diesel"] +server = ["dioxus/server", "dep:diesel", "dep:tokio", "dep:tokio-util", "dep:axum-core"] [profile.release] opt-level = "z" diff --git a/assets/tailwind.css b/assets/tailwind.css index 9396f7c..2438a8f 100644 --- a/assets/tailwind.css +++ b/assets/tailwind.css @@ -7,7 +7,6 @@ 'Noto Color Emoji'; --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; - --color-blue-100: oklch(93.2% 0.032 255.585); --color-blue-800: oklch(42.4% 0.199 265.638); --color-zinc-900: oklch(21% 0.006 285.885); --color-white: #fff; @@ -171,6 +170,9 @@ .static { position: static; } + .contents { + display: contents; + } .flex { display: flex; } @@ -192,25 +194,12 @@ .w-screen { width: 100vw; } - .border-collapse { - border-collapse: collapse; - } .transform { transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,); } - .resize { - resize: both; - } .flex-col { flex-direction: column; } - .flex-row { - flex-direction: row; - } - .border { - border-style: var(--tw-border-style); - border-width: 1px; - } .bg-zinc-900 { background-color: var(--color-zinc-900); } @@ -231,9 +220,6 @@ --tw-font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold); } - .text-blue-100 { - color: var(--color-blue-100); - } .text-blue-800 { color: var(--color-blue-800); } @@ -243,10 +229,6 @@ .underline { text-decoration-line: underline; } - .outline { - outline-style: var(--tw-outline-style); - outline-width: 1px; - } } @property --tw-rotate-x { syntax: "*"; @@ -268,20 +250,10 @@ syntax: "*"; inherits: false; } -@property --tw-border-style { - syntax: "*"; - inherits: false; - initial-value: solid; -} @property --tw-font-weight { syntax: "*"; inherits: false; } -@property --tw-outline-style { - syntax: "*"; - inherits: false; - initial-value: solid; -} @layer properties { @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) { *, ::before, ::after, ::backdrop { @@ -290,9 +262,7 @@ --tw-rotate-z: initial; --tw-skew-x: initial; --tw-skew-y: initial; - --tw-border-style: solid; --tw-font-weight: initial; - --tw-outline-style: solid; } } } diff --git a/src/components/upload/get.rs b/src/components/upload/get.rs index e69de29..b7163ca 100644 --- a/src/components/upload/get.rs +++ b/src/components/upload/get.rs @@ -0,0 +1,72 @@ +use std::fs::create_dir; + +use dioxus::prelude::*; +use dioxus::fullstack::FileStream; +#[cfg(feature = "server")] +use httpserver::models::GetFile; +#[cfg(feature = "server")] +use diesel::prelude::*; +#[cfg(feature = "server")] +use httpserver::DB; + +#[cfg(feature = "server")] +use httpserver::schema; + +#[cfg(feature = "server")] +use crate::config::ServerConfig; + +use std::path::Path; + + +#[cfg(feature = "server")] +async fn create_filestream(filename: String, file: &Path) -> Result { + + let metadata = file.metadata()?; + let contents = tokio::fs::File::open(&file).await?; + let mime = dioxus_asset_resolver::native::get_mime_from_ext( + file.extension().and_then(|s| s.to_str()), + ); + let size = metadata.len(); + + let reader_stream = tokio_util::io::ReaderStream::new(contents); + + let body = axum_core::body::Body::from_stream(reader_stream).into_data_stream(); + Ok(FileStream::from_raw(filename, Some(size), mime.to_string(), body)) +} + +#[get("/upload/{id}/dl")] +async fn download_file(id: String) -> Result { + use schema::file; + let s_config = ServerConfig::load(); + + let result = DB.with(|pool| schema::file::table.select((file::filename)) + .filter(file::stored_filename.eq(id.clone())) + .filter(file::deleted.eq(false)) + .load::<(String)>(&mut pool.get().unwrap())); + + if let Err(_) = result { + return HttpError::internal_server_error("Database request failed"); + } + + let db_file = result.unwrap(); + + if db_file.len() == 0 { + return HttpError::internal_server_error("The requested file does not exist"); + } + + let file_path = s_config.upload_folder + &id; + let path = Path::new(&file_path); + + // create a filestream from raw to set the filename to the uploaded filename + let fstream = create_filestream(db_file[0].clone(), path.into()).await; + + match fstream { + Ok(stream) => { + Ok(stream) + }, + Err(err) => { + tracing::info!("Failed to open a file err : {err}"); + HttpError::internal_server_error(format!("Failed to open the file")) + } + } +} diff --git a/src/components/upload/info.rs b/src/components/upload/info.rs index 8654635..dd821a7 100644 --- a/src/components/upload/info.rs +++ b/src/components/upload/info.rs @@ -29,7 +29,7 @@ struct FetchedInfo { } -#[post("/api/upload/info")] +#[get("/api/upload/info?filename")] async fn get_file_info(filename: String) -> Result { use schema::file; let result = DB.with(|pool| schema::file::table.select((file::filename, file::file_size, file::created_at)) diff --git a/src/components/upload/mod.rs b/src/components/upload/mod.rs index 21028a8..41b10a1 100644 --- a/src/components/upload/mod.rs +++ b/src/components/upload/mod.rs @@ -3,3 +3,6 @@ pub use create::UploadFile; mod info; pub use info::FileInfo; + +mod get; +