diff --git a/assets/cmd.css b/assets/cmd.css
index 996d56d..5f26890 100644
--- a/assets/cmd.css
+++ b/assets/cmd.css
@@ -1,7 +1,7 @@
.cmd {
- word-wrap: break-word;
+ word-wrap: break-word;
}
pre {
- background-color: #eee;
- min-height: 1.5em;
+ background-color: #eee;
+ min-height: 1.5em;
}
diff --git a/assets/fonts.css b/assets/fonts.css
deleted file mode 100644
index bb0682e..0000000
--- a/assets/fonts.css
+++ /dev/null
@@ -1,6 +0,0 @@
-body {
- font-family: -apple-system, BlinkMacSystemFont,
- "Segoe UI", "Roboto", "Oxygen",
- "Ubuntu", "Cantarell", "Fira Sans",
- "Droid Sans", "Helvetica Neue", sans-serif;
-}
diff --git a/assets/list.css b/assets/list.css
index 3a98bba..d508986 100644
--- a/assets/list.css
+++ b/assets/list.css
@@ -1,4 +1,23 @@
.name {
overflow: hidden;
- word-wrap: break-word;
+ word-wrap: break-word;
+}
+.badge-alignment {
+ margin-top: 0.25em;
+}
+.stretched-invisible-label {
+ display: block;
+}
+.stretched-invisible-label > * {
+ position: relative;
+ z-index: 1;
+}
+.stretched-invisible-label::after {
+ content: "";
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 0;
}
diff --git a/assets/list.js b/assets/list.js
deleted file mode 100644
index 9d88772..0000000
--- a/assets/list.js
+++ /dev/null
@@ -1,5 +0,0 @@
-/* jshint esversion: 6 */
-
-$(document).ready(() => {
- $("[title]").tooltip();
-});
diff --git a/assets/login.css b/assets/login.css
index 1dfa508..8d2cab9 100644
--- a/assets/login.css
+++ b/assets/login.css
@@ -8,12 +8,12 @@
}
.input-group-digits input[type=number] {
- -moz-appearance: textfield;
+ -moz-appearance: textfield;
}
.input-group-digits input[type=number]::-webkit-inner-spin-button,
.input-group-digits input[type=number]::-webkit-outer-spin-button {
- -webkit-appearance: none;
- margin: 0;
+ -webkit-appearance: none;
+ margin: 0;
}
.login {
diff --git a/assets/login.js b/assets/login.js
index 9e0ccc4..c7bd866 100644
--- a/assets/login.js
+++ b/assets/login.js
@@ -1,53 +1,51 @@
/* jshint esversion: 6 */
-$(document).ready(() => {
- let $inputs = $(".input-group-digits");
- $inputs.each((i, input) => {
- let cleanup = () => {
- $(input).find("input").each((i, ele) => {
- let cleaned = $(ele).val().replace(/[^0-9]/, "");
- $(ele).val(cleaned);
- });
- };
- let update = (e) => {
- $digits = $(input).find("input");
+let $inputs = $(".input-group-digits");
+$inputs.each((i, input) => {
+ let cleanup = () => {
+ $(input).find("input").each((i, ele) => {
+ let cleaned = $(ele).val().replace(/[^0-9]/, "");
+ $(ele).val(cleaned);
+ });
+ };
+ let update = (e) => {
+ $digits = $(input).find("input");
- // Cleanup
- cleanup();
+ // Cleanup
+ cleanup();
- // Shift characters
- let excess = "";
- $digits.each((i, ele) => {
- let now = excess + $(ele).val();
- $(ele).val(now.charAt(0));
- excess = now.substr(1);
- });
+ // Shift characters
+ let excess = "";
+ $digits.each((i, ele) => {
+ let now = excess + $(ele).val();
+ $(ele).val(now.charAt(0));
+ excess = now.substr(1);
+ });
- // Move cursor to empty
- $digits.each((i, ele) => {
- if (!$(ele).val()) {
- $(ele).focus();
- if (e.which == 8) {
- $(ele).prev().focus().val("");
- }
- return false;
- }
- });
+ // Move cursor to empty
+ $digits.each((i, ele) => {
+ if (!$(ele).val()) {
+ $(ele).focus();
+ if (e.which == 8) {
+ $(ele).prev().focus().val("");
+ }
+ return false;
+ }
+ });
- // Submit if last digit is filled
- if ($($digits[$digits.length - 1]).val()) {
- let token = $.map(
- $digits,
- d => $(d).val()
- ).join("");
- let $value = $(input).parent().find("#login-token-value");
- $value.val(token);
- $value.closest("form").submit();
- }
- };
+ // Submit if last digit is filled
+ if ($($digits[$digits.length - 1]).val()) {
+ let token = $.map(
+ $digits,
+ d => $(d).val()
+ ).join("");
+ let $value = $(input).parent().find("#login-token-value");
+ $value.val(token);
+ $value.closest("form").submit();
+ }
+ };
- $digits = $(input).find("input");
- $digits.on("keyup", update);
- $digits.on("change", update);
- });
+ $digits = $(input).find("input");
+ $digits.on("keyup", update);
+ $digits.on("change", update);
});
diff --git a/assets/multi.js b/assets/multi.js
index 097f007..68ed51c 100644
--- a/assets/multi.js
+++ b/assets/multi.js
@@ -1,30 +1,51 @@
/* jshint esversion: 6 */
-$(document).ready(() => {
- let $select = $(".multi-select");
+function htmlEscape(text) {
+ const p = document.createElement('p');
+ p.innerText = text;
+ return p.innerHTML;
+}
- let setSelected = (files) => {
- $(".multi-files-value").val(JSON.stringify(files.map(f => f.name)));
- $(".multi-files").html(
- files.map(f => {
- return `
${f.name} ${f.size}`;
- }).join("")
- );
- };
+let $select = $(".multi-select");
- let updateSelected = () => {
- let $selected = $(".multi-select:checked");
- let files = [];
- $selected.each((i, ele) => {
- files.push({
- name: $(ele).data("select"),
- size: $(ele).data("select-size")
- });
- });
+let setSelected = (files) => {
+ $(".multi-files-value").val(JSON.stringify(files.map(f => f.name)));
+ if (files.length == 0) {
+ $(".multi-files").html(`No files selected`);
+ return
+ }
+ $(".multi-files").html(
+ files.map(f => {
+ return `
+
+ ${htmlEscape(f.name)}
+ ${f.type == "directory" ? `` : `${filesize(f.size)}`}
+
+ `;
+ }).join("")
+ );
+ const hasDirectory = files.reduce((a, f) => a || f.type == "directory", false);
+ const totalSize = files.map(f => f.size).reduce((a, b) => a + b);
+ if (hasDirectory) {
+ $(".multi-files-total").text("Unknown");
+ } else {
+ $(".multi-files-total").text(filesize(totalSize));
+ }
+};
- setSelected(files);
- }
+const updateSelected = () => {
+ let $selected = $(".multi-select:checked");
+ let files = [];
+ $selected.each((i, ele) => {
+ files.push({
+ name: $(ele).data("select"),
+ type: $(ele).data("select-type"),
+ size: $(ele).data("select-size")
+ });
+ });
- $select.on("change", updateSelected);
- updateSelected();
-});
+ setSelected(files);
+}
+
+$select.on("change", updateSelected);
+updateSelected();
diff --git a/assets/navbar.css b/assets/navbar.css
new file mode 100644
index 0000000..a17b84d
--- /dev/null
+++ b/assets/navbar.css
@@ -0,0 +1,6 @@
+#navbar {
+ overflow-x: auto;
+}
+.nav-link {
+ white-space: nowrap;
+}
diff --git a/assets/shell.js b/assets/shell.js
index 3e5fe7c..9293e52 100644
--- a/assets/shell.js
+++ b/assets/shell.js
@@ -1,11 +1,9 @@
/* jshint esversion: 6 */
-$(document).ready(() => {
- let $shell = $("#shell");
- if ($shell.length < 1) {
- return;
- }
- let $close = $("#shell-close");
+const $shell = $("#shell");
+const $close = $("#shell-close");
+
+if ($shell.length > 0) {
const ws = new WebSocket("ws" + (window.location.protocol === "https:" ? "s" : "") + "://" + window.location.host + "/websocket?path=" + encodeURIComponent($shell.data("path")));
@@ -54,4 +52,5 @@ $(document).ready(() => {
});
});
-});
+
+}
diff --git a/assets/tooltip.js b/assets/tooltip.js
new file mode 100644
index 0000000..299b392
--- /dev/null
+++ b/assets/tooltip.js
@@ -0,0 +1,5 @@
+/* jshint esversion: 6 */
+
+document.querySelectorAll("[title]").forEach(element => {
+ new bootstrap.Tooltip(element);
+});
diff --git a/assets/upload.js b/assets/upload.js
index 4cf0a0a..5165d03 100644
--- a/assets/upload.js
+++ b/assets/upload.js
@@ -1,24 +1,22 @@
/* jshint esversion: 6 */
-$(document).ready(() => {
- let $form = $("form[action='@upload']");
- let $file = $("#upload-file");
-
- $(".upload-unhide").fadeOut();
-
- $file.on("change", () => {
- let file = $file[0].files[0];
- let fnElement = $file.parent().find(".custom-file-label");
- fnElement.addClass("file-selected");
- fnElement.text(file.name);
+const $form = $("form[action='@upload']");
+const $file = $("#upload-file");
- $form.find("#upload-file-size").val(filesize(file.size));
- $form.find("[name=saveas]").val(file.name);
- $(".upload-unhide").fadeIn();
- });
+$(".upload-unhide").fadeOut();
- $form.on("submit", () => {
- let putresource = $form.find("[name=saveas]").val();
- // TODO: do XHR to PUT at putresource
- });
+$file.on("change", () => {
+ const file = $file[0].files[0];
+ const fnElement = $file.parent().find(".custom-file-label");
+ fnElement.addClass("file-selected");
+ fnElement.text(file.name);
+
+ $form.find("#upload-file-size").val(filesize(file.size));
+ $form.find("[name=saveas]").val(file.name);
+ $(".upload-unhide").fadeIn();
+});
+
+$form.on("submit", () => {
+ let putresource = $form.find("[name=saveas]").val();
+ // TODO: do XHR to PUT at putresource
});
diff --git a/index.js b/index.js
index 82381c9..248228f 100755
--- a/index.js
+++ b/index.js
@@ -34,6 +34,12 @@ app.engine("handlebars", hbs({
layoutsDir: path.join(__dirname, "views", "layouts"),
defaultLayout: "main",
helpers: {
+ either: (a, b, options) => {
+ if (a || b) {
+ return options.fn();
+ }
+ },
+ filesize: filesize,
octicon: (i, options) => {
if (!octicons[i]) {
return new handlebars.SafeString(octicons.question.toSVG());
@@ -174,7 +180,7 @@ app.put("/*", (req, res) => {
res.redirect("back");
});
save.on("error", (err) => {
- res.flash("error", err);
+ res.flash("error", err.toString());
res.redirect("back");
});
}
@@ -230,9 +236,13 @@ app.post("/*@upload", (req, res) => {
req.flash("error", "File exists, cannot overwrite. ");
res.redirect("back");
}).catch((err) => {
- console.log("saving");
- let save = fs.createWriteStream(relative(res.filename, saveas));
+ const saveName = relative(res.filename, saveas);
+ console.log("saving file to " + saveName);
+ let save = fs.createWriteStream(saveName);
save.on("close", () => {
+ if (res.headersSent) {
+ return;
+ }
if (buff.length === 0) {
req.flash("success", "File saved. Warning: empty file.");
}
@@ -243,7 +253,7 @@ app.post("/*@upload", (req, res) => {
res.redirect("back");
});
save.on("error", (err) => {
- req.flash("error", err);
+ req.flash("error", err.toString());
res.redirect("back");
});
save.write(buff);
@@ -277,7 +287,7 @@ app.post("/*@mkdir", (req, res) => {
}).catch((err) => {
fs.mkdir(relative(res.filename, folder), (err) => {
if (err) {
- req.flash("error", err);
+ req.flash("error", err.toString());
res.redirect("back");
return;
}
@@ -341,7 +351,7 @@ app.post("/*@delete", (req, res) => {
res.redirect("back");
});
}).catch((err) => {
- req.flash("error", err);
+ req.flash("error", err.toString());
res.redirect("back");
});
});
@@ -394,7 +404,7 @@ app.get("/*@download", (req, res) => {
zip.finalize();
}).catch((err) => {
console.log(err);
- req.flash("error", err);
+ req.flash("error", err.toString());
res.redirect("back");
});
});
@@ -515,7 +525,7 @@ app.get("/*", (req, res) => {
resolve({
name: f,
isdirectory: stats.isDirectory(),
- size: filesize(stats.size)
+ size: stats.size
});
});
});
@@ -551,6 +561,9 @@ app.get("/*", (req, res) => {
}
else if (res.stats.isFile()) {
res.sendFile(relative(res.filename), {
+ headers: {
+ "Content-Security-Policy": "default-src 'self'; script-src 'none'; sandbox"
+ },
dotfiles: "allow"
});
}
diff --git a/package-lock.json b/package-lock.json
index d22520d..e0a95c1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,7 @@
"dependencies": {
"archiver": "^5.3.0",
"body-parser": "^1.19.0",
- "bootstrap": "^4.6.0",
+ "bootstrap": "^5.0.0",
"connect-busboy": "^0.0.2",
"connect-flash": "^0.1.1",
"express": "^4.17.1",
@@ -32,6 +32,16 @@
"file-manager": "index.js"
}
},
+ "node_modules/@popperjs/core": {
+ "version": "2.9.2",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.2.tgz",
+ "integrity": "sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==",
+ "peer": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
"node_modules/accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
@@ -168,16 +178,15 @@
}
},
"node_modules/bootstrap": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.0.tgz",
- "integrity": "sha512-Io55IuQY3kydzHtbGvQya3H+KorS/M9rSNyfCGCg9WZ4pyT/lCxIlpJgG1GXW/PswzC84Tr2fBYi+7+jFVQQBw==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.0.0.tgz",
+ "integrity": "sha512-tmhPET9B9qCl8dCofvHeiIhi49iBt0EehmIsziZib65k1erBW1rHhj2s/2JsuQh5Pq+xz2E9bEbzp9B7xHG+VA==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/bootstrap"
},
"peerDependencies": {
- "jquery": "1.9.1 - 3",
- "popper.js": "^1.16.1"
+ "@popperjs/core": "^2.9.2"
}
},
"node_modules/brace-expansion": {
@@ -914,17 +923,6 @@
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
- "node_modules/popper.js": {
- "version": "1.16.1",
- "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
- "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
- "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1",
- "peer": true,
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/popperjs"
- }
- },
"node_modules/printj": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz",
@@ -1295,6 +1293,12 @@
}
},
"dependencies": {
+ "@popperjs/core": {
+ "version": "2.9.2",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.2.tgz",
+ "integrity": "sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==",
+ "peer": true
+ },
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
@@ -1407,9 +1411,9 @@
}
},
"bootstrap": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.0.tgz",
- "integrity": "sha512-Io55IuQY3kydzHtbGvQya3H+KorS/M9rSNyfCGCg9WZ4pyT/lCxIlpJgG1GXW/PswzC84Tr2fBYi+7+jFVQQBw==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.0.0.tgz",
+ "integrity": "sha512-tmhPET9B9qCl8dCofvHeiIhi49iBt0EehmIsziZib65k1erBW1rHhj2s/2JsuQh5Pq+xz2E9bEbzp9B7xHG+VA==",
"requires": {}
},
"brace-expansion": {
@@ -1976,12 +1980,6 @@
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
- "popper.js": {
- "version": "1.16.1",
- "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
- "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
- "peer": true
- },
"printj": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz",
diff --git a/package.json b/package.json
index d10455a..565524d 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
"dependencies": {
"archiver": "^5.3.0",
"body-parser": "^1.19.0",
- "bootstrap": "^4.6.0",
+ "bootstrap": "^5.0.0",
"connect-busboy": "^0.0.2",
"connect-flash": "^0.1.1",
"express": "^4.17.1",
diff --git a/views/cmd.handlebars b/views/cmd.handlebars
index 2755a1e..70d847e 100644
--- a/views/cmd.handlebars
+++ b/views/cmd.handlebars
@@ -2,36 +2,36 @@
- {{#each errors as |error|}}
-
- {{error}}
-
- {{/each}}
- {{#each successes as |success|}}
-
- {{success}}
-
- {{/each}}
- command
- {{cmd}}
- stdout
- {{stdout}}
- stderr
- {{stderr}}
+ {{#each errors as |error|}}
+
+ {{error}}
+
+ {{/each}}
+ {{#each successes as |success|}}
+
+ {{success}}
+
+ {{/each}}
+ command
+ {{cmd}}
+ stdout
+ {{stdout}}
+ stderr
+ {{stderr}}
{{> dialogue-cmd}}
diff --git a/views/layouts/main.handlebars b/views/layouts/main.handlebars
index 2ee8d51..c6f48b3 100644
--- a/views/layouts/main.handlebars
+++ b/views/layouts/main.handlebars
@@ -1,38 +1,40 @@
-
-
- File Manager
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ File Manager
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
- {{{body}}}
+
+ {{{body}}}
diff --git a/views/list.handlebars b/views/list.handlebars
index f4b28dd..1971b18 100644
--- a/views/list.handlebars
+++ b/views/list.handlebars
@@ -2,37 +2,39 @@
- {{#each errors as |error|}}
-
- {{error}}
-
- {{/each}}
- {{#each successes as |success|}}
-
- {{success}}
-
- {{/each}}
-
- {{#each files}}
- -
-
-
- {{else}}
- -
- No files
-
- {{/each}}
-
+ {{#each errors as |error|}}
+
+ {{error}}
+
+ {{/each}}
+ {{#each successes as |success|}}
+
+ {{success}}
+
+ {{/each}}
+
+ {{#each files}}
+ -
+
+
+ {{else}}
+ -
+ No files
+
+ {{/each}}
+
diff --git a/views/login.handlebars b/views/login.handlebars
index 896887d..891aa3e 100644
--- a/views/login.handlebars
+++ b/views/login.handlebars
@@ -2,31 +2,31 @@
-
- {{#each errors as |error|}}
-
- {{error}}
-
- {{/each}}
- {{#each successes as |success|}}
-
- {{success}}
-
- {{/each}}
-
-
+
+ {{#each errors as |error|}}
+
+ {{error}}
+
+ {{/each}}
+ {{#each successes as |success|}}
+
+ {{success}}
+
+ {{/each}}
+
+
diff --git a/views/partials/dialogue-cmd.handlebars b/views/partials/dialogue-cmd.handlebars
index 23a6bb1..0ac94fc 100644
--- a/views/partials/dialogue-cmd.handlebars
+++ b/views/partials/dialogue-cmd.handlebars
@@ -1,24 +1,22 @@
diff --git a/views/partials/dialogue-delete.handlebars b/views/partials/dialogue-delete.handlebars
index 119847d..6e3c7a6 100644
--- a/views/partials/dialogue-delete.handlebars
+++ b/views/partials/dialogue-delete.handlebars
@@ -1,25 +1,23 @@
diff --git a/views/partials/dialogue-download.handlebars b/views/partials/dialogue-download.handlebars
index 155df4d..194241c 100644
--- a/views/partials/dialogue-download.handlebars
+++ b/views/partials/dialogue-download.handlebars
@@ -1,25 +1,24 @@
diff --git a/views/partials/dialogue-mkdir.handlebars b/views/partials/dialogue-mkdir.handlebars
index fd220ac..657c12f 100644
--- a/views/partials/dialogue-mkdir.handlebars
+++ b/views/partials/dialogue-mkdir.handlebars
@@ -1,24 +1,22 @@
diff --git a/views/partials/dialogue-upload.handlebars b/views/partials/dialogue-upload.handlebars
index 14acbec..5d3137d 100644
--- a/views/partials/dialogue-upload.handlebars
+++ b/views/partials/dialogue-upload.handlebars
@@ -1,34 +1,27 @@
diff --git a/views/partials/navbar.handlebars b/views/partials/navbar.handlebars
index 556eb93..9c9c4bf 100644
--- a/views/partials/navbar.handlebars
+++ b/views/partials/navbar.handlebars
@@ -1,26 +1,28 @@
diff --git a/views/partials/toolbar.handlebars b/views/partials/toolbar.handlebars
index adea23b..d1ebccf 100644
--- a/views/partials/toolbar.handlebars
+++ b/views/partials/toolbar.handlebars
@@ -1,44 +1,52 @@
-