# It is not intended for manual editing.
version = 3
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
+
[[package]]
name = "autocfg"
version = "1.1.0"
"os_str_bytes",
]
+[[package]]
+name = "colored"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
+dependencies = [
+ "atty",
+ "lazy_static",
+ "winapi",
+]
+
[[package]]
name = "duf"
version = "0.1.0"
"clap",
"futures",
"hyper",
+ "log",
"percent-encoding",
"serde",
"serde_json",
+ "simple_logger",
"tokio",
"tokio-util",
]
"libc",
]
+[[package]]
+name = "num_threads"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "once_cell"
version = "1.12.0"
"serde",
]
+[[package]]
+name = "simple_logger"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c75a9723083573ace81ad0cdfc50b858aa3c366c48636edb4109d73122a0c0ea"
+dependencies = [
+ "atty",
+ "colored",
+ "log",
+ "time",
+ "winapi",
+]
+
[[package]]
name = "slab"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
+[[package]]
+name = "time"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd"
+dependencies = [
+ "itoa",
+ "libc",
+ "num_threads",
+ "time-macros",
+]
+
+[[package]]
+name = "time-macros"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
+
[[package]]
name = "tokio"
version = "1.18.2"
serde_json = "1"
tokio-util = { version = "0.7", features = ["codec", "io-util"] }
futures = "0.3"
-base64 = "0.13.0"
+base64 = "0.13"
+log = "0.4"
+simple_logger = "2.1.0"
[profile.release]
lto = true
## Features
- Serve static files
-- Upload/Delete files
-- Support basic auth
+- Upload files
+- Delete files
+- Basic authentication
- Easy to use with curl
## Install
-## Curl
+### Curl
Download a file
```
.help("Authenticate with user and pass")
.value_name("user:pass");
+ let arg_no_log = Arg::new("no-log")
+ .long("--no-log")
+ .help("Don't log any request/response information.");
+
clap::command!()
.about(ABOUT)
.arg(arg_address)
.arg(arg_path)
.arg(arg_readonly)
.arg(arg_auth)
+ .arg(arg_no_log)
}
pub fn matches() -> ArgMatches {
pub path: PathBuf,
pub readonly: bool,
pub auth: Option<String>,
+ pub log: bool,
}
impl Args {
let path = Args::parse_path(path)?;
let readonly = matches.is_present("readonly");
let auth = matches.value_of("auth").map(|v| v.to_owned());
+ let log = !matches.is_present("no-log");
Ok(Args {
address,
path,
readonly,
auth,
+ log,
})
}
const ajax = new XMLHttpRequest();
ajax.upload.addEventListener("progress", e => this.progress(e), false);
ajax.addEventListener("load", e => this.complete(e), false);
- ajax.addEventListener("error", e => this.error(e), false);
- ajax.addEventListener("abort", e => this.abort(e), false);
+ ajax.addEventListener("error", e => this.fail(e), false);
+ ajax.addEventListener("abort", e => this.fail(e), false);
ajax.open("PUT", path);
ajax.send(file);
}
this.$elem.innerHTML = `${this.file.name}`;
}
- error(event) {
- this.$elem.innerHTML = `${this.file.name} (x)`;
- }
-
- abort(event) {
- this.$elem.innerHTML = `${this.file.name} (x)`;
+ fail(event) {
+ this.$elem.innerHTML = `<strike>${this.file.name}</strike>`;
}
}
async function deletePath(index) {
const file = paths[index];
if (!file) return;
- try {
- const res = await fetch(encodeURI(file.path), {
- method: "DELETE",
- });
- if (res.status !== 200) {
- const text = await res.text();
- throw new Error(text);
+
+ const ajax = new XMLHttpRequest();
+ ajax.open("DELETE", encodeURI(file.path));
+ ajax.addEventListener("readystatechange", function() {
+ if(ajax.readyState === 4 && ajax.status === 200) {
+ document.getElementById(`addPath${index}`).remove();
}
- document.getElementById(`addPath${index}`).remove();
- } catch (err) {
- alert(`Failed to delete ${file.name}, ${err.message}`);
- }
+ });
+ ajax.send();
}
function addUploadControl() {
}
}
+#[macro_use]
+extern crate log;
+
mod args;
mod server;
pub type BoxResult<T> = Result<T, Box<dyn std::error::Error>>;
+use log::LevelFilter;
+
use crate::args::{matches, Args};
use crate::server::serve;
#[tokio::main]
async fn main() {
- Args::parse(matches())
- .map(serve)
- .unwrap_or_else(handle_err)
- .await
- .unwrap_or_else(handle_err);
+ run().await.unwrap_or_else(handle_err)
+}
+
+async fn run() -> BoxResult<()> {
+ let args = Args::parse(matches())?;
+
+ let level = if args.log {
+ LevelFilter::Info
+ } else {
+ LevelFilter::Error
+ };
+ simple_logger::SimpleLogger::default()
+ .with_level(level)
+ .init()?;
+ serve(args).await
}
fn handle_err<T>(err: Box<dyn std::error::Error>) -> T {
async {
Ok::<_, Infallible>(service_fn(move |req| {
let inner = inner.clone();
- inner.handle(req)
+ inner.call(req)
}))
}
});
Self { args }
}
- pub async fn handle(self: Arc<Self>, req: Request) -> Result<Response, hyper::Error> {
+ pub async fn call(self: Arc<Self>, req: Request) -> Result<Response, hyper::Error> {
+ let method = req.method().clone();
+ let uri = req.uri().clone();
+ let res = self
+ .handle(req)
+ .await
+ .unwrap_or_else(|_| status_code!(StatusCode::INTERNAL_SERVER_ERROR));
+ info!(r#""{} {}" - {}"#, method, uri, res.status());
+ Ok(res)
+ }
+
+ pub async fn handle(self: Arc<Self>, req: Request) -> BoxResult<Response> {
if !self.auth_guard(&req).unwrap_or_default() {
let mut res = status_code!(StatusCode::UNAUTHORIZED);
res.headers_mut()
return Ok(res);
}
- let res = if req.method() == Method::GET {
+ if req.method() == Method::GET {
self.handle_static(req).await
} else if req.method() == Method::PUT {
if self.args.readonly {
self.handle_delete(req).await
} else {
return Ok(status_code!(StatusCode::NOT_FOUND));
- };
- Ok(res.unwrap_or_else(|_| status_code!(StatusCode::INTERNAL_SERVER_ERROR)))
+ }
}
async fn handle_static(&self, req: Request) -> BoxResult<Response> {