Some small changes to make it work for my uses

This commit is contained in:
Lilith Ashley Nyx Arson 🔥 2024-11-07 14:16:29 +01:00
parent ba6c98a314
commit 1a5cee437c
Signed by: lilith
SSH key fingerprint: SHA256:LAjgsAMyT3LO2JVtr6fQ4N3RTYoRRlIm5wAKsbDife4
8 changed files with 108 additions and 215 deletions

79
Cargo.lock generated
View file

@ -563,28 +563,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "filetime"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
dependencies = [
"cfg-if",
"libc",
"redox_syscall 0.4.1",
"windows-sys 0.52.0",
]
[[package]]
name = "flate2"
version = "1.0.30"
@ -795,7 +773,6 @@ dependencies = [
"futures",
"log",
"percent-encoding",
"tar",
"v_htmlescape",
]
@ -876,15 +853,9 @@ checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
[[package]]
name = "libc"
version = "0.2.153"
version = "0.2.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
[[package]]
name = "local-channel"
@ -1007,7 +978,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
"redox_syscall 0.5.1",
"redox_syscall",
"smallvec",
"windows-targets 0.52.5",
]
@ -1102,15 +1073,6 @@ dependencies = [
"getrandom",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_syscall"
version = "0.5.1"
@ -1164,19 +1126,6 @@ dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
"bitflags 2.5.0",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
]
[[package]]
name = "ryu"
version = "1.0.18"
@ -1311,17 +1260,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "tar"
version = "0.4.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb"
dependencies = [
"filetime",
"libc",
"xattr",
]
[[package]]
name = "termcolor"
version = "1.4.1"
@ -1674,17 +1612,6 @@ version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
[[package]]
name = "xattr"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f"
dependencies = [
"libc",
"linux-raw-sys",
"rustix",
]
[[package]]
name = "zerocopy"
version = "0.7.34"

View file

@ -15,7 +15,6 @@ clap = { version = "3.2.20", features = ["cargo"] }
env_logger = "*"
log = "*"
futures = "0.3.24"
tar = "0.4.38"
percent-encoding = "2.2"
v_htmlescape = "0.15"

View file

@ -27,46 +27,56 @@ pub fn directory_listing(
let mut body = String::new();
let base = Path::new(req.path());
for entry in dir.path.read_dir()? {
if dir.is_visible(&entry) {
let entry = entry.unwrap();
let p = match entry.path().strip_prefix(&dir.path) {
Ok(p) if cfg!(windows) => base.join(p).to_string_lossy().replace('\\', "/"),
Ok(p) => base.join(p).to_string_lossy().into_owned(),
Err(_) => continue,
};
let mut paths: Vec<_> = dir.path.read_dir()?.map(|r| r.unwrap()).collect();
paths.sort_by(|a, b| {
let (oan, obn) = (a.file_name(), b.file_name());
let (an, bn) = (oan.to_str().unwrap(), obn.to_str().unwrap());
// if file is a directory, add '/' to the end of the name
if let Ok(metadata) = entry.metadata() {
if metadata.is_dir() {
let _ = write!(
body,
"<tr><td>📂 <a href='{}/'>{}/</a></td> <td><small>[<a href='{}.tar'>.tar</a>]</small></td></tr>",
encode_file_url!(p),
encode_file_name!(entry),
encode_file_url!(p),
);
} else {
let _ = write!(
body,
"<tr><td>🗎 <a href='{}'>{}</a></td> <td>{}</td></tr>",
encode_file_url!(p),
encode_file_name!(entry),
metadata.len(),
);
}
if let (Ok(am), Ok(bm)) = (a.metadata(), b.metadata()) {
return bm.is_dir().cmp(&am.is_dir()).then(an.cmp(bn));
} else {
return an.cmp(bn);
}
});
for entry in paths {
let p = match entry.path().strip_prefix(&dir.path) {
Ok(p) if cfg!(windows) => base.join(p).to_string_lossy().replace('\\', "/"),
Ok(p) => base.join(p).to_string_lossy().into_owned(),
Err(_) => continue,
};
if !base.has_root() {
let _ = write!(
body,
"<tr><td><a href='../'>../</a></td><td>Size</td></tr>\n"
);
}
// if file is a directory, add '/' to the end of the name
if let Ok(metadata) = entry.metadata() {
if metadata.is_dir() {
let _ = write!(
body,
"<tr><td><a href='{}/'>{}/</a></td> <td>dir</td></tr>",
encode_file_url!(p),
encode_file_name!(entry),
);
} else {
continue;
let _ = write!(
body,
"<tr><td><a href='{}'>{}</a></td> <td>{}</td></tr>",
encode_file_url!(p),
encode_file_name!(entry),
metadata.len(),
);
}
} else {
continue;
}
}
let header = format!(
"<h1>Index of {}/</h1>\n\
<small>[<a href='{}.tar'>.tar</a> of whole directory]</small>",
index_of,
if index_of.is_empty() { "_" } else { index_of }
);
let header = format!("<h1>Index of {}/</h1>\n", index_of);
let footer = format!(
r#"<footer><a href="{}">{} {}</a></footer>"#,
@ -81,11 +91,10 @@ pub fn directory_listing(
"<!DOCTYPE html>\n\
<html>\n\
<head>\n\
<title>Index of {}</title>\n\
<title>Index of {}/</title>\n\
<style>\n{}</style></head>\n\
<body>\n{}\n\
<table>\n\
<tr><td>📁 <a href='../'>../</a></td><td>Size</td></tr>\n\
{}\
</table>\n\
{}\

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 135 KiB

View file

@ -1,5 +1,4 @@
mod directory_listing;
mod threaded_archiver;
mod web;
#[actix_web::main]

View file

@ -1,14 +1,35 @@
body {background-color: #282f34; color: #aeb9a6; font-family:monospace; font-size:10.5pt;}
a { color: #adad9d; }
footer a {color: darkgray; text-decoration:none; font-size:smaller}
h1 {margin-bottom: 0}
table td:nth-child(2) {text-align:right}
body {
background-color: #282f34;
color: #aeb9a6;
font-family: monospace;
font-size: 10.5pt;
}
a {
color: #adad9d;
}
footer a {
color: darkgray;
text-decoration: none;
font-size: smaller
}
h1 {
margin-bottom: 0
}
table td:nth-child(2) {
text-align: right
}
table {
width:100%;
margin: 1em auto;
width: 100%;
max-width: 35em;
margin: 1em 0;
padding: 0.5em 0;
border-top: 1px;
border-bottom: 1px;
border-color: #444;
border-style: solid none;
}
}

View file

@ -1,57 +0,0 @@
use futures::prelude::*;
use std::io;
use std::path::Path;
use std::thread;
/*
* TODO:
* don't tar hidden files
*/
type Stream = futures::channel::mpsc::Receiver<bytes::Bytes>;
type Sender = futures::channel::mpsc::Sender<bytes::Bytes>;
pub fn stream_tar_in_thread<P>(path: P) -> Stream
where
P: AsRef<Path> + Send + 'static,
{
let (writer, stream) = StreamWriter::new(64);
thread::spawn(move || {
let mut a = tar::Builder::new(writer);
let last_path_component = path.as_ref().file_name().unwrap();
a.mode(tar::HeaderMode::Deterministic);
a.append_dir_all(last_path_component, &path)
.unwrap_or_else(|e| println!("{}", e));
a.finish().unwrap_or_else(|e| println!("{}", e));
});
stream
}
struct StreamWriter {
tx: Sender,
}
impl StreamWriter {
fn new(size: usize) -> (Self, Stream) {
let (tx, rx) = futures::channel::mpsc::channel(size);
(StreamWriter { tx }, rx)
}
}
impl io::Write for StreamWriter {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
let len = data.len();
futures::executor::block_on(async move {
let buf = bytes::Bytes::copy_from_slice(data);
self.tx.send(buf).await.ok(); // maybe propagate any errors back
});
Ok(len)
}
fn flush(&mut self) -> io::Result<()> {
futures::executor::block_on(async move { self.tx.flush().await.ok() });
Ok(())
}
}

View file

@ -1,8 +1,8 @@
use actix_files::{Files, NamedFile};
use actix_files::Files;
use actix_web::{
get, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer, Responder,
dev::{fn_service, ServiceRequest, ServiceResponse},
get, middleware, App, HttpResponse, HttpServer, Responder,
};
use futures::StreamExt;
use std::path::PathBuf;
@ -10,15 +10,41 @@ pub async fn run(bind_addr: &str, root: &PathBuf) -> std::io::Result<()> {
let root_ = root.clone();
let s = HttpServer::new(move || {
let static_files = Files::new("/", &root_)
.prefer_utf8(true)
.index_file("index.html")
.use_hidden_files()
.show_files_listing()
.redirect_to_slash_directory()
.files_listing_renderer(crate::directory_listing::directory_listing);
.files_listing_renderer(crate::directory_listing::directory_listing)
.default_handler(fn_service(|req: ServiceRequest| async {
let (req, _) = req.into_parts();
let style = include_str!("style.css");
let html = format!(
"<!DOCTYPE html>\n\
<html>\n\
<head>\n\
<title>Error</title>\n\
<style>\n{}</style></head>\n\
<body>\n\
<h1>Error</h1>\
<p>File not found</p>\
</body>\n</html>",
style
);
Ok(ServiceResponse::new(
req,
HttpResponse::NotFound()
.content_type("text/html; charset=utf-8")
.body(html),
))
}));
App::new()
.app_data(root_.clone())
.wrap(middleware::Logger::default())
.service(favicon_ico)
.service(handle_tar)
.service(static_files)
})
.bind(bind_addr)?
@ -28,37 +54,6 @@ pub async fn run(bind_addr: &str, root: &PathBuf) -> std::io::Result<()> {
s.await
}
#[get("/{tail:.*}.tar")]
async fn handle_tar(
req: HttpRequest,
root: web::Data<PathBuf>,
tail: web::Path<String>,
) -> impl Responder {
let relpath = PathBuf::from(tail.trim_end_matches('/'));
let fullpath = root.join(&relpath).canonicalize().unwrap();
// if a .tar already exists, just return it as-is
let mut fullpath_tar = fullpath.clone();
fullpath_tar.set_extension("tar");
if fullpath_tar.is_file() {
return NamedFile::open_async(fullpath_tar)
.await
.unwrap()
.into_response(&req);
}
if !(fullpath.is_dir()) {
return HttpResponse::NotFound().body("Directory not found\n");
}
let stream = crate::threaded_archiver::stream_tar_in_thread(fullpath).map(Ok::<_, Error>);
let response = HttpResponse::Ok()
.content_type("application/x-tar")
.streaming(stream);
response
}
const FAVICON_ICO: &[u8] = include_bytes!("favicon.png");
#[get("/favicon.ico")]