code style: standard
This commit is contained in:
parent
4827072994
commit
67bb3ec4c9
|
|
@ -1,255 +1,243 @@
|
|||
var blessed = require('blessed'),
|
||||
chalk = require('chalk'),
|
||||
async = require('async'),
|
||||
widgets = require('./widgets'),
|
||||
conf = require('../util/conf'),
|
||||
_ = require('lodash'),
|
||||
stats = require('../stat'),
|
||||
Log = require('../util/log');
|
||||
var blessed = require('blessed')
|
||||
var chalk = require('chalk')
|
||||
var async = require('async')
|
||||
var _ = require('lodash')
|
||||
var widgets = require('./widgets')
|
||||
var conf = require('../util/conf')
|
||||
var Log = require('../util/log')
|
||||
|
||||
module.exports = Layout;
|
||||
module.exports = Layout
|
||||
|
||||
var exiting = false;
|
||||
var exiting = false
|
||||
/**
|
||||
* Create layout.
|
||||
* @param {Object} options
|
||||
*/
|
||||
function Layout(options) {
|
||||
function Layout (options) {
|
||||
if (!(this instanceof Layout)) {
|
||||
return new Layout(options);
|
||||
return new Layout(options)
|
||||
}
|
||||
options = _.clone(options || {});
|
||||
options = _.clone(options || {})
|
||||
if (!options.hostname) {
|
||||
options.hostname = '127.0.0.1';
|
||||
options.hostname = '127.0.0.1'
|
||||
}
|
||||
if (!options.port) {
|
||||
throw new Error('Port of socket.io server is required!');
|
||||
throw new Error('Port of socket.io server is required!')
|
||||
}
|
||||
options.sockets = options.sockets || {};
|
||||
this.options = options;
|
||||
this._eles = {};
|
||||
this._procCount = 0;
|
||||
};
|
||||
options.sockets = options.sockets || {}
|
||||
this.options = options
|
||||
this._eles = {}
|
||||
this._procCount = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Render GUI.
|
||||
*/
|
||||
Layout.prototype.render = function (monitor) {
|
||||
var self = this,
|
||||
options = this.options,
|
||||
jobs = {};
|
||||
var self = this
|
||||
var options = this.options
|
||||
|
||||
// Preparing all socket.io clients.
|
||||
Object.keys(conf.NSP).forEach(function (ns) {
|
||||
var nsl = ns.toLowerCase();
|
||||
if (options.sockets[nsl]) {
|
||||
return;
|
||||
}
|
||||
jobs[nsl] = function (next) {
|
||||
var opts = _.extend({
|
||||
async.series(Object.keys(conf.NSP).map(function (ns) {
|
||||
return function (callback) {
|
||||
var callbackOnce = _.once(callback)
|
||||
var nsl = ns.toLowerCase()
|
||||
if (options.sockets[nsl]) {
|
||||
return callbackOnce()
|
||||
}
|
||||
|
||||
monitor.connect(_.extend({
|
||||
namespace: conf.NSP[ns]
|
||||
}, options);
|
||||
|
||||
monitor.connect(opts, function (socket) {
|
||||
console.info('Connected to', socket.nsp);
|
||||
!next._called && next(null, socket);
|
||||
next._called = true;
|
||||
}, options), function (socket) {
|
||||
console.info('Connected to', socket.nsp)
|
||||
callbackOnce(null, socket)
|
||||
}, function (err, socket) {
|
||||
console.log(err);
|
||||
if (!next._called) {
|
||||
next(err, socket);
|
||||
next._called = true;
|
||||
} else {
|
||||
//Log(options.log);
|
||||
if (err) {
|
||||
return callbackOnce(new Error('Failed to connect to [' + ns + '] due to ' + err.message))
|
||||
}
|
||||
console.error('Failed due to', err.message, 'when connecting to', socket.nsp);
|
||||
if (next._called) {
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
var done = function (err, res) {
|
||||
}), function (err, res) {
|
||||
if (err) {
|
||||
return process.exit(0);
|
||||
console.error(err.message)
|
||||
return process.exit(0)
|
||||
}
|
||||
Log({
|
||||
level: 1000
|
||||
});
|
||||
self.sockets = _.extend(res, options.sockets);
|
||||
delete options.sockets;
|
||||
})
|
||||
var connectedSockets = {}
|
||||
res.forEach(function (socket) {
|
||||
connectedSockets[socket.nsp] = socket
|
||||
})
|
||||
self.sockets = _.extend(connectedSockets, options.sockets)
|
||||
delete options.sockets
|
||||
|
||||
self._observe();
|
||||
self._draw();
|
||||
self._observe()
|
||||
self._draw()
|
||||
|
||||
setInterval(function () {
|
||||
self._bindProcesses();
|
||||
}, 1000);
|
||||
};
|
||||
if (_.keys(jobs).length == 0) {
|
||||
return done();
|
||||
}
|
||||
async.parallel(jobs, done);
|
||||
};
|
||||
self._bindProcesses()
|
||||
}, 1000)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Observe socket.io events.
|
||||
*/
|
||||
Layout.prototype._observe = function () {
|
||||
var self = this;
|
||||
console.info('Listening socket events...');
|
||||
var socketSys = this._socket(conf.NSP.SYS);
|
||||
var self = this
|
||||
console.info('Listening socket events...')
|
||||
var socketSys = this._socket(conf.NSP.SYS)
|
||||
socketSys.on('procs', function (procs) {
|
||||
self._procs = {
|
||||
data: procs,
|
||||
tick: Date.now()
|
||||
};
|
||||
(typeof self._procs == 'undefined') && self._bindProcesses();
|
||||
});
|
||||
socketSys.emit('procs');
|
||||
}
|
||||
if (typeof self._procs === 'undefined') {
|
||||
self._bindProcesses()
|
||||
}
|
||||
})
|
||||
socketSys.emit('procs')
|
||||
|
||||
this._socket(conf.NSP.PROC).on('proc', function (proc) {
|
||||
if (!self._usages || proc.pid != self._usages.pid || self._usages.time == proc.time) {
|
||||
return;
|
||||
if (!self._usages || proc.pid !== self._usages.pid || self._usages.time === proc.time) {
|
||||
return
|
||||
}
|
||||
self._usages.time = proc.time;
|
||||
self._usages.cpu.shift();
|
||||
self._usages.cpu.push(Math.min(100, Math.max(proc.usage.cpu, 1)));
|
||||
self._usages.mem.shift();
|
||||
self._usages.mem.push(Math.min(100, Math.max(proc.usage.memory, 1)));
|
||||
});
|
||||
self._usages.time = proc.time
|
||||
self._usages.cpu.shift()
|
||||
self._usages.cpu.push(Math.min(100, Math.max(proc.usage.cpu, 1)))
|
||||
self._usages.mem.shift()
|
||||
self._usages.mem.push(Math.min(100, Math.max(proc.usage.memory, 1)))
|
||||
})
|
||||
|
||||
this._socket(conf.NSP.LOG).on('log', function (log) {
|
||||
if (!self._eles.logs || self._lastLogPMId != log.pm_id) {
|
||||
return;
|
||||
if (!self._eles.logs || self._lastLogPMId !== log.pm_id) {
|
||||
return
|
||||
}
|
||||
self._eles.logs.log(log.msg);
|
||||
});
|
||||
};
|
||||
self._eles.logs.log(log.msg)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind processes to table.
|
||||
*/
|
||||
Layout.prototype._bindProcesses = function () {
|
||||
if (exiting || !this._eles.processes || !this._procs) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (this._procs.tick == this._procsLastTick) {
|
||||
if (this._procs.tick === this._procsLastTick) {
|
||||
// Update tick only.
|
||||
return setRows.call(this, true);
|
||||
return setRows.call(this, true)
|
||||
}
|
||||
|
||||
if (typeof this._procsLastTick == 'undefined') {
|
||||
this._describeInfo(0);
|
||||
this._eles.processes.rows.on('select', onSelect.bind(this));
|
||||
if (typeof this._procsLastTick === 'undefined') {
|
||||
this._describeInfo(0)
|
||||
this._eles.processes.rows.on('select', onSelect.bind(this))
|
||||
}
|
||||
|
||||
this._procsLastTick = this._procs.tick;
|
||||
this._procsLastTick = this._procs.tick
|
||||
|
||||
setRows.call(this, true);
|
||||
setRows.call(this, true)
|
||||
|
||||
function setRows(forceRefresh) {
|
||||
var rows = [],
|
||||
selectedIndex = this._eles.processes.rows.selected,
|
||||
len = this._procs.data.length;
|
||||
function setRows (forceRefresh) {
|
||||
var rows = []
|
||||
var selectedIndex = this._eles.processes.rows.selected
|
||||
var len = this._procs.data.length
|
||||
|
||||
this._procs.data.forEach(function (p, i) {
|
||||
var pm2 = p.pm2_env,
|
||||
index = '[' + i + '/' + len + ']';
|
||||
var pm2 = p.pm2_env
|
||||
var index = '[' + i + '/' + len + ']'
|
||||
rows.push([
|
||||
' ' + chalk.grey((index + Array(8 - index.length).join(' '))) + ' ' + p.name,
|
||||
pm2.restart_time,
|
||||
pm2.status != 'online' ? '0s' : _fromNow(Math.ceil((Date.now() - pm2.pm_uptime) / 1000), true),
|
||||
pm2.status == 'online' ? chalk.green('✔') : chalk.red('✘')
|
||||
]);
|
||||
});
|
||||
pm2.status !== 'online' ? '0s' : _fromNow(Math.ceil((Date.now() - pm2.pm_uptime) / 1000), true),
|
||||
pm2.status === 'online' ? chalk.green('✔') : chalk.red('✘')
|
||||
])
|
||||
})
|
||||
this._eles.processes.setData({
|
||||
headers: [' Name', 'Restarts', 'Uptime', ''],
|
||||
rows: rows
|
||||
});
|
||||
})
|
||||
|
||||
selectedIndex = typeof selectedIndex != 'undefined' ? selectedIndex : 0;
|
||||
var maxIndex = this._eles.processes.rows.items.length - 1;
|
||||
selectedIndex = typeof selectedIndex !== 'undefined' ? selectedIndex : 0
|
||||
var maxIndex = this._eles.processes.rows.items.length - 1
|
||||
if (selectedIndex > maxIndex) {
|
||||
selectedIndex = maxIndex;
|
||||
selectedIndex = maxIndex
|
||||
}
|
||||
this._eles.processes.rows.select(selectedIndex);
|
||||
this._eles.processes.rows.select(selectedIndex)
|
||||
|
||||
if (forceRefresh) {
|
||||
onSelect.call(this);
|
||||
onSelect.call(this)
|
||||
}
|
||||
}
|
||||
|
||||
function onSelect(item, selectedIndex) {
|
||||
if (!!item) {
|
||||
var lastIndex = this._lastSelectedIndex;
|
||||
function onSelect (item, selectedIndex) {
|
||||
if (!!item) { // eslint-disable-line no-extra-boolean-cast
|
||||
var lastIndex = this._lastSelectedIndex
|
||||
|
||||
this._lastSelectedIndex = selectedIndex;
|
||||
if (selectedIndex != lastIndex) {
|
||||
this._describeInfo(selectedIndex);
|
||||
this._lastSelectedIndex = selectedIndex
|
||||
if (selectedIndex !== lastIndex) {
|
||||
this._describeInfo(selectedIndex)
|
||||
}
|
||||
}
|
||||
this._cpuAndMemUsage(this._lastSelectedIndex || 0);
|
||||
this._displayLogs(this._lastSelectedIndex || 0);
|
||||
this.screen.render();
|
||||
this._cpuAndMemUsage(this._lastSelectedIndex || 0)
|
||||
this._displayLogs(this._lastSelectedIndex || 0)
|
||||
this.screen.render()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get description of a specified process.
|
||||
* @param {Number} index the selected row index.
|
||||
*
|
||||
*/
|
||||
Layout.prototype._describeInfo = function (index) {
|
||||
var pm2 = this._dataOf(index);
|
||||
var pm2 = this._dataOf(index)
|
||||
if (!pm2) {
|
||||
return this._eles.json.setContent(_formatJSON({
|
||||
message: 'There is no process running!'
|
||||
}));
|
||||
}))
|
||||
}
|
||||
if (pm2.pm2_env && pm2.pm2_env.env) {
|
||||
// Remove useless large-bytes attributes.
|
||||
delete pm2.pm2_env.env['LS_COLORS'];
|
||||
delete pm2.pm2_env.env['LS_COLORS']
|
||||
}
|
||||
delete pm2.monit;
|
||||
this._eles.json.setContent(_formatJSON(pm2));
|
||||
};
|
||||
delete pm2.monit
|
||||
this._eles.json.setContent(_formatJSON(pm2))
|
||||
}
|
||||
|
||||
/**
|
||||
* CPU and Memory usage of a specific process
|
||||
* @param {Number} index the selected row index.
|
||||
*
|
||||
*/
|
||||
Layout.prototype._cpuAndMemUsage = function (index) {
|
||||
var pm2 = this._dataOf(index);
|
||||
var pm2 = this._dataOf(index)
|
||||
if (!pm2) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (!this._usages) {
|
||||
this._usages = {
|
||||
mem: [],
|
||||
cpu: []
|
||||
};
|
||||
var len = this._eles.cpu.width - 4;
|
||||
}
|
||||
var len = this._eles.cpu.width - 4
|
||||
for (var i = 0; i < len; i++) {
|
||||
this._usages.cpu.push(1);
|
||||
this._usages.mem.push(1);
|
||||
this._usages.cpu.push(1)
|
||||
this._usages.mem.push(1)
|
||||
}
|
||||
}
|
||||
if (pm2.pid != 0 && this._procCount == 2) {
|
||||
this._procCount = -1;
|
||||
this._socket(conf.NSP.PROC).emit('proc', pm2.pid);
|
||||
if (pm2.pid !== 0 && this._procCount === 2) {
|
||||
this._procCount = -1
|
||||
this._socket(conf.NSP.PROC).emit('proc', pm2.pid)
|
||||
}
|
||||
this._procCount++;
|
||||
this._usages.pid = pm2.pid;
|
||||
this._procCount++
|
||||
this._usages.pid = pm2.pid
|
||||
|
||||
this._eles.cpu.setData(this._usages.cpu, 0, 100);
|
||||
this._eles.cpu.setLabel('CPU Usage (' + (this._usages.cpu[this._usages.cpu.length - 1]).toFixed(2) + '%)');
|
||||
this._eles.cpu.setData(this._usages.cpu, 0, 100)
|
||||
this._eles.cpu.setLabel('CPU Usage (' + (this._usages.cpu[this._usages.cpu.length - 1]).toFixed(2) + '%)')
|
||||
|
||||
this._eles.mem.setData(this._usages.mem, 0, 100);
|
||||
this._eles.mem.setLabel('Memory Usage (' + (this._usages.mem[this._usages.mem.length - 1]).toFixed(2) + '%)');
|
||||
};
|
||||
this._eles.mem.setData(this._usages.mem, 0, 100)
|
||||
this._eles.mem.setLabel('Memory Usage (' + (this._usages.mem[this._usages.mem.length - 1]).toFixed(2) + '%)')
|
||||
}
|
||||
|
||||
/**
|
||||
* Display logs.
|
||||
|
|
@ -257,24 +245,24 @@ Layout.prototype._cpuAndMemUsage = function (index) {
|
|||
* @return {[type]} [description]
|
||||
*/
|
||||
Layout.prototype._displayLogs = function (index) {
|
||||
var pm2 = this._dataOf(index);
|
||||
if (!pm2 || this._lastLogPMId == pm2.pm_id) {
|
||||
return;
|
||||
var pm2 = this._dataOf(index)
|
||||
if (!pm2 || this._lastLogPMId === pm2.pm_id) {
|
||||
return
|
||||
}
|
||||
this._killLogs();
|
||||
this._socket(conf.NSP.LOG).emit('tail', this._lastLogPMId = pm2.pm_id, true);
|
||||
};
|
||||
this._killLogs()
|
||||
this._socket(conf.NSP.LOG).emit('tail', this._lastLogPMId = pm2.pm_id, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill `tail` process
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
Layout.prototype._killLogs = function () {
|
||||
if (typeof this._lastLogPMId == 'undefined') {
|
||||
return;
|
||||
if (typeof this._lastLogPMId === 'undefined') {
|
||||
return
|
||||
}
|
||||
this._socket(conf.NSP.LOG).emit('tail_kill', this._lastLogPMId);
|
||||
};
|
||||
this._socket(conf.NSP.LOG).emit('tail_kill', this._lastLogPMId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data by index.
|
||||
|
|
@ -283,74 +271,74 @@ Layout.prototype._killLogs = function () {
|
|||
*/
|
||||
Layout.prototype._dataOf = function (index) {
|
||||
if (!this._procs || !Array.isArray(this._procs.data) || index >= this._procs.data.length) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
return this._procs.data[index];
|
||||
};
|
||||
return this._procs.data[index]
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw elements.
|
||||
*/
|
||||
Layout.prototype._draw = function () {
|
||||
console.info('Rendering dashboard...');
|
||||
var self = this;
|
||||
var screen = blessed.Screen();
|
||||
screen.title = 'PM2 Monitor';
|
||||
console.info('Rendering dashboard...')
|
||||
var self = this
|
||||
var screen = blessed.Screen()
|
||||
screen.title = 'PM2 Monitor'
|
||||
|
||||
var grid = _grid(screen);
|
||||
var grid = _grid(screen)
|
||||
|
||||
// Processes.
|
||||
this._eles.processes = grid.get(0, 0);
|
||||
this._bindProcesses();
|
||||
this._eles.processes = grid.get(0, 0)
|
||||
this._bindProcesses()
|
||||
|
||||
this._eles.cpu = grid.get(1, 0);
|
||||
this._eles.mem = grid.get(1, 1);
|
||||
this._eles.cpu = grid.get(1, 0)
|
||||
this._eles.mem = grid.get(1, 1)
|
||||
|
||||
// Logs.
|
||||
this._eles.logs = grid.get(2, 0);
|
||||
this._eles.logs = grid.get(2, 0)
|
||||
|
||||
// Detail.
|
||||
this._eles.json = grid.get(0, 2);
|
||||
var offset = Math.round(this._eles.json.height * 100 / this._eles.json.getScrollHeight()),
|
||||
dir;
|
||||
this._eles.json = grid.get(0, 2)
|
||||
var offset = Math.round(this._eles.json.height * 100 / this._eles.json.getScrollHeight())
|
||||
var dir
|
||||
// Key bindings
|
||||
screen.key('s', function (ch, key) {
|
||||
if (exiting) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
var perc = Math.min((dir != 'down' ? offset : 0) + self._eles.json.getScrollPerc() + 5, 100);
|
||||
dir = 'down';
|
||||
var perc = Math.min((dir !== 'down' ? offset : 0) + self._eles.json.getScrollPerc() + 5, 100)
|
||||
dir = 'down'
|
||||
self._eles.json.setScrollPerc(perc)
|
||||
});
|
||||
})
|
||||
screen.key('w', function (ch, key) {
|
||||
if (exiting) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
var perc = Math.max(self._eles.json.getScrollPerc() - 5 - (dir != 'up' ? offset : 0), 0);
|
||||
dir = 'up';
|
||||
var perc = Math.max(self._eles.json.getScrollPerc() - 5 - (dir !== 'up' ? offset : 0), 0)
|
||||
dir = 'up'
|
||||
self._eles.json.setScrollPerc(perc)
|
||||
});
|
||||
})
|
||||
|
||||
screen.key(['escape', 'q', 'C-c'], function (ch, key) {
|
||||
if (exiting) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
exiting = true;
|
||||
this._killLogs();
|
||||
screen.title = 'PM2 Monitor (Exiting...)';
|
||||
screen.destroy();
|
||||
screen.title = '';
|
||||
exiting = true
|
||||
this._killLogs()
|
||||
screen.title = 'PM2 Monitor (Exiting...)'
|
||||
screen.destroy()
|
||||
screen.title = ''
|
||||
screen.cursorReset()
|
||||
setTimeout(function () {
|
||||
// clear screen.
|
||||
// process.stdout.write('\u001B[2J\u001B[0;0f');
|
||||
process.exit(0);
|
||||
// process.stdout.write('\u001B[2J\u001B[0;0f')
|
||||
process.exit(0)
|
||||
}, 1000)
|
||||
}.bind(this));
|
||||
}.bind(this))
|
||||
|
||||
screen.render();
|
||||
this.screen = screen;
|
||||
};
|
||||
screen.render()
|
||||
this.screen = screen
|
||||
}
|
||||
|
||||
/**
|
||||
* Get socket.io object by namespace
|
||||
|
|
@ -358,10 +346,10 @@ Layout.prototype._draw = function () {
|
|||
*/
|
||||
Layout.prototype._socket = function (ns) {
|
||||
if (ns && this.sockets) {
|
||||
return this.sockets[_.trimLeft(ns, '/').toLowerCase()];
|
||||
return this.sockets[_.trimLeft(ns, '/').toLowerCase()]
|
||||
}
|
||||
return null;
|
||||
};
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Grid of screen elements.
|
||||
|
|
@ -369,7 +357,7 @@ Layout.prototype._socket = function (ns) {
|
|||
* @returns {*}
|
||||
* @private
|
||||
*/
|
||||
function _grid(screen) {
|
||||
function _grid (screen) {
|
||||
var style = {
|
||||
fg: '#013409',
|
||||
label: {
|
||||
|
|
@ -379,7 +367,7 @@ function _grid(screen) {
|
|||
border: {
|
||||
fg: '#5e9166'
|
||||
}
|
||||
};
|
||||
}
|
||||
// Layout.
|
||||
var grid = widgets.Grid({
|
||||
rows: 3,
|
||||
|
|
@ -387,7 +375,7 @@ function _grid(screen) {
|
|||
margin: 0,
|
||||
widths: [25, 25, 50],
|
||||
heights: [35, 10, 55]
|
||||
});
|
||||
})
|
||||
// Table of processes
|
||||
grid.set({
|
||||
row: 0,
|
||||
|
|
@ -403,7 +391,7 @@ function _grid(screen) {
|
|||
label: 'Processes (↑/↓ to move up/down, enter to select)',
|
||||
widths: [35, 15, 20, 15]
|
||||
}
|
||||
});
|
||||
})
|
||||
// Sparkline of CPU
|
||||
grid.set({
|
||||
row: 1,
|
||||
|
|
@ -425,7 +413,7 @@ function _grid(screen) {
|
|||
},
|
||||
label: 'CPU Usage(%)'
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
// Sparkline of Memory
|
||||
grid.set({
|
||||
|
|
@ -448,7 +436,7 @@ function _grid(screen) {
|
|||
},
|
||||
label: 'Memory Usage(%)'
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
// Logs
|
||||
grid.set({
|
||||
|
|
@ -463,7 +451,7 @@ function _grid(screen) {
|
|||
style: style,
|
||||
label: 'Logs'
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
// JSON data.
|
||||
grid.set({
|
||||
|
|
@ -479,10 +467,10 @@ function _grid(screen) {
|
|||
style: style,
|
||||
keys: true
|
||||
}
|
||||
});
|
||||
grid.draw(screen);
|
||||
})
|
||||
grid.draw(screen)
|
||||
|
||||
return grid;
|
||||
return grid
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -491,20 +479,20 @@ function _grid(screen) {
|
|||
* @returns {XML|*|string|void}
|
||||
* @private
|
||||
*/
|
||||
function _formatJSON(data) {
|
||||
data = JSON.stringify(typeof data != 'string' ? data : JSON.parse(data), null, 2);
|
||||
function _formatJSON (data) {
|
||||
data = JSON.stringify(typeof data !== 'string' ? data : JSON.parse(data), null, 2)
|
||||
|
||||
return data.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (m) {
|
||||
var color = 'blue';
|
||||
var color = 'blue'
|
||||
if (/^"/.test(m)) {
|
||||
color = ['magenta', 'green'][/:$/.test(m) ? 0 : 1];
|
||||
color = ['magenta', 'green'][/:$/.test(m) ? 0 : 1]
|
||||
} else if (/true|false/.test(m)) {
|
||||
color = 'blue';
|
||||
color = 'blue'
|
||||
} else if (/null|undefined/.test(m)) {
|
||||
color = 'blue';
|
||||
color = 'blue'
|
||||
}
|
||||
return chalk[color](m);
|
||||
});
|
||||
return chalk[color](m)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -513,40 +501,18 @@ function _formatJSON(data) {
|
|||
* @param {Boolean} tiny show all of it.
|
||||
* @returns {string}
|
||||
*/
|
||||
function _fromNow(tick, tiny) {
|
||||
function _fromNow (tick, tiny) {
|
||||
if (tick < 60) {
|
||||
return tick + 's';
|
||||
return tick + 's'
|
||||
}
|
||||
var s = tick % 60 + 's';
|
||||
var s = tick % 60 + 's'
|
||||
if (tick < 3600) {
|
||||
return parseInt(tick / 60) + 'm ' + s;
|
||||
return parseInt(tick / 60) + 'm ' + s
|
||||
}
|
||||
var m = parseInt((tick % 3600) / 60) + 'm ';
|
||||
var m = parseInt((tick % 3600) / 60) + 'm '
|
||||
if (tick < 86400) {
|
||||
return parseInt(tick / 3600) + 'h ' + m + (!tiny ? '' : s);
|
||||
return parseInt(tick / 3600) + 'h ' + m + (!tiny ? '' : s)
|
||||
}
|
||||
var h = parseInt((tick % 86400) / 3600) + 'h ';
|
||||
return parseInt(tick / 86400) + 'd ' + h + (!tiny ? '' : m + s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap memory.
|
||||
* @param {Float} mem
|
||||
* @returns {string}
|
||||
*/
|
||||
function _getMem(mem) {
|
||||
if (typeof mem == 'string') {
|
||||
return mem;
|
||||
}
|
||||
|
||||
if (mem < 1024) {
|
||||
return mem + 'B';
|
||||
}
|
||||
if (mem < 1048576) {
|
||||
return Math.round(mem / 1024) + 'K';
|
||||
}
|
||||
if (mem < 1073741824) {
|
||||
return Math.round(mem / 1048576) + 'M';
|
||||
}
|
||||
return Math.round(mem / 1073741824) + 'G';
|
||||
var h = parseInt((tick % 86400) / 3600) + 'h '
|
||||
return parseInt(tick / 86400) + 'd ' + h + (!tiny ? '' : m + s)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +1,39 @@
|
|||
// Inspired by the blessed-contrib, but more powerful and free.
|
||||
// (c) Tjatse
|
||||
|
||||
var blessed = require('blessed'),
|
||||
util = require('util'),
|
||||
re_stripANSI = /(?:(?:\u001b\[)|\u009b)(?:(?:[0-9]{1,3})?(?:(?:;[0-9]{0,3})*)?[A-M|f-m])|\u001b[A-M]/g;
|
||||
var blessed = require('blessed')
|
||||
var util = require('util')
|
||||
var re_stripANSI = /(?:(?:\u001b\[)|\u009b)(?:(?:[0-9]{1,3})?(?:(?:;[0-9]{0,3})*)?[A-M|f-m])|\u001b[A-M]/g
|
||||
|
||||
exports.Grid = Grid;
|
||||
exports.Table = Table;
|
||||
exports.Sparkline = Sparkline;
|
||||
exports.Log = Log;
|
||||
exports.Grid = Grid
|
||||
exports.Table = Table
|
||||
exports.Sparkline = Sparkline
|
||||
exports.Log = Log
|
||||
|
||||
/***************************
|
||||
Grid
|
||||
***************************/
|
||||
/**
|
||||
* Grid cells.
|
||||
* @param {Object} options
|
||||
* @returns {Grid}
|
||||
* @constructor
|
||||
*/
|
||||
function Grid(options){
|
||||
function Grid (options) {
|
||||
if (!(this instanceof Grid)) {
|
||||
return new Grid(options);
|
||||
return new Grid(options)
|
||||
}
|
||||
options = util._extend({
|
||||
margin: 2
|
||||
}, options || {});
|
||||
}, options || {})
|
||||
|
||||
this.grids = [];
|
||||
this.grids = []
|
||||
|
||||
for (var r = 0; r < options.rows; r++) {
|
||||
this.grids[r] = [];
|
||||
this.grids[r] = []
|
||||
for (var c = 0; c < options.cols; c++) {
|
||||
this.grids[r][c] = {};
|
||||
this.grids[r][c] = {}
|
||||
}
|
||||
}
|
||||
|
||||
this.options = options;
|
||||
this.options = options
|
||||
}
|
||||
/**
|
||||
* Get instance in the specific row and column.
|
||||
|
|
@ -44,99 +41,84 @@ function Grid(options){
|
|||
* @param {Number} col
|
||||
* @returns {*}
|
||||
*/
|
||||
Grid.prototype.get = function(row, col){
|
||||
return this.grids[row][col].instance;
|
||||
};
|
||||
Grid.prototype.get = function (row, col) {
|
||||
return this.grids[row][col].instance
|
||||
}
|
||||
/**
|
||||
* Set element in the cell.
|
||||
* @param ele
|
||||
*/
|
||||
Grid.prototype.set = function(ele){
|
||||
Grid.prototype.set = function (ele) {
|
||||
if (Array.isArray(ele)) {
|
||||
for (var i = 0; i < ele.length; i++) {
|
||||
this.set(ele[i]);
|
||||
this.set(ele[i])
|
||||
}
|
||||
return;
|
||||
return
|
||||
}
|
||||
this.grids[ele.row][ele.col] = util._extend({rowSpan: 1, colSpan: 1}, ele);
|
||||
};
|
||||
this.grids[ele.row][ele.col] = util._extend({rowSpan: 1, colSpan: 1}, ele)
|
||||
}
|
||||
/**
|
||||
* Draw grid.
|
||||
* @param {blessed.screen} screen
|
||||
*/
|
||||
Grid.prototype.draw = function(screen, rect){
|
||||
var margin = this.options.margin,
|
||||
rect = rect || {
|
||||
width : 100,
|
||||
height: 100,
|
||||
top : 0,
|
||||
left : 0
|
||||
},
|
||||
widths = this.options.widths || [],
|
||||
heights = this.options.heights || [],
|
||||
cols = this.options.cols,
|
||||
rows = this.options.rows;
|
||||
Grid.prototype.draw = function (screen, rect) {
|
||||
rect = rect || {
|
||||
width: 100,
|
||||
height: 100,
|
||||
top: 0,
|
||||
left: 0
|
||||
}
|
||||
var margin = this.options.margin
|
||||
var widths = this.options.widths || []
|
||||
var heights = this.options.heights || []
|
||||
var cols = this.options.cols
|
||||
var rows = this.options.rows
|
||||
|
||||
if (widths.length != cols) {
|
||||
var avg = (rect.width - margin) / cols;
|
||||
if (widths.length !== cols) {
|
||||
var avg = (rect.width - margin) / cols
|
||||
for (var c = 0; c < cols; c++) {
|
||||
widths.push(avg);
|
||||
widths.push(avg)
|
||||
}
|
||||
}
|
||||
if (heights.length != rows) {
|
||||
var avg = (rect.height - margin) / rows;
|
||||
if (heights.length !== rows) {
|
||||
var avg = (rect.height - margin) / rows // eslint-disable-line no-redeclare
|
||||
for (var r = 0; r < rows; r++) {
|
||||
heights.push(avg);
|
||||
heights.push(avg)
|
||||
}
|
||||
}
|
||||
|
||||
for (var r = 0; r < rows; r++) {
|
||||
for (var c = 0; c < cols; c++) {
|
||||
var ele = this.grids[r][c];
|
||||
for (var r = 0; r < rows; r++) { // eslint-disable-line no-redeclare
|
||||
for (var c = 0; c < cols; c++) { // eslint-disable-line no-redeclare
|
||||
var ele = this.grids[r][c]
|
||||
if (!ele.element) {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
|
||||
var factorWidth = (rect.width - margin) / 100,
|
||||
factorHeight = (rect.height - margin) / 100,
|
||||
width = widths.slice(c, c + ele.colSpan).reduce(function(x, y){
|
||||
return x + y
|
||||
}) * factorWidth,
|
||||
height = heights.slice(r, r + ele.rowSpan).reduce(function(x, y){
|
||||
return x + y
|
||||
}) * factorHeight,
|
||||
top = rect.top + margin / 2 + (r == 0 ? 0 : heights.slice(0, r).reduce(function(x, y){
|
||||
return x + y
|
||||
})) * factorHeight,
|
||||
left = rect.left + margin / 2 + (c == 0 ? 0 : widths.slice(0, c).reduce(function(x, y){
|
||||
return x + y
|
||||
})) * factorWidth;
|
||||
var factorWidth = (rect.width - margin) / 100
|
||||
var factorHeight = (rect.height - margin) / 100
|
||||
var width = widths.slice(c, c + ele.colSpan).reduce(_reduce) * factorWidth
|
||||
var height = heights.slice(r, r + ele.rowSpan).reduce(_reduce) * factorHeight
|
||||
var top = rect.top + margin / 2 + (r === 0 ? 0 : heights.slice(0, r).reduce(_reduce)) * factorHeight
|
||||
var left = rect.left + margin / 2 + (c === 0 ? 0 : widths.slice(0, c).reduce(_reduce)) * factorWidth
|
||||
|
||||
if (ele.element instanceof Grid) {
|
||||
ele.element.draw(screen, {
|
||||
width : width,
|
||||
width: width,
|
||||
height: height,
|
||||
top : top,
|
||||
left : left
|
||||
});
|
||||
top: top,
|
||||
left: left
|
||||
})
|
||||
} else {
|
||||
screen.append(ele.instance = ele.element(util._extend(ele.options || {}, {
|
||||
top : top + '%',
|
||||
left : left + '%',
|
||||
width : width + '%',
|
||||
top: top + '%',
|
||||
left: left + '%',
|
||||
width: width + '%',
|
||||
height: height + '%'
|
||||
})));
|
||||
})))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
/***************************
|
||||
End Of Grid
|
||||
***************************/
|
||||
|
||||
/***************************
|
||||
Table
|
||||
***************************/
|
||||
}
|
||||
|
||||
/**
|
||||
* Table list.
|
||||
|
|
@ -144,168 +126,156 @@ Grid.prototype.draw = function(screen, rect){
|
|||
* @returns {Table}
|
||||
* @constructor
|
||||
*/
|
||||
function Table(options){
|
||||
function Table (options) {
|
||||
if (!(this instanceof Table)) {
|
||||
return new Table(options);
|
||||
return new Table(options)
|
||||
}
|
||||
|
||||
this.options = options || {};
|
||||
this.options.tags = true;
|
||||
this.options = options || {}
|
||||
this.options.tags = true
|
||||
|
||||
blessed.Box.call(this, this.options);
|
||||
blessed.Box.call(this, this.options)
|
||||
|
||||
this.rows = blessed.list(util._extend(this.options.rows || {}, {
|
||||
height : 0,
|
||||
top : 1,
|
||||
width : 0,
|
||||
left : 0,
|
||||
height: 0,
|
||||
top: 1,
|
||||
width: 0,
|
||||
left: 0,
|
||||
selectedFg: '#fcfbac',
|
||||
selectedBg: '#398cc6',
|
||||
fg : "#333",
|
||||
keys : true
|
||||
}));
|
||||
this.append(this.rows);
|
||||
fg: '#333',
|
||||
keys: true
|
||||
}))
|
||||
this.append(this.rows)
|
||||
}
|
||||
|
||||
util.inherits(Table, blessed.Box);
|
||||
util.inherits(Table, blessed.Box)
|
||||
|
||||
/**
|
||||
* Inherits from blessed.Box
|
||||
*/
|
||||
Table.prototype.render = function(){
|
||||
this.rows.focus();
|
||||
this.rows.width = this.width - 2;
|
||||
this.rows.height = this.height - 4;
|
||||
blessed.Box.prototype.render.call(this, this.options);
|
||||
};
|
||||
Table.prototype.render = function () {
|
||||
this.rows.focus()
|
||||
this.rows.width = this.width - 2
|
||||
this.rows.height = this.height - 4
|
||||
blessed.Box.prototype.render.call(this, this.options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind data to Table.
|
||||
* @param {Object} data
|
||||
*/
|
||||
Table.prototype.setData = function(data){
|
||||
var widths = this.options.widths, def = true;
|
||||
Table.prototype.setData = function (data) {
|
||||
var widths = this.options.widths
|
||||
var def = true
|
||||
if (!widths) {
|
||||
widths = 24;
|
||||
def = false;
|
||||
widths = 24
|
||||
def = false
|
||||
}
|
||||
var dataToString = function(d){
|
||||
return d.map(function(s, i){
|
||||
s = s.toString();
|
||||
var s1 = s.replace(re_stripANSI, '');
|
||||
var size = !def ? widths : widths[i],
|
||||
len = size - s1.length;
|
||||
var dataToString = function (d) {
|
||||
return d.map(function (s, i) {
|
||||
s = s.toString()
|
||||
var s1 = s.replace(re_stripANSI, '')
|
||||
var size = !def ? widths : widths[i]
|
||||
var len = size - s1.length
|
||||
|
||||
if (len < 0) {
|
||||
s = s1.substr(0, size - 1) + '...';
|
||||
s = s1.substr(0, size - 1) + '...'
|
||||
} else {
|
||||
s += Array(len).join(' ');
|
||||
s += Array(len).join(' ')
|
||||
}
|
||||
return s;
|
||||
}).join('');
|
||||
};
|
||||
return s
|
||||
}).join('')
|
||||
}
|
||||
|
||||
var rows = [];
|
||||
var rows = []
|
||||
|
||||
data.rows.forEach(function(d){
|
||||
data.rows.forEach(function (d) {
|
||||
rows.push(dataToString(d))
|
||||
});
|
||||
this.setContent('{bold}' + dataToString(data.headers) + '{/bold}');
|
||||
})
|
||||
this.setContent('{bold}' + dataToString(data.headers) + '{/bold}')
|
||||
this.rows.setItems(rows)
|
||||
};
|
||||
/***************************
|
||||
End Of TABLE
|
||||
***************************/
|
||||
}
|
||||
|
||||
/***************************
|
||||
Sparkline
|
||||
***************************/
|
||||
/**
|
||||
* Sparkline.
|
||||
* @param {Object} options
|
||||
* @returns {Sparkline}
|
||||
* @constructor
|
||||
*/
|
||||
function Sparkline(options){
|
||||
function Sparkline (options) {
|
||||
if (!(this instanceof Sparkline)) {
|
||||
return new Sparkline(options);
|
||||
return new Sparkline(options)
|
||||
}
|
||||
|
||||
this.options = util._extend({
|
||||
chars : ['▂', '▃', '▄', '▅', '▆', '▇', '█'],
|
||||
tags : true,
|
||||
chars: ['▂', '▃', '▄', '▅', '▆', '▇', '█'],
|
||||
tags: true,
|
||||
padding: {
|
||||
left: 1,
|
||||
top : 1
|
||||
top: 1
|
||||
}
|
||||
}, options || {});
|
||||
blessed.Box.call(this, this.options);
|
||||
}, options || {})
|
||||
blessed.Box.call(this, this.options)
|
||||
}
|
||||
util.inherits(Sparkline, blessed.Box);
|
||||
util.inherits(Sparkline, blessed.Box)
|
||||
|
||||
/**
|
||||
* Set data.
|
||||
* @param {Array} data
|
||||
*/
|
||||
Sparkline.prototype.setData = function(data, min, max){
|
||||
var chars = this.options.chars,
|
||||
max = typeof max == 'undefined' ? Math.max.apply(null, data) : max,
|
||||
min = typeof min == 'undefined' ? Math.min.apply(null, data) : min,
|
||||
dis = max - min,
|
||||
len = chars.length - 1;
|
||||
Sparkline.prototype.setData = function (data, min, max) {
|
||||
var chars = this.options.chars
|
||||
max = typeof max === 'undefined' ? Math.max.apply(null, data) : max
|
||||
min = typeof min === 'undefined' ? Math.min.apply(null, data) : min
|
||||
var dis = max - min
|
||||
var len = chars.length - 1
|
||||
|
||||
if (dis == 0) {
|
||||
dis = 1;
|
||||
if (dis === 0) {
|
||||
dis = 1
|
||||
}
|
||||
|
||||
var content = data.map(function(n){
|
||||
var index = Math.round((n - min) / dis * len);
|
||||
return chars[index];
|
||||
}).join('');
|
||||
this.setContent(content);
|
||||
};
|
||||
var content = data.map(function (n) {
|
||||
var index = Math.round((n - min) / dis * len)
|
||||
return chars[index]
|
||||
}).join('')
|
||||
this.setContent(content)
|
||||
}
|
||||
|
||||
/***************************
|
||||
END OF Sparkline
|
||||
***************************/
|
||||
|
||||
/***************************
|
||||
Log
|
||||
***************************/
|
||||
/**
|
||||
* Log.
|
||||
* @param {Object} options
|
||||
* @returns {Log}
|
||||
* @constructor
|
||||
*/
|
||||
function Log(options){
|
||||
function Log (options) {
|
||||
if (!(this instanceof Log)) {
|
||||
return new Log(options);
|
||||
return new Log(options)
|
||||
}
|
||||
|
||||
this.options = options || {};
|
||||
this.options = options || {}
|
||||
|
||||
blessed.ScrollableBox.call(this, this.options);
|
||||
blessed.ScrollableBox.call(this, this.options)
|
||||
|
||||
this.logs = [];
|
||||
this.logs = []
|
||||
}
|
||||
util.inherits(Log, blessed.ScrollableBox);
|
||||
util.inherits(Log, blessed.ScrollableBox)
|
||||
|
||||
/**
|
||||
* Log logs.
|
||||
* @param {String} str
|
||||
*/
|
||||
Log.prototype.log = function(str, size){
|
||||
size = size || this.height;
|
||||
this.logs.push(str);
|
||||
var len = this.logs.length - size;
|
||||
Log.prototype.log = function (str, size) {
|
||||
size = size || this.height
|
||||
this.logs.push(str)
|
||||
var len = this.logs.length - size
|
||||
if (len > 0) {
|
||||
this.logs.splice(0, len)
|
||||
}
|
||||
this.setContent(this.logs.join('\n'));
|
||||
this.setScrollPerc(100);
|
||||
this.setContent(this.logs.join('\n'))
|
||||
this.setScrollPerc(100)
|
||||
}
|
||||
|
||||
/***************************
|
||||
END OF Log
|
||||
***************************/
|
||||
function _reduce (x, y) {
|
||||
return x + y
|
||||
}
|
||||
|
|
|
|||
267
lib/daemon.js
267
lib/daemon.js
|
|
@ -1,158 +1,159 @@
|
|||
var chalk = require('chalk'),
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
async = require('async'),
|
||||
cp = require('child_process'),
|
||||
fork = cp.fork,
|
||||
spawn = cp.spawn,
|
||||
Monitor = require('./monitor'),
|
||||
Log = require('./util/log');
|
||||
var chalk = require('chalk')
|
||||
var path = require('path')
|
||||
var fs = require('fs')
|
||||
var async = require('async')
|
||||
var cp = require('child_process')
|
||||
var fork = cp.fork
|
||||
var spawn = cp.spawn
|
||||
var Monitor = require('./monitor')
|
||||
var Log = require('./util/log')
|
||||
|
||||
var processDirname = path.resolve(__dirname, '../'),
|
||||
confFile = './pm2-gui.ini',
|
||||
cmd = 'start';
|
||||
var processDirname = path.resolve(__dirname, '../')
|
||||
var confFile = './pm2-gui.ini'
|
||||
var cmd = 'start'
|
||||
|
||||
if (process.argv.length > 2) {
|
||||
cmd = process.argv[2];
|
||||
cmd = process.argv[2]
|
||||
}
|
||||
|
||||
if (process.argv.length > 3) {
|
||||
confFile = process.argv[3];
|
||||
confFile = process.argv[3]
|
||||
}
|
||||
|
||||
confFile = path.resolve(processDirname, confFile);
|
||||
confFile = path.resolve(processDirname, confFile)
|
||||
|
||||
if (!fs.existsSync(confFile)) {
|
||||
console.error(chalk.bold(confFile), chalk.red('does not exist!'));
|
||||
return process.exit(0);
|
||||
}
|
||||
|
||||
var monitor = Monitor({
|
||||
console.error(chalk.bold(confFile), chalk.red('does not exist!'))
|
||||
process.exit(0)
|
||||
} else {
|
||||
var monitor = Monitor({
|
||||
confFile: confFile
|
||||
}),
|
||||
daemonize = monitor.options.daemonize && cmd != 'mon';
|
||||
})
|
||||
var daemonize = monitor.options.daemonize && cmd !== 'mon'
|
||||
|
||||
Log(monitor.options.log);
|
||||
Log(monitor.options.log)
|
||||
|
||||
var pidfile = path.resolve(processDirname, './pm2-gui.pid');
|
||||
var pidfile = path.resolve(processDirname, './pm2-gui.pid')
|
||||
|
||||
var Daemon = {
|
||||
restarts: 0,
|
||||
init: function(next) {
|
||||
process.on('SIGTERM', Daemon.stop);
|
||||
process.on('SIGINT', Daemon.stop);
|
||||
process.on('SIGHUP', Daemon.restart);
|
||||
next && next();
|
||||
},
|
||||
start: function(next) {
|
||||
Daemon.worker = Daemon.fork();
|
||||
next && next();
|
||||
},
|
||||
restart: function() {
|
||||
console.info('Restarting...');
|
||||
Daemon.kill();
|
||||
Daemon.start();
|
||||
},
|
||||
stop: function() {
|
||||
console.info('Stopping...');
|
||||
Daemon.kill();
|
||||
daemonize && fs.existsSync(pidfile) && fs.unlinkSync(pidfile);
|
||||
process.exit(0);
|
||||
},
|
||||
kill: function() {
|
||||
if (Daemon.timer) {
|
||||
clearTimeout(Daemon.timer);
|
||||
Daemon.timer = null;
|
||||
}
|
||||
if (Daemon.worker) {
|
||||
Daemon.worker.suicide = true;
|
||||
Daemon.worker.kill();
|
||||
}
|
||||
},
|
||||
fork: function() {
|
||||
console.info('Forking slave...');
|
||||
Daemon.timer = null;
|
||||
var worker = fork(path.resolve(processDirname, 'pm2-gui.js'), [cmd, confFile, '--color'], {
|
||||
silent: daemonize,
|
||||
env: process.env
|
||||
});
|
||||
worker.on('exit', function(code, signal) {
|
||||
if (code != 0) {
|
||||
if (Daemon.restarts < 10) {
|
||||
Daemon.restarts++;
|
||||
setTimeout(function() {
|
||||
Daemon.restarts--;
|
||||
}, 20000);
|
||||
} else {
|
||||
console.error(Daemon.restarts + ' restarts in 20 seconds, view the logs to investigate the crash problem.');
|
||||
return process.exit(0);
|
||||
}
|
||||
var Daemon = {
|
||||
restarts: 0,
|
||||
init: function (next) {
|
||||
process.on('SIGTERM', Daemon.stop)
|
||||
process.on('SIGINT', Daemon.stop)
|
||||
process.on('SIGHUP', Daemon.restart)
|
||||
next && next()
|
||||
},
|
||||
start: function (next) {
|
||||
Daemon.worker = Daemon.fork()
|
||||
next && next()
|
||||
},
|
||||
restart: function () {
|
||||
console.info('Restarting...')
|
||||
Daemon.kill()
|
||||
Daemon.start()
|
||||
},
|
||||
stop: function () {
|
||||
console.info('Stopping...')
|
||||
Daemon.kill()
|
||||
daemonize && fs.existsSync(pidfile) && fs.unlinkSync(pidfile)
|
||||
process.exit(0)
|
||||
},
|
||||
kill: function () {
|
||||
if (Daemon.timer) {
|
||||
clearTimeout(Daemon.timer)
|
||||
Daemon.timer = null
|
||||
}
|
||||
if (!worker.suicide && code !== 0) {
|
||||
Daemon.timer = setTimeout(Daemon.fork, 3000);
|
||||
if (Daemon.worker) {
|
||||
Daemon.worker.suicide = true
|
||||
Daemon.worker.kill()
|
||||
}
|
||||
});
|
||||
|
||||
worker.on('message', function(message) {
|
||||
if (typeof message == 'object' && message.action)
|
||||
if (message.action == 'restart') {
|
||||
Daemon.restart();
|
||||
},
|
||||
fork: function () {
|
||||
console.info('Forking slave...')
|
||||
Daemon.timer = null
|
||||
var worker = fork(path.resolve(processDirname, 'pm2-gui.js'), [cmd, confFile, '--color'], {
|
||||
silent: daemonize,
|
||||
env: process.env
|
||||
})
|
||||
worker.on('exit', function (code, signal) {
|
||||
if (code !== 0) {
|
||||
if (Daemon.restarts < 10) {
|
||||
Daemon.restarts++
|
||||
setTimeout(function () {
|
||||
Daemon.restarts--
|
||||
}, 20000)
|
||||
} else {
|
||||
console.error(Daemon.restarts + ' restarts in 20 seconds, view the logs to investigate the crash problem.')
|
||||
return process.exit(0)
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!worker.suicide && code !== 0) {
|
||||
Daemon.timer = setTimeout(Daemon.fork, 3000)
|
||||
}
|
||||
})
|
||||
|
||||
var logDir = monitor.options.log.dir,
|
||||
stdout = 'pm2-gui.out',
|
||||
stderr = 'pm2-gui.err';
|
||||
worker.on('message', function (message) {
|
||||
if (typeof message === 'object' && message.action) {
|
||||
if (message.action === 'restart') {
|
||||
Daemon.restart()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (!logDir) {
|
||||
logDir = './logs';
|
||||
}
|
||||
logDir = path.resolve(processDirname, logDir);
|
||||
if (!fs.existsSync(logDir)) {
|
||||
fs.mkdirSync(logDir);
|
||||
}
|
||||
var logDir = monitor.options.log.dir
|
||||
var stdout = 'pm2-gui.out'
|
||||
var stderr = 'pm2-gui.err'
|
||||
|
||||
if (daemonize) {
|
||||
stdout = fs.createWriteStream(path.join(logDir, stdout));
|
||||
stderr = fs.createWriteStream(path.join(logDir, stderr));
|
||||
worker.stdout.pipe(stdout);
|
||||
worker.stderr.pipe(stderr);
|
||||
if (!logDir) {
|
||||
logDir = './logs'
|
||||
}
|
||||
logDir = path.resolve(processDirname, logDir)
|
||||
if (!fs.existsSync(logDir)) {
|
||||
fs.mkdirSync(logDir)
|
||||
}
|
||||
|
||||
fs.writeFile(pidfile, worker.pid);
|
||||
if (daemonize) {
|
||||
stdout = fs.createWriteStream(path.join(logDir, stdout))
|
||||
stderr = fs.createWriteStream(path.join(logDir, stderr))
|
||||
worker.stdout.pipe(stdout)
|
||||
worker.stderr.pipe(stderr)
|
||||
|
||||
fs.writeFile(pidfile, worker.pid)
|
||||
}
|
||||
return worker
|
||||
},
|
||||
daemonize: function () {
|
||||
if (process.env.daemonized) {
|
||||
console.info('Daemonized with pid [' + process.pid + '].')
|
||||
return
|
||||
}
|
||||
console.info('Spawning daemon...')
|
||||
var args = [].concat(process.argv)
|
||||
args.shift()
|
||||
var env = process.env
|
||||
env.daemonized = true
|
||||
var child = spawn(process.execPath, args, {
|
||||
env: env,
|
||||
detached: false,
|
||||
cwd: processDirname,
|
||||
stdio: ['ignore', process.stdout, process.stderr]
|
||||
})
|
||||
child.unref()
|
||||
process.exit()
|
||||
}
|
||||
return worker;
|
||||
},
|
||||
daemonize: function() {
|
||||
if (process.env.daemonized) {
|
||||
console.info('Daemonized with pid [' + process.pid + '].');
|
||||
return;
|
||||
}
|
||||
console.info('Spawning daemon...');
|
||||
var args = [].concat(process.argv);
|
||||
args.shift();
|
||||
var env = process.env;
|
||||
env.daemonized = true;
|
||||
var child = spawn(process.execPath, args, {
|
||||
env: env,
|
||||
detached: false,
|
||||
cwd: processDirname,
|
||||
stdio: ['ignore', process.stdout, process.stderr]
|
||||
});
|
||||
child.unref();
|
||||
process.exit();
|
||||
}
|
||||
};
|
||||
|
||||
if (daemonize) {
|
||||
Daemon.daemonize();
|
||||
if (daemonize) {
|
||||
Daemon.daemonize()
|
||||
}
|
||||
|
||||
process.title = 'pm2-gui daemon ' + confFile
|
||||
async.series([
|
||||
Daemon.init,
|
||||
Daemon.start
|
||||
], function (err) {
|
||||
if (err) {
|
||||
console.error(err.stack)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
process.title = 'pm2-gui daemon';
|
||||
async.series([
|
||||
Daemon.init,
|
||||
Daemon.start
|
||||
], function(err) {
|
||||
if (err) {
|
||||
console.error(err.stack);
|
||||
}
|
||||
});
|
||||
559
lib/monitor.js
559
lib/monitor.js
|
|
@ -1,19 +1,19 @@
|
|||
var fs = require('fs'),
|
||||
path = require('path'),
|
||||
_ = require('lodash'),
|
||||
chalk = require('chalk'),
|
||||
ansiHTML = require('ansi-html'),
|
||||
totalmem = require('os').totalmem(),
|
||||
pidusage = require('pidusage'),
|
||||
url = require('url'),
|
||||
socketIOClient = require('socket.io-client'),
|
||||
pm = require('./pm'),
|
||||
stat = require('./stat'),
|
||||
conf = require('./util/conf'),
|
||||
Log = require('./util/log'),
|
||||
defConf;
|
||||
var fs = require('fs')
|
||||
var path = require('path')
|
||||
var _ = require('lodash')
|
||||
var chalk = require('chalk')
|
||||
var ansiHTML = require('ansi-html')
|
||||
var totalmem = require('os').totalmem()
|
||||
var pidusage = require('pidusage')
|
||||
var url = require('url')
|
||||
var socketIOClient = require('socket.io-client')
|
||||
var pm = require('./pm')
|
||||
var stat = require('./stat')
|
||||
var conf = require('./util/conf')
|
||||
var Log = require('./util/log')
|
||||
var defConf
|
||||
|
||||
module.exports = Monitor;
|
||||
module.exports = Monitor
|
||||
|
||||
/**
|
||||
* Monitor of project monitor web.
|
||||
|
|
@ -21,33 +21,33 @@ module.exports = Monitor;
|
|||
* @returns {Monitor}
|
||||
* @constructor
|
||||
*/
|
||||
function Monitor(options) {
|
||||
function Monitor (options) {
|
||||
if (!(this instanceof Monitor)) {
|
||||
return new Monitor(options);
|
||||
return new Monitor(options)
|
||||
}
|
||||
|
||||
// Initialize...
|
||||
this._init(options);
|
||||
};
|
||||
this._init(options)
|
||||
}
|
||||
|
||||
Monitor.ACCEPT_KEYS = ['pm2', 'refresh', 'daemonize', 'max_restarts', 'port', 'log', 'agent', 'remotes', 'origins'];
|
||||
Monitor.DEF_CONF_FILE = 'pm2-gui.ini';
|
||||
Monitor.PM2_DAEMON_PROPS = ['DAEMON_RPC_PORT', 'DAEMON_PUB_PORT'];
|
||||
Monitor.ACCEPT_KEYS = ['pm2', 'refresh', 'daemonize', 'max_restarts', 'port', 'log', 'agent', 'remotes', 'origins']
|
||||
Monitor.DEF_CONF_FILE = 'pm2-gui.ini'
|
||||
Monitor.PM2_DAEMON_PROPS = ['DAEMON_RPC_PORT', 'DAEMON_PUB_PORT']
|
||||
|
||||
/**
|
||||
* Run socket.io server.
|
||||
*/
|
||||
Monitor.prototype.run = function () {
|
||||
this._noClient = true;
|
||||
this._noClient = true
|
||||
|
||||
this._tails = {};
|
||||
this._usages = {};
|
||||
this._tails = {}
|
||||
this._usages = {}
|
||||
|
||||
// Observe PM2
|
||||
this._observePM2();
|
||||
this._observePM2()
|
||||
|
||||
this._listeningSocketIO();
|
||||
};
|
||||
this._listeningSocketIO()
|
||||
}
|
||||
|
||||
/**
|
||||
* Quit monitor.
|
||||
|
|
@ -55,48 +55,47 @@ Monitor.prototype.run = function () {
|
|||
*/
|
||||
Monitor.prototype.quit = function () {
|
||||
if (this.pm2Sock) {
|
||||
console.debug('Closing pm2 pub emitter socket.');
|
||||
this.pm2Sock.close();
|
||||
console.debug('Closing pm2 pub emitter socket.')
|
||||
this.pm2Sock.close()
|
||||
}
|
||||
if (this._sockio) {
|
||||
console.debug('Closing socket.io server.');
|
||||
this._sockio.close();
|
||||
console.debug('Closing socket.io server.')
|
||||
this._sockio.close()
|
||||
|
||||
console.debug('Destroying tails.');
|
||||
this._killTailProcess();
|
||||
console.debug('Destroying tails.')
|
||||
this._killTailProcess()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to socket.io server.
|
||||
* @param {String} ns the namespace.
|
||||
* @param {Function} success
|
||||
* @param {Function} failure
|
||||
* @param {Function} success
|
||||
* @param {Function} failure
|
||||
*/
|
||||
Monitor.prototype.connect = function (options, success, failure) {
|
||||
if (!options.port) {
|
||||
throw new Error('Port is required!');
|
||||
throw new Error('Port is required!')
|
||||
}
|
||||
var auth,
|
||||
serverUri = Monitor.toConnectionString(options);
|
||||
var serverUri = Monitor.toConnectionString(options)
|
||||
|
||||
console.info('Connecting to', serverUri);
|
||||
var socket = socketIOClient(serverUri);
|
||||
success = _.once(success)
|
||||
failure = _.once(failure)
|
||||
|
||||
console.info('Connecting to', serverUri)
|
||||
var socket = socketIOClient(serverUri)
|
||||
socket.on('connect', function () {
|
||||
!success._called && success(socket);
|
||||
success._called = true;
|
||||
});
|
||||
success(socket)
|
||||
})
|
||||
|
||||
socket.on('error', function (err) {
|
||||
!failure._called && failure(err, socket);
|
||||
failure._called = true;
|
||||
});
|
||||
!failure(err, socket)
|
||||
})
|
||||
|
||||
socket.on('connect_error', function (err) {
|
||||
!failure._called && failure(err, socket);
|
||||
failure._called = true;
|
||||
});
|
||||
};
|
||||
!failure(err, socket)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve home path.
|
||||
|
|
@ -105,69 +104,69 @@ Monitor.prototype.connect = function (options, success, failure) {
|
|||
* @private
|
||||
*/
|
||||
Monitor.prototype._resolveHome = function (pm2Home) {
|
||||
if (pm2Home && pm2Home.indexOf('~/') == 0) {
|
||||
if (pm2Home && pm2Home.indexOf('~/') === 0) {
|
||||
// Get root directory of PM2.
|
||||
pm2Home = process.env.PM2_HOME || path.resolve(process.env.HOME || process.env.HOMEPATH, pm2Home.substr(2));
|
||||
pm2Home = process.env.PM2_HOME || path.resolve(process.env.HOME || process.env.HOMEPATH, pm2Home.substr(2))
|
||||
|
||||
// Make sure exist.
|
||||
if (!pm2Home || !fs.existsSync(pm2Home)) {
|
||||
throw new Error('PM2 root can not be located, try to initialize PM2 by executing `pm2 ls` or set environment variable vi `export PM2_HOME=[ROOT]`.');
|
||||
throw new Error('PM2 root can not be located, try to initialize PM2 by executing `pm2 ls` or set environment variable vi `export PM2_HOME=[ROOT]`.')
|
||||
}
|
||||
}
|
||||
return pm2Home;
|
||||
};
|
||||
return pm2Home
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize options and configurations.
|
||||
* @private
|
||||
*/
|
||||
Monitor.prototype._init = function (options) {
|
||||
options = options || {};
|
||||
options = options || {}
|
||||
|
||||
defConf = conf.File(options.confFile || path.resolve(__dirname, '..', Monitor.DEF_CONF_FILE)).loadSync().valueOf();
|
||||
defConf = _.pick.call(null, defConf, Monitor.ACCEPT_KEYS);
|
||||
defConf = conf.File(options.confFile || path.resolve(__dirname, '..', Monitor.DEF_CONF_FILE)).loadSync().valueOf()
|
||||
defConf = _.pick.call(null, defConf, Monitor.ACCEPT_KEYS)
|
||||
|
||||
options = _.pick.apply(options, Monitor.ACCEPT_KEYS).valueOf();
|
||||
options = _.defaults(options, defConf);
|
||||
options = _.pick.apply(options, Monitor.ACCEPT_KEYS).valueOf()
|
||||
options = _.defaults(options, defConf)
|
||||
|
||||
options.pm2 = this._resolveHome(options.pm2);
|
||||
Log(options.log);
|
||||
options.pm2 = this._resolveHome(options.pm2)
|
||||
Log(options.log)
|
||||
|
||||
// Load PM2 config.
|
||||
var pm2ConfPath = path.join(options.pm2, 'conf.js'),
|
||||
fbMsg = '';
|
||||
var pm2ConfPath = path.join(options.pm2, 'conf.js')
|
||||
var fbMsg = ''
|
||||
try {
|
||||
options.pm2Conf = require(pm2ConfPath)(options.pm2);
|
||||
options.pm2Conf = require(pm2ConfPath)(options.pm2)
|
||||
if (!options.pm2Conf) {
|
||||
throw new Error(404);
|
||||
throw new Error(404)
|
||||
}
|
||||
} catch (err) {
|
||||
fbMsg = 'Can not load PM2 config, the file "' + pm2ConfPath + '" does not exist or empty, fallback to auto-load by pm2 home. ';
|
||||
console.warn(fbMsg);
|
||||
fbMsg = 'Can not load PM2 config, the file "' + pm2ConfPath + '" does not exist or empty, fallback to auto-load by pm2 home. '
|
||||
console.warn(fbMsg)
|
||||
options.pm2Conf = {
|
||||
DAEMON_RPC_PORT: path.resolve(options.pm2, 'rpc.sock'),
|
||||
DAEMON_PUB_PORT: path.resolve(options.pm2, 'pub.sock'),
|
||||
PM2_LOG_FILE_PATH: path.resolve(options.pm2, 'pm2.log')
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Monitor.PM2_DAEMON_PROPS.forEach(function (prop) {
|
||||
var val = options.pm2Conf[prop];
|
||||
var val = options.pm2Conf[prop]
|
||||
if (!val || !fs.existsSync(val)) {
|
||||
throw new Error(fbMsg + 'Unfortunately ' + (val || prop) + ' can not found, please makesure that your pm2 is running and the home path is correct.');
|
||||
throw new Error(fbMsg + 'Unfortunately ' + (val || prop) + ' can not found, please makesure that your pm2 is running and the home path is correct.')
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
// Bind socket.io server to context.
|
||||
if (options.sockio) {
|
||||
this.sockio = options.sockio;
|
||||
delete options.sockio;
|
||||
this.sockio = options.sockio
|
||||
delete options.sockio
|
||||
}
|
||||
|
||||
// Bind to context.
|
||||
this.options = options;
|
||||
Object.freeze(this.options);
|
||||
};
|
||||
this.options = options
|
||||
Object.freeze(this.options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Connection event of `sys` namespace.
|
||||
|
|
@ -175,39 +174,41 @@ Monitor.prototype._init = function (options) {
|
|||
* @private
|
||||
*/
|
||||
Monitor.prototype._connectSysSock = function (socket) {
|
||||
var self = this;
|
||||
var self = this
|
||||
// Still has one client connects to server at least.
|
||||
self._noClient = false;
|
||||
self._noClient = false
|
||||
|
||||
socket.on('disconnect', function () {
|
||||
// Check connecting client.
|
||||
self._noClient = self._sockio.of(conf.NSP.SYS).sockets.length == 0;
|
||||
});
|
||||
self._noClient = self._sockio.of(conf.NSP.SYS).sockets.length === 0
|
||||
})
|
||||
|
||||
// Trigger actions of process.
|
||||
socket.on('action', function (action, id) {
|
||||
console.debug('[pm2:' + id + ']', action, 'sending to pm2 daemon...');
|
||||
console.debug('[pm2:' + id + ']', action, 'sending to pm2 daemon...')
|
||||
pm.action(self.options.pm2Conf.DAEMON_RPC_PORT, action, id, function (err, forceRefresh) {
|
||||
if (err) {
|
||||
console.error(action, err.message);
|
||||
return socket.emit('action', id, err.message);
|
||||
console.error(action, err.message)
|
||||
return socket.emit('action', id, err.message)
|
||||
}
|
||||
console.debug('[pm2:' + id + ']', action, 'completed!');
|
||||
forceRefresh && self._throttleRefresh();
|
||||
});
|
||||
});
|
||||
sendProcs();
|
||||
socket.on('procs', sendProcs);
|
||||
self._pm2Ver(socket);
|
||||
this._sysStat && this._broadcast('system_stat', this._sysStat);
|
||||
console.debug('[pm2:' + id + ']', action, 'completed!')
|
||||
forceRefresh && self._throttleRefresh()
|
||||
})
|
||||
})
|
||||
sendProcs()
|
||||
socket.on('procs', sendProcs)
|
||||
self._pm2Ver(socket)
|
||||
this._sysStat && this._broadcast('system_stat', this._sysStat)
|
||||
|
||||
// Grep system states once and again.
|
||||
(this._status != 'R') && this._nextTick(this.options.refresh || 5000);
|
||||
|
||||
function sendProcs() {
|
||||
self._procs && socket.emit(typeof self._procs == 'string' ? 'info' : 'procs', self._procs);
|
||||
if (this._status !== 'R') {
|
||||
this._nextTick(this.options.refresh || 5000)
|
||||
}
|
||||
};
|
||||
|
||||
function sendProcs () {
|
||||
self._procs && socket.emit(typeof self._procs === 'string' ? 'info' : 'procs', self._procs)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connection event of `log` namespace.
|
||||
|
|
@ -215,22 +216,22 @@ Monitor.prototype._connectSysSock = function (socket) {
|
|||
* @private
|
||||
*/
|
||||
Monitor.prototype._connectLogSock = function (socket) {
|
||||
var self = this;
|
||||
var self = this
|
||||
|
||||
// Emit error.
|
||||
function emitError(err, pm_id, keepANSI) {
|
||||
function emitError (err, pm_id, keepANSI) {
|
||||
var data = {
|
||||
pm_id: pm_id,
|
||||
msg: keepANSI ? chalk.red(err.message) : '<span style="color: #ff0000">Error: ' + err.message + '</span>'
|
||||
};
|
||||
self._broadcast.call(self, 'log', data, conf.NSP.LOG);
|
||||
}
|
||||
self._broadcast.call(self, 'log', data, conf.NSP.LOG) // eslint-disable-line no-useless-call
|
||||
}
|
||||
|
||||
function startTailProcess(pm_id, keepANSI) {
|
||||
socket._pm_id = pm_id;
|
||||
function startTailProcess (pm_id, keepANSI) {
|
||||
socket._pm_id = pm_id
|
||||
|
||||
if (self._tails[pm_id]) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
// Tail logs.
|
||||
|
|
@ -240,39 +241,39 @@ Monitor.prototype._connectLogSock = function (socket) {
|
|||
pm_id: pm_id
|
||||
}, function (err, lines) {
|
||||
if (err) {
|
||||
return emitError(err, pm_id, keepANSI);
|
||||
return emitError(err, pm_id, keepANSI)
|
||||
}
|
||||
// Emit logs to clients.
|
||||
var data = {
|
||||
pm_id: pm_id,
|
||||
msg: lines.map(function (line) {
|
||||
if (!keepANSI) {
|
||||
line = line.replace(/\s/, ' ');
|
||||
return '<span>' + ansiHTML(line) + '</span>';
|
||||
line = line.replace(/\s/, ' ')
|
||||
return '<span>' + ansiHTML(line) + '</span>'
|
||||
} else {
|
||||
return line;
|
||||
return line
|
||||
}
|
||||
}).join(keepANSI ? '\n' : '')
|
||||
};
|
||||
self._broadcast.call(self, 'log', data, conf.NSP.LOG);
|
||||
}
|
||||
self._broadcast.call(self, 'log', data, conf.NSP.LOG) // eslint-disable-line no-useless-call
|
||||
}, function (err, tail) {
|
||||
if (err) {
|
||||
return emitError(err, pm_id, keepANSI);
|
||||
return emitError(err, pm_id, keepANSI)
|
||||
}
|
||||
if (!tail) {
|
||||
return emitError(new Error('No log can be found.'), pm_id, keepANSI);
|
||||
return emitError(new Error('No log can be found.'), pm_id, keepANSI)
|
||||
}
|
||||
|
||||
console.info('[pm2:' + pm_id + ']', 'tail starting...');
|
||||
self._tails[pm_id] = tail;
|
||||
});
|
||||
console.info('[pm2:' + pm_id + ']', 'tail starting...')
|
||||
self._tails[pm_id] = tail
|
||||
})
|
||||
}
|
||||
|
||||
socket.on('disconnect', self._killTailProcess.bind(self));
|
||||
socket.on('tail_kill', self._killTailProcess.bind(self));
|
||||
socket.on('tail', startTailProcess);
|
||||
console.info('Connected to ' + socket.nsp.name + '!');
|
||||
};
|
||||
socket.on('disconnect', self._killTailProcess.bind(self))
|
||||
socket.on('tail_kill', self._killTailProcess.bind(self))
|
||||
socket.on('tail', startTailProcess)
|
||||
console.info('Connected to ' + socket.nsp.name + '!')
|
||||
}
|
||||
|
||||
/**
|
||||
* Connection event of `proc` namespace.
|
||||
|
|
@ -280,74 +281,74 @@ Monitor.prototype._connectLogSock = function (socket) {
|
|||
* @private
|
||||
*/
|
||||
Monitor.prototype._connectProcSock = function (socket) {
|
||||
var self = this;
|
||||
var self = this
|
||||
// Emit error.
|
||||
function emitError(err, pid) {
|
||||
function emitError (err, pid) {
|
||||
var data = {
|
||||
pid: pid,
|
||||
msg: '<span style="color: #ff0000">Error: ' + err.message + '</span>'
|
||||
};
|
||||
self._broadcast.call(self, 'proc', data, conf.NSP.PROC);
|
||||
}
|
||||
self._broadcast.call(self, 'proc', data, conf.NSP.PROC) // eslint-disable-line no-useless-call
|
||||
}
|
||||
|
||||
function killObserver() {
|
||||
var socks = self._sockio.of(conf.NSP.PROC).sockets,
|
||||
canNotBeDeleted = {};
|
||||
function killObserver () {
|
||||
var socks = self._sockio.of(conf.NSP.PROC).sockets
|
||||
var canNotBeDeleted = {}
|
||||
|
||||
if (Array.isArray(socks) && socks.length > 0) {
|
||||
socks.forEach(function (sock) {
|
||||
if (sock._pid) {
|
||||
canNotBeDeleted[sock._pid.toString()] = 1;
|
||||
canNotBeDeleted[sock._pid.toString()] = 1
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
for (var pid in self._usages) {
|
||||
var timer;
|
||||
var timer
|
||||
if (!canNotBeDeleted[pid] && (timer = self._usages[pid])) {
|
||||
clearInterval(timer);
|
||||
delete self._usages[pid];
|
||||
console.debug('[pid:' + pid + ']', 'cpu and memory observer destroyed!');
|
||||
clearInterval(timer)
|
||||
delete self._usages[pid]
|
||||
console.debug('[pid:' + pid + ']', 'cpu and memory observer destroyed!')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function runObserver(pid) {
|
||||
socket._pid = pid;
|
||||
function runObserver (pid) {
|
||||
socket._pid = pid
|
||||
|
||||
var pidStr = pid.toString();
|
||||
var pidStr = pid.toString()
|
||||
if (self._usages[pidStr]) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
console.debug('[pid:' + pidStr + ']', 'cpu and memory observer is running...');
|
||||
console.debug('[pid:' + pidStr + ']', 'cpu and memory observer is running...')
|
||||
|
||||
function runTimer() {
|
||||
function runTimer () {
|
||||
pidusage.stat(pid, function (err, stat) {
|
||||
if (err) {
|
||||
clearInterval(ctx._usages[pidStr]);
|
||||
delete ctx._usages[pidStr];
|
||||
return emitError.call(self, err, pid);
|
||||
clearInterval(self._usages[pidStr])
|
||||
delete self._usages[pidStr]
|
||||
return emitError.call(self, err, pid)
|
||||
}
|
||||
stat.memory = stat.memory * 100 / totalmem;
|
||||
stat.memory = stat.memory * 100 / totalmem
|
||||
|
||||
var data = {
|
||||
pid: pid,
|
||||
time: Date.now(),
|
||||
usage: stat
|
||||
};
|
||||
self._broadcast.call(self, 'proc', data, conf.NSP.PROC);
|
||||
});
|
||||
}
|
||||
self._broadcast.call(self, 'proc', data, conf.NSP.PROC) // eslint-disable-line no-useless-call
|
||||
})
|
||||
}
|
||||
|
||||
self._usages[pidStr] = setInterval(runTimer, 3000);
|
||||
runTimer(this);
|
||||
self._usages[pidStr] = setInterval(runTimer, 3000)
|
||||
runTimer(this)
|
||||
}
|
||||
|
||||
socket.on('disconnect', killObserver);
|
||||
socket.on('proc', runObserver);
|
||||
console.info('Connected to ' + socket.nsp.name + '!');
|
||||
};
|
||||
socket.on('disconnect', killObserver)
|
||||
socket.on('proc', runObserver)
|
||||
console.info('Connected to ' + socket.nsp.name + '!')
|
||||
}
|
||||
|
||||
/**
|
||||
* Grep system state loop
|
||||
|
|
@ -356,23 +357,23 @@ Monitor.prototype._connectProcSock = function (socket) {
|
|||
*/
|
||||
Monitor.prototype._nextTick = function (tick, continuously) {
|
||||
// Return it if worker is running.
|
||||
if (this._status == 'R' && !continuously) {
|
||||
return;
|
||||
if (this._status === 'R' && !continuously) {
|
||||
return
|
||||
}
|
||||
// Running
|
||||
this._status = 'R';
|
||||
console.debug('monitor heartbeat per', tick + 'ms');
|
||||
this._status = 'R'
|
||||
console.debug('monitor heartbeat per', tick + 'ms')
|
||||
// Grep system state
|
||||
this._systemStat(function () {
|
||||
// If there still has any client, grep again after `tick` ms.
|
||||
if (!this._noClient) {
|
||||
return setTimeout(this._nextTick.bind(this, tick, true), tick);
|
||||
return setTimeout(this._nextTick.bind(this, tick, true), tick)
|
||||
}
|
||||
// Stop
|
||||
delete this._status;
|
||||
console.debug('monitor heartbeat destroyed!');
|
||||
});
|
||||
};
|
||||
delete this._status
|
||||
console.debug('monitor heartbeat destroyed!')
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Grep system states.
|
||||
|
|
@ -383,33 +384,33 @@ Monitor.prototype._systemStat = function (cb) {
|
|||
stat.cpuUsage(function (err, cpu_usage) {
|
||||
if (err) {
|
||||
// Log only.
|
||||
console.error('Can not load system/cpu/memory information: ', err.message);
|
||||
console.error('Can not load system/cpu/memory information: ', err.message)
|
||||
} else {
|
||||
// System states.
|
||||
this._sysStat = _.defaults(_(stat).pick('cpus', 'arch', 'hostname', 'platform', 'release', 'uptime', 'memory').clone(), {
|
||||
cpu: cpu_usage
|
||||
});
|
||||
this._broadcast.call(this, 'system_stat', this._sysStat);
|
||||
})
|
||||
this._broadcast.call(this, 'system_stat', this._sysStat) // eslint-disable-line no-useless-call
|
||||
}
|
||||
cb.call(this);
|
||||
}, this);
|
||||
};
|
||||
cb.call(this)
|
||||
}, this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Observe PM2
|
||||
* @private
|
||||
*/
|
||||
Monitor.prototype._observePM2 = function () {
|
||||
var pm2Daemon = this.options.pm2Conf.DAEMON_PUB_PORT;
|
||||
console.info('Connecting to pm2 daemon:', pm2Daemon);
|
||||
var pm2Daemon = this.options.pm2Conf.DAEMON_PUB_PORT
|
||||
console.info('Connecting to pm2 daemon:', pm2Daemon)
|
||||
this.pm2Sock = pm.sub(pm2Daemon, function (data) {
|
||||
console.info(chalk.magenta(data.event), data.process.name + '-' + data.process.pm_id);
|
||||
this._throttleRefresh();
|
||||
}, this);
|
||||
console.info(chalk.magenta(data.event), data.process.name + '-' + data.process.pm_id)
|
||||
this._throttleRefresh()
|
||||
}, this)
|
||||
|
||||
// Enforce a refresh operation if RPC is not online.
|
||||
this._throttleRefresh();
|
||||
};
|
||||
this._throttleRefresh()
|
||||
}
|
||||
|
||||
/**
|
||||
* Throttle the refresh behavior to avoid refresh bomb
|
||||
|
|
@ -417,13 +418,13 @@ Monitor.prototype._observePM2 = function () {
|
|||
*/
|
||||
Monitor.prototype._throttleRefresh = function () {
|
||||
if (this._throttle) {
|
||||
clearTimeout(this._throttle);
|
||||
clearTimeout(this._throttle)
|
||||
}
|
||||
this._throttle = setTimeout(function (ctx) {
|
||||
ctx._throttle = null;
|
||||
ctx._refreshProcs();
|
||||
}, 500, this);
|
||||
};
|
||||
ctx._throttle = null
|
||||
ctx._refreshProcs()
|
||||
}, 500, this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh processes
|
||||
|
|
@ -432,45 +433,45 @@ Monitor.prototype._throttleRefresh = function () {
|
|||
Monitor.prototype._refreshProcs = function () {
|
||||
pm.list(this.options.pm2Conf.DAEMON_RPC_PORT, function (err, procs) {
|
||||
if (err) {
|
||||
return this._broadcast('info', 'Can not connect to pm2 daemon, ' + err.message);
|
||||
return this._broadcast('info', 'Can not connect to pm2 daemon, ' + err.message)
|
||||
}
|
||||
// Wrap processes and cache them.
|
||||
this._procs = procs.map(function (proc) {
|
||||
proc.pm2_env = proc.pm2_env || {
|
||||
USER: 'UNKNOWN'
|
||||
};
|
||||
}
|
||||
var pm2_env = {
|
||||
user: proc.pm2_env.USER
|
||||
};
|
||||
}
|
||||
|
||||
for (var key in proc.pm2_env) {
|
||||
// Ignore useless fields.
|
||||
if (key.slice(0, 1) == '_' ||
|
||||
key.indexOf('axm_') == 0 || !!~['versioning', 'command'].indexOf(key) ||
|
||||
if (key.slice(0, 1) === '_' ||
|
||||
key.indexOf('axm_') === 0 || !!~['versioning', 'command'].indexOf(key) ||
|
||||
key.charCodeAt(0) <= 90) {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
pm2_env[key] = proc.pm2_env[key];
|
||||
pm2_env[key] = proc.pm2_env[key]
|
||||
}
|
||||
proc.pm2_env = pm2_env;
|
||||
return proc;
|
||||
});
|
||||
proc.pm2_env = pm2_env
|
||||
return proc
|
||||
})
|
||||
// Emit to client.
|
||||
this._broadcast('procs', this._procs);
|
||||
this._broadcast('procs', this._procs)
|
||||
}, this)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get PM2 version and return it to client.
|
||||
* @private
|
||||
*/
|
||||
Monitor.prototype._pm2Ver = function (socket) {
|
||||
var pm2RPC = this.options.pm2Conf.DAEMON_RPC_PORT;
|
||||
console.info('Fetching pm2 version:', pm2RPC);
|
||||
var pm2RPC = this.options.pm2Conf.DAEMON_RPC_PORT
|
||||
console.info('Fetching pm2 version:', pm2RPC)
|
||||
pm.version(pm2RPC, function (err, version) {
|
||||
socket.emit('pm2_ver', (err || !version) ? '0.0.0' : version);
|
||||
});
|
||||
};
|
||||
socket.emit('pm2_ver', (err || !version) ? '0.0.0' : version)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast to all connected clients.
|
||||
|
|
@ -480,14 +481,14 @@ Monitor.prototype._pm2Ver = function (socket) {
|
|||
* @private
|
||||
*/
|
||||
Monitor.prototype._broadcast = function (event, data, nsp) {
|
||||
nsp = nsp || conf.NSP.SYS;
|
||||
nsp = nsp || conf.NSP.SYS
|
||||
|
||||
if (this._noClient) {
|
||||
return console.debug('No client is connecting, ignore broadcasting', event, 'to', nsp)
|
||||
}
|
||||
console.debug('Broadcasting', event, 'to', nsp);
|
||||
this._sockio.of(nsp).emit(event, data);
|
||||
};
|
||||
console.debug('Broadcasting', event, 'to', nsp)
|
||||
this._sockio.of(nsp).emit(event, data)
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy tails.
|
||||
|
|
@ -495,65 +496,65 @@ Monitor.prototype._broadcast = function (event, data, nsp) {
|
|||
* @return {[type]}
|
||||
*/
|
||||
Monitor.prototype._killTailProcess = function (pm_id) {
|
||||
var self = this;
|
||||
var self = this
|
||||
|
||||
function killTail(id) {
|
||||
var tail = self._tails[id];
|
||||
function killTail (id) {
|
||||
var tail = self._tails[id]
|
||||
if (!tail) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
try {
|
||||
tail.kill('SIGTERM');
|
||||
tail.kill('SIGTERM')
|
||||
} catch (err) {}
|
||||
|
||||
delete self._tails[id];
|
||||
console.info('[pm2:' + id + ']', 'tail destroyed!');
|
||||
delete self._tails[id]
|
||||
console.info('[pm2:' + id + ']', 'tail destroyed!')
|
||||
}
|
||||
if (!isNaN(pm_id)) {
|
||||
return killTail(pm_id);
|
||||
return killTail(pm_id)
|
||||
}
|
||||
|
||||
var socks = self._sockio.of(conf.NSP.LOG).sockets,
|
||||
canNotBeDeleted = {};
|
||||
var socks = self._sockio.of(conf.NSP.LOG).sockets
|
||||
var canNotBeDeleted = {}
|
||||
if (socks && socks.length > 0) {
|
||||
socks.forEach(function (sock) {
|
||||
canNotBeDeleted[sock._pm_id] = 1;
|
||||
});
|
||||
canNotBeDeleted[sock._pm_id] = 1
|
||||
})
|
||||
}
|
||||
|
||||
for (var pm_id in self._tails) {
|
||||
if (!canNotBeDeleted[pm_id]) {
|
||||
killTail(pm_id);
|
||||
for (var _id in self._tails) {
|
||||
if (!canNotBeDeleted[_id]) {
|
||||
killTail(_id)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Listening all the nsp.
|
||||
*/
|
||||
Monitor.prototype._listeningSocketIO = function () {
|
||||
if (!this._sockio || this._sockio._listening) {
|
||||
console.warn('Avoid duplicated listening!');
|
||||
return;
|
||||
console.warn('Avoid duplicated listening!')
|
||||
return
|
||||
}
|
||||
|
||||
this._sockio._listening = true;
|
||||
this._sockio._listening = true
|
||||
for (var nsp in conf.NSP) {
|
||||
this._sockio.of(conf.NSP[nsp]).on('connection', this['_connect' + (nsp[0] + nsp.toLowerCase().slice(1)) + 'Sock'].bind(this));
|
||||
console.info('Listening connection event on', nsp.toLowerCase());
|
||||
this._sockio.of(conf.NSP[nsp]).on('connection', this['_connect' + (nsp[0] + nsp.toLowerCase().slice(1)) + 'Sock'].bind(this))
|
||||
console.info('Listening connection event on', nsp.toLowerCase())
|
||||
}
|
||||
|
||||
var auth;
|
||||
var auth
|
||||
if (!(this.options.agent && (auth = this.options.agent.authorization))) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
this._sockio.use(function (socket, next) {
|
||||
if (auth !== socket.handshake.query.auth) {
|
||||
return next(new Error('unauthorized'));
|
||||
return next(new Error('unauthorized'))
|
||||
}
|
||||
next();
|
||||
});
|
||||
};
|
||||
next()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* List all available monitors.
|
||||
|
|
@ -561,54 +562,54 @@ Monitor.prototype._listeningSocketIO = function () {
|
|||
* @return {Object}
|
||||
*/
|
||||
Monitor.available = function (options) {
|
||||
options.agent = options.agent || {};
|
||||
var remotable = options.remotes && _.keys(options.remotes).length > 0;
|
||||
options.agent = options.agent || {}
|
||||
var remotable = options.remotes && _.keys(options.remotes).length > 0
|
||||
|
||||
if (options.agent.offline && !remotable) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
options.port = options.port || 8088;
|
||||
options.port = options.port || 8088
|
||||
|
||||
var q = {
|
||||
name: 'socket_server',
|
||||
message: 'Which socket server would you wanna connect to',
|
||||
type: 'list',
|
||||
choices: []
|
||||
},
|
||||
wrapLocal = function () {
|
||||
return {
|
||||
value: (options.agent && options.agent.authorization ? options.agent.authorization + '@' : '') + '127.0.0.1:' + options.port,
|
||||
short: 'localhost'
|
||||
};
|
||||
};
|
||||
if (!remotable) {
|
||||
q.choices = [wrapLocal()];
|
||||
return q;
|
||||
name: 'socket_server',
|
||||
message: 'Which socket server would you wanna connect to',
|
||||
type: 'list',
|
||||
choices: []
|
||||
}
|
||||
var maxShortLength = 0;
|
||||
var wrapLocal = function () {
|
||||
return {
|
||||
value: (options.agent && options.agent.authorization ? options.agent.authorization + '@' : '') + '127.0.0.1:' + options.port,
|
||||
short: 'localhost'
|
||||
}
|
||||
}
|
||||
if (!remotable) {
|
||||
q.choices = [wrapLocal()]
|
||||
return q
|
||||
}
|
||||
var maxShortLength = 0
|
||||
for (var remote in options.remotes) {
|
||||
var connectionString = options.remotes[remote];
|
||||
var connectionString = options.remotes[remote]
|
||||
q.choices.push({
|
||||
value: connectionString,
|
||||
short: remote
|
||||
});
|
||||
maxShortLength = Math.max(maxShortLength, remote.length);
|
||||
})
|
||||
maxShortLength = Math.max(maxShortLength, remote.length)
|
||||
}
|
||||
if (!options.agent.offline) {
|
||||
var conn = wrapLocal();
|
||||
q.choices.push(conn);
|
||||
maxShortLength = Math.max(maxShortLength, conn.short.length);
|
||||
var conn = wrapLocal()
|
||||
q.choices.push(conn)
|
||||
maxShortLength = Math.max(maxShortLength, conn.short.length)
|
||||
}
|
||||
|
||||
if (q.choices.length > 1) {
|
||||
q.choices.forEach(function (c) {
|
||||
c.name = '[' + c.short + Array(maxShortLength - c.short.length + 1).join(options.blank || ' ') + '] ' + c.value;
|
||||
});
|
||||
c.name = '[' + c.short + Array(maxShortLength - c.short.length + 1).join(options.blank || ' ') + '] ' + c.value
|
||||
})
|
||||
}
|
||||
|
||||
return q;
|
||||
};
|
||||
return q
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert connection object to string.
|
||||
|
|
@ -617,13 +618,13 @@ Monitor.available = function (options) {
|
|||
*/
|
||||
Monitor.toConnectionString = function (connection) {
|
||||
var uri = (connection.protocol || 'http:') + '//' + (connection.hostname || '127.0.0.1') + ':' + connection.port +
|
||||
(connection.path || '') + (connection.namespace || '');
|
||||
(connection.path || '') + (connection.namespace || '')
|
||||
|
||||
if (connection.authorization) {
|
||||
uri += (uri.indexOf('?') > 0 ? '&' : '?') + 'auth=' + connection.authorization;
|
||||
uri += (uri.indexOf('?') > 0 ? '&' : '?') + 'auth=' + connection.authorization
|
||||
}
|
||||
return uri;
|
||||
};
|
||||
return uri
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse connection string to an uri object.
|
||||
|
|
@ -635,35 +636,35 @@ Monitor.parseConnectionString = function (connectionString) {
|
|||
port: 8088,
|
||||
hostname: '127.0.0.1',
|
||||
authorization: ''
|
||||
};
|
||||
var lastAt = connectionString.lastIndexOf('@');
|
||||
}
|
||||
var lastAt = connectionString.lastIndexOf('@')
|
||||
if (lastAt >= 0) {
|
||||
connection.authorization = connectionString.slice(0, lastAt);
|
||||
connectionString = connectionString.slice(lastAt + 1);
|
||||
connection.authorization = connectionString.slice(0, lastAt)
|
||||
connectionString = connectionString.slice(lastAt + 1)
|
||||
}
|
||||
if (!/^http(s)?:\/\//i.test(connectionString)) {
|
||||
connectionString = 'http://' + connectionString;
|
||||
connectionString = 'http://' + connectionString
|
||||
}
|
||||
|
||||
if (connectionString) {
|
||||
connectionString = url.parse(connectionString);
|
||||
connection.hostname = connectionString.hostname;
|
||||
connection.port = connectionString.port;
|
||||
connection.path = _.trimLeft(connectionString.path, '/');
|
||||
connection.protocol = connectionString.protocol;
|
||||
connectionString = url.parse(connectionString)
|
||||
connection.hostname = connectionString.hostname
|
||||
connection.port = connectionString.port
|
||||
connection.path = _.trimLeft(connectionString.path, '/')
|
||||
connection.protocol = connectionString.protocol
|
||||
}
|
||||
return connection;
|
||||
};
|
||||
return connection
|
||||
}
|
||||
|
||||
Object.defineProperty(Monitor.prototype, 'sockio', {
|
||||
set: function (io) {
|
||||
if (this._sockio) {
|
||||
this._sockio.close();
|
||||
this._sockio.close()
|
||||
}
|
||||
this._sockio = io;
|
||||
this._listeningSocketIO();
|
||||
this._sockio = io
|
||||
this._listeningSocketIO()
|
||||
},
|
||||
get: function () {
|
||||
return this._sockio;
|
||||
return this._sockio
|
||||
}
|
||||
});
|
||||
})
|
||||
|
|
|
|||
209
lib/pm.js
209
lib/pm.js
|
|
@ -1,21 +1,18 @@
|
|||
var spawn = require('child_process').spawn,
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
_ = require('lodash'),
|
||||
async = require('async'),
|
||||
chalk = require('chalk'),
|
||||
stat = require('./stat'),
|
||||
rpc = require('pm2-axon-rpc'),
|
||||
axon = require('pm2-axon');
|
||||
var spawn = require('child_process').spawn
|
||||
var fs = require('fs')
|
||||
var _ = require('lodash')
|
||||
var async = require('async')
|
||||
var rpc = require('pm2-axon-rpc')
|
||||
var axon = require('pm2-axon')
|
||||
|
||||
/**
|
||||
* Forever lib.
|
||||
* @type {{}}
|
||||
*/
|
||||
var pm = module.exports = {};
|
||||
var pm = module.exports = {}
|
||||
|
||||
var re_blank = /^[\s\r\t]*$/,
|
||||
allowedEvents = ['start', 'restart', 'exit', 'online'];
|
||||
var re_blank = /^[\s\r\t]*$/
|
||||
var allowedEvents = ['start', 'restart', 'exit', 'online']
|
||||
|
||||
/**
|
||||
* Subscribe event BUS.
|
||||
|
|
@ -24,24 +21,24 @@ var re_blank = /^[\s\r\t]*$/,
|
|||
* @param {Object} context
|
||||
*/
|
||||
pm.sub = function (sockPath, cb, context) {
|
||||
var sub = axon.socket('sub-emitter');
|
||||
var sub = axon.socket('sub-emitter')
|
||||
// Once awake from sleeping.
|
||||
sub.on('log:*', function (e, d) {
|
||||
// Do not subscribe it.
|
||||
sub.off('log:*');
|
||||
d.event = 'awake';
|
||||
cb.call(context, d);
|
||||
});
|
||||
sub.off('log:*')
|
||||
d.event = 'awake'
|
||||
cb.call(context, d)
|
||||
})
|
||||
|
||||
// Process events.
|
||||
sub.on('process:*', function (e, d) {
|
||||
if (d && !!~allowedEvents.indexOf(d.event)) {
|
||||
cb.call(context, d);
|
||||
cb.call(context, d)
|
||||
}
|
||||
});
|
||||
sub.connect(sockPath);
|
||||
return sub;
|
||||
};
|
||||
})
|
||||
sub.connect(sockPath)
|
||||
return sub
|
||||
}
|
||||
|
||||
/**
|
||||
* Get PM2 version.
|
||||
|
|
@ -54,8 +51,8 @@ pm.version = function (sockPath, cb) {
|
|||
events: [
|
||||
['getVersion', {}, cb]
|
||||
]
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* List available processes.
|
||||
|
|
@ -65,7 +62,7 @@ pm.version = function (sockPath, cb) {
|
|||
*/
|
||||
pm.list = function (sockPath, cb, context) {
|
||||
if (!fs.existsSync(sockPath)) {
|
||||
return cb.call(context, []);
|
||||
return cb.call(context, [])
|
||||
}
|
||||
pm._rpc({
|
||||
sockPath: sockPath,
|
||||
|
|
@ -73,8 +70,8 @@ pm.list = function (sockPath, cb, context) {
|
|||
['getMonitorData', {}, cb]
|
||||
],
|
||||
context: context || this
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute remote RPC events.
|
||||
|
|
@ -88,38 +85,38 @@ pm.list = function (sockPath, cb, context) {
|
|||
* @private
|
||||
*/
|
||||
pm._rpc = function (opts) {
|
||||
var req = axon.socket("req"),
|
||||
rpcSock = req.connect(opts.sockPath),
|
||||
rpcClient = new rpc.Client(req);
|
||||
var req = axon.socket('req')
|
||||
var rpcSock = req.connect(opts.sockPath)
|
||||
var rpcClient = new rpc.Client(req)
|
||||
|
||||
// Connect RPC server.
|
||||
rpcSock.on('connect', function () {
|
||||
// Execute request.
|
||||
var waterfalls = opts.events.map(function (event) {
|
||||
return function (next) {
|
||||
var cb = typeof event[event.length - 1] == 'function' ? event.pop() : null;
|
||||
var cb = typeof event[event.length - 1] === 'function' ? event.pop() : null
|
||||
if (cb) {
|
||||
event.push(function () {
|
||||
// Wrap arguments, no [].slice (avoid leak)!!!
|
||||
var args = new Array(arguments.length);
|
||||
var args = new Array(arguments.length)
|
||||
for (var i = 0; i < args; i++) {
|
||||
args[i] = arguments[i];
|
||||
args[i] = arguments[i]
|
||||
}
|
||||
cb.apply(opts.context, arguments);
|
||||
next();
|
||||
});
|
||||
cb.apply(opts.context, arguments)
|
||||
next()
|
||||
})
|
||||
}
|
||||
rpcClient.call.apply(rpcClient, event);
|
||||
rpcClient.call.apply(rpcClient, event)
|
||||
if (!cb) {
|
||||
next();
|
||||
next()
|
||||
}
|
||||
};
|
||||
});
|
||||
async.waterfall(waterfalls, function (err, res) {
|
||||
rpcSock.close();
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
})
|
||||
async.waterfall(waterfalls, function () {
|
||||
rpcSock.close()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Find process by pm_id.
|
||||
|
|
@ -131,22 +128,22 @@ pm._rpc = function (opts) {
|
|||
pm._findById = function (sockPath, id, cb) {
|
||||
pm.list(sockPath, function (err, procs) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
return cb(err)
|
||||
}
|
||||
if (!procs || procs.length == 0) {
|
||||
return cb(new Error('No PM2 process running, the sockPath is "' + sockPath + '", please make sure it is existing!'));
|
||||
if (!procs || procs.length === 0) {
|
||||
return cb(new Error('No PM2 process running, the sockPath is "' + sockPath + '", please make sure it is existing!'))
|
||||
}
|
||||
|
||||
var proc = _.find(procs, function (p) {
|
||||
return p && p.pm_id == id;
|
||||
});
|
||||
return p && p.pm_id === id
|
||||
})
|
||||
|
||||
if (!proc) {
|
||||
return cb(new Error('Cannot find pm process by pm_id: ' + id));
|
||||
return cb(new Error('Cannot find pm process by pm_id: ' + id))
|
||||
}
|
||||
|
||||
cb(null, proc);
|
||||
}, true);
|
||||
cb(null, proc)
|
||||
}, true)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -156,28 +153,28 @@ pm._findById = function (sockPath, id, cb) {
|
|||
* @param {Function} cb
|
||||
*/
|
||||
pm.action = function (sockPath, action, id, cb) {
|
||||
if (id == 'all') {
|
||||
if (id === 'all') {
|
||||
pm.list(sockPath, function (err, procs) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
return cb(err)
|
||||
}
|
||||
if (!procs || procs.length == 0) {
|
||||
return cb(new Error('No PM2 process running, the sockPath is "' + sockPath + '", please make sure it is existing!'));
|
||||
if (!procs || procs.length === 0) {
|
||||
return cb(new Error('No PM2 process running, the sockPath is "' + sockPath + '", please make sure it is existing!'))
|
||||
}
|
||||
|
||||
async.map(procs, function (proc, next) {
|
||||
pm._actionByPMId(sockPath, proc, action, next.bind(null, null));
|
||||
}, cb);
|
||||
});
|
||||
pm._actionByPMId(sockPath, proc, action, next.bind(null, null))
|
||||
}, cb)
|
||||
})
|
||||
} else {
|
||||
pm._findById(sockPath, id, function (err, proc) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
return cb(err)
|
||||
}
|
||||
pm._actionByPMId(sockPath, proc, action, cb);
|
||||
});
|
||||
pm._actionByPMId(sockPath, proc, action, cb)
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger actions of process by pm_id.
|
||||
|
|
@ -188,27 +185,27 @@ pm.action = function (sockPath, action, id, cb) {
|
|||
* @private
|
||||
*/
|
||||
pm._actionByPMId = function (sockPath, proc, action, cb) {
|
||||
var noBusEvent = action == 'delete' && proc.pm2_env.status != 'online',
|
||||
pm_id = proc.pm_id;
|
||||
var noBusEvent = action === 'delete' && proc.pm2_env.status !== 'online'
|
||||
var pm_id = proc.pm_id
|
||||
|
||||
action += 'ProcessId';
|
||||
action += 'ProcessId'
|
||||
var watchEvent = ['stopWatch', action, {
|
||||
id: pm_id
|
||||
}, function (err, success) {}];
|
||||
}, function () {}]
|
||||
|
||||
if (!!~['restart'].indexOf(action)) {
|
||||
watchEvent.splice(0, 1, 'restartWatch');
|
||||
watchEvent.pop();
|
||||
if (!!~['restart'].indexOf(action)) { // eslint-disable-line no-extra-boolean-cast
|
||||
watchEvent.splice(0, 1, 'restartWatch')
|
||||
watchEvent.pop()
|
||||
}
|
||||
|
||||
var actionEvent = [action, pm_id, function (err, sock) {
|
||||
cb(err, noBusEvent);
|
||||
}];
|
||||
cb(err, noBusEvent)
|
||||
}]
|
||||
|
||||
if (action == 'restartProcessId') {
|
||||
if (action === 'restartProcessId') {
|
||||
actionEvent.splice(1, 1, {
|
||||
id: pm_id
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
pm._rpc({
|
||||
|
|
@ -217,8 +214,8 @@ pm._actionByPMId = function (sockPath, proc, action, cb) {
|
|||
watchEvent,
|
||||
actionEvent
|
||||
]
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Tail logs.
|
||||
|
|
@ -231,13 +228,13 @@ pm.tail = function (opts, each, cb) {
|
|||
// Fetch the proccess that we need.
|
||||
pm._findById(opts.sockPath, opts.pm_id, function (err, proc) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
return cb(err)
|
||||
}
|
||||
proc.pm2_log = opts.logPath;
|
||||
proc.pm2_log = opts.logPath
|
||||
// Tail logs.
|
||||
cb(null, pm._tailLogs(proc, each));
|
||||
});
|
||||
};
|
||||
cb(null, pm._tailLogs(proc, each))
|
||||
})
|
||||
}
|
||||
/**
|
||||
* Use linux `tail` command to grep logs.
|
||||
* @param {Object} proc
|
||||
|
|
@ -248,58 +245,58 @@ pm.tail = function (opts, each, cb) {
|
|||
pm._tailLogs = function (proc, cb) {
|
||||
var logs = {
|
||||
'pm2': proc.pm2_log
|
||||
};
|
||||
}
|
||||
if (proc.pm_log_path) {
|
||||
logs.entire = proc.pm2_env.pm_log_path;
|
||||
logs.entire = proc.pm2_env.pm_log_path
|
||||
} else {
|
||||
if (proc.pm2_env.pm_out_log_path) {
|
||||
logs.out = proc.pm2_env.pm_out_log_path;
|
||||
logs.out = proc.pm2_env.pm_out_log_path
|
||||
}
|
||||
if (proc.pm2_env.pm_err_log_path) {
|
||||
logs.err = proc.pm2_env.pm_err_log_path;
|
||||
logs.err = proc.pm2_env.pm_err_log_path
|
||||
}
|
||||
}
|
||||
|
||||
var logFiles = [];
|
||||
var logFiles = []
|
||||
for (var key in logs) {
|
||||
var file = logs[key];
|
||||
var file = logs[key]
|
||||
if (fs.existsSync(file)) {
|
||||
logFiles.push(file);
|
||||
logFiles.push(file)
|
||||
}
|
||||
}
|
||||
if (logFiles.length == 0) {
|
||||
return null;
|
||||
if (logFiles.length === 0) {
|
||||
return null
|
||||
}
|
||||
var tail = spawn('tail', ['-n', 20, '-f'].concat(logFiles), {
|
||||
killSignal: 'SIGTERM',
|
||||
detached: true,
|
||||
stdio: ['ignore', 'pipe', 'pipe']
|
||||
});
|
||||
})
|
||||
|
||||
// Use utf8 encoding.
|
||||
tail.stdio.forEach(function (stdio) {
|
||||
stdio && stdio.setEncoding('utf8');
|
||||
});
|
||||
stdio && stdio.setEncoding('utf8')
|
||||
})
|
||||
|
||||
// stdout.
|
||||
tail.stdout.on('data', function (data) {
|
||||
var lines = [];
|
||||
var lines = []
|
||||
data.split(/\n/).forEach(function (line) {
|
||||
if (!re_blank.test(line)) {
|
||||
lines.push(line);
|
||||
lines.push(line)
|
||||
}
|
||||
});
|
||||
})
|
||||
if (lines.length > 0) {
|
||||
cb(null, lines);
|
||||
cb(null, lines)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
// handle error.
|
||||
tail.stderr.on('data', function (data) {
|
||||
console.error(data.toString());
|
||||
tail.disconnect();
|
||||
cb(new Error(data.toString().replace(/\n/, '')));
|
||||
});
|
||||
tail.unref();
|
||||
return tail;
|
||||
};
|
||||
console.error(data.toString())
|
||||
tail.disconnect()
|
||||
cb(new Error(data.toString().replace(/\n/, '')))
|
||||
})
|
||||
tail.unref()
|
||||
return tail
|
||||
}
|
||||
|
|
|
|||
38
lib/stat.js
38
lib/stat.js
|
|
@ -1,4 +1,4 @@
|
|||
var os = require('os');
|
||||
var os = require('os')
|
||||
|
||||
/**
|
||||
* System states
|
||||
|
|
@ -25,28 +25,28 @@ var stat = module.exports = {
|
|||
* List all CPUs.
|
||||
* @returns {*}
|
||||
*/
|
||||
get cpus() {
|
||||
return os.cpus();
|
||||
get cpus () {
|
||||
return os.cpus()
|
||||
},
|
||||
/**
|
||||
* Uptime.
|
||||
* @returns {*}
|
||||
*/
|
||||
get uptime() {
|
||||
return os.uptime();
|
||||
get uptime () {
|
||||
return os.uptime()
|
||||
},
|
||||
/**
|
||||
* System memory usage.
|
||||
* @returns {{free: *, total: *, percentage: number}}
|
||||
*/
|
||||
get memory() {
|
||||
get memory () {
|
||||
return {
|
||||
free: os.freemem(),
|
||||
total: os.totalmem(),
|
||||
percentage: Math.round(100 * (1 - os.freemem() / os.totalmem()))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* System CPU usage percentage (total).
|
||||
|
|
@ -55,28 +55,28 @@ var stat = module.exports = {
|
|||
*/
|
||||
stat.cpuUsage = function (fn, context) {
|
||||
setTimeout(function (ctx, stat1) {
|
||||
var stat2 = ctx.cpuInfo(),
|
||||
perc = 100 * (1 - (stat2.idle - stat1.idle) / (stat2.total - stat1.total));
|
||||
fn.call(context, null, perc.toFixed(2));
|
||||
}, 1000, this, this.cpuInfo());
|
||||
};
|
||||
var stat2 = ctx.cpuInfo()
|
||||
var perc = 100 * (1 - (stat2.idle - stat1.idle) / (stat2.total - stat1.total))
|
||||
fn.call(context, null, perc.toFixed(2))
|
||||
}, 1000, this, this.cpuInfo())
|
||||
}
|
||||
|
||||
/**
|
||||
* System CPU usage detail information.
|
||||
* @returns {{idle: number, total: number}}
|
||||
*/
|
||||
stat.cpuInfo = function () {
|
||||
var cpus = this.cpus,
|
||||
idle = 0,
|
||||
total = 0;
|
||||
var cpus = this.cpus
|
||||
var idle = 0
|
||||
var total = 0
|
||||
for (var i in cpus) {
|
||||
idle += cpus[i].times.idle;
|
||||
idle += cpus[i].times.idle
|
||||
for (var k in cpus[i].times) {
|
||||
total += cpus[i].times[k];
|
||||
total += cpus[i].times[k]
|
||||
}
|
||||
}
|
||||
return {
|
||||
'idle': idle,
|
||||
'total': total
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
164
lib/util/conf.js
164
lib/util/conf.js
|
|
@ -1,5 +1,10 @@
|
|||
var fs = require('fs'),
|
||||
_ = require('lodash');
|
||||
var fs = require('fs')
|
||||
var _ = require('lodash')
|
||||
|
||||
var re_comment = /^\s*;/
|
||||
var re_setion = /^\s*\[([^\]]+)\]\s*$/
|
||||
var re_kv = /^([^=]+)=(.*)$/
|
||||
var re_boolean = /^(true|false)$/i
|
||||
|
||||
/**
|
||||
* Namespaces of socket.io
|
||||
|
|
@ -9,35 +14,35 @@ exports.NSP = {
|
|||
SYS: '/sys',
|
||||
LOG: '/log',
|
||||
PROC: '/proc'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Configurations
|
||||
* @type {[type]}
|
||||
*/
|
||||
exports.File = File;
|
||||
exports.File = File
|
||||
|
||||
/**
|
||||
* Configurations store in a disk file.
|
||||
* @param {Object} options
|
||||
* @constructor
|
||||
*/
|
||||
function File(options) {
|
||||
function File (options) {
|
||||
if (!(this instanceof File)) {
|
||||
return new File(options);
|
||||
return new File(options)
|
||||
}
|
||||
|
||||
if (typeof options == 'string') {
|
||||
if (typeof options === 'string') {
|
||||
options = {
|
||||
file: options
|
||||
};
|
||||
}
|
||||
}
|
||||
options = _.assign({}, options || {});
|
||||
options = _.assign({}, options || {})
|
||||
if (!options.file) {
|
||||
throw new Error('`file` is required.');
|
||||
throw new Error('`file` is required.')
|
||||
}
|
||||
Object.freeze(options);
|
||||
this.options = options;
|
||||
Object.freeze(options)
|
||||
this.options = options
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -45,90 +50,85 @@ function File(options) {
|
|||
*/
|
||||
File.prototype.loadSync = function () {
|
||||
if (!fs.existsSync(this.options.file)) {
|
||||
this._data = {};
|
||||
return this;
|
||||
this._data = {}
|
||||
return this
|
||||
}
|
||||
var re_comment = /^\s*;/,
|
||||
re_setion = /^\s*\[([^\]]+)\]\s*$/,
|
||||
re_kv = /^([^=]+)=(.*)$/,
|
||||
re_boolean = /^(true|false)$/i;
|
||||
|
||||
var json = {},
|
||||
sec;
|
||||
var json = {}
|
||||
var sec
|
||||
fs.readFileSync(this.options.file, {
|
||||
encoding: 'utf8'
|
||||
})
|
||||
.split(/[\r\n]/).forEach(function (line) {
|
||||
// Empty line.
|
||||
if (!line) {
|
||||
sec = null;
|
||||
return;
|
||||
}
|
||||
// Remove comments.
|
||||
if (re_comment.test(line)) {
|
||||
return;
|
||||
}
|
||||
var ms;
|
||||
// Sections.
|
||||
if ((ms = line.match(re_setion)) && ms.length == 2) {
|
||||
json[sec = ms[1].trim()] = {};
|
||||
return;
|
||||
}
|
||||
encoding: 'utf8'
|
||||
}).split(/[\r\n]/).forEach(function (line) {
|
||||
// Empty line.
|
||||
if (!line) {
|
||||
sec = null
|
||||
return
|
||||
}
|
||||
// Remove comments.
|
||||
if (re_comment.test(line)) {
|
||||
return
|
||||
}
|
||||
var ms
|
||||
// Sections.
|
||||
if ((ms = line.match(re_setion)) && ms.length === 2) {
|
||||
json[sec = ms[1].trim()] = {}
|
||||
return
|
||||
}
|
||||
|
||||
// Key-value pairs.
|
||||
if ((ms = line.match(re_kv)) && ms.length == 3) {
|
||||
var key = ms[1].trim(),
|
||||
value = ms[2].trim();
|
||||
// Parse boolean and number.
|
||||
if (!isNaN(value)) {
|
||||
value = parseFloat(value);
|
||||
} else if (re_boolean.test(value)) {
|
||||
value = value.toLowerCase() == 'true';
|
||||
}
|
||||
if (sec) {
|
||||
json[sec][key] = value;
|
||||
} else {
|
||||
json[key] = value;
|
||||
}
|
||||
// Key-value pairs.
|
||||
if ((ms = line.match(re_kv)) && ms.length === 3) {
|
||||
var key = ms[1].trim()
|
||||
var value = ms[2].trim()
|
||||
// Parse boolean and number.
|
||||
if (!isNaN(value)) {
|
||||
value = parseFloat(value)
|
||||
} else if (re_boolean.test(value)) {
|
||||
value = value.toLowerCase() === 'true'
|
||||
}
|
||||
});
|
||||
if (sec) {
|
||||
json[sec][key] = value
|
||||
} else {
|
||||
json[key] = value
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
this._data = json;
|
||||
return this;
|
||||
};
|
||||
this._data = json
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Save data to a disk file (sync).
|
||||
* @returns {File}
|
||||
*/
|
||||
File.prototype.saveSync = function () {
|
||||
function wrapValue(key, value) {
|
||||
return key + ' = ' + (typeof value == 'string' ? value : JSON.stringify(value)) + '\n';
|
||||
function wrapValue (key, value) {
|
||||
return key + ' = ' + (typeof value === 'string' ? value : JSON.stringify(value)) + '\n'
|
||||
}
|
||||
var ini = '';
|
||||
var ini = ''
|
||||
for (var key in this._data) {
|
||||
var value = this._data[key];
|
||||
var value = this._data[key]
|
||||
// TODO: Array type.
|
||||
if (typeof value == 'object') {
|
||||
ini += '[ ' + key + ' ]\n';
|
||||
if (typeof value === 'object') {
|
||||
ini += '[ ' + key + ' ]\n'
|
||||
for (var subKey in value) {
|
||||
ini += wrapValue(subKey, value[subKey])
|
||||
}
|
||||
ini += '\n';
|
||||
ini += '\n'
|
||||
}
|
||||
ini += wrapValue(key, value);
|
||||
ini += wrapValue(key, value)
|
||||
}
|
||||
fs.writeFileSync(this.options.file, ini);
|
||||
return this;
|
||||
};
|
||||
fs.writeFileSync(this.options.file, ini)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data.
|
||||
* @returns {{}|*}
|
||||
*/
|
||||
File.prototype.valueOf = function () {
|
||||
return this._data;
|
||||
};
|
||||
return this._data
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set/remove key-value pairs.
|
||||
|
|
@ -139,25 +139,27 @@ File.prototype.valueOf = function () {
|
|||
*/
|
||||
File.prototype.val = function (key, value, def) {
|
||||
if (!key) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
// Load config from File.
|
||||
this.loadSync();
|
||||
this.loadSync()
|
||||
|
||||
if (typeof value == 'undefined') {
|
||||
if (typeof value === 'undefined') {
|
||||
// Get config.
|
||||
return this._data[key];
|
||||
return this._data[key]
|
||||
} else if (value == null) {
|
||||
// Clear config.
|
||||
delete this._data[key];
|
||||
delete this._data[key]
|
||||
// Reset to default if necessary.
|
||||
(typeof def != 'undefined') && (this._data[key] = def);
|
||||
return this.saveSync();
|
||||
if (typeof def !== 'undefined') {
|
||||
this._data[key] = def
|
||||
}
|
||||
return this.saveSync()
|
||||
}
|
||||
|
||||
this._data[key] = value;
|
||||
this._data[key] = value
|
||||
|
||||
// Save it.
|
||||
this.saveSync();
|
||||
return this;
|
||||
};
|
||||
this.saveSync()
|
||||
return this
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,44 +1,44 @@
|
|||
var chalk = require('chalk');
|
||||
var chalk = require('chalk')
|
||||
|
||||
module.exports = function (options) {
|
||||
options = options || {};
|
||||
var lev = options.level,
|
||||
hacks = ['debug', 'log', 'info', 'warn', 'error'];
|
||||
options = options || {}
|
||||
var lev = options.level
|
||||
var hacks = ['debug', 'log', 'info', 'warn', 'error']
|
||||
|
||||
if ((typeof lev == 'string' && typeof (lev = hacks.indexOf(lev)) == 'undefined') || (isFinite(lev) && (lev < 0 || lev > hacks.length))) {
|
||||
options.level = 0;
|
||||
if ((typeof lev === 'string' && typeof (lev = hacks.indexOf(lev)) === 'undefined') || (isFinite(lev) && (lev < 0 || lev > hacks.length))) {
|
||||
options.level = 0
|
||||
}
|
||||
options.level = !isNaN(lev) ? lev : 0;
|
||||
options.level = !isNaN(lev) ? lev : 0
|
||||
|
||||
console.__level = options.level;
|
||||
console.__level = options.level
|
||||
|
||||
if (console.__hacked) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
var colors = ['grey', '', 'green', 'yellow', 'red'],
|
||||
consoled = {};
|
||||
var colors = ['grey', '', 'green', 'yellow', 'red']
|
||||
var consoled = {}
|
||||
|
||||
hacks.forEach(function (method) {
|
||||
if (method == 'debug') {
|
||||
consoled.debug = console.log;
|
||||
return;
|
||||
if (method === 'debug') {
|
||||
consoled.debug = console.log
|
||||
return
|
||||
}
|
||||
consoled[method] = console[method];
|
||||
});
|
||||
consoled[method] = console[method]
|
||||
})
|
||||
|
||||
hacks.forEach(function (method, index) {
|
||||
console[method] = function () {
|
||||
if (index < console.__level) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (method != 'log' && arguments.length > 0) {
|
||||
if (method !== 'log' && arguments.length > 0) {
|
||||
arguments[0] = (options.prefix ? chalk.bold[colors[index]]('[' + method.toUpperCase() + '] ') : '') +
|
||||
(options.date ? (new Date()).toLocaleString() + ' ' : '') + arguments[0];
|
||||
(options.date ? (new Date()).toLocaleString() + ' ' : '') + arguments[0]
|
||||
}
|
||||
consoled[method].apply(console, arguments);
|
||||
};
|
||||
});
|
||||
consoled[method].apply(console, arguments)
|
||||
}
|
||||
})
|
||||
|
||||
console.__hacked = true;
|
||||
};
|
||||
console.__hacked = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,44 +1,41 @@
|
|||
var _ = require('lodash'),
|
||||
path = require('path'),
|
||||
chalk = require('chalk'),
|
||||
fs = require('fs'),
|
||||
url = require('url');
|
||||
var path = require('path')
|
||||
var fs = require('fs')
|
||||
|
||||
var routes = []
|
||||
|
||||
// bind actions.
|
||||
global.action = function(method, path, func){
|
||||
if (typeof method == 'function') {
|
||||
func = method;
|
||||
method = 'get';
|
||||
path = func.name;
|
||||
} else if (typeof path == 'function') {
|
||||
func = path;
|
||||
path = func.name;
|
||||
global.action = function (method, path, func) {
|
||||
if (typeof method === 'function') {
|
||||
func = method
|
||||
method = 'get'
|
||||
path = func.name
|
||||
} else if (typeof path === 'function') {
|
||||
func = path
|
||||
path = func.name
|
||||
}
|
||||
if (typeof method != 'string' || typeof path != 'string' || typeof func != 'function') {
|
||||
if (typeof method !== 'string' || typeof path !== 'string' || typeof func !== 'function') {
|
||||
throw new Error('Arguments of action() should be one of `[FUNCTION]` / `[METHOD], [FUNCTION]` / `[METHOD], [PATH], [FUNCTION]`.')
|
||||
}
|
||||
routes.push({
|
||||
method: method,
|
||||
path : '/' + (!!~['index', 'home', 'main'].indexOf(__route_root) ? '' : __route_root) + (path ? '/' + path : ''),
|
||||
fn : func
|
||||
});
|
||||
};
|
||||
path: '/' + (!!~['index', 'home', 'main'].indexOf(__route_root) ? '' : __route_root) + (path ? '/' + path : ''), // eslint-disable-line no-extra-boolean-cast, no-undef
|
||||
fn: func
|
||||
})
|
||||
}
|
||||
|
||||
var _cwd = path.resolve(__dirname, '../../', 'web/routes');
|
||||
var _cwd = path.resolve(__dirname, '../../', 'web/routes')
|
||||
// initialize.
|
||||
module.exports = function(server){
|
||||
fs.readdirSync(_cwd).forEach(function(f){
|
||||
if (path.extname(f) != '.js') {
|
||||
return;
|
||||
module.exports = function (server) {
|
||||
fs.readdirSync(_cwd).forEach(function (f) {
|
||||
if (path.extname(f) !== '.js') {
|
||||
return
|
||||
}
|
||||
global.__route_root = path.basename(f, '.js');
|
||||
require(path.resolve(_cwd, f));
|
||||
delete global.__route_root;
|
||||
});
|
||||
routes.forEach(function(route){
|
||||
route.path = route.path.replace(/\/+/g, '/');
|
||||
server[route.method](route.path, route.fn);
|
||||
});
|
||||
};
|
||||
global.__route_root = path.basename(f, '.js')
|
||||
require(path.resolve(_cwd, f))
|
||||
delete global.__route_root
|
||||
})
|
||||
routes.forEach(function (route) {
|
||||
route.path = route.path.replace(/\/+/g, '/')
|
||||
server[route.method](route.path, route.fn)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,9 +56,12 @@
|
|||
"standard": "*"
|
||||
},
|
||||
"standard": {
|
||||
"ignore": [],
|
||||
"ignore": [
|
||||
"web/public/"
|
||||
],
|
||||
"globals": [
|
||||
"action"
|
||||
"action",
|
||||
"__route_root"
|
||||
]
|
||||
},
|
||||
"homepage": "https://github.com/Tjatse/pm2-gui"
|
||||
|
|
|
|||
20
pm2-gui.ini
20
pm2-gui.ini
|
|
@ -11,9 +11,13 @@ refresh = 5000
|
|||
;
|
||||
port = 8088
|
||||
;
|
||||
; A value indicates whether run the pm2-gui damonized or not.
|
||||
; A value indicates whether or not run the pm2-gui damonized.
|
||||
;
|
||||
daemonize = true
|
||||
;
|
||||
; A value indicates whether or not the action buttons (i.e. `restart`, `stop all`...) should be displayed on web page.
|
||||
;
|
||||
readonly = true
|
||||
|
||||
[log]
|
||||
;
|
||||
|
|
@ -21,11 +25,11 @@ daemonize = true
|
|||
;
|
||||
dir = ./logs
|
||||
;
|
||||
; A value indicates whether display the [INFO], [ERROR].. prefixes before log message or not.
|
||||
; A value indicates whether or not display the [INFO], [ERROR].. prefixes before log message.
|
||||
;
|
||||
prefix = true
|
||||
;
|
||||
; A value indicates whether display the local date string before log message or not.
|
||||
; A value indicates whether or not display the local date string before log message.
|
||||
;
|
||||
date = false
|
||||
;
|
||||
|
|
@ -55,4 +59,12 @@ authorization = AuTh
|
|||
; pm2@171 = AuTh@https://192.168.1.171:9002/sockserv
|
||||
; pm2@172 = 192.168.1.172:9001
|
||||
; pm2@173 = 192.168.1.173:9000
|
||||
;
|
||||
;
|
||||
spider_other = AuTh@192.168.100.79:8088
|
||||
spider_bbs = AuTh@192.168.100.78:8088
|
||||
spider_web = AuTh@192.168.100.77:8088
|
||||
level_web = AuTh@192.168.100.128:9009
|
||||
leve_bbs = AuTh@192.168.100.129:8088
|
||||
level_other = AuTh@192.168.100.130:8088
|
||||
spider_visionow = AuTh@192.168.100.138:8088
|
||||
spider_hst = AuTh@192.168.100.70:8088
|
||||
269
pm2-gui.js
269
pm2-gui.js
|
|
@ -1,232 +1,231 @@
|
|||
var chalk = require('chalk'),
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
_ = require('lodash'),
|
||||
socketIO = require('socket.io'),
|
||||
inquirer = require("inquirer"),
|
||||
conf = require('./lib/util/conf'),
|
||||
Monitor = require('./lib/monitor'),
|
||||
Log = require('./lib/util/log'),
|
||||
Web = require('./web/index'),
|
||||
layout = require('./lib/blessed-widget/layout');
|
||||
var chalk = require('chalk')
|
||||
var path = require('path')
|
||||
var fs = require('fs')
|
||||
var _ = require('lodash')
|
||||
var socketIO = require('socket.io')
|
||||
var inquirer = require('inquirer')
|
||||
var Monitor = require('./lib/monitor')
|
||||
var Log = require('./lib/util/log')
|
||||
var Web = require('./web/index')
|
||||
var layout = require('./lib/blessed-widget/layout')
|
||||
|
||||
if (path.basename(process.mainModule.filename, '.js') == 'pm2-gui') {
|
||||
var cmd, file;
|
||||
if (path.basename(process.mainModule.filename, '.js') === 'pm2-gui') {
|
||||
var cmd, file
|
||||
if (process.argv.length > 2) {
|
||||
cmd = process.argv[2];
|
||||
cmd = process.argv[2]
|
||||
}
|
||||
if (process.argv.length > 3) {
|
||||
file = process.argv[3];
|
||||
file = process.argv[3]
|
||||
}
|
||||
cmd = cmd || 'start';
|
||||
cmd = cmd || 'start'
|
||||
|
||||
switch (cmd) {
|
||||
case 'start':
|
||||
startWebServer(file);
|
||||
break;
|
||||
case 'agent':
|
||||
startAgent(file);
|
||||
break;
|
||||
case 'mon':
|
||||
dashboard(file);
|
||||
break;
|
||||
default:
|
||||
Log({
|
||||
level: 0,
|
||||
prefix: true
|
||||
});
|
||||
console.error('Command', cmd, 'is not supported!')
|
||||
break;
|
||||
case 'start':
|
||||
startWebServer(file)
|
||||
break
|
||||
case 'agent':
|
||||
startAgent(file)
|
||||
break
|
||||
case 'mon':
|
||||
dashboard(file)
|
||||
break
|
||||
default:
|
||||
Log({
|
||||
level: 0,
|
||||
prefix: true
|
||||
})
|
||||
console.error('Command', cmd, 'is not supported!')
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
exports.startWebServer = startWebServer;
|
||||
exports.startAgent = startAgent;
|
||||
exports.dashboard = dashboard;
|
||||
exports.exitGraceful = exitGraceful;
|
||||
exports.startWebServer = startWebServer
|
||||
exports.startAgent = startAgent
|
||||
exports.dashboard = dashboard
|
||||
exports.exitGraceful = exitGraceful
|
||||
|
||||
function startWebServer(confFile) {
|
||||
function startWebServer (confFile) {
|
||||
var monitor = slave({
|
||||
confFile: confFile
|
||||
}),
|
||||
options = monitor.options;
|
||||
confFile: confFile
|
||||
})
|
||||
var options = monitor.options
|
||||
|
||||
options.port = options.port || 8088;
|
||||
options.port = options.port || 8088
|
||||
var server = Web({
|
||||
middleware: function (req, res, next) {
|
||||
req._config = options;
|
||||
next();
|
||||
req._config = options
|
||||
next()
|
||||
},
|
||||
port: options.port
|
||||
});
|
||||
})
|
||||
|
||||
monitor.sockio = socketIO(server, {
|
||||
origins: options.origins || '*:*'
|
||||
});
|
||||
monitor.run();
|
||||
console.info('Web server is listening on 127.0.0.1:' + options.port);
|
||||
})
|
||||
monitor.run()
|
||||
console.info('Web server is listening on 127.0.0.1:' + options.port)
|
||||
}
|
||||
|
||||
function startAgent(confFile) {
|
||||
function startAgent (confFile) {
|
||||
var monitor = slave({
|
||||
confFile: confFile
|
||||
});
|
||||
})
|
||||
|
||||
var options = monitor.options;
|
||||
options.agent = options.agent || {};
|
||||
var options = monitor.options
|
||||
options.agent = options.agent || {}
|
||||
if (options.agent.offline) {
|
||||
console.error('Agent is offline, can not start it.');
|
||||
return process.exit(0);
|
||||
console.error('Agent is offline, can not start it.')
|
||||
return process.exit(0)
|
||||
}
|
||||
options.port = options.port || 8088;
|
||||
var sockio = socketIO();
|
||||
options.port = options.port || 8088
|
||||
var sockio = socketIO()
|
||||
sockio.listen(options.port, {
|
||||
origins: options.origins || '*:*'
|
||||
});
|
||||
monitor.sockio = sockio;
|
||||
monitor.run();
|
||||
console.info('Socket.io server is listening on 0.0.0.0:' + options.port);
|
||||
})
|
||||
monitor.sockio = sockio
|
||||
monitor.run()
|
||||
console.info('Socket.io server is listening on 0.0.0.0:' + options.port)
|
||||
}
|
||||
|
||||
function dashboard(confFile) {
|
||||
// restore cursor;
|
||||
function dashboard (confFile) {
|
||||
// restore cursor
|
||||
process.on('exit', function () {
|
||||
process.stdout.write('\u001b[?25h');
|
||||
});
|
||||
process.stdout.write('\u001b[?25h')
|
||||
})
|
||||
var monitor = slave({
|
||||
confFile: confFile
|
||||
}),
|
||||
options = _.clone(monitor.options),
|
||||
q = Monitor.available(options);
|
||||
confFile: confFile
|
||||
})
|
||||
var options = _.clone(monitor.options)
|
||||
var q = Monitor.available(options)
|
||||
|
||||
if (!q) {
|
||||
console.error('No agent is online, can not start it.');
|
||||
return process.exit(0);
|
||||
console.error('No agent is online, can not start it.')
|
||||
return process.exit(0)
|
||||
}
|
||||
var ql = q.choices.length;
|
||||
if (ql == 1) {
|
||||
if (q.choices[0].short != 'localhost') {
|
||||
var ql = q.choices.length
|
||||
if (ql === 1) {
|
||||
if (q.choices[0].short !== 'localhost') {
|
||||
console.info('There is just one remoting server online, try to connect it.')
|
||||
}
|
||||
return _connectToDashboard(monitor, options, Monitor.parseConnectionString(q.choices[0].value));
|
||||
return _connectToDashboard(monitor, options, Monitor.parseConnectionString(q.choices[0].value))
|
||||
}
|
||||
if (!options.agent || !options.agent.offline) {
|
||||
q.choices.splice(ql - 1, 0, new inquirer.Separator());
|
||||
q.choices.splice(ql - 1, 0, new inquirer.Separator())
|
||||
}
|
||||
|
||||
console.info('Remoting servers are online, choose one you are intrested in.')
|
||||
console.log('');
|
||||
console.log('')
|
||||
|
||||
inquirer.prompt(q, function (answers) {
|
||||
console.log('');
|
||||
_connectToDashboard(monitor, options, Monitor.parseConnectionString(answers.socket_server));
|
||||
});
|
||||
console.log('')
|
||||
_connectToDashboard(monitor, options, Monitor.parseConnectionString(answers.socket_server))
|
||||
})
|
||||
}
|
||||
|
||||
function exitGraceful(code, signal) {
|
||||
code = code || 0;
|
||||
if (signal != '-f') {
|
||||
console.debug('Slave has exited, code: ' + code + ', signal: ' + (signal || 'NULL'));
|
||||
function exitGraceful (code, signal) {
|
||||
code = code || 0
|
||||
if (signal !== '-f') {
|
||||
console.debug('Slave has exited, code: ' + code + ', signal: ' + (signal || 'NULL'))
|
||||
}
|
||||
var fds = 0;
|
||||
var fds = 0
|
||||
|
||||
function tryToExit() {
|
||||
function tryToExit () {
|
||||
if ((fds & 1) && (fds & 2)) {
|
||||
process.exit(code);
|
||||
process.exit(code)
|
||||
}
|
||||
}
|
||||
|
||||
[process.stdout, process.stderr].forEach(function (std) {
|
||||
var fd = std.fd;
|
||||
var fd = std.fd
|
||||
if (!std.bufferSize) {
|
||||
fds = fds | fd;
|
||||
fds = fds | fd
|
||||
} else {
|
||||
std.write && std.write('', function () {
|
||||
fds = fds | fd;
|
||||
tryToExit();
|
||||
});
|
||||
fds = fds | fd
|
||||
tryToExit()
|
||||
})
|
||||
}
|
||||
});
|
||||
tryToExit();
|
||||
})
|
||||
tryToExit()
|
||||
}
|
||||
|
||||
function slave(options) {
|
||||
process.title = 'pm2-gui slave';
|
||||
options = options || {};
|
||||
var confFile = options.confFile;
|
||||
function slave (options) {
|
||||
process.title = 'pm2-gui slave'
|
||||
options = options || {}
|
||||
var confFile = options.confFile
|
||||
if (!confFile) {
|
||||
confFile = path.resolve(__dirname, './pm2-gui.ini');
|
||||
confFile = path.resolve(__dirname, './pm2-gui.ini')
|
||||
|
||||
if (!fs.existsSync(confFile)) {
|
||||
console.error(chalk.bold(confFile), 'does not exist!');
|
||||
return process.exit(0);
|
||||
console.error(chalk.bold(confFile), 'does not exist!')
|
||||
return process.exit(0)
|
||||
}
|
||||
}
|
||||
var monitor = Monitor({
|
||||
confFile: confFile
|
||||
});
|
||||
})
|
||||
|
||||
Log(monitor.options.log);
|
||||
Log(monitor.options.log)
|
||||
|
||||
console.log(chalk.cyan(
|
||||
'\n' +
|
||||
'█▀▀█ █▀▄▀█ █▀█ ░░ ▒█▀▀█ ▒█░▒█ ▀█▀\n' +
|
||||
'█░░█ █░▀░█ ░▄▀ ▀▀ ▒█░▄▄ ▒█░▒█ ▒█░\n' +
|
||||
'█▀▀▀ ▀░░░▀ █▄▄ ░░ ▒█▄▄█ ░▀▄▄▀ ▄█▄\n'));
|
||||
'█▀▀▀ ▀░░░▀ █▄▄ ░░ ▒█▄▄█ ░▀▄▄▀ ▄█▄\n'))
|
||||
|
||||
process.on('SIGTERM', shutdown);
|
||||
process.on('SIGINT', shutdown);
|
||||
process.on('SIGHUP', restart);
|
||||
process.on('uncaughtException', caughtException);
|
||||
process.on('SIGTERM', shutdown)
|
||||
process.on('SIGINT', shutdown)
|
||||
process.on('SIGHUP', restart)
|
||||
process.on('uncaughtException', caughtException)
|
||||
process.on('exit', exitGraceful)
|
||||
|
||||
function shutdown(code, signal) {
|
||||
console.info('Shutting down....');
|
||||
monitor.quit();
|
||||
console.info('Shutdown complete!');
|
||||
exitGraceful(code, '-f');
|
||||
function shutdown (code, signal) {
|
||||
console.info('Shutting down....')
|
||||
monitor.quit()
|
||||
console.info('Shutdown complete!')
|
||||
exitGraceful(code, '-f')
|
||||
}
|
||||
|
||||
function restart() {
|
||||
function restart () {
|
||||
if (process.send) {
|
||||
process.send({
|
||||
action: 'restart'
|
||||
});
|
||||
})
|
||||
} else {
|
||||
console.error('No IPC found, could not restart monitor, shutting down.');
|
||||
shutdown(1);
|
||||
console.error('No IPC found, could not restart monitor, shutting down.')
|
||||
shutdown(1)
|
||||
}
|
||||
}
|
||||
|
||||
function caughtException(err) {
|
||||
console.error(err.stack);
|
||||
shutdown(1);
|
||||
function caughtException (err) {
|
||||
console.error(err.stack)
|
||||
shutdown(1)
|
||||
}
|
||||
|
||||
return monitor;
|
||||
return monitor
|
||||
}
|
||||
|
||||
function _connectToDashboard(monitor, options, connection) {
|
||||
connection = _.extend({}, options, connection);
|
||||
if (!!~['127.0.0.1', '0.0.0.0', 'localhost'].indexOf(connection.hostname)) {
|
||||
function _connectToDashboard (monitor, options, connection) {
|
||||
connection = _.extend({}, options, connection)
|
||||
if (!!~['127.0.0.1', '0.0.0.0', 'localhost'].indexOf(connection.hostname)) { // eslint-disable-line no-extra-boolean-cast
|
||||
return monitor.connect(connection, function (socket) {
|
||||
console.info('Agent is online, try to connect it in dashboard directly.');
|
||||
layout(connection).render(monitor);
|
||||
console.info('Agent is online, try to connect it in dashboard directly.')
|
||||
layout(connection).render(monitor)
|
||||
}, function (err, socket) {
|
||||
if (err == 'unauthorized') {
|
||||
console.error('There was an error with the authentication:', err);
|
||||
return process.exit(0);
|
||||
if (err === 'unauthorized') {
|
||||
console.error('There was an error with the authentication:', err)
|
||||
return process.exit(0)
|
||||
}
|
||||
console.warn('Agent is offline, try to start it.');
|
||||
var sockio = socketIO();
|
||||
console.warn('Agent is offline, try to start it.')
|
||||
var sockio = socketIO()
|
||||
sockio.listen(connection.port, {
|
||||
origins: options.origins || '*:*'
|
||||
});
|
||||
monitor.sockio = sockio;
|
||||
monitor.run();
|
||||
layout(connection).render(monitor);
|
||||
});
|
||||
})
|
||||
monitor.sockio = sockio
|
||||
monitor.run()
|
||||
layout(connection).render(monitor)
|
||||
})
|
||||
}
|
||||
|
||||
layout(connection).render(monitor);
|
||||
layout(connection).render(monitor)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
var chalk = require('chalk');
|
||||
var chalk = require('chalk')
|
||||
|
||||
console.log('This is', chalk.bold.red('red'));
|
||||
console.log('This is', chalk.dim.green('green'));
|
||||
console.log('This is', chalk.bold.green('green'));
|
||||
console.log('This is', chalk.bold.italic.yellow('yellow'));
|
||||
console.log('This is', chalk.bold.strikethrough.blue('blue'));
|
||||
console.log('This is', chalk.bold.underline.magenta('magenta'));
|
||||
console.log('This is', chalk.bold.cyan('cyan'));
|
||||
console.log('This is', chalk.bold.grey('grey'));
|
||||
console.log('This is', chalk.bold.red('red'))
|
||||
console.log('This is', chalk.dim.green('green'))
|
||||
console.log('This is', chalk.bold.green('green'))
|
||||
console.log('This is', chalk.bold.italic.yellow('yellow'))
|
||||
console.log('This is', chalk.bold.strikethrough.blue('blue'))
|
||||
console.log('This is', chalk.bold.underline.magenta('magenta'))
|
||||
console.log('This is', chalk.bold.cyan('cyan'))
|
||||
console.log('This is', chalk.bold.grey('grey'))
|
||||
|
||||
setTimeout(function(){
|
||||
|
||||
}, 3000000);
|
||||
setTimeout(function () {}, 3000000)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
console.log('ok at', Date.now());
|
||||
console.log('ok at', Date.now())
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
function fib(n){
|
||||
if (n == 1) return 1;
|
||||
if (n == 0) return 0;
|
||||
function fib (n) {
|
||||
if (n === 1) return 1
|
||||
if (n === 0) return 0
|
||||
if (n > 1) return fib(n - 2) + fib(n - 1)
|
||||
}
|
||||
|
||||
function fi(){
|
||||
console.log('fibonacci...');
|
||||
var f = fib((parseInt(Math.random() * 10000) + 30) % 42);
|
||||
console.log('is:', f);
|
||||
setTimeout(fi, 1000);
|
||||
function fi () {
|
||||
console.log('fibonacci...')
|
||||
var f = fib((parseInt(Math.random() * 10000) + 30) % 42)
|
||||
console.log('is:', f)
|
||||
setTimeout(fi, 1000)
|
||||
}
|
||||
fi();
|
||||
fi()
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
setInterval(function(){
|
||||
console.log(Math.random());
|
||||
}, 3000);
|
||||
setInterval(function () {
|
||||
console.log(Math.random())
|
||||
}, 3000)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
console.log('App started.');
|
||||
setTimeout(function(){
|
||||
throw new Error('uncaughtException has been thrown.');
|
||||
}, 15000);
|
||||
console.log('App started.')
|
||||
setTimeout(function () {
|
||||
throw new Error('uncaughtException has been thrown.')
|
||||
}, 15000)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
var chalk = require('chalk');
|
||||
setInterval(function(){
|
||||
console.log(chalk.bold.green('Tick'), Date.now());
|
||||
}, 1000);
|
||||
var chalk = require('chalk')
|
||||
setInterval(function () {
|
||||
console.log(chalk.bold.green('Tick'), Date.now())
|
||||
}, 1000)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
var chalk = require('chalk');
|
||||
var chalk = require('chalk')
|
||||
|
||||
console.log(chalk.magenta('Tock every 5 seconds.'));
|
||||
setInterval(function(){
|
||||
console.log(chalk.bold.red.underline('Tock'), Date.now());
|
||||
}, 5000);
|
||||
console.log(chalk.magenta('Tock every 5 seconds.'))
|
||||
setInterval(function () {
|
||||
console.log(chalk.bold.red.underline('Tock'), Date.now())
|
||||
}, 5000)
|
||||
|
|
|
|||
35
web/index.js
35
web/index.js
|
|
@ -1,28 +1,27 @@
|
|||
var express = require('express'),
|
||||
session = require('express-session'),
|
||||
path = require('path'),
|
||||
http = require('http'),
|
||||
Monitor = require('../lib/monitor'),
|
||||
router = require('../lib/util/router');
|
||||
var express = require('express')
|
||||
var session = require('express-session')
|
||||
var path = require('path')
|
||||
var http = require('http')
|
||||
var router = require('../lib/util/router')
|
||||
|
||||
module.exports = runServer;
|
||||
module.exports = runServer
|
||||
|
||||
function runServer(options) {
|
||||
var app = express();
|
||||
app.set('view engine', 'jade');
|
||||
app.set('views', path.join(__dirname, 'templates/views'));
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
function runServer (options) {
|
||||
var app = express()
|
||||
app.set('view engine', 'jade')
|
||||
app.set('views', path.join(__dirname, 'templates/views'))
|
||||
app.use(express.static(path.join(__dirname, 'public')))
|
||||
app.use(session({
|
||||
secret: 'pm2@gui',
|
||||
resave: false,
|
||||
saveUninitialized: true
|
||||
}));
|
||||
}))
|
||||
if (options.middleware) {
|
||||
app.use(options.middleware);
|
||||
app.use(options.middleware)
|
||||
}
|
||||
router(app);
|
||||
router(app)
|
||||
|
||||
var server = http.Server(app);
|
||||
server.listen(options.port);
|
||||
return server;
|
||||
var server = http.Server(app)
|
||||
server.listen(options.port)
|
||||
return server
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,58 +1,57 @@
|
|||
var Monitor = require('../../lib/monitor'),
|
||||
_ = require('lodash');
|
||||
var _ = require('lodash')
|
||||
var Monitor = require('../../lib/monitor')
|
||||
|
||||
// Authorization
|
||||
action(function auth(req, res) {
|
||||
action(function auth (req, res) {
|
||||
if (!req._config.agent || (req._config.agent.authorization === req.session['authorization'])) {
|
||||
return res.redirect('/');
|
||||
return res.redirect('/')
|
||||
}
|
||||
res.render('auth', {
|
||||
title: 'Authorization'
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
// Index
|
||||
action(function (req, res) {
|
||||
var auth;
|
||||
if (req._config.agent && ((auth = req._config.agent.authorization) !== req.session['authorization'])) {
|
||||
return res.redirect('/auth');
|
||||
if (req._config.agent && (req._config.agent.authorization !== req.session['authorization'])) {
|
||||
return res.redirect('/auth')
|
||||
}
|
||||
var options = _.clone(req._config),
|
||||
q = Monitor.available(_.extend(options, {
|
||||
blank: ' '
|
||||
})),
|
||||
connections = [];
|
||||
var options = _.clone(req._config)
|
||||
var q = Monitor.available(_.extend(options, {
|
||||
blank: ' '
|
||||
}))
|
||||
var connections = []
|
||||
|
||||
q.choices.forEach(function (c) {
|
||||
c.value = Monitor.toConnectionString(Monitor.parseConnectionString(c.value));
|
||||
connections.push(c);
|
||||
});
|
||||
c.value = Monitor.toConnectionString(Monitor.parseConnectionString(c.value))
|
||||
connections.push(c)
|
||||
})
|
||||
res.render('index', {
|
||||
title: 'Monitor',
|
||||
connections: connections
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
// API
|
||||
action(function auth_api(req, res) {
|
||||
action(function auth_api (req, res) {
|
||||
if (!req._config.agent || !req._config.agent.authorization) {
|
||||
return res.json({
|
||||
error: 'Can not found agent[.authorization] config, no need to authorize!'
|
||||
});
|
||||
})
|
||||
}
|
||||
if (!req.query || !req.query.authorization) {
|
||||
return res.json({
|
||||
error: 'Authorization is required!'
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
if (req._config.agent && req.query.authorization === req._config.agent.authorization) {
|
||||
req.session['authorization'] = req.query.authorization;
|
||||
req.session['authorization'] = req.query.authorization
|
||||
return res.json({
|
||||
status: 200
|
||||
});
|
||||
})
|
||||
}
|
||||
return res.json({
|
||||
error: 'Failed, authorization is incorrect.'
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue