diff --git a/src/index.html b/src/index.html deleted file mode 100644 index 19b6809..0000000 --- a/src/index.html +++ /dev/null @@ -1,246 +0,0 @@ - - - - - - - Duf file server - __STYLE__ - - -
- -
-
- - - -
-
- -
-
-
-
- - - - - - - - - - - -
NameDate modifySizeActions
-
- - - \ No newline at end of file diff --git a/src/server.rs b/src/server.rs index c72fe42..fea10ab 100644 --- a/src/server.rs +++ b/src/server.rs @@ -36,8 +36,9 @@ macro_rules! status_code { }; } -const INDEX_HTML: &str = include_str!("index.html"); -const INDEX_CSS: &str = include_str!("index.css"); +const INDEX_HTML: &str = include_str!("static/index.html"); +const INDEX_CSS: &str = include_str!("static/index.css"); +const INDEX_JS: &str = include_str!("static/index.js"); const BUF_SIZE: usize = 1024 * 16; pub async fn serve(args: Args) -> BoxResult<()> { @@ -277,16 +278,30 @@ impl InnerService { fn send_index(&self, path: &Path, mut paths: Vec) -> BoxResult { paths.sort_unstable(); - let breadcrumb = self.get_breadcrumb(path); + let rel_path = match self.args.path.parent() { + Some(p) => path.strip_prefix(p).unwrap(), + None => path, + }; let data = IndexData { - breadcrumb, + breadcrumb: normalize_path(rel_path), paths, readonly: self.args.readonly, }; let data = serde_json::to_string(&data).unwrap(); - let mut output = - INDEX_HTML.replace("__STYLE__", &format!("", INDEX_CSS)); - output = output.replace("__DATA__", &data); + let output = INDEX_HTML.replace( + "__SLOB__", + &format!( + r#" +Files in {} - Duf/ + + +"#, + rel_path.display(), + INDEX_CSS, + data, + INDEX_JS + ), + ); Ok(Response::new(output.into())) } @@ -310,14 +325,6 @@ impl InnerService { Ok(true) } - fn get_breadcrumb(&self, path: &Path) -> String { - let path = match self.args.path.parent() { - Some(p) => path.strip_prefix(p).unwrap(), - None => path, - }; - normalize_path(path) - } - fn get_file_path(&self, path: &str) -> BoxResult> { let decoded_path = percent_decode(path[1..].as_bytes()).decode_utf8()?; let slashes_switched = if cfg!(windows) { diff --git a/src/index.css b/src/static/index.css similarity index 99% rename from src/index.css rename to src/static/index.css index 718f7b4..c8a2457 100644 --- a/src/index.css +++ b/src/static/index.css @@ -115,7 +115,7 @@ body { .main .cell-size { text-align: right; - width: 60px; + width: 70px; padding-left: 0.6em; } diff --git a/src/static/index.html b/src/static/index.html new file mode 100644 index 0000000..8a5eb3d --- /dev/null +++ b/src/static/index.html @@ -0,0 +1,47 @@ + + + + + + + __SLOB__ + + +
+ +
+
+ + + +
+
+ +
+
+
+
+ + + + + + + + + + + +
NameDate modifySizeActions
+
+ + + \ No newline at end of file diff --git a/src/static/index.js b/src/static/index.js new file mode 100644 index 0000000..f819542 --- /dev/null +++ b/src/static/index.js @@ -0,0 +1,202 @@ +var uploaderIdx = 0; +var breadcrumb, paths, readonly; +var $toolbox, $tbody, $breadcrumb, $uploaders, $uploadControl; + +class Uploader { + idx = 0; + file; + $elem; + constructor(idx, file) { + this.idx = idx; + this.file = file; + } + + upload() { + var { file, idx } = this; + var url = getUrl(file.name); + $uploaders.insertAdjacentHTML("beforeend", ` + `); + this.$elem = document.getElementById(`file${idx}`); + + var 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.fail(e), false); + ajax.addEventListener("abort", e => this.fail(e), false); + ajax.open("PUT", url); + ajax.send(file); + } + + progress(event) { + var percent = (event.loaded / event.total) * 100; + this.$elem.innerHTML = `${this.file.name} (${percent.toFixed(2)}%)`; + } + + complete(event) { + this.$elem.innerHTML = `${this.file.name}`; + } + + fail(event) { + this.$elem.innerHTML = `${this.file.name}`; + } +} + +function addBreadcrumb(value) { + var parts = value.split("/").filter(v => !!v); + var len = parts.length; + var path = ""; + for (var i = 0; i < len; i++) { + var name = parts[i]; + if (i > 0) { + path += "/" + name; + } + if (i === len - 1) { + $breadcrumb.insertAdjacentHTML("beforeend", `${name}`); + } else if (i === 0) { + $breadcrumb.insertAdjacentHTML("beforeend", `${name}`); + } else { + $breadcrumb.insertAdjacentHTML("beforeend", `${name}`); + } + $breadcrumb.insertAdjacentHTML("beforeend", `/`); + } +} + +function addPath(file, index) { + var url = getUrl(file.name) + var actionDelete = ""; + var actionDownload = ""; + if (file.path_type.endsWith("Dir")) { + actionDownload = ` +
+ + + +
`; + } else { + actionDownload = ` +
+ + + +
`; + } + if (!readonly) { + actionDelete = ` +
+ +
`; + } + var actionCell = ` + + ${actionDownload} + ${actionDelete} + ` + + $tbody.insertAdjacentHTML("beforeend", ` + + +
${getSvg(file.path_type)}
+ ${file.name} + +${formatMtime(file.mtime)} +${formatSize(file.size)} +${actionCell} +`) +} + +async function deletePath(index) { + var file = paths[index]; + if (!file) return; + + var ajax = new XMLHttpRequest(); + ajax.open("DELETE", getUrl(file.name)); + ajax.addEventListener("readystatechange", function() { + if(ajax.readyState === 4 && ajax.status === 200) { + document.getElementById(`addPath${index}`).remove(); + } + }); + ajax.send(); +} + +function addUploadControl() { + $toolbox.insertAdjacentHTML("beforeend", ` +
+ + +
+`); +} + +function getUrl(name) { + var url = location.href.split('?')[0]; + if (!url.endsWith("/")) url += "/"; + url += encodeURI(name); + return url; +} + +function getSvg(path_type) { + switch (path_type) { + case "Dir": + return ``; + case "File": + return ``; + case "SymlinkDir": + return ``; + default: + return ``; + } +} + +function formatMtime(mtime) { + if (!mtime) return "" + var date = new Date(mtime); + var year = date.getFullYear(); + var month = padZero(date.getMonth() + 1, 2); + var day = padZero(date.getDate(), 2); + var hours = padZero(date.getHours(), 2); + var minutes = padZero(date.getMinutes(), 2); + return `${year}/${month}/${day} ${hours}:${minutes}`; +} + +function padZero(value, size) { + return ("0".repeat(size) + value).slice(-1 * size) +} + +function formatSize(size) { + if (!size) return "" + var sizes = ['B', 'KB', 'MB', 'GB', 'TB']; + if (size == 0) return '0 Byte'; + var i = parseInt(Math.floor(Math.log(size) / Math.log(1024))); + return Math.round(size / Math.pow(1024, i), 2) + ' ' + sizes[i]; +} + + +function ready() { + breadcrumb = DATA.breadcrumb; + paths = DATA.paths; + readonly = DATA.readonly; + $toolbox = document.querySelector(".toolbox"); + $tbody = document.querySelector(".main tbody"); + $breadcrumb = document.querySelector(".breadcrumb"); + $uploaders = document.querySelector(".uploaders"); + $uploadControl = document.querySelector(".upload-control"); + + addBreadcrumb(breadcrumb); + paths.forEach((file, index) => addPath(file, index)); + if (!readonly) { + addUploadControl(); + document.getElementById("file").addEventListener("change", e => { + var files = e.target.files; + for (var file of files) { + uploaderIdx += 1; + var uploader = new Uploader(uploaderIdx, file); + uploader.upload(); + } + }); + } +} \ No newline at end of file