1
0
Fork 0

UI improvements

pull/17/head
Ambrose Chua 2021-05-08 01:54:37 +08:00
parent a265519ffb
commit c91e94d0a2
10 changed files with 63 additions and 73 deletions

View File

@ -58,6 +58,10 @@ file-manager
The following environmental variables can be used to configure `file-manager`.
### SESSION_KEY=
Express session key, generate something random.
### SHELL=
Enable the shell feature, which allows users to start a login shell (when set to `login`) or the binary specified by this option (example: `/bin/bash`). Be careful when enabling this feature as anyone with access to this portal can execute any command on your machine.

View File

@ -1,5 +1,7 @@
/* jshint esversion: 6 */
document.querySelectorAll("[title]").forEach(element => {
new bootstrap.Tooltip(element);
new bootstrap.Tooltip(element, {
delay: 500,
});
});

View File

@ -77,7 +77,7 @@ app.use("/@assets/xterm-addon-attach", express.static(path.join(__dirname, "node
app.use("/@assets/xterm-addon-fit", express.static(path.join(__dirname, "node_modules/xterm-addon-fit")));
app.use(session({
secret: "meowmeow"
secret: process.env.SESSION_KEY || "meowmeow"
}));
app.use(flash());
app.use(busboy());
@ -103,7 +103,6 @@ app.get("/@login", (req, res) => {
});
app.post("/@login", (req, res) => {
let pass = notp.totp.verify(req.body.token.replace(" ", ""), KEY);
console.log(pass, req.body.token.replace(" ", ""));
if (pass) {
req.session.login = true;
res.redirect("/");
@ -168,34 +167,6 @@ app.all("/*", (req, res, next) => {
});
});
// currently unused
app.put("/*", (req, res) => {
if (res.stats.error) {
req.busboy.on("file", (key, file, filename) => {
if (key == "file") {
let save = fs.createWriteStream(relative(res.filename));
file.pipe(save);
save.on("close", () => {
res.flash("success", "File saved. ");
res.redirect("back");
});
save.on("error", (err) => {
res.flash("error", err.toString());
res.redirect("back");
});
}
});
req.busboy.on("field", (key, value) => {
});
req.pipe(req.busboy);
}
else {
req.flash("error", "File exists, cannot overwrite. ");
res.redirect("back");
}
});
app.post("/*@upload", (req, res) => {
res.filename = req.params[0];
@ -233,6 +204,7 @@ app.post("/*@upload", (req, res) => {
});
fileExists.then((stats) => {
console.warn("file exists, cannot overwrite");
req.flash("error", "File exists, cannot overwrite. ");
res.redirect("back");
}).catch((err) => {
@ -253,6 +225,7 @@ app.post("/*@upload", (req, res) => {
res.redirect("back");
});
save.on("error", (err) => {
console.warn(err);
req.flash("error", err.toString());
res.redirect("back");
});
@ -287,6 +260,7 @@ app.post("/*@mkdir", (req, res) => {
}).catch((err) => {
fs.mkdir(relative(res.filename, folder), (err) => {
if (err) {
console.warn(err);
req.flash("error", err.toString());
res.redirect("back");
return;
@ -347,10 +321,12 @@ app.post("/*@delete", (req, res) => {
req.flash("success", "Files deleted. ");
res.redirect("back");
}).catch((err) => {
console.warn(err);
req.flash("error", "Unable to delete some files: " + err);
res.redirect("back");
});
}).catch((err) => {
console.warn(err);
req.flash("error", err.toString());
res.redirect("back");
});
@ -386,6 +362,7 @@ app.get("/*@download", (req, res) => {
Promise.all(promises).then((files) => {
let zip = archiver("zip", {});
zip.on("error", function(err) {
console.warn(err);
res.status(500).send({
error: err.message
});
@ -403,7 +380,7 @@ app.get("/*@download", (req, res) => {
zip.finalize();
}).catch((err) => {
console.log(err);
console.warn(err);
req.flash("error", err.toString());
res.redirect("back");
});
@ -425,12 +402,14 @@ if (shellable || cmdable) {
if (!cmd || cmd.length < 1) {
return res.status(400).end();
}
console.log("running command " + cmd);
child_process.exec(cmd, {
cwd: relative(res.filename),
timeout: 60 * 1000,
}, (err, stdout, stderr) => {
if (err) {
console.log("command run failed: " + JSON.stringify(err));
req.flash("error", "Command failed due to non-zero exit code");
}
res.render("cmd", flashify(req, {
@ -455,7 +434,6 @@ if (shellable || cmdable) {
const ws = new WebSocket.Server({ server: http });
ws.on("connection", (socket, request) => {
console.log(request.url);
const { path } = querystring.parse(request.url.split("?")[1]);
let cwd = relative(path);
let term = pty.spawn(exec, args, {
@ -529,6 +507,7 @@ app.get("/*", (req, res) => {
const promises = filenames.map(f => new Promise((resolve, reject) => {
fs.stat(relative(res.filename, f), (err, stats) => {
if (err) {
console.warn(err);
return resolve({
name: f,
error: err
@ -551,6 +530,7 @@ app.get("/*", (req, res) => {
files: files,
}));
}).catch((err) => {
console.error(err);
res.render("list", flashify(req, {
shellable: shellable,
cmdable: cmdable,
@ -561,6 +541,7 @@ app.get("/*", (req, res) => {
}));
});
}).catch((err) => {
console.warn(err);
res.render("list", flashify(req, {
shellable: shellable,
cmdable: cmdable,

View File

@ -21,16 +21,20 @@
</main>
</div>
<nav class="navbar navbar-light bg-light fixed-bottom p-1 px-2">
<div class="btn-group m-1" role="group">
<a class="btn btn-primary" href="/{{path}}" data-placement="top" title="Return to folder">
{{octicon "chevron-left"}}
<span class="d-none d-sm-inline">Back</span>
</a>
<a class="btn btn-warning" href="@cmd" data-toggle="modal" data-target="#cmd" data-placement="top" title="Run another command">
{{octicon "terminal"}}
<span class="d-none d-sm-inline">Run command</span>
</a>
<nav class="navbar navbar-light bg-light fixed-bottom">
<div class="container-fluid px-2">
<div class="d-flex">
<div class="btn-group me-2" role="group">
<a class="btn btn-secondary" href="/{{path}}">
{{octicon "chevron-left"}}
<span class="d-none d-sm-inline">Back</span>
</a>
<a class="btn btn-info" href="@cmd" data-bs-toggle="modal" data-bs-target="#cmd" title="Run another command" data-bs-placement="top">
{{octicon "terminal"}}
<span class="d-none d-sm-inline">Run command</span>
</a>
</div>
</div>
</div>
</nav>

View File

@ -26,6 +26,7 @@
{{else}}
{{#if error}}
<a href="./{{name}}/" class="name" title="{{error}}">{{name}}/</a>
<span class="badge rounded-pill bg-danger badge-alignment">err</span>
{{else}}
<a href="./{{name}}" class="name">{{name}}</a>
<span class="badge rounded-pill bg-secondary badge-alignment">{{filesize size}}</span>

View File

@ -7,10 +7,7 @@
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="cmd-cmd">Command: </label>
<input name="cmd" class="form-control" type="text" id="cmd-cmd" placeholder="g++ sort.c" required />
</div>
<input class="form-control" name="cmd" type="text" id="cmd-cmd" placeholder="g++ sort.c" required />
</div>
<div class="modal-footer bg-light">
<button type="reset" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>

View File

@ -7,10 +7,7 @@
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div>
<label class="form-label" for="mkdir-folder">Folder name</label>
<input class="form-control" name="folder" type="text" id="mkdir-folder" placeholder="folder" required />
</div>
<input class="form-control" name="folder" type="text" id="mkdir-folder" placeholder="folder-name" required />
</div>
<div class="modal-footer bg-light">
<button type="reset" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>

View File

@ -5,7 +5,7 @@
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbar">
<div class="navbar-nav mr-auto">
<div class="navbar-nav me-auto">
{{#eachpath path}}
<a class="nav-item nav-link{{#if current}} active{{/if}}" href="{{path}}">{{name}}</a>
{{/eachpath}}
@ -13,12 +13,12 @@
<div class="navbar-nav">
{{#if isloginenabled}}
{{#if isloggingin}}
<a class="nav-item nav-link" href="/@login" title="Sign in">
<span class="octicon octicon-sign-in"></span>
<a class="nav-item nav-link" href="/@login" title="Sign in" data-bs-placement="left">
{{octicon "sign-in"}}
</a>
{{else}}
<a class="nav-item nav-link" href="/@logout" title="Sign out">
<span class="octicon octicon-sign-out"></span>
<a class="nav-item nav-link" href="/@logout" title="Sign out" data-bs-placement="left">
{{octicon "sign-out"}}
</a>
{{/if}}
{{/if}}

View File

@ -2,11 +2,11 @@
<div class="container-fluid px-2">
<div class="d-flex">
<div class="btn-group me-2" role="group">
<a class="btn btn-primary" href="@upload" data-bs-toggle="modal" data-bs-target="#upload" data-placement="top" title="Upload">
<a class="btn btn-primary" href="@upload" data-bs-toggle="modal" data-bs-target="#upload" title="Upload a file" data-bs-placement="top">
{{octicon "cloud-upload"}}
<span class="d-none d-sm-inline">Upload</span>
</a>
<a class="btn btn-secondary" href="@mkdir" data-bs-toggle="modal" data-bs-target="#mkdir" data-placement="top" title="New folder">
<a class="btn btn-secondary" href="@mkdir" data-bs-toggle="modal" data-bs-target="#mkdir" title="Create a new folder" data-bs-placement="top">
{{octicon "file-directory"}}
<span class="d-none d-md-inline">New folder</span>
</a>
@ -14,13 +14,13 @@
{{#either cmdable shellable}}
<div class="btn-group me-2" role="group">
{{#if cmdable}}
<a class="btn btn-info" href="@cmd" data-bs-toggle="modal" data-bs-target="#cmd" data-placement="top" title="Run command">
<a class="btn btn-info" href="@cmd" data-bs-toggle="modal" data-bs-target="#cmd" title="Run a command" data-bs-placement="top">
{{octicon "terminal"}}
<span class="d-none d-lg-inline">Run command</span>
</a>
{{/if}}
{{#if shellable}}
<a class="btn btn-warning" href="@shell" data-placement="top" title="Open shell">
<a class="btn btn-warning" href="@shell" title="Open a new shell" data-bs-placement="top">
{{octicon "terminal"}}
<span class="d-none d-md-inline">Open shell</span>
</a>
@ -28,13 +28,13 @@
</div>
{{/either}}
<div class="btn-group me-2" role="group">
<a class="btn btn-success" href="@download" data-bs-toggle="modal" data-bs-target="#download" data-placement="top" title="Download file archive">
<a class="btn btn-success" href="@download" data-bs-toggle="modal" data-bs-target="#download" title="Download file archive" data-bs-placement="top">
{{octicon "file-zip"}}
<span class="d-none d-md-inline">Download</span>
</a>
</div>
<div class="btn-group me-2" role="group">
<a class="btn btn-danger" href="@delete" data-bs-toggle="modal" data-bs-target="#delete" data-placement="top" title="Delete files">
<a class="btn btn-danger" href="@delete" data-bs-toggle="modal" data-bs-target="#delete" title="Delete selected files" data-bs-placement="top">
{{octicon "trashcan"}}
<span class="d-none d-md-inline">Delete</span>
</a>
@ -42,7 +42,7 @@
</div>
<div class="d-flex">
<div class="btn-group ms-2" role="group">
<a class="btn btn-warning" href="./" data-placement="top" title="Refresh list">
<a class="btn btn-warning" href="./" title="Refresh list" data-bs-placement="top">
{{octicon "sync"}}
<span class="d-none d-lg-inline">Refresh</span>
</a>

View File

@ -4,15 +4,19 @@
<main id="shell" style="height: 100%; background: #000" data-path="/{{path}}"></main>
</div>
<nav class="navbar navbar-light bg-light fixed-bottom p-1 px-2">
<div class="btn-group m-1" role="group">
<a class="btn btn-danger" href="/{{path}}" id="shell-close" data-placement="top" title="Close and return to folder">
{{octicon "chevron-left"}}
<span class="d-none d-sm-inline">Close shell</span>
</a>
<a class="btn btn-warning" href="@shell" target="_blank" data-placement="top" title="Open shell in new tab">
{{octicon "terminal"}}
<span class="d-none d-sm-inline">New shell</span>
</a>
<nav class="navbar navbar-light bg-light fixed-bottom">
<div class="container-fluid px-2">
<div class="d-flex">
<div class="btn-group me-2" role="group">
<a class="btn btn-danger" href="/{{path}}" id="shell-close">
{{octicon "chevron-left"}}
<span class="d-none d-sm-inline">Exit shell</span>
</a>
<a class="btn btn-warning" href="@shell" target="_blank" title="Open a new shell tab" data-bs-placement="top">
{{octicon "terminal"}}
<span class="d-none d-sm-inline">New shell</span>
</a>
</div>
</div>
</div>
</nav>