Initial commit
commit
0c793e04b1
|
@ -0,0 +1,19 @@
|
|||
/* jshint esversion: 6 */
|
||||
|
||||
$(document).ready(() => {
|
||||
let $token = $("#login-token");
|
||||
let cleanup = () => {
|
||||
let val = $token.val();
|
||||
val = val.replace(/[^0-9 ]/, "");
|
||||
val = val.split("");
|
||||
if (val.length > 3 && val[3] != " ") {
|
||||
val.splice(3, 0, " ");
|
||||
}
|
||||
val.splice(7);
|
||||
val = val.join("");
|
||||
$token.val(val);
|
||||
};
|
||||
$token.on("keyup", cleanup);
|
||||
$token.on("keydown", cleanup);
|
||||
$token.on("change", cleanup);
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
/* jshint esversion: 6 */
|
||||
|
||||
$(document).ready(() => {
|
||||
let $select = $(".multi-select");
|
||||
|
||||
let setSelected = (files) => {
|
||||
$(".multi-files-value").val(JSON.stringify(files));
|
||||
$(".multi-files").html(
|
||||
files.map(f => {
|
||||
return `<li class="list-group-item">${f}</li>`;
|
||||
}).join("")
|
||||
);
|
||||
};
|
||||
|
||||
$select.on("change", () => {
|
||||
let $selected = $(".multi-select:checked");
|
||||
let files = [];
|
||||
$selected.each((i, ele) => {
|
||||
files.push($(ele).data("select"));
|
||||
});
|
||||
|
||||
setSelected(files);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,7 @@
|
|||
.custom-file-control.file-selected:after {
|
||||
display: none;
|
||||
}
|
||||
.custom-file-control {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/* 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-control");
|
||||
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
|
||||
});
|
||||
});
|
|
@ -0,0 +1,453 @@
|
|||
/* jshint esversion: 6 */
|
||||
/* jshint node: true */
|
||||
"use strict";
|
||||
|
||||
const express = require("express");
|
||||
const hbs = require("express-handlebars");
|
||||
const bodyparser = require("body-parser");
|
||||
const session = require("express-session");
|
||||
const busboy = require("connect-busboy");
|
||||
const flash = require("connect-flash");
|
||||
|
||||
const archiver = require("archiver");
|
||||
|
||||
const notp = require("notp");
|
||||
const base32 = require("thirty-two");
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const filesize = require("filesize");
|
||||
|
||||
let app = express();
|
||||
|
||||
app.set("views", path.join(__dirname, "views"));
|
||||
app.engine("handlebars", hbs({
|
||||
partialsDir: path.join(__dirname, "views", "partials"),
|
||||
layoutsDir: path.join(__dirname, "views", "layouts"),
|
||||
defaultLayout: "main",
|
||||
helpers: {
|
||||
eachpath: (path, options) => {
|
||||
if (typeof path != "string") {
|
||||
return "";
|
||||
}
|
||||
let out = "";
|
||||
path = path.split("/");
|
||||
path.splice(path.length - 1, 1);
|
||||
path.unshift("");
|
||||
path.forEach((folder, index) => {
|
||||
out += options.fn({
|
||||
name: folder + "/",
|
||||
path: "/" + path.slice(1, index + 1).join("/"),
|
||||
current: index === path.length - 1
|
||||
});
|
||||
});
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}));
|
||||
app.set("view engine", "handlebars");
|
||||
|
||||
app.use("/bootstrap", express.static(path.join(__dirname, "node_modules/bootstrap/dist")));
|
||||
app.use("/octicons", express.static(path.join(__dirname, "node_modules/octicons/build")));
|
||||
app.use("/jquery", express.static(path.join(__dirname, "node_modules/jquery/dist")));
|
||||
app.use("/filesize", express.static(path.join(__dirname, "node_modules/filesize/lib")));
|
||||
app.use("/assets", express.static(path.join(__dirname, "assets")));
|
||||
|
||||
app.use(session({
|
||||
secret: "meowmeow"
|
||||
}));
|
||||
app.use(flash());
|
||||
app.use(busboy());
|
||||
app.use(bodyparser.urlencoded());
|
||||
|
||||
// AUTH
|
||||
|
||||
const KEY = process.env.KEY ? base32.decode(process.env.KEY) : null;
|
||||
|
||||
app.get("/@logout", (req, res) => {
|
||||
if (KEY) {
|
||||
req.session.login = false;
|
||||
req.flash("success", "Signed out.");
|
||||
res.redirect("/@login");
|
||||
}
|
||||
else {
|
||||
req.flash("error", "You were never logged in...");
|
||||
res.redirect("back");
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/@login", (req, res) => {
|
||||
res.render("login", flashify(req, {}));
|
||||
});
|
||||
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("/");
|
||||
}
|
||||
else {
|
||||
req.flash("error", "Bad token.");
|
||||
res.redirect("/@login");
|
||||
}
|
||||
});
|
||||
|
||||
app.use((req, res, next) => {
|
||||
if (!KEY) {
|
||||
return next();
|
||||
}
|
||||
if (req.session.login === true) {
|
||||
return next();
|
||||
}
|
||||
else {
|
||||
req.flash("error", "Please sign in.");
|
||||
res.redirect("/@login");
|
||||
}
|
||||
});
|
||||
|
||||
function relative(...paths) {
|
||||
return paths.reduce((a, b) => path.join(a, b), process.cwd());
|
||||
}
|
||||
function flashify(req, obj) {
|
||||
let error = req.flash("error");
|
||||
if (error && error.length > 0) {
|
||||
if (!obj.errors) {
|
||||
obj.errors = [];
|
||||
}
|
||||
obj.errors.push(error);
|
||||
}
|
||||
let success = req.flash("success");
|
||||
if (success && success.length > 0) {
|
||||
if (!obj.successes) {
|
||||
obj.successes = [];
|
||||
}
|
||||
obj.successes.push(success);
|
||||
}
|
||||
obj.isloginenabled = !!KEY;
|
||||
return obj;
|
||||
}
|
||||
|
||||
app.all("/*", (req, res, next) => {
|
||||
res.filename = req.params[0];
|
||||
|
||||
let fileExists = new Promise((resolve, reject) => {
|
||||
// Check if file exists
|
||||
fs.stat(relative(res.filename), (err, stats) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
return resolve(stats);
|
||||
});
|
||||
});
|
||||
|
||||
fileExists.then((stats) => {
|
||||
res.stats = stats;
|
||||
next();
|
||||
}).catch((err) => {
|
||||
res.stats = { error: err };
|
||||
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);
|
||||
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];
|
||||
|
||||
let buff = null;
|
||||
let saveas = null;
|
||||
req.busboy.on("file", (key, stream, filename) => {
|
||||
if (key == "file") {
|
||||
let buffs = [];
|
||||
stream.on("data", (d) => {
|
||||
buffs.push(d);
|
||||
});
|
||||
stream.on("end", () => {
|
||||
buff = Buffer.concat(buffs);
|
||||
});
|
||||
}
|
||||
});
|
||||
req.busboy.on("field", (key, value) => {
|
||||
if (key == "saveas") {
|
||||
saveas = value;
|
||||
}
|
||||
});
|
||||
req.busboy.on("finish", () => {
|
||||
if (!buff || !saveas) {
|
||||
return res.status(400).end();
|
||||
}
|
||||
let fileExists = new Promise((resolve, reject) => {
|
||||
// Check if file exists
|
||||
fs.stat(relative(res.filename, saveas), (err, stats) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
return resolve(stats);
|
||||
});
|
||||
});
|
||||
|
||||
fileExists.then((stats) => {
|
||||
req.flash("error", "File exists, cannot overwrite. ");
|
||||
res.redirect("back");
|
||||
}).catch((err) => {
|
||||
console.log("saving");
|
||||
let save = fs.createWriteStream(relative(res.filename, saveas));
|
||||
save.on("close", () => {
|
||||
if (buff.length === 0) {
|
||||
req.flash("success", "File saved. Warning: empty file.");
|
||||
}
|
||||
else {
|
||||
req.flash("success", "File saved. ");
|
||||
}
|
||||
res.redirect("back");
|
||||
});
|
||||
save.on("error", (err) => {
|
||||
req.flash("error", err);
|
||||
res.redirect("back");
|
||||
});
|
||||
save.write(buff);
|
||||
save.end();
|
||||
});
|
||||
});
|
||||
req.pipe(req.busboy);
|
||||
});
|
||||
|
||||
app.post("/*@mkdir", (req, res) => {
|
||||
res.filename = req.params[0];
|
||||
|
||||
let folder = req.body.folder;
|
||||
if (!folder || folder.length < 1) {
|
||||
return res.status(400).end();
|
||||
}
|
||||
|
||||
let fileExists = new Promise((resolve, reject) => {
|
||||
// Check if file exists
|
||||
fs.stat(relative(res.filename, folder), (err, stats) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
return resolve(stats);
|
||||
});
|
||||
});
|
||||
|
||||
fileExists.then((stats) => {
|
||||
req.flash("error", "Folder exists, cannot overwrite. ");
|
||||
res.redirect("back");
|
||||
}).catch((err) => {
|
||||
fs.mkdir(relative(res.filename, folder), (err) => {
|
||||
if (err) {
|
||||
req.flash("error", err);
|
||||
res.redirect("back");
|
||||
}
|
||||
req.flash("success", "Folder created. ");
|
||||
res.redirect("back");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.post("/*@delete", (req, res) => {
|
||||
res.filename = req.params[0];
|
||||
|
||||
let files = JSON.parse(req.body.files);
|
||||
if (!files || !files.map) {
|
||||
req.flash("error", "No files selected.");
|
||||
res.redirect("back");
|
||||
return; // res.status(400).end();
|
||||
}
|
||||
|
||||
let promises = files.map(f => {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.stat(relative(res.filename, f), (err, stats) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve({
|
||||
name: f,
|
||||
isdirectory: stats.isDirectory(),
|
||||
isfile: stats.isFile()
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Promise.all(promises).then((files) => {
|
||||
let promises = files.map(f => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let op = null;
|
||||
if (f.isdirectory) {
|
||||
op = fs.rmdir;
|
||||
}
|
||||
else if (f.isfile) {
|
||||
op = fs.unlink;
|
||||
}
|
||||
if (op) {
|
||||
op(relative(res.filename, f.name), (err) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
Promise.all(promises).then(() => {
|
||||
req.flash("success", "Files deleted. ");
|
||||
res.redirect("back");
|
||||
}).catch((err) => { // TODO: recursive rmdir https://github.com/isaacs/rimraf
|
||||
req.flash("error", "Unable to delete some files: " + err);
|
||||
res.redirect("back");
|
||||
});
|
||||
}).catch((err) => {
|
||||
req.flash("error", err);
|
||||
res.redirect("back");
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/*@download", (req, res) => {
|
||||
res.filename = req.params[0];
|
||||
|
||||
let files = null;
|
||||
try {
|
||||
files = JSON.parse(req.query.files);
|
||||
} catch (e) {}
|
||||
if (!files || !files.map) {
|
||||
req.flash("error", "No files selected.");
|
||||
res.redirect("back");
|
||||
return; // res.status(400).end();
|
||||
}
|
||||
|
||||
let promises = files.map(f => {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.stat(relative(res.filename, f), (err, stats) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve({
|
||||
name: f,
|
||||
isdirectory: stats.isDirectory(),
|
||||
isfile: stats.isFile()
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Promise.all(promises).then((files) => {
|
||||
let zip = archiver.create("zip", {});
|
||||
zip.on("error", function(err) {
|
||||
res.status(500).send({
|
||||
error: err.message
|
||||
});
|
||||
});
|
||||
|
||||
files.filter(f => f.isfile).forEach((f) => {
|
||||
zip.file(relative(res.filename, f.name), { name: f.name });
|
||||
});
|
||||
files.filter(f => f.isdirectory).forEach((f) => {
|
||||
zip.directory(relative(res.filename, f.name), f.name);
|
||||
});
|
||||
|
||||
res.attachment("Archive.zip");
|
||||
zip.pipe(res);
|
||||
|
||||
zip.finalize();
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
req.flash("error", err);
|
||||
res.redirect("back");
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/*", (req, res) => {
|
||||
if (res.stats.error) {
|
||||
res.render("list", flashify(req, {
|
||||
path: res.filename,
|
||||
errors: [
|
||||
res.stats.error
|
||||
]
|
||||
}));
|
||||
}
|
||||
else if (res.stats.isDirectory()) {
|
||||
if (!req.url.endsWith("/")) {
|
||||
return res.redirect(req.url + "/");
|
||||
}
|
||||
|
||||
let readDir = new Promise((resolve, reject) => {
|
||||
fs.readdir(relative(res.filename), (err, filenames) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
return resolve(filenames);
|
||||
});
|
||||
});
|
||||
|
||||
readDir.then((filenames) => {
|
||||
let promises = filenames.map(f => {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.stat(relative(res.filename, f), (err, stats) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve({
|
||||
name: f,
|
||||
isdirectory: stats.isDirectory(),
|
||||
size: filesize(stats.size)
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Promise.all(promises).then((files) => {
|
||||
res.render("list", flashify(req, {
|
||||
path: res.filename,
|
||||
files: files,
|
||||
}));
|
||||
}).catch((err) => {
|
||||
res.render("list", flashify(req, {
|
||||
path: res.filename,
|
||||
errors: [
|
||||
err
|
||||
]
|
||||
}));
|
||||
});
|
||||
}).catch((err) => {
|
||||
res.render("list", flashify(req, {
|
||||
path: res.filename,
|
||||
errors: [
|
||||
err
|
||||
]
|
||||
}));
|
||||
});
|
||||
}
|
||||
else if (res.stats.isFile()) {
|
||||
res.download(relative(res.filename));
|
||||
}
|
||||
});
|
||||
|
||||
// startup
|
||||
|
||||
app.listen(process.env.PORT || 8080);
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "yourdump",
|
||||
"description": "A simple file manager",
|
||||
"dependencies": {
|
||||
"archiver": "^1.1.0",
|
||||
"body-parser": "^1.15.2",
|
||||
"bootstrap": "^4.0.0-alpha.4",
|
||||
"connect-busboy": "0.0.2",
|
||||
"connect-flash": "^0.1.1",
|
||||
"express": "^4.14.0",
|
||||
"express-handlebars": "^3.0.0",
|
||||
"express-session": "^1.14.1",
|
||||
"filesize": "^3.3.0",
|
||||
"jquery": "^3.1.1",
|
||||
"notp": "^2.0.3",
|
||||
"octicons": "^4.4.0",
|
||||
"thirty-two": "^1.0.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>File Manager</title>
|
||||
<link rel="stylesheet" href="/octicons/font/octicons.min.css" />
|
||||
<link rel="stylesheet" href="/bootstrap/css/bootstrap.min.css" />
|
||||
<script src="/jquery/jquery.min.js"></script>
|
||||
<script src="/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="/filesize/filesize.es6.js"></script>
|
||||
|
||||
<script src="/assets/multi.js"></script>
|
||||
<link rel="stylesheet" href="/assets/upload.css" />
|
||||
<script src="/assets/upload.js"></script>
|
||||
<script src="/assets/login.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
{{{body}}}
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,42 @@
|
|||
{{> navbar path=path}}
|
||||
|
||||
<main class="container" style="margin-top: 64px; margin-bottom: 64px;">
|
||||
{{#each errors as |error|}}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{{error}}
|
||||
</div>
|
||||
{{/each}}
|
||||
{{#each successes as |success|}}
|
||||
<div class="alert alert-success" role="alert">
|
||||
{{success}}
|
||||
</div>
|
||||
{{/each}}
|
||||
<ul class="list-group">
|
||||
{{#each files}}
|
||||
<li class="list-group-item">
|
||||
<label class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input multi-select" data-select="{{name}}">
|
||||
<span class="custom-control-indicator"></span>
|
||||
</label>
|
||||
{{#if isdirectory}}
|
||||
<a href="{{name}}/">{{name}}/</a>
|
||||
{{else}}
|
||||
<a href="{{name}}">{{name}}</a>
|
||||
<span class="tag tag-default tag-pill pull-xs-right">{{size}}</span>
|
||||
{{/if}}
|
||||
</li>
|
||||
{{else}}
|
||||
<li class="list-group-item">
|
||||
No files
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</main>
|
||||
|
||||
{{> toolbar}}
|
||||
|
||||
{{> dialogue-upload}}
|
||||
{{> dialogue-mkdir}}
|
||||
|
||||
{{> dialogue-download}}
|
||||
{{> dialogue-delete}}
|
|
@ -0,0 +1,31 @@
|
|||
{{> navbar isloggingin=true}}
|
||||
|
||||
<main class="container" style="margin-top: 64px; margin-bottom: 64px;">
|
||||
<div class="row">
|
||||
<div class="col-md-6 offset-md-3">
|
||||
{{#each errors as |error|}}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{{error}}
|
||||
</div>
|
||||
{{/each}}
|
||||
{{#each successes as |success|}}
|
||||
<div class="alert alert-success" role="alert">
|
||||
{{success}}
|
||||
</div>
|
||||
{{/each}}
|
||||
<div class="card">
|
||||
<form class="card-block" action="/@login" method="post">
|
||||
<div class="form-group">
|
||||
<label>Token:</label>
|
||||
<div class="input-group input-group-lg">
|
||||
<input name="token" id="login-token" type="text" class="form-control" placeholder="000 000" size="6" />
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-success" type="submit">Sign in</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
|
@ -0,0 +1,25 @@
|
|||
<form action="@delete" method="post">
|
||||
<div id="delete" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">Are you sure?</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>You are deleting the following files: </p>
|
||||
<ul class="list-group multi-files">
|
||||
|
||||
</ul>
|
||||
<input type="hidden" name="files" value="" class="multi-files-value" />
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="reset" class="btn btn-primary" data-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-danger">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,25 @@
|
|||
<form action="@download" method="get">
|
||||
<div id="download" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">Download file archive?</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Your archive will contain the following files: </p>
|
||||
<ul class="list-group multi-files">
|
||||
|
||||
</ul>
|
||||
<input type="hidden" name="files" value="" class="multi-files-value" />
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="reset" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Download</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,24 @@
|
|||
<form action="@mkdir" method="post">
|
||||
<div id="mkdir" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">Create a folder</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="mkdir-folder">Folder name: </label>
|
||||
<input name="folder" class="form-control" type="text" id="mkdir-folder" placeholder="folder" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="reset" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Create</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,34 @@
|
|||
<form action="@upload" method="post" enctype="multipart/form-data">
|
||||
<div id="upload" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">Upload a file</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label class="custom-file">
|
||||
<input name="file" type="file" id="upload-file" class="custom-file-input">
|
||||
<span class="custom-file-control"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group upload-unhide">
|
||||
<label for="upload-file-size">Filesize: </label>
|
||||
<input class="form-control" type="text" disabled id="upload-file-size" placeholder="0B" />
|
||||
</div>
|
||||
<div class="form-group upload-unhide">
|
||||
<label for="upload-file-saveas">Save as: </label>
|
||||
<input name="saveas" class="form-control" type="text" id="upload-file-saveas" placeholder="filename" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="reset" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Upload</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,25 @@
|
|||
<nav class="navbar navbar-dark bg-primary navbar-fixed-top">
|
||||
<a class="navbar-brand" href="/">File Manager</a>
|
||||
<ul class="nav navbar-nav">
|
||||
{{#eachpath path}}
|
||||
<li class="nav-item{{#if current}} active{{/if}}">
|
||||
<a class="nav-link" href="{{path}}">{{name}}</a>
|
||||
</li>
|
||||
{{/eachpath}}
|
||||
</ul>
|
||||
<ul class="nav navbar-nav pull-xs-right">
|
||||
<li class="nav-item">
|
||||
{{#if isloginenabled}}
|
||||
{{#if isloggingin}}
|
||||
<a class="nav-link" href="/@login" title="Sign in">
|
||||
<span class="octicon octicon-sign-in"></span>
|
||||
</a>
|
||||
{{else}}
|
||||
<a class="nav-link" href="/@logout" title="Sign out">
|
||||
<span class="octicon octicon-sign-out"></span>
|
||||
</a>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
|
@ -0,0 +1,28 @@
|
|||
<nav class="navbar navbar-light bg-faded navbar-fixed-bottom">
|
||||
<div class="btn-group" role="group">
|
||||
<a class="btn btn-secondary" href="@upload" data-toggle="modal" data-target="#upload">
|
||||
<span class="octicon octicon-cloud-upload"></span>
|
||||
Upload
|
||||
</a>
|
||||
<a class="btn btn-secondary" href="@mkdir" data-toggle="modal" data-target="#mkdir">
|
||||
<span class="octicon octicon-file-directory"></span>
|
||||
New folder
|
||||
</a>
|
||||
</div>
|
||||
<div class="btn-group" role="group">
|
||||
<a class="btn btn-secondary" href="@download" data-toggle="modal" data-target="#download">
|
||||
<span class="octicon octicon-file-zip"></span>
|
||||
Download
|
||||
</a>
|
||||
<a class="btn btn-secondary" href="@delete" data-toggle="modal" data-target="#delete">
|
||||
<span class="octicon octicon-trashcan"></span>
|
||||
Delete
|
||||
</a>
|
||||
</div>
|
||||
<div class="btn-group pull-xs-right" role="group">
|
||||
<a class="btn btn-secondary" href="./">
|
||||
<span class="octicon octicon-sync"></span>
|
||||
Refresh
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
Loading…
Reference in New Issue