From: sigoden Date: Mon, 30 May 2022 03:47:57 +0000 (+0800) Subject: chore: reorganize web static files X-Git-Url: https://git.ozva.co.uk/?a=commitdiff_plain;h=8fbc742c52dc3b800ad77b07ee7fcf6c641824e4;p=ozva-cloud chore: reorganize web static files --- diff --git a/src/index.css b/src/index.css deleted file mode 100644 index 718f7b4..0000000 --- a/src/index.css +++ /dev/null @@ -1,165 +0,0 @@ -html { - font-family: -apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif; - line-height: 1.5; - color: #24292e; -} - -body { - width: 700px; -} - -.head { - display: flex; - flex-wrap: wrap; - align-items: center; - padding: 1em 1em 0; -} - -.breadcrumb { - font-size: 1.25em; -} - -.breadcrumb > a { - color: #0366d6; - text-decoration: none; -} - -.breadcrumb > a:hover { - text-decoration: underline; -} - -/* final breadcrumb */ -.breadcrumb > b { - color: #24292e; -} - -.breadcrumb > .separator { - color: #586069; - padding: 0 0.25em; -} - -.breadcrumb svg { - height: 100%; - fill: rgba(3,47,98,0.5); - padding-right: 0.5em; - padding-left: 0.5em; -} - -.toolbox { - display: flex; -} - -.searchbar { - display: flex; - flex-wrap: nowrap; - width: 246px; - height: 22px; - background-color: #fafafa; - transition: all .15s; - border: 1px #ddd solid; - border-radius: 15px; - margin: 0 0 2px 10px; -} - -.searchbar #search { - box-sizing: border-box; - width: 100%; - height: 100%; - font-size: 16px; - line-height: 16px; - padding: 1px; - font-family: helvetica neue,luxi sans,Tahoma,hiragino sans gb,STHeiti,sans-serif; - background-color: transparent; - border: none; - outline: none; -} - -.searchbar .icon { - color: #9a9a9a; - padding: 3px 3px; - cursor: pointer; -} - -.upload-control { - cursor: pointer; - padding-left: 0.25em; -} - -.upload-control input { - display: none; -} - -.main { - padding: 0 1em; -} - -.main th { - text-align: left; - font-weight: unset; - color: #5c5c5c; - white-space: nowrap; -} - -.main td { - white-space: nowrap; -} - -.main .cell-name { - width: 400px; -} - -.main .cell-mtime { - width: 120px; - padding-left: 0.6em; -} - -.main .cell-size { - text-align: right; - width: 60px; - padding-left: 0.6em; -} - -.main .cell-actions { - width: 60px; - display: flex; - padding-left: 0.6em; -} - - -.path svg { - height: 100%; - fill: rgba(3,47,98,0.5); - padding-right: 0.5em; -} - -.path { - display: flex; - list-style: none; -} - -.path a { - color: #0366d6; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - display: block; - text-decoration: none; -} - -.path a:hover { - text-decoration: underline; -} - -.action-btn { - padding-left: 0.4em; -} - -.uploaders { - display: flex; - flex-wrap: wrap; - padding: 0.5em 0; -} - -.uploader { - padding-right: 1em; -} 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/static/index.css b/src/static/index.css new file mode 100644 index 0000000..c8a2457 --- /dev/null +++ b/src/static/index.css @@ -0,0 +1,165 @@ +html { + font-family: -apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif; + line-height: 1.5; + color: #24292e; +} + +body { + width: 700px; +} + +.head { + display: flex; + flex-wrap: wrap; + align-items: center; + padding: 1em 1em 0; +} + +.breadcrumb { + font-size: 1.25em; +} + +.breadcrumb > a { + color: #0366d6; + text-decoration: none; +} + +.breadcrumb > a:hover { + text-decoration: underline; +} + +/* final breadcrumb */ +.breadcrumb > b { + color: #24292e; +} + +.breadcrumb > .separator { + color: #586069; + padding: 0 0.25em; +} + +.breadcrumb svg { + height: 100%; + fill: rgba(3,47,98,0.5); + padding-right: 0.5em; + padding-left: 0.5em; +} + +.toolbox { + display: flex; +} + +.searchbar { + display: flex; + flex-wrap: nowrap; + width: 246px; + height: 22px; + background-color: #fafafa; + transition: all .15s; + border: 1px #ddd solid; + border-radius: 15px; + margin: 0 0 2px 10px; +} + +.searchbar #search { + box-sizing: border-box; + width: 100%; + height: 100%; + font-size: 16px; + line-height: 16px; + padding: 1px; + font-family: helvetica neue,luxi sans,Tahoma,hiragino sans gb,STHeiti,sans-serif; + background-color: transparent; + border: none; + outline: none; +} + +.searchbar .icon { + color: #9a9a9a; + padding: 3px 3px; + cursor: pointer; +} + +.upload-control { + cursor: pointer; + padding-left: 0.25em; +} + +.upload-control input { + display: none; +} + +.main { + padding: 0 1em; +} + +.main th { + text-align: left; + font-weight: unset; + color: #5c5c5c; + white-space: nowrap; +} + +.main td { + white-space: nowrap; +} + +.main .cell-name { + width: 400px; +} + +.main .cell-mtime { + width: 120px; + padding-left: 0.6em; +} + +.main .cell-size { + text-align: right; + width: 70px; + padding-left: 0.6em; +} + +.main .cell-actions { + width: 60px; + display: flex; + padding-left: 0.6em; +} + + +.path svg { + height: 100%; + fill: rgba(3,47,98,0.5); + padding-right: 0.5em; +} + +.path { + display: flex; + list-style: none; +} + +.path a { + color: #0366d6; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + display: block; + text-decoration: none; +} + +.path a:hover { + text-decoration: underline; +} + +.action-btn { + padding-left: 0.4em; +} + +.uploaders { + display: flex; + flex-wrap: wrap; + padding: 0.5em 0; +} + +.uploader { + padding-right: 1em; +} 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