diff --git a/Cargo.lock b/Cargo.lock index 291a6db..252de71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -763,6 +763,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +[[package]] +name = "human-size" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9994b79e8c1a39b3166c63ae7823bb2b00831e2a96a31399c50fe69df408eaeb" + [[package]] name = "humansize" version = "2.1.3" @@ -1979,10 +1985,12 @@ name = "serves3" version = "0.1.0" dependencies = [ "config", + "human-size", "lazy_static", "rocket", "rocket_dyn_templates", "rust-s3", + "serde", "tempdir", ] diff --git a/Cargo.toml b/Cargo.toml index e594759..df460ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,10 @@ license = "EUPL-1.2" [dependencies] config = "0.13" +human-size = "0.4" lazy_static = "1.4" rocket = { git = "https://github.com/SergioBenitez/Rocket", rev = "v0.5.0-rc.3" } rocket_dyn_templates = { git = "https://github.com/SergioBenitez/Rocket.git", rev = "v0.5.0-rc.3", features = ["tera"] } rust-s3 = { version = "0.32", default-features = false, features = ["tokio-rustls-tls"] } -tempdir = "0.3" +serde = { version = "1.0" } +tempdir = { version = "0.3" } diff --git a/src/main.rs b/src/main.rs index 9e16865..e270657 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ use { lazy_static::lazy_static, rocket::response::Responder, + rocket::serde::Serialize, rocket_dyn_templates::{context, Template}, std::path::PathBuf, }; @@ -75,6 +76,14 @@ enum FileView { File(Vec), } +#[derive(Serialize)] +struct FileViewItem { + path: String, + size: String, + size_bytes: u64, + last_modification: String, +} + #[derive(Responder, Debug)] enum Error { #[response(status = 404)] @@ -136,7 +145,7 @@ async fn s3_serve_file(path: &PathBuf) -> Result { } } -async fn s3_fileview(path: &PathBuf) -> Result, Error> { +async fn s3_fileview(path: &PathBuf) -> Result, Error> { /* if listing a folder: - folders will be under 'common_prefixes' @@ -156,33 +165,64 @@ async fn s3_fileview(path: &PathBuf) -> Result, Error> { let objects = s3_objects .iter() - .flat_map(|list| -> Vec> { + .flat_map(|list| -> Vec> { let prefix = if let Some(p) = &list.prefix { p.as_str() } else { "" }; - let folders = list - .common_prefixes - .iter() - .flatten() - .map(|dir| dir.prefix.strip_prefix(&prefix)); + let folders = list.common_prefixes.iter().flatten().map(|dir| { + let path = dir.prefix.strip_prefix(&prefix); + path.map(|path| FileViewItem { + path: path.to_owned(), + size_bytes: 0, + size: "[DIR]".to_owned(), + last_modification: String::default(), + }) + }); - let files = list - .contents - .iter() - .map(|obj| obj.key.strip_prefix(&prefix)); + let files = list.contents.iter().map(|obj| { + let path = obj.key.strip_prefix(&prefix); + path.map(|path| FileViewItem { + path: path.to_owned(), + size_bytes: obj.size, + size: size_bytes_to_human(obj.size), + last_modification: obj.last_modified.clone(), + }) + }); folders.chain(files).collect() }) .flatten() - .map(str::to_owned) .collect(); Ok(objects) } +fn size_bytes_to_human(bytes: u64) -> String { + use human_size::{Any, SpecificSize}; + + let size: f64 = bytes as f64; + let digits = size.log10().floor() as u32; + let mut order = digits / 3; + let unit = match order { + 0 => Any::Byte, + 1 => Any::Kilobyte, + 2 => Any::Megabyte, + _ => { + order = 3; // Let's stop here. + Any::Gigabyte + } + }; + + format!( + "{:.3}", + SpecificSize::new(size / 10u64.pow(order * 3) as f64, unit) + .unwrap_or(SpecificSize::new(0., Any::Byte).unwrap()) + ) +} + #[rocket::launch] fn rocket() -> _ { eprintln!("Proxying to {} for {}", BUCKET.host(), BUCKET.name()); diff --git a/templates/index.html.tera b/templates/index.html.tera index f6c7f42..9401663 100644 --- a/templates/index.html.tera +++ b/templates/index.html.tera @@ -4,17 +4,53 @@ SPDX-License-Identifier: EUPL-1.2 --> - + + +

{{ path }}

-
    - {% if path != "/" %} -
  • ..
  • - {% endif %} + + + + + + + + + + {% if path != "/" %} + + + + + + {% endif %} - {% for object in objects %} -
  • {{ object }}
  • - {% endfor %} - + {% for object in objects %} + + + + + + {% endfor %} + + +
    FilenameSizeModified
    ..
    {{ object.path }}{{ object.size }}{{ object.last_modification}}