293 lines
9.9 KiB
JavaScript
293 lines
9.9 KiB
JavaScript
// ENiGMA½
|
|
const { MenuModule } = require('./menu_module');
|
|
|
|
const { getActiveConnectionList } = require('./client_connections');
|
|
const StatLog = require('./stat_log');
|
|
const SysProps = require('./system_property');
|
|
const UserProps = require('./user_property');
|
|
const Log = require('./logger');
|
|
const Config = require('./config.js').get;
|
|
|
|
// deps
|
|
const async = require('async');
|
|
const _ = require('lodash');
|
|
const moment = require('moment');
|
|
const SysInfo = require('systeminformation');
|
|
const bunyan = require('bunyan');
|
|
|
|
exports.moduleInfo = {
|
|
name : 'WFC',
|
|
desc : 'Semi-Traditional Waiting For Caller',
|
|
author : 'NuSkooler',
|
|
};
|
|
|
|
const FormIds = {
|
|
main : 0,
|
|
};
|
|
|
|
const MciViewIds = {
|
|
main : {
|
|
nodeStatus : 1,
|
|
quickLogView : 2,
|
|
|
|
customRangeStart : 10,
|
|
}
|
|
};
|
|
|
|
// Secure + 2FA + root user + 'wfc' group.
|
|
const DefaultACS = 'SCAF2ID1GM[wfc]';
|
|
|
|
exports.getModule = class WaitingForCallerModule extends MenuModule {
|
|
constructor(options) {
|
|
super(options);
|
|
|
|
this.config = Object.assign({}, _.get(options, 'menuConfig.config'), { extraArgs : options.extraArgs });
|
|
|
|
this.config.acs = this.config.acs || DefaultACS;
|
|
if (!this.config.acs.includes('SC')) {
|
|
this.config.acs = 'SC' + this.config.acs; // secure connection at the very least
|
|
}
|
|
}
|
|
|
|
mciReady(mciData, cb) {
|
|
super.mciReady(mciData, err => {
|
|
if (err) {
|
|
return cb(err);
|
|
}
|
|
|
|
async.series(
|
|
[
|
|
(callback) => {
|
|
return this.prepViewController('main', FormIds.main, mciData.menu, callback);
|
|
},
|
|
(callback) => {
|
|
// const requiredCodes = [
|
|
// ];
|
|
// return this.validateMCIByViewIds('main', requiredCodes, callback);
|
|
return callback(null);
|
|
},
|
|
(callback) => {
|
|
const quickLogView = this.viewControllers.main.getView(MciViewIds.main.quickLogView);
|
|
if (!quickLogView) {
|
|
return callback(null);
|
|
}
|
|
|
|
const logLevel = this.config.quickLogLevel || // WFC specific
|
|
_.get(Config(), 'logging.rotatingFile.level') || // ...or system setting
|
|
'info'; // ...or default to info
|
|
|
|
this.logRingBuffer = new bunyan.RingBuffer({ limit : quickLogView.dimens.height || 24 });
|
|
Log.log.addStream({
|
|
name : 'wfc-ringbuffer',
|
|
type : 'raw',
|
|
level : logLevel,
|
|
stream : this.logRingBuffer
|
|
});
|
|
|
|
return callback(null);
|
|
},
|
|
(callback) => {
|
|
return this._refreshAll(callback);
|
|
}
|
|
],
|
|
err => {
|
|
if (!err) {
|
|
this._startRefreshing();
|
|
}
|
|
return cb(err);
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
leave() {
|
|
_.remove(Log.log.streams, stream => {
|
|
return stream.name === 'wfc-ringbuffer';
|
|
});
|
|
|
|
this._stopRefreshing();
|
|
|
|
super.leave();
|
|
}
|
|
|
|
_startRefreshing() {
|
|
this.mainRefreshTimer = setInterval( () => {
|
|
this._refreshAll();
|
|
}, 5000);
|
|
}
|
|
|
|
_stopRefreshing() {
|
|
if (this.mainRefreshTimer) {
|
|
clearInterval(this.mainRefreshTimer);
|
|
delete this.mainRefreshTimer;
|
|
}
|
|
}
|
|
|
|
_refreshAll(cb) {
|
|
async.series(
|
|
[
|
|
(callback) => {
|
|
return this._refreshStats(callback);
|
|
},
|
|
(callback) => {
|
|
return this._refreshNodeStatus(callback);
|
|
},
|
|
(callback) => {
|
|
return this._refreshQuickLog(callback);
|
|
},
|
|
(callback) => {
|
|
this.updateCustomViewTextsWithFilter(
|
|
'main',
|
|
MciViewIds.main.customRangeStart,
|
|
this.stats
|
|
);
|
|
return callback(null);
|
|
}
|
|
],
|
|
err => {
|
|
if (cb) {
|
|
return cb(err);
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
_refreshStats(cb) {
|
|
const fileAreaStats = StatLog.getSystemStat(SysProps.FileBaseAreaStats);
|
|
const sysMemStats = StatLog.getSystemStat(SysProps.SystemMemoryStats);
|
|
const sysLoadStats = StatLog.getSystemStat(SysProps.SystemLoadStats);
|
|
const lastLoginStats = StatLog.getSystemStat(SysProps.LastLogin);
|
|
|
|
const now = moment();
|
|
|
|
// Some stats we can just fill right away
|
|
this.stats = {
|
|
// Date/Time
|
|
nowDate : now.format(this.getDateFormat()),
|
|
nowTime : now.format(this.getTimeFormat()),
|
|
now : now.format(this._dateTimeFormat('now')),
|
|
|
|
// Current process (our Node.js service)
|
|
processUptimeSeconds : process.uptime(),
|
|
|
|
// Totals
|
|
totalCalls : StatLog.getSystemStatNum(SysProps.LoginCount),
|
|
totalPosts : StatLog.getSystemStatNum(SysProps.MessageTotalCount),
|
|
//totalUsers :
|
|
totalFiles : fileAreaStats.totalFiles || 0,
|
|
totalFileBytes : fileAreaStats.totalFileBytes || 0,
|
|
|
|
// totalUploads :
|
|
// totalUploadBytes :
|
|
// totalDownloads :
|
|
// totalDownloadBytes :
|
|
|
|
// Today's Stats
|
|
callsToday : StatLog.getSystemStatNum(SysProps.LoginsToday),
|
|
postsToday : StatLog.getSystemStatNum(SysProps.MessagesToday),
|
|
uploadsToday : StatLog.getSystemStatNum(SysProps.FileUlTodayCount),
|
|
uploadBytesToday : StatLog.getSystemStatNum(SysProps.FileUlTodayBytes),
|
|
downloadsToday : StatLog.getSystemStatNum(SysProps.FileDlTodayCount),
|
|
downloadsBytesToday : StatLog.getSystemStatNum(SysProps.FileDlTodayBytes),
|
|
|
|
// Current
|
|
currentUserName : this.client.user.username,
|
|
currentUserRealName : this.client.user.getProperty(UserProps.RealName) || this.client.user.username,
|
|
lastLoginUserName : lastLoginStats.userName,
|
|
lastLoginRealName : lastLoginStats.realName,
|
|
lastLoginDate : moment(lastLoginStats.timestamp).format(this.getDateFormat()),
|
|
lastLoginTime : moment(lastLoginStats.timestamp).format(this.getTimeFormat()),
|
|
lastLogin : moment(lastLoginStats.timestamp).format(this._dateTimeFormat('lastLogin')),
|
|
|
|
totalMemoryBytes : sysMemStats.totalBytes,
|
|
freeMemoryBytes : sysMemStats.freeBytes,
|
|
systemAvgLoad : sysLoadStats.average,
|
|
systemCurrentLoad : sysLoadStats.current,
|
|
};
|
|
|
|
return cb(null);
|
|
}
|
|
|
|
_refreshNodeStatus(cb) {
|
|
const nodeStatusView = this.getView('main', MciViewIds.main.nodeStatus);
|
|
if (!nodeStatusView) {
|
|
return cb(null);
|
|
}
|
|
|
|
const nodeStatusItems = getActiveConnectionList(false).slice(0, nodeStatusView.dimens.height).map(ac => {
|
|
// Handle pre-authenticated
|
|
if (!ac.authenticated) {
|
|
ac.text = ac.userName = 'Pre Auth';
|
|
ac.action = 'Logging In';
|
|
}
|
|
|
|
return Object.assign(ac, {
|
|
timeOn : _.upperFirst((ac.timeOn || moment.duration(0)).humanize()), // make friendly
|
|
});
|
|
});
|
|
|
|
nodeStatusView.setItems(nodeStatusItems);
|
|
nodeStatusView.redraw();
|
|
|
|
return cb(null);
|
|
}
|
|
|
|
_refreshQuickLog(cb) {
|
|
const quickLogView = this.viewControllers.main.getView(MciViewIds.main.quickLogView);
|
|
if (!quickLogView) {
|
|
return cb(null);
|
|
}
|
|
|
|
const records = this.logRingBuffer.records;
|
|
if (records.length === 0) {
|
|
return cb(null);
|
|
}
|
|
|
|
const hasChanged = this.lastLogTime !== records[records.length - 1].time;
|
|
this.lastLogTime = records[records.length - 1].time;
|
|
|
|
if (!hasChanged) {
|
|
return cb(null);
|
|
}
|
|
|
|
const quickLogTimestampFormat = this.config.quickLogTimestampFormat ||
|
|
this.getDateTimeFormat('short');
|
|
|
|
const levelIndicators = this.config.quickLogLevelIndicators ||
|
|
{
|
|
trace : 'T',
|
|
debug : 'D',
|
|
info : 'I',
|
|
warn : 'W',
|
|
error : 'E',
|
|
fatal : 'F',
|
|
};
|
|
|
|
const makeLevelIndicator = (level) => {
|
|
return levelIndicators[bunyan.nameFromLevel[level]] || '?';
|
|
};
|
|
|
|
const logItems = records.map(rec => {
|
|
return {
|
|
timestamp : moment(rec.time).format(quickLogTimestampFormat),
|
|
level : rec.level,
|
|
levelIndicator : makeLevelIndicator(rec.level),
|
|
nodeId : rec.nodeId,
|
|
sessionId : rec.sessionId || '',
|
|
message : rec.msg,
|
|
};
|
|
});
|
|
|
|
quickLogView.setItems(logItems);
|
|
quickLogView.redraw();
|
|
|
|
return cb(null);
|
|
}
|
|
|
|
_dateTimeFormat(element) {
|
|
const format = this.config[`${element}DateTimeFormat`];
|
|
return format || this.getDateFormat();
|
|
}
|
|
};
|
|
|