droppy
Version:
Self-hosted file storage
220 lines (193 loc) • 6.03 kB
JavaScript
;
const fs = require("fs");
const pkg = require("./package.json");
const untildify = require("untildify");
const path = require("path");
require("util").inspect.defaultOptions.depth = 4;
const argv = require("minimist")(process.argv.slice(2), {
boolean: ["color", "d", "daemon", "dev"]
});
if (!argv.dev) {
process.env.NODE_ENV = "production";
}
process.title = pkg.name;
process.chdir(__dirname);
const cmds = {
start: "start Start the server",
stop: "stop Stop all daemonized servers",
config: "config Edit the config",
list: "list List users",
add: "add <user> <pass> [p] Add or update a user. Specify 'p' for privileged",
del: "del <user> Delete a user",
build: "build Build client resources",
version: "version, -v Print version",
};
const opts = {
configdir: "-c, --configdir <dir> Config directory. Default: ~/.droppy/config",
filesdir: "-f, --filesdir <dir> Files directory. Default: ~/.droppy/files",
daemon: "-d, --daemon Daemonize (background) process",
log: "-l, --log <file> Log to file instead of stdout",
dev: "--dev Enable developing mode",
color: "--color Force-enable colored log output",
nocolor: "--no-color Force-disable colored log output",
};
if (argv.v || argv.V || argv.version) {
console.info(pkg.version);
process.exit(0);
}
if (argv.daemon || argv.d) {
require("daemonize-process")();
}
if (argv._[0] === "build") {
console.info("Building resources ...");
require("./server/resources.js").build(err => {
console.info(err || "Resources built successfully");
process.exit(err ? 1 : 0);
});
}
if (argv.configdir || argv.filesdir || argv.c || argv.f) {
require("./server/paths.js").seed(argv.configdir || argv.c, argv.filesdir || argv.f);
}
if (argv.log || argv.l) {
try {
require("./server/log.js").setLogFile(fs.openSync(untildify(path.resolve(argv.log || argv.l)), "a", "644"));
} catch (err) {
console.error(`Unable to open log file for writing: ${err.message}`);
process.exit(1);
}
}
if (!argv._.length) {
printHelp();
process.exit(0);
}
const cmd = argv._[0];
const args = argv._.slice(1);
if (cmds[cmd]) {
let db;
if (cmd === "start") {
require("./server/server.js")(null, true, argv.dev, err => {
if (err) {
require("./server/log.js").error(err);
process.exit(1);
}
});
} else if (cmd === "stop") {
const ps = require("ps-node");
const log = require("./server/log.js");
ps.lookup({command: pkg.name}, async (err, procs) => {
if (err) {
log.error(err);
process.exit(1);
} else {
procs = procs.filter(proc => Number(proc.pid) !== process.pid);
if (!procs.length) {
log.info("No processes found");
process.exit(0);
}
const pids = await Promise.all(procs.map(proc => {
return new Promise(resolve => {
ps.kill(proc.pid, err => {
if (err) {
log.error(err);
return process.exit(1);
}
resolve(proc.pid);
});
});
}));
if (pids.length) {
console.info(`Killed PIDs: ${pids.join(", ")}`);
}
process.exit(0);
}
});
} else if (cmd === "version") {
console.info(pkg.version);
} else if (cmd === "config") {
const paths = require("./server/paths.js").get();
const cfg = require("./server/cfg.js");
const edit = function() {
findEditor(editor => {
if (!editor) return console.error(`No suitable editor found, please edit ${paths.cfgFile}`);
require("child_process").spawn(editor, [paths.cfgFile], {stdio: "inherit"});
});
};
fs.stat(paths.cfgFile, err => {
if (err && err.code === "ENOENT") {
fs.mkdir(paths.config, {recursive: true}, () => {
cfg.init(null, err => {
if (err) return console.error(new Error(err.message || err).stack);
edit();
});
});
} else {
edit();
}
});
} else if (cmd === "list") {
db = require("./server/db.js");
db.load(() => {
printUsers(db.get("users"));
});
} else if (cmd === "add") {
if (args.length !== 2 && args.length !== 3) printHelp();
db = require("./server/db.js");
db.load(() => {
db.addOrUpdateUser(args[0], args[1], args[2] === "p", () => {
printUsers(db.get("users"));
});
});
} else if (cmd === "del") {
if (args.length !== 1) printHelp();
db = require("./server/db.js");
db.load(() => {
db.delUser(args[0], () => {
printUsers(db.get("users"));
});
});
}
} else {
printHelp();
}
function printHelp() {
let help = `Usage: ${pkg.name} command [options]\n\n Commands:`;
Object.keys(cmds).forEach(command => {
help += `\n ${cmds[command]}`;
});
help += "\n\n Options:";
Object.keys(opts).forEach(option => {
help += `\n ${opts[option]}`;
});
console.info(help);
process.exit();
}
function printUsers(users) {
if (Object.keys(users).length === 0) {
console.info("No users defined. Use 'add' to add one.");
} else {
console.info(`Current Users:\n${Object.keys(users).map(user => {
return ` - ${user}`;
}).join("\n")}`);
}
}
function findEditor(cb) {
const editors = ["vim", "nano", "vi", "npp", "pico", "emacs", "notepad"];
const basename = require("path").basename;
const which = require("which");
const userEditor = basename(process.env.VISUAL || process.env.EDITOR);
if (!editors.includes(userEditor)) {
editors.unshift(userEditor);
}
(function find(editor) {
try {
cb(which.sync(editor));
} catch {
if (editors.length) {
find(editors.shift());
} else {
cb();
}
}
})(editors.shift());
}