UI improvements
parent
a265519ffb
commit
c91e94d0a2
|
@ -58,6 +58,10 @@ file-manager
|
||||||
|
|
||||||
The following environmental variables can be used to configure `file-manager`.
|
The following environmental variables can be used to configure `file-manager`.
|
||||||
|
|
||||||
|
### SESSION_KEY=
|
||||||
|
|
||||||
|
Express session key, generate something random.
|
||||||
|
|
||||||
### SHELL=
|
### 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.
|
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.
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
/* jshint esversion: 6 */
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
document.querySelectorAll("[title]").forEach(element => {
|
document.querySelectorAll("[title]").forEach(element => {
|
||||||
new bootstrap.Tooltip(element);
|
new bootstrap.Tooltip(element, {
|
||||||
|
delay: 500,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
45
index.js
45
index.js
|
@ -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("/@assets/xterm-addon-fit", express.static(path.join(__dirname, "node_modules/xterm-addon-fit")));
|
||||||
|
|
||||||
app.use(session({
|
app.use(session({
|
||||||
secret: "meowmeow"
|
secret: process.env.SESSION_KEY || "meowmeow"
|
||||||
}));
|
}));
|
||||||
app.use(flash());
|
app.use(flash());
|
||||||
app.use(busboy());
|
app.use(busboy());
|
||||||
|
@ -103,7 +103,6 @@ app.get("/@login", (req, res) => {
|
||||||
});
|
});
|
||||||
app.post("/@login", (req, res) => {
|
app.post("/@login", (req, res) => {
|
||||||
let pass = notp.totp.verify(req.body.token.replace(" ", ""), KEY);
|
let pass = notp.totp.verify(req.body.token.replace(" ", ""), KEY);
|
||||||
console.log(pass, req.body.token.replace(" ", ""));
|
|
||||||
if (pass) {
|
if (pass) {
|
||||||
req.session.login = true;
|
req.session.login = true;
|
||||||
res.redirect("/");
|
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) => {
|
app.post("/*@upload", (req, res) => {
|
||||||
res.filename = req.params[0];
|
res.filename = req.params[0];
|
||||||
|
|
||||||
|
@ -233,6 +204,7 @@ app.post("/*@upload", (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
fileExists.then((stats) => {
|
fileExists.then((stats) => {
|
||||||
|
console.warn("file exists, cannot overwrite");
|
||||||
req.flash("error", "File exists, cannot overwrite. ");
|
req.flash("error", "File exists, cannot overwrite. ");
|
||||||
res.redirect("back");
|
res.redirect("back");
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
@ -253,6 +225,7 @@ app.post("/*@upload", (req, res) => {
|
||||||
res.redirect("back");
|
res.redirect("back");
|
||||||
});
|
});
|
||||||
save.on("error", (err) => {
|
save.on("error", (err) => {
|
||||||
|
console.warn(err);
|
||||||
req.flash("error", err.toString());
|
req.flash("error", err.toString());
|
||||||
res.redirect("back");
|
res.redirect("back");
|
||||||
});
|
});
|
||||||
|
@ -287,6 +260,7 @@ app.post("/*@mkdir", (req, res) => {
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
fs.mkdir(relative(res.filename, folder), (err) => {
|
fs.mkdir(relative(res.filename, folder), (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
console.warn(err);
|
||||||
req.flash("error", err.toString());
|
req.flash("error", err.toString());
|
||||||
res.redirect("back");
|
res.redirect("back");
|
||||||
return;
|
return;
|
||||||
|
@ -347,10 +321,12 @@ app.post("/*@delete", (req, res) => {
|
||||||
req.flash("success", "Files deleted. ");
|
req.flash("success", "Files deleted. ");
|
||||||
res.redirect("back");
|
res.redirect("back");
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
console.warn(err);
|
||||||
req.flash("error", "Unable to delete some files: " + err);
|
req.flash("error", "Unable to delete some files: " + err);
|
||||||
res.redirect("back");
|
res.redirect("back");
|
||||||
});
|
});
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
console.warn(err);
|
||||||
req.flash("error", err.toString());
|
req.flash("error", err.toString());
|
||||||
res.redirect("back");
|
res.redirect("back");
|
||||||
});
|
});
|
||||||
|
@ -386,6 +362,7 @@ app.get("/*@download", (req, res) => {
|
||||||
Promise.all(promises).then((files) => {
|
Promise.all(promises).then((files) => {
|
||||||
let zip = archiver("zip", {});
|
let zip = archiver("zip", {});
|
||||||
zip.on("error", function(err) {
|
zip.on("error", function(err) {
|
||||||
|
console.warn(err);
|
||||||
res.status(500).send({
|
res.status(500).send({
|
||||||
error: err.message
|
error: err.message
|
||||||
});
|
});
|
||||||
|
@ -403,7 +380,7 @@ app.get("/*@download", (req, res) => {
|
||||||
|
|
||||||
zip.finalize();
|
zip.finalize();
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.log(err);
|
console.warn(err);
|
||||||
req.flash("error", err.toString());
|
req.flash("error", err.toString());
|
||||||
res.redirect("back");
|
res.redirect("back");
|
||||||
});
|
});
|
||||||
|
@ -425,12 +402,14 @@ if (shellable || cmdable) {
|
||||||
if (!cmd || cmd.length < 1) {
|
if (!cmd || cmd.length < 1) {
|
||||||
return res.status(400).end();
|
return res.status(400).end();
|
||||||
}
|
}
|
||||||
|
console.log("running command " + cmd);
|
||||||
|
|
||||||
child_process.exec(cmd, {
|
child_process.exec(cmd, {
|
||||||
cwd: relative(res.filename),
|
cwd: relative(res.filename),
|
||||||
timeout: 60 * 1000,
|
timeout: 60 * 1000,
|
||||||
}, (err, stdout, stderr) => {
|
}, (err, stdout, stderr) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
console.log("command run failed: " + JSON.stringify(err));
|
||||||
req.flash("error", "Command failed due to non-zero exit code");
|
req.flash("error", "Command failed due to non-zero exit code");
|
||||||
}
|
}
|
||||||
res.render("cmd", flashify(req, {
|
res.render("cmd", flashify(req, {
|
||||||
|
@ -455,7 +434,6 @@ if (shellable || cmdable) {
|
||||||
|
|
||||||
const ws = new WebSocket.Server({ server: http });
|
const ws = new WebSocket.Server({ server: http });
|
||||||
ws.on("connection", (socket, request) => {
|
ws.on("connection", (socket, request) => {
|
||||||
console.log(request.url);
|
|
||||||
const { path } = querystring.parse(request.url.split("?")[1]);
|
const { path } = querystring.parse(request.url.split("?")[1]);
|
||||||
let cwd = relative(path);
|
let cwd = relative(path);
|
||||||
let term = pty.spawn(exec, args, {
|
let term = pty.spawn(exec, args, {
|
||||||
|
@ -529,6 +507,7 @@ app.get("/*", (req, res) => {
|
||||||
const promises = filenames.map(f => new Promise((resolve, reject) => {
|
const promises = filenames.map(f => new Promise((resolve, reject) => {
|
||||||
fs.stat(relative(res.filename, f), (err, stats) => {
|
fs.stat(relative(res.filename, f), (err, stats) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
console.warn(err);
|
||||||
return resolve({
|
return resolve({
|
||||||
name: f,
|
name: f,
|
||||||
error: err
|
error: err
|
||||||
|
@ -551,6 +530,7 @@ app.get("/*", (req, res) => {
|
||||||
files: files,
|
files: files,
|
||||||
}));
|
}));
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
console.error(err);
|
||||||
res.render("list", flashify(req, {
|
res.render("list", flashify(req, {
|
||||||
shellable: shellable,
|
shellable: shellable,
|
||||||
cmdable: cmdable,
|
cmdable: cmdable,
|
||||||
|
@ -561,6 +541,7 @@ app.get("/*", (req, res) => {
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
console.warn(err);
|
||||||
res.render("list", flashify(req, {
|
res.render("list", flashify(req, {
|
||||||
shellable: shellable,
|
shellable: shellable,
|
||||||
cmdable: cmdable,
|
cmdable: cmdable,
|
||||||
|
|
|
@ -21,16 +21,20 @@
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav class="navbar navbar-light bg-light fixed-bottom p-1 px-2">
|
<nav class="navbar navbar-light bg-light fixed-bottom">
|
||||||
<div class="btn-group m-1" role="group">
|
<div class="container-fluid px-2">
|
||||||
<a class="btn btn-primary" href="/{{path}}" data-placement="top" title="Return to folder">
|
<div class="d-flex">
|
||||||
{{octicon "chevron-left"}}
|
<div class="btn-group me-2" role="group">
|
||||||
<span class="d-none d-sm-inline">Back</span>
|
<a class="btn btn-secondary" href="/{{path}}">
|
||||||
</a>
|
{{octicon "chevron-left"}}
|
||||||
<a class="btn btn-warning" href="@cmd" data-toggle="modal" data-target="#cmd" data-placement="top" title="Run another command">
|
<span class="d-none d-sm-inline">Back</span>
|
||||||
{{octicon "terminal"}}
|
</a>
|
||||||
<span class="d-none d-sm-inline">Run command</span>
|
<a class="btn btn-info" href="@cmd" data-bs-toggle="modal" data-bs-target="#cmd" title="Run another command" data-bs-placement="top">
|
||||||
</a>
|
{{octicon "terminal"}}
|
||||||
|
<span class="d-none d-sm-inline">Run command</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if error}}
|
{{#if error}}
|
||||||
<a href="./{{name}}/" class="name" title="{{error}}">{{name}}/</a>
|
<a href="./{{name}}/" class="name" title="{{error}}">{{name}}/</a>
|
||||||
|
<span class="badge rounded-pill bg-danger badge-alignment">err</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
<a href="./{{name}}" class="name">{{name}}</a>
|
<a href="./{{name}}" class="name">{{name}}</a>
|
||||||
<span class="badge rounded-pill bg-secondary badge-alignment">{{filesize size}}</span>
|
<span class="badge rounded-pill bg-secondary badge-alignment">{{filesize size}}</span>
|
||||||
|
|
|
@ -7,10 +7,7 @@
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="form-group">
|
<input class="form-control" name="cmd" type="text" id="cmd-cmd" placeholder="g++ sort.c" required />
|
||||||
<label for="cmd-cmd">Command: </label>
|
|
||||||
<input name="cmd" class="form-control" type="text" id="cmd-cmd" placeholder="g++ sort.c" required />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer bg-light">
|
<div class="modal-footer bg-light">
|
||||||
<button type="reset" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
<button type="reset" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||||
|
|
|
@ -7,10 +7,7 @@
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div>
|
<input class="form-control" name="folder" type="text" id="mkdir-folder" placeholder="folder-name" required />
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer bg-light">
|
<div class="modal-footer bg-light">
|
||||||
<button type="reset" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
<button type="reset" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="collapse navbar-collapse" id="navbar">
|
<div class="collapse navbar-collapse" id="navbar">
|
||||||
<div class="navbar-nav mr-auto">
|
<div class="navbar-nav me-auto">
|
||||||
{{#eachpath path}}
|
{{#eachpath path}}
|
||||||
<a class="nav-item nav-link{{#if current}} active{{/if}}" href="{{path}}">{{name}}</a>
|
<a class="nav-item nav-link{{#if current}} active{{/if}}" href="{{path}}">{{name}}</a>
|
||||||
{{/eachpath}}
|
{{/eachpath}}
|
||||||
|
@ -13,12 +13,12 @@
|
||||||
<div class="navbar-nav">
|
<div class="navbar-nav">
|
||||||
{{#if isloginenabled}}
|
{{#if isloginenabled}}
|
||||||
{{#if isloggingin}}
|
{{#if isloggingin}}
|
||||||
<a class="nav-item nav-link" href="/@login" title="Sign in">
|
<a class="nav-item nav-link" href="/@login" title="Sign in" data-bs-placement="left">
|
||||||
<span class="octicon octicon-sign-in"></span>
|
{{octicon "sign-in"}}
|
||||||
</a>
|
</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
<a class="nav-item nav-link" href="/@logout" title="Sign out">
|
<a class="nav-item nav-link" href="/@logout" title="Sign out" data-bs-placement="left">
|
||||||
<span class="octicon octicon-sign-out"></span>
|
{{octicon "sign-out"}}
|
||||||
</a>
|
</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
<div class="container-fluid px-2">
|
<div class="container-fluid px-2">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<div class="btn-group me-2" role="group">
|
<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"}}
|
{{octicon "cloud-upload"}}
|
||||||
<span class="d-none d-sm-inline">Upload</span>
|
<span class="d-none d-sm-inline">Upload</span>
|
||||||
</a>
|
</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"}}
|
{{octicon "file-directory"}}
|
||||||
<span class="d-none d-md-inline">New folder</span>
|
<span class="d-none d-md-inline">New folder</span>
|
||||||
</a>
|
</a>
|
||||||
|
@ -14,13 +14,13 @@
|
||||||
{{#either cmdable shellable}}
|
{{#either cmdable shellable}}
|
||||||
<div class="btn-group me-2" role="group">
|
<div class="btn-group me-2" role="group">
|
||||||
{{#if cmdable}}
|
{{#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"}}
|
{{octicon "terminal"}}
|
||||||
<span class="d-none d-lg-inline">Run command</span>
|
<span class="d-none d-lg-inline">Run command</span>
|
||||||
</a>
|
</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if shellable}}
|
{{#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"}}
|
{{octicon "terminal"}}
|
||||||
<span class="d-none d-md-inline">Open shell</span>
|
<span class="d-none d-md-inline">Open shell</span>
|
||||||
</a>
|
</a>
|
||||||
|
@ -28,13 +28,13 @@
|
||||||
</div>
|
</div>
|
||||||
{{/either}}
|
{{/either}}
|
||||||
<div class="btn-group me-2" role="group">
|
<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"}}
|
{{octicon "file-zip"}}
|
||||||
<span class="d-none d-md-inline">Download</span>
|
<span class="d-none d-md-inline">Download</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group me-2" role="group">
|
<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"}}
|
{{octicon "trashcan"}}
|
||||||
<span class="d-none d-md-inline">Delete</span>
|
<span class="d-none d-md-inline">Delete</span>
|
||||||
</a>
|
</a>
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<div class="btn-group ms-2" role="group">
|
<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"}}
|
{{octicon "sync"}}
|
||||||
<span class="d-none d-lg-inline">Refresh</span>
|
<span class="d-none d-lg-inline">Refresh</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -4,15 +4,19 @@
|
||||||
<main id="shell" style="height: 100%; background: #000" data-path="/{{path}}"></main>
|
<main id="shell" style="height: 100%; background: #000" data-path="/{{path}}"></main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav class="navbar navbar-light bg-light fixed-bottom p-1 px-2">
|
<nav class="navbar navbar-light bg-light fixed-bottom">
|
||||||
<div class="btn-group m-1" role="group">
|
<div class="container-fluid px-2">
|
||||||
<a class="btn btn-danger" href="/{{path}}" id="shell-close" data-placement="top" title="Close and return to folder">
|
<div class="d-flex">
|
||||||
{{octicon "chevron-left"}}
|
<div class="btn-group me-2" role="group">
|
||||||
<span class="d-none d-sm-inline">Close shell</span>
|
<a class="btn btn-danger" href="/{{path}}" id="shell-close">
|
||||||
</a>
|
{{octicon "chevron-left"}}
|
||||||
<a class="btn btn-warning" href="@shell" target="_blank" data-placement="top" title="Open shell in new tab">
|
<span class="d-none d-sm-inline">Exit shell</span>
|
||||||
{{octicon "terminal"}}
|
</a>
|
||||||
<span class="d-none d-sm-inline">New shell</span>
|
<a class="btn btn-warning" href="@shell" target="_blank" title="Open a new shell tab" data-bs-placement="top">
|
||||||
</a>
|
{{octicon "terminal"}}
|
||||||
|
<span class="d-none d-sm-inline">New shell</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
Loading…
Reference in New Issue