From: sigoden Date: Wed, 1 Jun 2022 12:26:03 +0000 (+0800) Subject: chore: move src/assets out of src X-Git-Url: https://git.ozva.co.uk/?a=commitdiff_plain;h=1fe391f9d9f79f7975ecd047e9b0c55857ceb2ed;p=ozva-cloud chore: move src/assets out of src --- diff --git a/assets/index.css b/assets/index.css new file mode 100644 index 0000000..b32e7d0 --- /dev/null +++ b/assets/index.css @@ -0,0 +1,167 @@ +html { + font-family: -apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif; + line-height: 1.5; + color: #24292e; +} + +body { + width: 700px; +} + +.hidden { + display: none; +} + +.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 { + padding: 0.5em 0; +} + +.uploader { + padding-right: 1em; +} diff --git a/assets/index.html b/assets/index.html new file mode 100644 index 0000000..e536409 --- /dev/null +++ b/assets/index.html @@ -0,0 +1,53 @@ + + + + + + + __SLOT__ + + +
+ +
+
+ + + +
+ +
+ +
+
+
+
+ + + + + + + + + + + +
NameDate modifySizeActions
+
+ + + \ No newline at end of file diff --git a/assets/index.js b/assets/index.js new file mode 100644 index 0000000..6cbe802 --- /dev/null +++ b/assets/index.js @@ -0,0 +1,247 @@ +let $tbody, $uploaders; +let baseDir; + +class Uploader { + idx; + file; + name; + $elem; + static globalIdx = 0; + constructor(file, dirs) { + this.name = [...dirs, file.name].join("/"); + this.idx = Uploader.globalIdx++; + this.file = file; + } + + upload() { + const { file, idx, name } = this; + let url = getUrl(name); + if (file.name == baseDir + ".zip") { + url += "?unzip"; + } + $uploaders.insertAdjacentHTML("beforeend", ` +
+
+ ${name} (0%) +
`); + this.$elem = document.getElementById(`file${idx}`); + + const ajax = new XMLHttpRequest(); + ajax.upload.addEventListener("progress", e => this.progress(e), false); + ajax.addEventListener("readystatechange", () => { + if(ajax.readyState === 4) { + if (ajax.status == 200) { + this.complete(); + } else { + this.fail(); + } + } + }) + ajax.addEventListener("error", () => this.fail(), false); + ajax.addEventListener("abort", () => this.fail(), false); + ajax.open("PUT", url); + ajax.send(file); + } + + progress(event) { + const percent = (event.loaded / event.total) * 100; + this.$elem.innerHTML = `${this.name} (${percent.toFixed(2)}%)`; + } + + complete() { + this.$elem.innerHTML = `${this.name}`; + } + + fail() { + this.$elem.innerHTML = `${this.name}`; + } +} + +function addBreadcrumb(value) { + const $breadcrumb = document.querySelector(".breadcrumb"); + const parts = value.split("/").filter(v => !!v); + const len = parts.length; + let path = ""; + for (let i = 0; i < len; i++) { + const name = parts[i]; + if (i > 0) { + path += "/" + name; + } + if (i === len - 1) { + $breadcrumb.insertAdjacentHTML("beforeend", `${name}`); + baseDir = name; + } else if (i === 0) { + $breadcrumb.insertAdjacentHTML("beforeend", `${name}`); + } else { + $breadcrumb.insertAdjacentHTML("beforeend", `${name}`); + } + $breadcrumb.insertAdjacentHTML("beforeend", `/`); + } +} + +function addPath(file, index) { + const url = getUrl(file.name) + let actionDelete = ""; + let actionDownload = ""; + if (file.path_type.endsWith("Dir")) { + actionDownload = ` +
+ + + +
`; + } else { + actionDownload = ` +
+ + + +
`; + } + if (DATA.allow_delete) { + actionDelete = ` +
+ +
`; + } + let actionCell = ` + + ${actionDownload} + ${actionDelete} + ` + + $tbody.insertAdjacentHTML("beforeend", ` + + +
${getSvg(file.path_type)}
+ ${file.name} + +${formatMtime(file.mtime)} +${formatSize(file.size)} +${actionCell} +`) +} + +async function deletePath(index) { + const file = DATA.paths[index]; + if (!file) return; + + if (!confirm(`Delete \`${file.name}\`?`)) return; + + try { + const res = await fetch(getUrl(file.name), { + method: "DELETE", + }); + if (res.status === 200) { + document.getElementById(`addPath${index}`).remove(); + } else { + throw new Error(await res.text()) + } + } catch (err) { + alert(`Cannot delete \`${file.name}\`, ${err.message}`); + } +} + +function dropzone() { + ["drag", "dragstart", "dragend", "dragover", "dragenter", "dragleave", "drop"].forEach(name => { + document.addEventListener(name, e => { + e.preventDefault(); + e.stopPropagation(); + }); + }); + document.addEventListener("drop", e => { + if (!e.dataTransfer.items[0].webkitGetAsEntry) { + const files = e.dataTransfer.files.filter(v => v.size > 0); + for (const file of files) { + new Uploader(file, []).upload(); + } + } else { + const entries = []; + const len = e.dataTransfer.items.length; + for (let i = 0; i < len; i++) { + entries.push(e.dataTransfer.items[i].webkitGetAsEntry()); + } + addFileEntries(entries, []) + } + }); +} + +async function addFileEntries(entries, dirs) { + for (const entry of entries) { + if (entry.isFile) { + entry.file(file => { + new Uploader(file, dirs).upload(); + }); + } else if (entry.isDirectory) { + const dirReader = entry.createReader() + dirReader.readEntries(entries => addFileEntries(entries, [...dirs, entry.name])); + } + } +} + + +function getUrl(name) { + let 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 "" + const date = new Date(mtime); + const year = date.getFullYear(); + const month = padZero(date.getMonth() + 1, 2); + const day = padZero(date.getDate(), 2); + const hours = padZero(date.getHours(), 2); + const 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 "" + const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; + if (size == 0) return '0 Byte'; + const i = parseInt(Math.floor(Math.log(size) / Math.log(1024))); + return Math.round(size / Math.pow(1024, i), 2) + ' ' + sizes[i]; +} + +function ready() { + $tbody = document.querySelector(".main tbody"); + $uploaders = document.querySelector(".uploaders"); + + addBreadcrumb(DATA.breadcrumb); + if (Array.isArray(DATA.paths)) { + const len = DATA.paths.length; + for (let i = 0; i < len; i++) { + addPath(DATA.paths[i], i); + } + } + if (DATA.allow_upload) { + dropzone(); + document.querySelector(".upload-control").classList.remove(["hidden"]); + document.getElementById("file").addEventListener("change", e => { + const files = e.target.files; + for (let file of files) { + new Uploader(file, []).upload(); + } + }); + } +} \ No newline at end of file diff --git a/src/assets/index.css b/src/assets/index.css deleted file mode 100644 index b32e7d0..0000000 --- a/src/assets/index.css +++ /dev/null @@ -1,167 +0,0 @@ -html { - font-family: -apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif; - line-height: 1.5; - color: #24292e; -} - -body { - width: 700px; -} - -.hidden { - display: none; -} - -.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 { - padding: 0.5em 0; -} - -.uploader { - padding-right: 1em; -} diff --git a/src/assets/index.html b/src/assets/index.html deleted file mode 100644 index e536409..0000000 --- a/src/assets/index.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - __SLOT__ - - -
- -
-
- - - -
- -
- -
-
-
-
- - - - - - - - - - - -
NameDate modifySizeActions
-
- - - \ No newline at end of file diff --git a/src/assets/index.js b/src/assets/index.js deleted file mode 100644 index 6cbe802..0000000 --- a/src/assets/index.js +++ /dev/null @@ -1,247 +0,0 @@ -let $tbody, $uploaders; -let baseDir; - -class Uploader { - idx; - file; - name; - $elem; - static globalIdx = 0; - constructor(file, dirs) { - this.name = [...dirs, file.name].join("/"); - this.idx = Uploader.globalIdx++; - this.file = file; - } - - upload() { - const { file, idx, name } = this; - let url = getUrl(name); - if (file.name == baseDir + ".zip") { - url += "?unzip"; - } - $uploaders.insertAdjacentHTML("beforeend", ` -
-
- ${name} (0%) -
`); - this.$elem = document.getElementById(`file${idx}`); - - const ajax = new XMLHttpRequest(); - ajax.upload.addEventListener("progress", e => this.progress(e), false); - ajax.addEventListener("readystatechange", () => { - if(ajax.readyState === 4) { - if (ajax.status == 200) { - this.complete(); - } else { - this.fail(); - } - } - }) - ajax.addEventListener("error", () => this.fail(), false); - ajax.addEventListener("abort", () => this.fail(), false); - ajax.open("PUT", url); - ajax.send(file); - } - - progress(event) { - const percent = (event.loaded / event.total) * 100; - this.$elem.innerHTML = `${this.name} (${percent.toFixed(2)}%)`; - } - - complete() { - this.$elem.innerHTML = `${this.name}`; - } - - fail() { - this.$elem.innerHTML = `${this.name}`; - } -} - -function addBreadcrumb(value) { - const $breadcrumb = document.querySelector(".breadcrumb"); - const parts = value.split("/").filter(v => !!v); - const len = parts.length; - let path = ""; - for (let i = 0; i < len; i++) { - const name = parts[i]; - if (i > 0) { - path += "/" + name; - } - if (i === len - 1) { - $breadcrumb.insertAdjacentHTML("beforeend", `${name}`); - baseDir = name; - } else if (i === 0) { - $breadcrumb.insertAdjacentHTML("beforeend", `${name}`); - } else { - $breadcrumb.insertAdjacentHTML("beforeend", `${name}`); - } - $breadcrumb.insertAdjacentHTML("beforeend", `/`); - } -} - -function addPath(file, index) { - const url = getUrl(file.name) - let actionDelete = ""; - let actionDownload = ""; - if (file.path_type.endsWith("Dir")) { - actionDownload = ` -
- - - -
`; - } else { - actionDownload = ` -
- - - -
`; - } - if (DATA.allow_delete) { - actionDelete = ` -
- -
`; - } - let actionCell = ` - - ${actionDownload} - ${actionDelete} - ` - - $tbody.insertAdjacentHTML("beforeend", ` - - -
${getSvg(file.path_type)}
- ${file.name} - -${formatMtime(file.mtime)} -${formatSize(file.size)} -${actionCell} -`) -} - -async function deletePath(index) { - const file = DATA.paths[index]; - if (!file) return; - - if (!confirm(`Delete \`${file.name}\`?`)) return; - - try { - const res = await fetch(getUrl(file.name), { - method: "DELETE", - }); - if (res.status === 200) { - document.getElementById(`addPath${index}`).remove(); - } else { - throw new Error(await res.text()) - } - } catch (err) { - alert(`Cannot delete \`${file.name}\`, ${err.message}`); - } -} - -function dropzone() { - ["drag", "dragstart", "dragend", "dragover", "dragenter", "dragleave", "drop"].forEach(name => { - document.addEventListener(name, e => { - e.preventDefault(); - e.stopPropagation(); - }); - }); - document.addEventListener("drop", e => { - if (!e.dataTransfer.items[0].webkitGetAsEntry) { - const files = e.dataTransfer.files.filter(v => v.size > 0); - for (const file of files) { - new Uploader(file, []).upload(); - } - } else { - const entries = []; - const len = e.dataTransfer.items.length; - for (let i = 0; i < len; i++) { - entries.push(e.dataTransfer.items[i].webkitGetAsEntry()); - } - addFileEntries(entries, []) - } - }); -} - -async function addFileEntries(entries, dirs) { - for (const entry of entries) { - if (entry.isFile) { - entry.file(file => { - new Uploader(file, dirs).upload(); - }); - } else if (entry.isDirectory) { - const dirReader = entry.createReader() - dirReader.readEntries(entries => addFileEntries(entries, [...dirs, entry.name])); - } - } -} - - -function getUrl(name) { - let 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 "" - const date = new Date(mtime); - const year = date.getFullYear(); - const month = padZero(date.getMonth() + 1, 2); - const day = padZero(date.getDate(), 2); - const hours = padZero(date.getHours(), 2); - const 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 "" - const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; - if (size == 0) return '0 Byte'; - const i = parseInt(Math.floor(Math.log(size) / Math.log(1024))); - return Math.round(size / Math.pow(1024, i), 2) + ' ' + sizes[i]; -} - -function ready() { - $tbody = document.querySelector(".main tbody"); - $uploaders = document.querySelector(".uploaders"); - - addBreadcrumb(DATA.breadcrumb); - if (Array.isArray(DATA.paths)) { - const len = DATA.paths.length; - for (let i = 0; i < len; i++) { - addPath(DATA.paths[i], i); - } - } - if (DATA.allow_upload) { - dropzone(); - document.querySelector(".upload-control").classList.remove(["hidden"]); - document.getElementById("file").addEventListener("change", e => { - const files = e.target.files; - for (let file of files) { - new Uploader(file, []).upload(); - } - }); - } -} \ No newline at end of file diff --git a/src/server.rs b/src/server.rs index deb5d76..d88049a 100644 --- a/src/server.rs +++ b/src/server.rs @@ -32,9 +32,9 @@ use tokio_util::io::{ReaderStream, StreamReader}; type Request = hyper::Request; type Response = hyper::Response; -const INDEX_HTML: &str = include_str!("assets/index.html"); -const INDEX_CSS: &str = include_str!("assets/index.css"); -const INDEX_JS: &str = include_str!("assets/index.js"); +const INDEX_HTML: &str = include_str!("../assets/index.html"); +const INDEX_CSS: &str = include_str!("../assets/index.css"); +const INDEX_JS: &str = include_str!("../assets/index.js"); const BUF_SIZE: usize = 1024 * 16; macro_rules! status {