pm2-web
Version:
A web based monitor for PM2
262 lines (205 loc) • 6.84 kB
JavaScript
var Autowire = require("wantsit").Autowire,
cjson = require("cjson"),
fs = require("fs"),
argv = require("minimist")(process.argv.slice(2)),
pwuid = require('pwuid');
var DEFAULT_CONFIG_FILE = __dirname + "/../../config.json";
var GLOBAL_CONFIG_FILE = "/etc/pm2-web/config.json";
var USER_CONFIG_FILE = process.env["HOME"] + "/.config/pm2-web/config.json";
var Configuration = function(options) {
this._logger = Autowire;
// load defaults from bundled config file
this._config = cjson.load(DEFAULT_CONFIG_FILE);
// override defaults with config file
this._override(this._loadConfigFile(), this._config);
// remove extra bits from command line arguments
if(argv) {
delete argv._;
delete argv.$0;
// respect arguments passed on the command line
var commandLine = {};
Object.keys(argv).forEach(function(key) {
this._apply(key, argv[key], commandLine);
}.bind(this));
// override config file with command line
this._override(commandLine, this._config);
}
// override everything with passed arguments
this._override(options || {}, this._config);
this._normaliseHosts();
}
Configuration.prototype.afterPropertiesSet = function() {
// need to rethink this
if(argv["config"]) {
this._logger.info("Configuration", "Loading config file from", argv["config"]);
} else if (fs.existsSync(USER_CONFIG_FILE)) {
this._logger.info("Configuration", "Loading config file from", USER_CONFIG_FILE);
} else if (fs.existsSync(GLOBAL_CONFIG_FILE)) {
this._logger.info("Configuration", "Loading config file from", GLOBAL_CONFIG_FILE);
}
// do not print passwords in the logs...
var config = JSON.parse(JSON.stringify(this._config));
config.www.authentication.password = "**** LA LA LA, NOTHING TO SEE HERE ****";
config.www.ssl.passphrase = "**** LA LA LA, NOTHING TO SEE HERE ****";
this._logger.info("Configuration", "Loaded default configuration from", DEFAULT_CONFIG_FILE);
this._logger.info("Configuration", "Final configuration:", JSON.stringify(config, null, 2));
}
Configuration.prototype._loadConfigFile = function() {
// try to find a config file
if(argv["config"]) {
// if a config file has been specified make it override all settings
//this._logger.info("Loading config file from", this.get("config"));
return cjson.load(argv["config"]);
} else if (fs.existsSync(USER_CONFIG_FILE)) {
// otherwise if a user specific config file is present, make that override all settings
//this._logger.info("Loading config file from", USER_CONFIG_FILE);
return cjson.load(USER_CONFIG_FILE);
} else if (fs.existsSync(GLOBAL_CONFIG_FILE)) {
// otherwise if a global config file is present, make that override all settings
//this._logger.info("Loading config file from", GLOBAL_CONFIG_FILE);
return cjson.load(GLOBAL_CONFIG_FILE);
}
return {};
}
Configuration.prototype._normaliseHosts = function() {
var args = this.get("pm2");
if(!args) {
args = [];
}
// command line arguments arrive like:
// --pm2:host=foo --pm2:rpc=6666 --pm2:host=bar
// {host: ["foo", "bar"], rpc: 6666}
if(Array.isArray(args.host)) {
var hosts = [];
args.rpc = this._arrayify(args.rpc);
args.events = this._arrayify(args.events);
args.inspector = this._arrayify(args.inspector);
args.host.forEach(function(host, index) {
hosts.push(this._defaults({
"host": host,
"rpc": args.rpc[index] ? args.rpc[index] : undefined,
"events": args.events[index] ? args.events[index] : undefined,
"inspector": args.inspector[index] ? args.inspector[index] : undefined
}, {
"host": "localhost",
"rpc": "~/.pm2/rpc.sock",
"events": "~/.pm2/pub.sock"
}));
}.bind(this));
args = hosts;
}
if(!Array.isArray(args) && typeof args == "object") {
args = [args];
}
var userDetails = pwuid()
// ensure data is correct for each host
args.forEach(function(host) {
host.host = host.host || "localhost";
host.rpc = host.rpc || "~/.pm2/rpc.sock";
host.events = host.events || "~/.pm2/pub.sock";
if(host.rpc.substring(0, 1) == "~") {
host.rpc = userDetails.dir + host.rpc.substring(1)
}
if(host.events.substring(0, 1) == "~") {
host.events = userDetails.dir + host.events.substring(1)
}
if(host.inspector === undefined) {
delete host.inspector;
}
});
this.set("pm2", args);
}
Configuration.prototype._arrayify = function(arg) {
if(!arg) {
return [];
}
if(Array.isArray(arg)) {
return arg;
}
return [arg];
}
Configuration.prototype.get = function(key) {
if(!this._config || !key) {
return null;
}
var value = this._config;
key.split(":").forEach(function(property) {
if(value && typeof(value[property]) != "undefined") {
value = value[property];
} else {
value = null;
}
});
return value;
}
Configuration.prototype.set = function(key, value) {
if(!this._config || !key) {
return;
}
this._apply(key, value, this._config);
}
Configuration.prototype._apply = function(key, value, target) {
var parts;
if(key.indexOf(":") != -1) {
parts = key.split(":");
} else {
parts = key.split(".");
}
parts.forEach(function(property, index) {
if((parts.length - 1) == index) {
target[property] = value;
} else {
if(!target[property]) {
target[property] = {};
}
target = target[property];
}
});
}
Configuration.prototype._defaults = function(object, defaults) {
if(typeof object == "undefined" || object == null) {
return defaults;
}
if(typeof defaults == "string" || defaults instanceof String) {
return object ? object : defaults;
}
if(typeof defaults == "boolean") {
return object ? true : false;
}
if(!isNaN(parseFloat(defaults)) && isFinite(defaults)) {
return typeof object == "undefined" ? defaults : object;
}
if(Array.isArray(defaults)) {
var output = [];
object.forEach(function(entry, index) {
output.push(this._defaults(entry, defaults[index] ? defaults[index] : defaults[0]));
}.bind(this));
return output;
}
if(typeof defaults == "object") {
var output = {};
Object.keys(defaults).forEach(function(key) {
output[key] = this._defaults(object[key], defaults[key]);
}.bind(this));
return output;
}
this._logger.error("Configuration", "Don't know what to do with", object, "expected", defaults);
}
Configuration.prototype._override = function(source, target) {
Object.keys(source).forEach(function(key) {
if(typeof source[key] == "string" || source[key] instanceof String ||
typeof source[key] == "boolean" ||
(!isNaN(parseFloat(source[key])) && isFinite(source[key]))) {
target[key] = source[key];
}
if(Array.isArray(source[key])) {
target[key] = source[key];
} else if(Array.isArray(target[key])) {
target[key] = [source[key]];
}
if(typeof source[key] == "object") {
this._override(source[key], target[key]);
}
}.bind(this));
}
module.exports = Configuration;