Some small changes to make it work for my uses
This commit is contained in:
parent
ba6c98a314
commit
1a5cee437c
8 changed files with 108 additions and 215 deletions
79
Cargo.lock
generated
79
Cargo.lock
generated
|
@ -563,28 +563,6 @@ version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
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]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.30"
|
version = "1.0.30"
|
||||||
|
@ -795,7 +773,6 @@ dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"log",
|
"log",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"tar",
|
|
||||||
"v_htmlescape",
|
"v_htmlescape",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -876,15 +853,9 @@ checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.153"
|
version = "0.2.161"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "linux-raw-sys"
|
|
||||||
version = "0.4.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "local-channel"
|
name = "local-channel"
|
||||||
|
@ -1007,7 +978,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall 0.5.1",
|
"redox_syscall",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-targets 0.52.5",
|
"windows-targets 0.52.5",
|
||||||
]
|
]
|
||||||
|
@ -1102,15 +1073,6 @@ dependencies = [
|
||||||
"getrandom",
|
"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]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
@ -1164,19 +1126,6 @@ dependencies = [
|
||||||
"semver",
|
"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]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
|
@ -1311,17 +1260,6 @@ dependencies = [
|
||||||
"unicode-ident",
|
"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]]
|
[[package]]
|
||||||
name = "termcolor"
|
name = "termcolor"
|
||||||
version = "1.4.1"
|
version = "1.4.1"
|
||||||
|
@ -1674,17 +1612,6 @@ version = "0.52.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
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]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.7.34"
|
version = "0.7.34"
|
||||||
|
|
|
@ -15,7 +15,6 @@ clap = { version = "3.2.20", features = ["cargo"] }
|
||||||
env_logger = "*"
|
env_logger = "*"
|
||||||
log = "*"
|
log = "*"
|
||||||
futures = "0.3.24"
|
futures = "0.3.24"
|
||||||
tar = "0.4.38"
|
|
||||||
percent-encoding = "2.2"
|
percent-encoding = "2.2"
|
||||||
v_htmlescape = "0.15"
|
v_htmlescape = "0.15"
|
||||||
|
|
||||||
|
|
|
@ -27,29 +27,45 @@ pub fn directory_listing(
|
||||||
let mut body = String::new();
|
let mut body = String::new();
|
||||||
let base = Path::new(req.path());
|
let base = Path::new(req.path());
|
||||||
|
|
||||||
for entry in dir.path.read_dir()? {
|
let mut paths: Vec<_> = dir.path.read_dir()?.map(|r| r.unwrap()).collect();
|
||||||
if dir.is_visible(&entry) {
|
paths.sort_by(|a, b| {
|
||||||
let entry = entry.unwrap();
|
let (oan, obn) = (a.file_name(), b.file_name());
|
||||||
|
let (an, bn) = (oan.to_str().unwrap(), obn.to_str().unwrap());
|
||||||
|
|
||||||
|
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) {
|
let p = match entry.path().strip_prefix(&dir.path) {
|
||||||
Ok(p) if cfg!(windows) => base.join(p).to_string_lossy().replace('\\', "/"),
|
Ok(p) if cfg!(windows) => base.join(p).to_string_lossy().replace('\\', "/"),
|
||||||
Ok(p) => base.join(p).to_string_lossy().into_owned(),
|
Ok(p) => base.join(p).to_string_lossy().into_owned(),
|
||||||
Err(_) => continue,
|
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 file is a directory, add '/' to the end of the name
|
||||||
if let Ok(metadata) = entry.metadata() {
|
if let Ok(metadata) = entry.metadata() {
|
||||||
if metadata.is_dir() {
|
if metadata.is_dir() {
|
||||||
let _ = write!(
|
let _ = write!(
|
||||||
body,
|
body,
|
||||||
"<tr><td>📂 <a href='{}/'>{}/</a></td> <td><small>[<a href='{}.tar'>.tar</a>]</small></td></tr>",
|
"<tr><td><a href='{}/'>{}/</a></td> <td>dir</td></tr>",
|
||||||
encode_file_url!(p),
|
encode_file_url!(p),
|
||||||
encode_file_name!(entry),
|
encode_file_name!(entry),
|
||||||
encode_file_url!(p),
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let _ = write!(
|
let _ = write!(
|
||||||
body,
|
body,
|
||||||
"<tr><td>🗎 <a href='{}'>{}</a></td> <td>{}</td></tr>",
|
"<tr><td><a href='{}'>{}</a></td> <td>{}</td></tr>",
|
||||||
encode_file_url!(p),
|
encode_file_url!(p),
|
||||||
encode_file_name!(entry),
|
encode_file_name!(entry),
|
||||||
metadata.len(),
|
metadata.len(),
|
||||||
|
@ -59,14 +75,8 @@ pub fn directory_listing(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let header = format!(
|
let header = format!("<h1>Index of {}/</h1>\n", index_of);
|
||||||
"<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 footer = format!(
|
let footer = format!(
|
||||||
r#"<footer><a href="{}">{} {}</a></footer>"#,
|
r#"<footer><a href="{}">{} {}</a></footer>"#,
|
||||||
|
@ -81,11 +91,10 @@ pub fn directory_listing(
|
||||||
"<!DOCTYPE html>\n\
|
"<!DOCTYPE html>\n\
|
||||||
<html>\n\
|
<html>\n\
|
||||||
<head>\n\
|
<head>\n\
|
||||||
<title>Index of {}</title>\n\
|
<title>Index of {}/</title>\n\
|
||||||
<style>\n{}</style></head>\n\
|
<style>\n{}</style></head>\n\
|
||||||
<body>\n{}\n\
|
<body>\n{}\n\
|
||||||
<table>\n\
|
<table>\n\
|
||||||
<tr><td>📁 <a href='../'>../</a></td><td>Size</td></tr>\n\
|
|
||||||
{}\
|
{}\
|
||||||
</table>\n\
|
</table>\n\
|
||||||
{}\
|
{}\
|
||||||
|
|
BIN
src/favicon.png
BIN
src/favicon.png
Binary file not shown.
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 135 KiB |
|
@ -1,5 +1,4 @@
|
||||||
mod directory_listing;
|
mod directory_listing;
|
||||||
mod threaded_archiver;
|
|
||||||
mod web;
|
mod web;
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
|
|
|
@ -1,11 +1,32 @@
|
||||||
body {background-color: #282f34; color: #aeb9a6; font-family:monospace; font-size:10.5pt;}
|
body {
|
||||||
a { color: #adad9d; }
|
background-color: #282f34;
|
||||||
footer a {color: darkgray; text-decoration:none; font-size:smaller}
|
color: #aeb9a6;
|
||||||
h1 {margin-bottom: 0}
|
font-family: monospace;
|
||||||
table td:nth-child(2) {text-align:right}
|
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 {
|
table {
|
||||||
width:100%;
|
width: 100%;
|
||||||
margin: 1em auto;
|
max-width: 35em;
|
||||||
|
margin: 1em 0;
|
||||||
padding: 0.5em 0;
|
padding: 0.5em 0;
|
||||||
border-top: 1px;
|
border-top: 1px;
|
||||||
border-bottom: 1px;
|
border-bottom: 1px;
|
||||||
|
|
|
@ -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(())
|
|
||||||
}
|
|
||||||
}
|
|
67
src/web.rs
67
src/web.rs
|
@ -1,8 +1,8 @@
|
||||||
use actix_files::{Files, NamedFile};
|
use actix_files::Files;
|
||||||
use actix_web::{
|
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;
|
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 root_ = root.clone();
|
||||||
let s = HttpServer::new(move || {
|
let s = HttpServer::new(move || {
|
||||||
let static_files = Files::new("/", &root_)
|
let static_files = Files::new("/", &root_)
|
||||||
|
.prefer_utf8(true)
|
||||||
|
.index_file("index.html")
|
||||||
|
.use_hidden_files()
|
||||||
.show_files_listing()
|
.show_files_listing()
|
||||||
.redirect_to_slash_directory()
|
.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::new()
|
||||||
.app_data(root_.clone())
|
.app_data(root_.clone())
|
||||||
.wrap(middleware::Logger::default())
|
.wrap(middleware::Logger::default())
|
||||||
.service(favicon_ico)
|
.service(favicon_ico)
|
||||||
.service(handle_tar)
|
|
||||||
.service(static_files)
|
.service(static_files)
|
||||||
})
|
})
|
||||||
.bind(bind_addr)?
|
.bind(bind_addr)?
|
||||||
|
@ -28,37 +54,6 @@ pub async fn run(bind_addr: &str, root: &PathBuf) -> std::io::Result<()> {
|
||||||
s.await
|
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");
|
const FAVICON_ICO: &[u8] = include_bytes!("favicon.png");
|
||||||
|
|
||||||
#[get("/favicon.ico")]
|
#[get("/favicon.ico")]
|
||||||
|
|
Loading…
Add table
Reference in a new issue