code style: standard

This commit is contained in:
Tjatse 2016-05-10 13:34:03 +08:00
parent 4827072994
commit 67bb3ec4c9
21 changed files with 1247 additions and 1303 deletions

View File

@ -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)
}

View File

@ -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
}

View File

@ -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);
}
});

View File

@ -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/, '&nbsp;');
return '<span>' + ansiHTML(line) + '</span>';
line = line.replace(/\s/, '&nbsp;')
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
View File

@ -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
}

View File

@ -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
};
};
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
})
}

View File

@ -56,9 +56,12 @@
"standard": "*"
},
"standard": {
"ignore": [],
"ignore": [
"web/public/"
],
"globals": [
"action"
"action",
"__route_root"
]
},
"homepage": "https://github.com/Tjatse/pm2-gui"

View File

@ -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

View File

@ -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)
}

View File

@ -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)

View File

@ -1 +1 @@
console.log('ok at', Date.now());
console.log('ok at', Date.now())

View File

@ -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()

View File

@ -1,3 +1,3 @@
setInterval(function(){
console.log(Math.random());
}, 3000);
setInterval(function () {
console.log(Math.random())
}, 3000)

View File

@ -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)

View File

@ -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)

10
test/fixtures/tock.js vendored
View File

@ -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)

View File

@ -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
}

View File

@ -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: '&nbsp;'
})),
connections = [];
var options = _.clone(req._config)
var q = Monitor.available(_.extend(options, {
blank: '&nbsp;'
}))
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.'
});
});
})
})