file download
This commit is contained in:
50
Cargo.lock
generated
50
Cargo.lock
generated
@ -152,9 +152,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
version = "0.8.8"
|
version = "0.8.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8"
|
checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum-core",
|
"axum-core",
|
||||||
"axum-macros",
|
"axum-macros",
|
||||||
@ -181,7 +181,7 @@ dependencies = [
|
|||||||
"sha1",
|
"sha1",
|
||||||
"sync_wrapper",
|
"sync_wrapper",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-tungstenite",
|
"tokio-tungstenite 0.29.0",
|
||||||
"tower",
|
"tower",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
@ -232,9 +232,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum-macros"
|
name = "axum-macros"
|
||||||
version = "0.5.0"
|
version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c"
|
checksum = "7aa268c23bfbbd2c4363b9cd302a4f504fb2a9dfe7e3451d66f35dd392e20aca"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1191,7 +1191,7 @@ dependencies = [
|
|||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tokio-tungstenite",
|
"tokio-tungstenite 0.28.0",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tower",
|
"tower",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
@ -1497,7 +1497,7 @@ dependencies = [
|
|||||||
"subsecond",
|
"subsecond",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-tungstenite",
|
"tokio-tungstenite 0.28.0",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tower",
|
"tower",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
@ -2540,15 +2540,19 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
|||||||
name = "httpserver"
|
name = "httpserver"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"axum-core",
|
||||||
"chrono",
|
"chrono",
|
||||||
"diesel",
|
"diesel",
|
||||||
"dioxus",
|
"dioxus",
|
||||||
|
"dioxus-asset-resolver",
|
||||||
"dioxus-html",
|
"dioxus-html",
|
||||||
"dioxus-primitives",
|
"dioxus-primitives",
|
||||||
"futures",
|
"futures",
|
||||||
"random-string",
|
"random-string",
|
||||||
"reqwest 0.13.2",
|
"reqwest 0.13.2",
|
||||||
"serde",
|
"serde",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
@ -5226,9 +5230,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.51.0"
|
version = "1.51.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd"
|
checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
@ -5284,6 +5288,18 @@ dependencies = [
|
|||||||
"tungstenite 0.28.0",
|
"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]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.7.18"
|
version = "0.7.18"
|
||||||
@ -5561,6 +5577,22 @@ dependencies = [
|
|||||||
"utf-8",
|
"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]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.19.0"
|
version = "1.19.0"
|
||||||
|
|||||||
@ -7,15 +7,19 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
axum-core = { version = "0.5.6", optional = true }
|
||||||
chrono = { version = "0.4.44", features = ["serde"] }
|
chrono = { version = "0.4.44", features = ["serde"] }
|
||||||
diesel = { version = "2.3.7", features = ["postgres", "r2d2"], optional = true }
|
diesel = { version = "2.3.7", features = ["postgres", "r2d2"], optional = true }
|
||||||
dioxus = { version = "0.7.1", features = ["router", "fullstack"] }
|
dioxus = { version = "0.7.1", features = ["router", "fullstack"] }
|
||||||
|
dioxus-asset-resolver = "0.7.4"
|
||||||
dioxus-html = "0.7.3"
|
dioxus-html = "0.7.3"
|
||||||
dioxus-primitives = { git = "https://github.com/DioxusLabs/components", version = "0.0.1", default-features = false }
|
dioxus-primitives = { git = "https://github.com/DioxusLabs/components", version = "0.0.1", default-features = false }
|
||||||
futures = "0.3.32"
|
futures = "0.3.32"
|
||||||
random-string = "1.1.0"
|
random-string = "1.1.0"
|
||||||
reqwest = { version = "0.13.2", features = ["stream"] }
|
reqwest = { version = "0.13.2", features = ["stream"] }
|
||||||
serde = "1.0.228"
|
serde = "1.0.228"
|
||||||
|
tokio = { version = "1.51.1", optional = true }
|
||||||
|
tokio-util = { version = "0.7.18", optional = true }
|
||||||
tracing = "0.1.44"
|
tracing = "0.1.44"
|
||||||
wasm-bindgen = "0.2.114"
|
wasm-bindgen = "0.2.114"
|
||||||
web-sys = { version = "0.3.91", features = [ "Navigator", "Clipboard" ] }
|
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
|
# 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"]
|
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
|
# 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]
|
[profile.release]
|
||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
|
|||||||
@ -7,7 +7,6 @@
|
|||||||
'Noto Color Emoji';
|
'Noto Color Emoji';
|
||||||
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
|
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
|
||||||
monospace;
|
monospace;
|
||||||
--color-blue-100: oklch(93.2% 0.032 255.585);
|
|
||||||
--color-blue-800: oklch(42.4% 0.199 265.638);
|
--color-blue-800: oklch(42.4% 0.199 265.638);
|
||||||
--color-zinc-900: oklch(21% 0.006 285.885);
|
--color-zinc-900: oklch(21% 0.006 285.885);
|
||||||
--color-white: #fff;
|
--color-white: #fff;
|
||||||
@ -171,6 +170,9 @@
|
|||||||
.static {
|
.static {
|
||||||
position: static;
|
position: static;
|
||||||
}
|
}
|
||||||
|
.contents {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
.flex {
|
.flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
@ -192,25 +194,12 @@
|
|||||||
.w-screen {
|
.w-screen {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
}
|
}
|
||||||
.border-collapse {
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
.transform {
|
.transform {
|
||||||
transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
|
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-col {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.flex-row {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
.border {
|
|
||||||
border-style: var(--tw-border-style);
|
|
||||||
border-width: 1px;
|
|
||||||
}
|
|
||||||
.bg-zinc-900 {
|
.bg-zinc-900 {
|
||||||
background-color: var(--color-zinc-900);
|
background-color: var(--color-zinc-900);
|
||||||
}
|
}
|
||||||
@ -231,9 +220,6 @@
|
|||||||
--tw-font-weight: var(--font-weight-bold);
|
--tw-font-weight: var(--font-weight-bold);
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
}
|
}
|
||||||
.text-blue-100 {
|
|
||||||
color: var(--color-blue-100);
|
|
||||||
}
|
|
||||||
.text-blue-800 {
|
.text-blue-800 {
|
||||||
color: var(--color-blue-800);
|
color: var(--color-blue-800);
|
||||||
}
|
}
|
||||||
@ -243,10 +229,6 @@
|
|||||||
.underline {
|
.underline {
|
||||||
text-decoration-line: underline;
|
text-decoration-line: underline;
|
||||||
}
|
}
|
||||||
.outline {
|
|
||||||
outline-style: var(--tw-outline-style);
|
|
||||||
outline-width: 1px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@property --tw-rotate-x {
|
@property --tw-rotate-x {
|
||||||
syntax: "*";
|
syntax: "*";
|
||||||
@ -268,20 +250,10 @@
|
|||||||
syntax: "*";
|
syntax: "*";
|
||||||
inherits: false;
|
inherits: false;
|
||||||
}
|
}
|
||||||
@property --tw-border-style {
|
|
||||||
syntax: "*";
|
|
||||||
inherits: false;
|
|
||||||
initial-value: solid;
|
|
||||||
}
|
|
||||||
@property --tw-font-weight {
|
@property --tw-font-weight {
|
||||||
syntax: "*";
|
syntax: "*";
|
||||||
inherits: false;
|
inherits: false;
|
||||||
}
|
}
|
||||||
@property --tw-outline-style {
|
|
||||||
syntax: "*";
|
|
||||||
inherits: false;
|
|
||||||
initial-value: solid;
|
|
||||||
}
|
|
||||||
@layer properties {
|
@layer properties {
|
||||||
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
|
@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 {
|
*, ::before, ::after, ::backdrop {
|
||||||
@ -290,9 +262,7 @@
|
|||||||
--tw-rotate-z: initial;
|
--tw-rotate-z: initial;
|
||||||
--tw-skew-x: initial;
|
--tw-skew-x: initial;
|
||||||
--tw-skew-y: initial;
|
--tw-skew-y: initial;
|
||||||
--tw-border-style: solid;
|
|
||||||
--tw-font-weight: initial;
|
--tw-font-weight: initial;
|
||||||
--tw-outline-style: solid;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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<FileStream, std::io::Error> {
|
||||||
|
|
||||||
|
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<FileStream, HttpError> {
|
||||||
|
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"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -29,7 +29,7 @@ struct FetchedInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[post("/api/upload/info")]
|
#[get("/api/upload/info?filename")]
|
||||||
async fn get_file_info(filename: String) -> Result<FetchedInfo, HttpError> {
|
async fn get_file_info(filename: String) -> Result<FetchedInfo, HttpError> {
|
||||||
use schema::file;
|
use schema::file;
|
||||||
let result = DB.with(|pool| schema::file::table.select((file::filename, file::file_size, file::created_at))
|
let result = DB.with(|pool| schema::file::table.select((file::filename, file::file_size, file::created_at))
|
||||||
|
|||||||
@ -3,3 +3,6 @@ pub use create::UploadFile;
|
|||||||
|
|
||||||
mod info;
|
mod info;
|
||||||
pub use info::FileInfo;
|
pub use info::FileInfo;
|
||||||
|
|
||||||
|
mod get;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user