Re-apply some Prettier formatting after merge

This commit is contained in:
Bryan Ashby 2022-06-12 14:12:03 -06:00
parent c93b8cda81
commit 9172fdda9d
No known key found for this signature in database
GPG Key ID: C2C1B501E4EFD994
37 changed files with 978 additions and 712 deletions

View File

@ -108,14 +108,22 @@ exports.getModule = class AbracadabraModule extends MenuModule {
name: self.config.name, name: self.config.name,
activeCount: activeDoorNodeInstances[self.config.name], activeCount: activeDoorNodeInstances[self.config.name],
}, },
`Too many active instances of door "${self.config.name}"`); `Too many active instances of door "${self.config.name}"`
);
if(_.isString(self.config.tooManyArt)) { if (_.isString(self.config.tooManyArt)) {
theme.displayThemeArt( { client : self.client, name : self.config.tooManyArt }, function displayed() { theme.displayThemeArt(
self.pausePrompt( () => { { client: self.client, name: self.config.tooManyArt },
return callback(Errors.AccessDenied('Too many active instances')); function displayed() {
}); self.pausePrompt(() => {
}); return callback(
Errors.AccessDenied(
'Too many active instances'
)
);
});
}
);
} else { } else {
self.client.term.write( self.client.term.write(
'\nToo many active instances. Try again later.\n' '\nToo many active instances. Try again later.\n'
@ -171,14 +179,14 @@ exports.getModule = class AbracadabraModule extends MenuModule {
this.client.term.write(ansi.resetScreen()); this.client.term.write(ansi.resetScreen());
const exeInfo = { const exeInfo = {
name : this.config.name, name: this.config.name,
cmd : this.config.cmd, cmd: this.config.cmd,
cwd : this.config.cwd || paths.dirname(this.config.cmd), cwd: this.config.cwd || paths.dirname(this.config.cmd),
args : this.config.args, args: this.config.args,
io : this.config.io || 'stdio', io: this.config.io || 'stdio',
encoding : this.config.encoding || 'cp437', encoding: this.config.encoding || 'cp437',
node : this.client.node, node: this.client.node,
env : this.config.env, env: this.config.env,
}; };
if (this.dropFile) { if (this.dropFile) {

View File

@ -6,14 +6,14 @@
//SegfaultHandler.registerHandler('enigma-bbs-segfault.log'); //SegfaultHandler.registerHandler('enigma-bbs-segfault.log');
// ENiGMA½ // ENiGMA½
const conf = require('./config.js'); const conf = require('./config.js');
const logger = require('./logger.js'); const logger = require('./logger.js');
const database = require('./database.js'); const database = require('./database.js');
const resolvePath = require('./misc_util.js').resolvePath; const resolvePath = require('./misc_util.js').resolvePath;
const UserProps = require('./user_property.js'); const UserProps = require('./user_property.js');
const SysProps = require('./system_property.js'); const SysProps = require('./system_property.js');
const SysLogKeys = require('./system_log.js'); const SysLogKeys = require('./system_log.js');
const UserLogNames = require('./user_log_name'); const UserLogNames = require('./user_log_name');
// deps // deps
const async = require('async'); const async = require('async');
@ -152,7 +152,9 @@ function shutdownSystem() {
[ [
function closeConnections(callback) { function closeConnections(callback) {
const ClientConns = require('./client_connections.js'); const ClientConns = require('./client_connections.js');
const activeConnections = ClientConns.getActiveConnections(ClientConns.AllConnections); const activeConnections = ClientConns.getActiveConnections(
ClientConns.AllConnections
);
let i = activeConnections.length; let i = activeConnections.length;
while (i--) { while (i--) {
const activeTerm = activeConnections[i].term; const activeTerm = activeConnections[i].term;
@ -327,68 +329,77 @@ function initialize(cb) {
const StatLog = require('./stat_log'); const StatLog = require('./stat_log');
const entries = [ const entries = [
[ UserLogNames.UlFiles, [ SysProps.FileUlTodayCount, 'count' ] ], [UserLogNames.UlFiles, [SysProps.FileUlTodayCount, 'count']],
[ UserLogNames.UlFileBytes, [ SysProps.FileUlTodayBytes, 'obj' ] ], [UserLogNames.UlFileBytes, [SysProps.FileUlTodayBytes, 'obj']],
[ UserLogNames.DlFiles, [ SysProps.FileDlTodayCount, 'count' ] ], [UserLogNames.DlFiles, [SysProps.FileDlTodayCount, 'count']],
[ UserLogNames.DlFileBytes, [ SysProps.FileDlTodayBytes, 'obj' ] ], [UserLogNames.DlFileBytes, [SysProps.FileDlTodayBytes, 'obj']],
[ UserLogNames.NewUser, [ SysProps.NewUsersTodayCount, 'count' ] ], [UserLogNames.NewUser, [SysProps.NewUsersTodayCount, 'count']],
]; ];
async.each(entries, (entry, nextEntry) => { async.each(
const [ logName, [sysPropName, resultType] ] = entry; entries,
(entry, nextEntry) => {
const [logName, [sysPropName, resultType]] = entry;
const filter = { const filter = {
logName, logName,
resultType, resultType,
date : moment(), date: moment(),
}; };
StatLog.findUserLogEntries(filter, (err, stat) => { StatLog.findUserLogEntries(filter, (err, stat) => {
if (!err) { if (!err) {
if (resultType === 'obj') { if (resultType === 'obj') {
stat = stat.reduce( (bytes, entry) => bytes + parseInt(entry.log_value) || 0, 0); stat = stat.reduce(
(bytes, entry) =>
bytes + parseInt(entry.log_value) || 0,
0
);
}
StatLog.setNonPersistentSystemStat(sysPropName, stat);
} }
return nextEntry(null);
StatLog.setNonPersistentSystemStat(sysPropName, stat); });
} },
return nextEntry(null); () => {
}); return callback(null);
}, }
() => { );
return callback(null);
});
}, },
function initLastLogin(callback) { function initLastLogin(callback) {
const StatLog = require('./stat_log'); const StatLog = require('./stat_log');
StatLog.getSystemLogEntries(SysLogKeys.UserLoginHistory, 'timestamp_desc', 1, (err, lastLogin) => { StatLog.getSystemLogEntries(
if (err) { SysLogKeys.UserLoginHistory,
return callback(null); 'timestamp_desc',
} 1,
(err, lastLogin) => {
if (err) {
return callback(null);
}
let loginObj; let loginObj;
try try {
{ loginObj = JSON.parse(lastLogin[0].log_value);
loginObj = JSON.parse(lastLogin[0].log_value); loginObj.timestamp = moment(lastLogin[0].timestamp);
loginObj.timestamp = moment(lastLogin[0].timestamp); } catch (e) {
} return callback(null);
catch (e) }
{
return callback(null);
}
// For live stats we want to resolve user ID -> name, etc. // For live stats we want to resolve user ID -> name, etc.
const User = require('./user'); const User = require('./user');
User.getUserInfo(loginObj.userId, (err, props) => { User.getUserInfo(loginObj.userId, (err, props) => {
const stat = Object.assign({}, props, loginObj); const stat = Object.assign({}, props, loginObj);
StatLog.setNonPersistentSystemStat(SysProps.LastLogin, stat); StatLog.setNonPersistentSystemStat(SysProps.LastLogin, stat);
return callback(null); return callback(null);
}); });
}); }
);
}, },
function initUserCount(callback) { function initUserCount(callback) {
const User = require('./user.js'); const User = require('./user.js');
User.getUserCount((err, count) => { User.getUserCount((err, count) => {
if(err) { if (err) {
return callback(err); return callback(err);
} }

View File

@ -592,12 +592,10 @@ Client.prototype.isLocal = function () {
return ['127.0.0.1', '::ffff:127.0.0.1'].includes(this.remoteAddress); return ['127.0.0.1', '::ffff:127.0.0.1'].includes(this.remoteAddress);
}; };
Client.prototype.friendlyRemoteAddress = function() { Client.prototype.friendlyRemoteAddress = function () {
// convert any :ffff: IPv4's to 32bit version // convert any :ffff: IPv4's to 32bit version
return this.remoteAddress return this.remoteAddress.replace(/^::ffff:/, '').replace(/^::1$/, 'localhost');
.replace(/^::ffff:/, '') };
.replace(/^::1$/, 'localhost');
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Default error handlers // Default error handlers

View File

@ -24,13 +24,23 @@ exports.clientConnections = clientConnections;
const AllConnections = { authUsersOnly: false, visibleOnly: false, availOnly: false }; const AllConnections = { authUsersOnly: false, visibleOnly: false, availOnly: false };
exports.AllConnections = AllConnections; exports.AllConnections = AllConnections;
const UserVisibleConnections = { authUsersOnly: false, visibleOnly: true, availOnly: false }; const UserVisibleConnections = {
authUsersOnly: false,
visibleOnly: true,
availOnly: false,
};
exports.UserVisibleConnections = UserVisibleConnections; exports.UserVisibleConnections = UserVisibleConnections;
const UserMessageableConnections = { authUsersOnly: true, visibleOnly: true, availOnly: true }; const UserMessageableConnections = {
authUsersOnly: true,
visibleOnly: true,
availOnly: true,
};
exports.UserMessageableConnections = UserMessageableConnections; exports.UserMessageableConnections = UserMessageableConnections;
function getActiveConnections(options = { authUsersOnly: true, visibleOnly: true, availOnly: false }) { function getActiveConnections(
options = { authUsersOnly: true, visibleOnly: true, availOnly: false }
) {
return clientConnections.filter(conn => { return clientConnections.filter(conn => {
if (options.authUsersOnly && !conn.user.isAuthenticated()) { if (options.authUsersOnly && !conn.user.isAuthenticated()) {
return false; return false;
@ -46,7 +56,9 @@ function getActiveConnections(options = { authUsersOnly: true, visibleOnly: true
}); });
} }
function getActiveConnectionList(options = { authUsersOnly: true, visibleOnly: true, availOnly: false }) { function getActiveConnectionList(
options = { authUsersOnly: true, visibleOnly: true, availOnly: false }
) {
const now = moment(); const now = moment();
return _.map(getActiveConnections(options), ac => { return _.map(getActiveConnections(options), ac => {
@ -54,33 +66,36 @@ function getActiveConnectionList(options = { authUsersOnly: true, visibleOnly: t
try { try {
// attempting to fetch a bad menu stack item can blow up/assert // attempting to fetch a bad menu stack item can blow up/assert
action = _.get(ac, 'currentMenuModule.menuConfig.desc', 'Unknown'); action = _.get(ac, 'currentMenuModule.menuConfig.desc', 'Unknown');
} catch(e) { } catch (e) {
action = 'Unknown'; action = 'Unknown';
} }
const entry = { const entry = {
node : ac.node, node: ac.node,
authenticated : ac.user.isAuthenticated(), authenticated: ac.user.isAuthenticated(),
userId : ac.user.userId, userId: ac.user.userId,
action : action, action: action,
serverName : ac.session.serverName, serverName: ac.session.serverName,
isSecure : ac.session.isSecure, isSecure: ac.session.isSecure,
isVisible : ac.user.isVisible(), isVisible: ac.user.isVisible(),
isAvailable : ac.user.isAvailable(), isAvailable: ac.user.isAvailable(),
}; };
// //
// There may be a connection, but not a logged in user as of yet // There may be a connection, but not a logged in user as of yet
// //
if(ac.user.isAuthenticated()) { if (ac.user.isAuthenticated()) {
entry.text = ac.user.username; entry.text = ac.user.username;
entry.userName = ac.user.username; entry.userName = ac.user.username;
entry.realName = ac.user.properties[UserProps.RealName]; entry.realName = ac.user.properties[UserProps.RealName];
entry.location = ac.user.properties[UserProps.Location]; entry.location = ac.user.properties[UserProps.Location];
entry.affils = entry.affiliation = ac.user.properties[UserProps.Affiliations]; entry.affils = entry.affiliation = ac.user.properties[UserProps.Affiliations];
const diff = now.diff(moment(ac.user.properties[UserProps.LastLoginTs]), 'minutes'); const diff = now.diff(
entry.timeOn = moment.duration(diff, 'minutes'); moment(ac.user.properties[UserProps.LastLoginTs]),
'minutes'
);
entry.timeOn = moment.duration(diff, 'minutes');
} }
return entry; return entry;
@ -115,10 +130,10 @@ function addNewClient(client, clientSock) {
client.log = logger.log.child({ nodeId, sessionId: client.session.uniqueId }); client.log = logger.log.child({ nodeId, sessionId: client.session.uniqueId });
const connInfo = { const connInfo = {
remoteAddress : remoteAddress, remoteAddress: remoteAddress,
freiendlyRemoteAddress: client.friendlyRemoteAddress(), freiendlyRemoteAddress: client.friendlyRemoteAddress(),
serverName : client.session.serverName, serverName: client.session.serverName,
isSecure : client.session.isSecure, isSecure: client.session.isSecure,
}; };
if (client.log.debug()) { if (client.log.debug()) {
@ -126,7 +141,10 @@ function addNewClient(client, clientSock) {
connInfo.family = clientSock.localFamily; connInfo.family = clientSock.localFamily;
} }
client.log.info(connInfo, `Client connected (${connInfo.serverName}/${connInfo.port})`); client.log.info(
connInfo,
`Client connected (${connInfo.serverName}/${connInfo.port})`
);
Events.emit(Events.getSystemEvents().ClientConnected, { Events.emit(Events.getSystemEvents().ClientConnected, {
client: client, client: client,
@ -170,9 +188,9 @@ function removeClient(client) {
} }
function getConnectionByUserId(userId) { function getConnectionByUserId(userId) {
return getActiveConnections(AllConnections).find( ac => userId === ac.user.userId ); return getActiveConnections(AllConnections).find(ac => userId === ac.user.userId);
} }
function getConnectionByNodeId(nodeId) { function getConnectionByNodeId(nodeId) {
return getActiveConnections(AllConnections).find( ac => nodeId == ac.node ); return getActiveConnections(AllConnections).find(ac => nodeId == ac.node);
} }

View File

@ -32,7 +32,10 @@ module.exports = class Door {
}); });
conn.once('error', err => { conn.once('error', err => {
this.client.log.warn( { error : err.message }, 'Door socket server connection'); this.client.log.warn(
{ error: err.message },
'Door socket server connection'
);
return this.restoreIo(conn); return this.restoreIo(conn);
}); });
@ -73,7 +76,7 @@ module.exports = class Door {
const args = exeInfo.args.map(arg => stringFormat(arg, formatObj)); const args = exeInfo.args.map(arg => stringFormat(arg, formatObj));
this.client.log.info( this.client.log.info(
{ cmd : exeInfo.cmd, args, io : this.io }, { cmd: exeInfo.cmd, args, io: this.io },
`Executing external door (${exeInfo.name})` `Executing external door (${exeInfo.name})`
); );

View File

@ -112,7 +112,10 @@ class ScheduledEvent {
} }
executeAction(reason, cb) { executeAction(reason, cb) {
Log.info( { eventName : this.name, action : this.action, reason : reason }, `Executing scheduled event "${this.name}"...`); Log.info(
{ eventName: this.name, action: this.action, reason: reason },
`Executing scheduled event "${this.name}"...`
);
if ('method' === this.action.type) { if ('method' === this.action.type) {
const modulePath = path.join(__dirname, '../', this.action.location); // enigma-bbs base + supplied location (path/file.js') const modulePath = path.join(__dirname, '../', this.action.location); // enigma-bbs base + supplied location (path/file.js')

View File

@ -351,7 +351,7 @@ exports.getModule = class FileAreaList extends MenuModule {
top: artData.mciMap.XY2.position, top: artData.mciMap.XY2.position,
bottom: artData.mciMap.XY3.position, bottom: artData.mciMap.XY3.position,
}; };
} catch(e) { } catch (e) {
throw Errors.DoesNotExist('Missing XY2 and XY3 position indicators!'); throw Errors.DoesNotExist('Missing XY2 and XY3 position indicators!');
} }
} }
@ -380,7 +380,10 @@ exports.getModule = class FileAreaList extends MenuModule {
return self.displayArtAndPrepViewController( return self.displayArtAndPrepViewController(
'browse', 'browse',
FormIds.browse, FormIds.browse,
{ clearScreen : clearScreen, artDataPrep: self.displayArtDataPrepCallback.bind(self) }, {
clearScreen: clearScreen,
artDataPrep: self.displayArtDataPrepCallback.bind(self),
},
callback callback
); );
}, },
@ -473,7 +476,10 @@ exports.getModule = class FileAreaList extends MenuModule {
return self.displayArtAndPrepViewController( return self.displayArtAndPrepViewController(
'details', 'details',
FormIds.details, FormIds.details,
{ clearScreen : true, artDataPrep: self.displayArtDataPrepCallback.bind(self) }, {
clearScreen: true,
artDataPrep: self.displayArtDataPrepCallback.bind(self),
},
callback callback
); );
}, },
@ -725,9 +731,9 @@ exports.getModule = class FileAreaList extends MenuModule {
name, name,
FormIds[name], FormIds[name],
{ {
clearScreen : false, clearScreen: false,
noInput : true, noInput: true,
artDataPrep: self.displayArtDataPrepCallback.bind(self) artDataPrep: self.displayArtDataPrepCallback.bind(self),
}, },
callback callback
); );

View File

@ -534,23 +534,22 @@ class FileAreaWebAccess {
StatLog.incrementSystemStat(SysProps.FileDlTotalCount, 1); StatLog.incrementSystemStat(SysProps.FileDlTotalCount, 1);
StatLog.incrementSystemStat(SysProps.FileDlTotalBytes, dlBytes); StatLog.incrementSystemStat(SysProps.FileDlTotalBytes, dlBytes);
StatLog.incrementNonPersistentSystemStat(SysProps.FileDlTodayCount, 1); StatLog.incrementNonPersistentSystemStat(SysProps.FileDlTodayCount, 1);
StatLog.incrementNonPersistentSystemStat(SysProps.FileDlTodayBytes, dlBytes); StatLog.incrementNonPersistentSystemStat(
SysProps.FileDlTodayBytes,
dlBytes
);
return callback(null, user); return callback(null, user);
}, },
function sendEvent(user, callback) { function sendEvent(user, callback) {
Events.emit( Events.emit(Events.getSystemEvents().UserDownload, {
Events.getSystemEvents().UserDownload, user: user,
{ files: fileEntries,
user : user, });
files : fileEntries, return callback(null);
} },
); ]);
return callback(null);
}
]
);
} }
} }

View File

@ -193,8 +193,9 @@ exports.getModule = class FileBaseDownloadQueueManager extends MenuModule {
[ [
function prepArtAndViewController(callback) { function prepArtAndViewController(callback) {
return self.displayArtAndPrepViewController( return self.displayArtAndPrepViewController(
'queueManager', FormIds.queueManager, 'queueManager',
{ clearScreen : clearScreen }, FormIds.queueManager,
{ clearScreen: clearScreen },
callback callback
); );
}, },

View File

@ -188,7 +188,7 @@ exports.getModule = class FileBaseWebDownloadQueueManager extends MenuModule {
return self.displayArtAndPrepViewController( return self.displayArtAndPrepViewController(
'queueManager', 'queueManager',
FormIds.queueManager, FormIds.queueManager,
{ clearScreen : clearScreen }, { clearScreen: clearScreen },
callback callback
); );
}, },

View File

@ -148,7 +148,10 @@ exports.getModule = class TransferFileModule extends MenuModule {
sentFiles.push(f.path); sentFiles.push(f.path);
}); });
this.client.log.info( { sentFiles : sentFiles }, `User "${self.client.user.username}" uploaded ${sentFiles.length} file(s)` ); this.client.log.info(
{ sentFiles: sentFiles },
`User "${self.client.user.username}" uploaded ${sentFiles.length} file(s)`
);
} }
return cb(err); return cb(err);
}); });

View File

@ -613,45 +613,48 @@ exports.MenuModule = class MenuModule extends PluginModule {
async.waterfall( async.waterfall(
[ [
(callback) => { callback => {
if(options.clearScreen) { if (options.clearScreen) {
this.client.term.rawWrite(ansi.resetScreen()); this.client.term.rawWrite(ansi.resetScreen());
} }
theme.displayThemedAsset( theme.displayThemedAsset(
config.art[name], config.art[name],
this.client, this.client,
{ font : this.menuConfig.font, trailingLF : false }, { font: this.menuConfig.font, trailingLF: false },
(err, artData) => { (err, artData) => {
return callback(err, artData); return callback(err, artData);
} }
); );
}, },
(artData, callback) => { (artData, callback) => {
if(_.isUndefined(this.viewControllers[name])) { if (_.isUndefined(this.viewControllers[name])) {
const vcOpts = { const vcOpts = {
client : this.client, client: this.client,
formId : formId, formId: formId,
}; };
if(!_.isUndefined(options.noInput)) { if (!_.isUndefined(options.noInput)) {
vcOpts.noInput = options.noInput; vcOpts.noInput = options.noInput;
} }
const vc = this.addViewController(name, new ViewController(vcOpts)); const vc = this.addViewController(
name,
new ViewController(vcOpts)
);
if (_.isFunction(options.artDataPrep)) { if (_.isFunction(options.artDataPrep)) {
try { try {
options.artDataPrep(name, artData, vc); options.artDataPrep(name, artData, vc);
} catch(e) { } catch (e) {
return callback(e); return callback(e);
} }
} }
const loadOpts = { const loadOpts = {
callingMenu : this, callingMenu: this,
mciMap : artData.mciMap, mciMap: artData.mciMap,
formId : formId, formId: formId,
}; };
return vc.loadFromMenuConfig(loadOpts, callback); return vc.loadFromMenuConfig(loadOpts, callback);
@ -815,18 +818,24 @@ exports.MenuModule = class MenuModule extends PluginModule {
} }
// Various common helpers // Various common helpers
getDateFormat(defaultStyle='short') { getDateFormat(defaultStyle = 'short') {
return this.config.dateFormat || return (
this.client.currentTheme.helpers.getDateFormat(defaultStyle); this.config.dateFormat ||
this.client.currentTheme.helpers.getDateFormat(defaultStyle)
);
} }
getTimeFormat(defaultStyle='short') { getTimeFormat(defaultStyle = 'short') {
return this.config.timeFormat || return (
this.client.currentTheme.helpers.getTimeFormat(defaultStyle); this.config.timeFormat ||
this.client.currentTheme.helpers.getTimeFormat(defaultStyle)
);
} }
getDateTimeFormat(defaultStyle='short') { getDateTimeFormat(defaultStyle = 'short') {
return this.config.dateTimeFormat || return (
this.client.currentTheme.helpers.getDateTimeFormat(defaultStyle); this.config.dateTimeFormat ||
this.client.currentTheme.helpers.getDateTimeFormat(defaultStyle)
);
} }
}; };

View File

@ -233,11 +233,11 @@ MenuView.prototype.setFocusItemIndex = function (index) {
this.focusedItemIndex = index; this.focusedItemIndex = index;
}; };
MenuView.prototype.getFocusItemIndex = function() { MenuView.prototype.getFocusItemIndex = function () {
return this.focusedItemIndex; return this.focusedItemIndex;
}; };
MenuView.prototype.onKeyPress = function(ch, key) { MenuView.prototype.onKeyPress = function (ch, key) {
const itemIndex = this.getHotKeyItemIndex(ch); const itemIndex = this.getHotKeyItemIndex(ch);
if (itemIndex >= 0) { if (itemIndex >= 0) {
this.setFocusItemIndex(itemIndex); this.setFocusItemIndex(itemIndex);

View File

@ -24,29 +24,29 @@ exports.getAvailableMessageConferences = getAvailableMessageConferences;
exports.getSortedAvailMessageConferences = getSortedAvailMessageConferences; exports.getSortedAvailMessageConferences = getSortedAvailMessageConferences;
exports.getAvailableMessageAreasByConfTag = getAvailableMessageAreasByConfTag; exports.getAvailableMessageAreasByConfTag = getAvailableMessageAreasByConfTag;
exports.getSortedAvailMessageAreasByConfTag = getSortedAvailMessageAreasByConfTag; exports.getSortedAvailMessageAreasByConfTag = getSortedAvailMessageAreasByConfTag;
exports.getAllAvailableMessageAreaTags = getAllAvailableMessageAreaTags; exports.getAllAvailableMessageAreaTags = getAllAvailableMessageAreaTags;
exports.getDefaultMessageConferenceTag = getDefaultMessageConferenceTag; exports.getDefaultMessageConferenceTag = getDefaultMessageConferenceTag;
exports.getDefaultMessageAreaTagByConfTag = getDefaultMessageAreaTagByConfTag; exports.getDefaultMessageAreaTagByConfTag = getDefaultMessageAreaTagByConfTag;
exports.getSuitableMessageConfAndAreaTags = getSuitableMessageConfAndAreaTags; exports.getSuitableMessageConfAndAreaTags = getSuitableMessageConfAndAreaTags;
exports.getMessageConferenceByTag = getMessageConferenceByTag; exports.getMessageConferenceByTag = getMessageConferenceByTag;
exports.getMessageAreaByTag = getMessageAreaByTag; exports.getMessageAreaByTag = getMessageAreaByTag;
exports.getMessageConfTagByAreaTag = getMessageConfTagByAreaTag; exports.getMessageConfTagByAreaTag = getMessageConfTagByAreaTag;
exports.changeMessageConference = changeMessageConference; exports.changeMessageConference = changeMessageConference;
exports.changeMessageArea = changeMessageArea; exports.changeMessageArea = changeMessageArea;
exports.hasMessageConfAndAreaRead = hasMessageConfAndAreaRead; exports.hasMessageConfAndAreaRead = hasMessageConfAndAreaRead;
exports.hasMessageConfAndAreaWrite = hasMessageConfAndAreaWrite; exports.hasMessageConfAndAreaWrite = hasMessageConfAndAreaWrite;
exports.filterMessageAreaTagsByReadACS = filterMessageAreaTagsByReadACS; exports.filterMessageAreaTagsByReadACS = filterMessageAreaTagsByReadACS;
exports.filterMessageListByReadACS = filterMessageListByReadACS; exports.filterMessageListByReadACS = filterMessageListByReadACS;
exports.tempChangeMessageConfAndArea = tempChangeMessageConfAndArea; exports.tempChangeMessageConfAndArea = tempChangeMessageConfAndArea;
exports.getMessageListForArea = getMessageListForArea; exports.getMessageListForArea = getMessageListForArea;
exports.getNewMessageCountInAreaForUser = getNewMessageCountInAreaForUser; exports.getNewMessageCountInAreaForUser = getNewMessageCountInAreaForUser;
exports.getNewMessageCountAddressedToUser = getNewMessageCountAddressedToUser; exports.getNewMessageCountAddressedToUser = getNewMessageCountAddressedToUser;
exports.getNewMessagesInAreaForUser = getNewMessagesInAreaForUser; exports.getNewMessagesInAreaForUser = getNewMessagesInAreaForUser;
exports.getMessageIdNewerThanTimestampByArea = getMessageIdNewerThanTimestampByArea; exports.getMessageIdNewerThanTimestampByArea = getMessageIdNewerThanTimestampByArea;
exports.getMessageAreaLastReadId = getMessageAreaLastReadId; exports.getMessageAreaLastReadId = getMessageAreaLastReadId;
exports.updateMessageAreaLastReadId = updateMessageAreaLastReadId; exports.updateMessageAreaLastReadId = updateMessageAreaLastReadId;
exports.persistMessage = persistMessage; exports.persistMessage = persistMessage;
exports.trimMessageAreasScheduledEvent = trimMessageAreasScheduledEvent; exports.trimMessageAreasScheduledEvent = trimMessageAreasScheduledEvent;
function startup(cb) { function startup(cb) {
// by default, private messages are NOT included // by default, private messages are NOT included
@ -536,20 +536,30 @@ function getNewMessageCountInAreaForUser(userId, areaTag, cb) {
// that are addressed to that user (ie: matching username) // that are addressed to that user (ie: matching username)
// Does NOT Include private messages. // Does NOT Include private messages.
function getNewMessageCountAddressedToUser(client, cb) { function getNewMessageCountAddressedToUser(client, cb) {
const areaTags = getAllAvailableMessageAreaTags(client).filter(areaTag => areaTag !== Message.WellKnownAreaTags.Private); const areaTags = getAllAvailableMessageAreaTags(client).filter(
areaTag => areaTag !== Message.WellKnownAreaTags.Private
);
let newMessageCount = 0; let newMessageCount = 0;
async.forEach(areaTags, (areaTag, nextAreaTag) => { async.forEach(
getMessageAreaLastReadId(client.user.userId, areaTag, (_, lastMessageId) => { areaTags,
lastMessageId = lastMessageId || 0; (areaTag, nextAreaTag) => {
getNewMessageCountInAreaForUser(client.user.userId, areaTag, (err, count) => { getMessageAreaLastReadId(client.user.userId, areaTag, (_, lastMessageId) => {
newMessageCount += count; lastMessageId = lastMessageId || 0;
return nextAreaTag(err); getNewMessageCountInAreaForUser(
client.user.userId,
areaTag,
(err, count) => {
newMessageCount += count;
return nextAreaTag(err);
}
);
}); });
}); },
}, () => { () => {
return cb(null, newMessageCount); return cb(null, newMessageCount);
}); }
);
} }
function getNewMessagesInAreaForUser(userId, areaTag, cb) { function getNewMessagesInAreaForUser(userId, areaTag, cb) {
@ -572,10 +582,8 @@ function getNewMessagesInAreaForUser(userId, areaTag, cb) {
}); });
} }
function getMessageListForArea(client, areaTag, filter, cb) {
function getMessageListForArea(client, areaTag, filter, cb) if (!cb && _.isFunction(filter)) {
{
if(!cb && _.isFunction(filter)) {
cb = filter; cb = filter;
filter = { filter = {
areaTag, areaTag,

View File

@ -12,7 +12,9 @@ function dailyMaintenanceScheduledEvent(args, cb) {
// //
// :TODO: files/etc. here // :TODO: files/etc. here
const resetProps = [ const resetProps = [
SysProps.LoginsToday, SysProps.MessagesToday, SysProps.NewUsersTodayCount, SysProps.LoginsToday,
SysProps.MessagesToday,
SysProps.NewUsersTodayCount,
]; ];
resetProps.forEach(prop => { resetProps.forEach(prop => {

View File

@ -46,7 +46,11 @@ exports.getModule = class AreaPostFSEModule extends FullScreenEditorModule {
} else { } else {
// note: not logging 'from' here as it's part of client.log.xxxx() // note: not logging 'from' here as it's part of client.log.xxxx()
self.client.log.info( self.client.log.info(
{ to : msg.toUserName, subject : msg.subject, uuid : msg.messageUuid }, {
to: msg.toUserName,
subject: msg.subject,
uuid: msg.messageUuid,
},
`User "${self.client.user.username}" posted message to "${msg.toUserName}" (${msg.areaTag})` `User "${self.client.user.username}" posted message to "${msg.toUserName}" (${msg.areaTag})`
); );
} }

View File

@ -7,13 +7,13 @@ const {
getActiveConnectionList, getActiveConnectionList,
getConnectionByNodeId, getConnectionByNodeId,
UserMessageableConnections, UserMessageableConnections,
} = require('./client_connections.js'); } = require('./client_connections.js');
const UserInterruptQueue = require('./user_interrupt_queue.js'); const UserInterruptQueue = require('./user_interrupt_queue.js');
const { getThemeArt } = require('./theme.js'); const { getThemeArt } = require('./theme.js');
const { pipeToAnsi } = require('./color_codes.js'); const { pipeToAnsi } = require('./color_codes.js');
const stringFormat = require('./string_format.js'); const stringFormat = require('./string_format.js');
const { renderStringLength } = require('./string_util.js'); const { renderStringLength } = require('./string_util.js');
const Events = require('./events.js'); const Events = require('./events.js');
// deps // deps
const series = require('async/series'); const series = require('async/series');
@ -221,22 +221,30 @@ exports.getModule = class NodeMessageModule extends MenuModule {
prepareNodeList() { prepareNodeList() {
// standard node list with {text} field added for compliance // standard node list with {text} field added for compliance
this.nodeList = [{ this.nodeList = [
text : '-ALL-', {
// dummy fields: text: '-ALL-',
node : -1, // dummy fields:
authenticated : false, node: -1,
userId : 0, authenticated: false,
action : 'N/A', userId: 0,
userName : 'Everyone', action: 'N/A',
realName : 'All Users', userName: 'Everyone',
location : 'N/A', realName: 'All Users',
affils : 'N/A', location: 'N/A',
timeOn : 'N/A', affils: 'N/A',
}].concat(getActiveConnectionList(UserMessageableConnections) timeOn: 'N/A',
.map(node => Object.assign(node, { text : -1 == node.node ? '-ALL-' : node.node.toString() } )) },
).filter(node => node.node !== this.client.node); // remove our client's node ]
this.nodeList.sort( (a, b) => a.node - b.node ); // sort by node .concat(
getActiveConnectionList(UserMessageableConnections).map(node =>
Object.assign(node, {
text: -1 == node.node ? '-ALL-' : node.node.toString(),
})
)
)
.filter(node => node.node !== this.client.node); // remove our client's node
this.nodeList.sort((a, b) => a.node - b.node); // sort by node
} }
nodeListSelectionIndexUpdate(idx) { nodeListSelectionIndexUpdate(idx) {

View File

@ -129,8 +129,11 @@ exports.getModule = class NewUserAppModule extends MenuModule {
sessionId: self.client.session.uniqueId, // used for events/etc. sessionId: self.client.session.uniqueId, // used for events/etc.
}; };
newUser.create(createUserInfo, err => { newUser.create(createUserInfo, err => {
if(err) { if (err) {
self.client.log.warn( { error : err, username : formData.value.username }, 'New user creation failed'); self.client.log.warn(
{ error: err, username: formData.value.username },
'New user creation failed'
);
self.gotoMenu(extraArgs.error, err => { self.gotoMenu(extraArgs.error, err => {
if (err) { if (err) {
@ -139,7 +142,10 @@ exports.getModule = class NewUserAppModule extends MenuModule {
return cb(null); return cb(null);
}); });
} else { } else {
self.client.log.info( { username : formData.value.username, userId : newUser.userId }, `New user "${formData.value.username}" created`); self.client.log.info(
{ username: formData.value.username, userId: newUser.userId },
`New user "${formData.value.username}" created`
);
// Cache SysOp information now // Cache SysOp information now
// :TODO: Similar to bbs.js. DRY // :TODO: Similar to bbs.js. DRY

View File

@ -15,11 +15,11 @@ const SysProps = require('./system_property.js');
const SysLogKeys = require('./system_log.js'); const SysLogKeys = require('./system_log.js');
// deps // deps
const packageJson = require('../package.json'); const packageJson = require('../package.json');
const os = require('os'); const os = require('os');
const _ = require('lodash'); const _ = require('lodash');
const moment = require('moment'); const moment = require('moment');
const async = require('async'); const async = require('async');
exports.getPredefinedMCIValue = getPredefinedMCIValue; exports.getPredefinedMCIValue = getPredefinedMCIValue;
exports.init = init; exports.init = init;
@ -27,14 +27,14 @@ exports.init = init;
function init(cb) { function init(cb) {
async.series( async.series(
[ [
(callback) => { callback => {
return setNextRandomRumor(callback); return setNextRandomRumor(callback);
}, },
(callback) => { callback => {
// by fetching a memory or load we'll force a refresh now // by fetching a memory or load we'll force a refresh now
StatLog.getSystemStat(SysProps.SystemMemoryStats); StatLog.getSystemStat(SysProps.SystemMemoryStats);
return callback(null); return callback(null);
} },
], ],
err => { err => {
return cb(err); return cb(err);
@ -98,38 +98,87 @@ const PREDEFINED_MCI_GENERATORS = {
}, },
// +op info // +op info
SN : function opUserName() { return StatLog.getSystemStat(SysProps.SysOpUsername); }, SN: function opUserName() {
SR : function opRealName() { return StatLog.getSystemStat(SysProps.SysOpRealName); }, return StatLog.getSystemStat(SysProps.SysOpUsername);
SL : function opLocation() { return StatLog.getSystemStat(SysProps.SysOpLocation); }, },
SA : function opAffils() { return StatLog.getSystemStat(SysProps.SysOpAffiliations); }, SR: function opRealName() {
SS : function opSex() { return StatLog.getSystemStat(SysProps.SysOpSex); }, return StatLog.getSystemStat(SysProps.SysOpRealName);
SE : function opEmail() { return StatLog.getSystemStat(SysProps.SysOpEmailAddress); }, },
SL: function opLocation() {
return StatLog.getSystemStat(SysProps.SysOpLocation);
},
SA: function opAffils() {
return StatLog.getSystemStat(SysProps.SysOpAffiliations);
},
SS: function opSex() {
return StatLog.getSystemStat(SysProps.SysOpSex);
},
SE: function opEmail() {
return StatLog.getSystemStat(SysProps.SysOpEmailAddress);
},
// //
// Current user / session // Current user / session
// //
UN : function userName(client) { return client.user.username; }, UN: function userName(client) {
UI : function userId(client) { return client.user.userId.toString(); }, return client.user.username;
UG : function groups(client) { return _.values(client.user.groups).join(', '); },
UR : function realName(client) { return userStatAsString(client, UserProps.RealName, ''); },
LO : function location(client) { return userStatAsString(client, UserProps.Location, ''); },
UA : function age(client) { return client.user.getAge().toString(); },
BD : function birthdate(client) { // iNiQUiTY
return moment(client.user.properties[UserProps.Birthdate]).format(client.currentTheme.helpers.getDateFormat());
}, },
US : function sex(client) { return userStatAsString(client, UserProps.Sex, ''); }, UI: function userId(client) {
UE : function emailAddress(client) { return userStatAsString(client, UserProps.EmailAddress, ''); }, return client.user.userId.toString();
UW : function webAddress(client) { return userStatAsString(client, UserProps.WebAddress, ''); },
UF : function affils(client) { return userStatAsString(client, UserProps.Affiliations, ''); },
UT : function themeName(client) {
return _.get(client, 'currentTheme.info.name', userStatAsString(client, UserProps.ThemeId, ''));
}, },
UD : function themeId(client) { return userStatAsString(client, UserProps.ThemeId, ''); }, UG: function groups(client) {
UC : function loginCount(client) { return userStatAsCountString(client, UserProps.LoginCount, 0); }, return _.values(client.user.groups).join(', ');
ND : function connectedNode(client) { return client.node.toString(); }, },
IP : function clientIpAddress(client) { return client.friendlyRemoteAddress() }, UR: function realName(client) {
ST : function serverName(client) { return client.session.serverName; }, return userStatAsString(client, UserProps.RealName, '');
FN : function activeFileBaseFilterName(client) { },
LO: function location(client) {
return userStatAsString(client, UserProps.Location, '');
},
UA: function age(client) {
return client.user.getAge().toString();
},
BD: function birthdate(client) {
// iNiQUiTY
return moment(client.user.properties[UserProps.Birthdate]).format(
client.currentTheme.helpers.getDateFormat()
);
},
US: function sex(client) {
return userStatAsString(client, UserProps.Sex, '');
},
UE: function emailAddress(client) {
return userStatAsString(client, UserProps.EmailAddress, '');
},
UW: function webAddress(client) {
return userStatAsString(client, UserProps.WebAddress, '');
},
UF: function affils(client) {
return userStatAsString(client, UserProps.Affiliations, '');
},
UT: function themeName(client) {
return _.get(
client,
'currentTheme.info.name',
userStatAsString(client, UserProps.ThemeId, '')
);
},
UD: function themeId(client) {
return userStatAsString(client, UserProps.ThemeId, '');
},
UC: function loginCount(client) {
return userStatAsCountString(client, UserProps.LoginCount, 0);
},
ND: function connectedNode(client) {
return client.node.toString();
},
IP: function clientIpAddress(client) {
return client.friendlyRemoteAddress();
},
ST: function serverName(client) {
return client.session.serverName;
},
FN: function activeFileBaseFilterName(client) {
const activeFilter = FileBaseFilters.getActiveFilter(client); const activeFilter = FileBaseFilters.getActiveFilter(client);
return activeFilter ? activeFilter.name : '(Unknown)'; return activeFilter ? activeFilter.name : '(Unknown)';
}, },
@ -234,19 +283,22 @@ const PREDEFINED_MCI_GENERATORS = {
const minutes = client.user.properties[UserProps.MinutesOnlineTotalCount] || 0; const minutes = client.user.properties[UserProps.MinutesOnlineTotalCount] || 0;
return moment.duration(minutes, 'minutes').humanize(); return moment.duration(minutes, 'minutes').humanize();
}, },
NM : function userNewMessagesAddressedToCount(client) { NM: function userNewMessagesAddressedToCount(client) {
return StatLog.getUserStatNumByClient(client, UserProps.NewAddressedToMessageCount); return StatLog.getUserStatNumByClient(
client,
UserProps.NewAddressedToMessageCount
);
}, },
NP : function userNewPrivateMailCount(client) { NP: function userNewPrivateMailCount(client) {
return StatLog.getUserStatNumByClient(client, UserProps.NewPrivateMailCount); return StatLog.getUserStatNumByClient(client, UserProps.NewPrivateMailCount);
}, },
IA : function userStatusAvailableIndicator(client) { IA: function userStatusAvailableIndicator(client) {
const indicators = client.currentTheme.helpers.getStatusAvailIndicators(); const indicators = client.currentTheme.helpers.getStatusAvailIndicators();
return client.user.isAvailable() ? (indicators[0] || 'Y') : (indicators[1] || 'N'); return client.user.isAvailable() ? indicators[0] || 'Y' : indicators[1] || 'N';
}, },
IV : function userStatusVisibleIndicator(client) { IV: function userStatusVisibleIndicator(client) {
const indicators = client.currentTheme.helpers.getStatusVisibleIndicators(); const indicators = client.currentTheme.helpers.getStatusVisibleIndicators();
return client.user.isVisible() ? (indicators[0] || 'Y') : (indicators[1] || 'N'); return client.user.isVisible() ? indicators[0] || 'Y' : indicators[1] || 'N';
}, },
// //
@ -294,27 +346,37 @@ const PREDEFINED_MCI_GENERATORS = {
.trim(); .trim();
}, },
MB : function totalMemoryBytes() { MB: function totalMemoryBytes() {
const stats = StatLog.getSystemStat(SysProps.SystemMemoryStats) || { totalBytes : 0 }; const stats = StatLog.getSystemStat(SysProps.SystemMemoryStats) || {
totalBytes: 0,
};
return formatByteSize(stats.totalBytes, true); // true=withAbbr return formatByteSize(stats.totalBytes, true); // true=withAbbr
}, },
MF : function totalMemoryFreeBytes() { MF: function totalMemoryFreeBytes() {
const stats = StatLog.getSystemStat(SysProps.SystemMemoryStats) || { freeBytes : 0 }; const stats = StatLog.getSystemStat(SysProps.SystemMemoryStats) || {
return formatByteSize(stats.freeBytes, true); // true=withAbbr freeBytes: 0,
};
return formatByteSize(stats.freeBytes, true); // true=withAbbr
}, },
LA : function systemLoadAverage() { LA: function systemLoadAverage() {
const stats = StatLog.getSystemStat(SysProps.SystemLoadStats) || { average : 0.0 }; const stats = StatLog.getSystemStat(SysProps.SystemLoadStats) || { average: 0.0 };
return stats.average.toLocaleString(); return stats.average.toLocaleString();
}, },
CL : function systemCurrentLoad() { CL: function systemCurrentLoad() {
const stats = StatLog.getSystemStat(SysProps.SystemLoadStats) || { current : 0 }; const stats = StatLog.getSystemStat(SysProps.SystemLoadStats) || { current: 0 };
return `${stats.current}%`; return `${stats.current}%`;
}, },
UU : function systemUptime() { UU: function systemUptime() {
return moment.duration(process.uptime(), 'seconds').humanize(); return moment.duration(process.uptime(), 'seconds').humanize();
}, },
NV : function nodeVersion() { return process.version; }, NV: function nodeVersion() {
AN : function activeNodes() { return clientConnections.getActiveConnections(clientConnections.UserVisibleConnections).length.toString(); }, return process.version;
},
AN: function activeNodes() {
return clientConnections
.getActiveConnections(clientConnections.UserVisibleConnections)
.length.toString();
},
TC: function totalCalls() { TC: function totalCalls() {
return StatLog.getSystemStat(SysProps.LoginCount).toLocaleString(); return StatLog.getSystemStat(SysProps.LoginCount).toLocaleString();
@ -333,13 +395,17 @@ const PREDEFINED_MCI_GENERATORS = {
// //
// System File Base, Up/Download Info // System File Base, Up/Download Info
// //
SD : function systemNumDownloads() { return StatLog.getFriendlySystemStat(SysProps.FileDlTotalCount, 0); }, SD: function systemNumDownloads() {
SO : function systemByteDownload() { return StatLog.getFriendlySystemStat(SysProps.FileDlTotalCount, 0);
},
SO: function systemByteDownload() {
const byteSize = StatLog.getSystemStatNum(SysProps.FileDlTotalBytes); const byteSize = StatLog.getSystemStatNum(SysProps.FileDlTotalBytes);
return formatByteSize(byteSize, true); // true=withAbbr return formatByteSize(byteSize, true); // true=withAbbr
}, },
SU : function systemNumUploads() { return StatLog.getFriendlySystemStat(SysProps.FileUlTotalCount, 0); }, SU: function systemNumUploads() {
SP : function systemByteUpload() { return StatLog.getFriendlySystemStat(SysProps.FileUlTotalCount, 0);
},
SP: function systemByteUpload() {
const byteSize = StatLog.getSystemStatNum(SysProps.FileUlTotalBytes); const byteSize = StatLog.getSystemStatNum(SysProps.FileUlTotalBytes);
return formatByteSize(byteSize, true); // true=withAbbr return formatByteSize(byteSize, true); // true=withAbbr
}, },
@ -352,49 +418,55 @@ const PREDEFINED_MCI_GENERATORS = {
const totalBytes = parseInt(_.get(areaStats, 'totalBytes', 0)); const totalBytes = parseInt(_.get(areaStats, 'totalBytes', 0));
return formatByteSize(totalBytes, true); // true=withAbbr return formatByteSize(totalBytes, true); // true=withAbbr
}, },
PT : function messagesPostedToday() { // Obv/2 PT: function messagesPostedToday() {
// Obv/2
return StatLog.getFriendlySystemStat(SysProps.MessagesToday, 0); return StatLog.getFriendlySystemStat(SysProps.MessagesToday, 0);
}, },
TP : function totalMessagesOnSystem() { // Obv/2 TP: function totalMessagesOnSystem() {
// Obv/2
return StatLog.getFriendlySystemStat(SysProps.MessageTotalCount, 0); return StatLog.getFriendlySystemStat(SysProps.MessageTotalCount, 0);
}, },
FT : function totalUploadsToday() { // Obv/2 FT: function totalUploadsToday() {
// Obv/2
return StatLog.getFriendlySystemStat(SysProps.FileUlTodayCount, 0); return StatLog.getFriendlySystemStat(SysProps.FileUlTodayCount, 0);
}, },
FB : function totalUploadBytesToday() { FB: function totalUploadBytesToday() {
const byteSize = StatLog.getSystemStatNum(SysProps.FileUlTodayBytes); const byteSize = StatLog.getSystemStatNum(SysProps.FileUlTodayBytes);
return formatByteSize(byteSize, true); // true=withAbbr return formatByteSize(byteSize, true); // true=withAbbr
}, },
DD : function totalDownloadsToday() { // iNiQUiTY DD: function totalDownloadsToday() {
// iNiQUiTY
return StatLog.getFriendlySystemStat(SysProps.FileDlTodayCount, 0); return StatLog.getFriendlySystemStat(SysProps.FileDlTodayCount, 0);
}, },
DB : function totalDownloadBytesToday() { DB: function totalDownloadBytesToday() {
const byteSize = StatLog.getSystemStatNum(SysProps.FileDlTodayBytes); const byteSize = StatLog.getSystemStatNum(SysProps.FileDlTodayBytes);
return formatByteSize(byteSize, true); // true=withAbbr return formatByteSize(byteSize, true); // true=withAbbr
}, },
NT : function totalNewUsersToday() { // Obv/2 NT: function totalNewUsersToday() {
// Obv/2
return StatLog.getSystemStatNum(SysProps.NewUsersTodayCount); return StatLog.getSystemStatNum(SysProps.NewUsersTodayCount);
}, },
// :TODO: TZ - Average *system* post/call ratio (iNiQUiTY) // :TODO: TZ - Average *system* post/call ratio (iNiQUiTY)
// :TODO: ?? - Total users on system // :TODO: ?? - Total users on system
TU : function totalSystemUsers() { TU: function totalSystemUsers() {
return StatLog.getSystemStatNum(SysProps.TotalUserCount) || 1; return StatLog.getSystemStatNum(SysProps.TotalUserCount) || 1;
}, },
LC : function lastCallerUserName() { // Obv/2 LC: function lastCallerUserName() {
// Obv/2
const lastLogin = StatLog.getSystemStat(SysProps.LastLogin) || {}; const lastLogin = StatLog.getSystemStat(SysProps.LastLogin) || {};
return lastLogin.userName || 'N/A'; return lastLogin.userName || 'N/A';
}, },
LD : function lastCallerDate(client) { LD: function lastCallerDate(client) {
const lastLogin = StatLog.getSystemStat(SysProps.LastLogin) || {}; const lastLogin = StatLog.getSystemStat(SysProps.LastLogin) || {};
if (!lastLogin.timestamp) { if (!lastLogin.timestamp) {
return 'N/A'; return 'N/A';
} }
return lastLogin.timestamp.format(client.currentTheme.helpers.getDateFormat()); return lastLogin.timestamp.format(client.currentTheme.helpers.getDateFormat());
}, },
LT : function lastCallerTime(client) { LT: function lastCallerTime(client) {
const lastLogin = StatLog.getSystemStat(SysProps.LastLogin) || {}; const lastLogin = StatLog.getSystemStat(SysProps.LastLogin) || {};
if (!lastLogin.timestamp) { if (!lastLogin.timestamp) {
return 'N/A'; return 'N/A';
@ -437,8 +509,11 @@ function getPredefinedMCIValue(client, code, extra) {
let value; let value;
try { try {
value = generator(client, extra); value = generator(client, extra);
} catch(e) { } catch (e) {
Log.error( { code : code, exception : e.message }, `Failed generating predefined MCI value (${code})` ); Log.error(
{ code: code, exception: e.message },
`Failed generating predefined MCI value (${code})`
);
} }
return value; return value;

View File

@ -1,19 +1,17 @@
/* jslint node: true */ /* jslint node: true */
'use strict'; 'use strict';
const sysDb = require('./database.js').dbs.system; const sysDb = require('./database.js').dbs.system;
const { const { getISOTimestampString } = require('./database.js');
getISOTimestampString const Errors = require('./enig_error.js');
} = require('./database.js'); const SysProps = require('./system_property.js');
const Errors = require('./enig_error.js');
const SysProps = require('./system_property.js');
const UserProps = require('./user_property'); const UserProps = require('./user_property');
const Message = require('./message'); const Message = require('./message');
// deps // deps
const _ = require('lodash'); const _ = require('lodash');
const moment = require('moment'); const moment = require('moment');
const SysInfo = require('systeminformation'); const SysInfo = require('systeminformation');
/* /*
System Event Log & Stats System Event Log & Stats
@ -171,7 +169,7 @@ class StatLog {
return parseInt(this.getUserStat(user, statName)) || 0; return parseInt(this.getUserStat(user, statName)) || 0;
} }
getUserStatNumByClient(client, statName, ttlSeconds=10) { getUserStatNumByClient(client, statName, ttlSeconds = 10) {
const stat = this.getUserStatNum(client.user, statName); const stat = this.getUserStatNum(client.user, statName);
this._refreshUserStat(client, statName, ttlSeconds); this._refreshUserStat(client, statName, ttlSeconds);
return stat; return stat;
@ -359,8 +357,8 @@ class StatLog {
_refreshSystemStat(statName) { _refreshSystemStat(statName) {
switch (statName) { switch (statName) {
case SysProps.SystemLoadStats : case SysProps.SystemLoadStats:
case SysProps.SystemMemoryStats : case SysProps.SystemMemoryStats:
return this._refreshSysInfoStats(); return this._refreshSysInfoStats();
} }
} }
@ -374,23 +372,27 @@ class StatLog {
this.lastSysInfoStatsRefresh = now; this.lastSysInfoStatsRefresh = now;
const basicSysInfo = { const basicSysInfo = {
mem : 'total, free', mem: 'total, free',
currentLoad : 'avgLoad, currentLoad', currentLoad: 'avgLoad, currentLoad',
}; };
SysInfo.get(basicSysInfo) SysInfo.get(basicSysInfo)
.then(sysInfo => { .then(sysInfo => {
const memStats = { const memStats = {
totalBytes : sysInfo.mem.total, totalBytes: sysInfo.mem.total,
freeBytes : sysInfo.mem.free, freeBytes: sysInfo.mem.free,
}; };
this.setNonPersistentSystemStat(SysProps.SystemMemoryStats, memStats); this.setNonPersistentSystemStat(SysProps.SystemMemoryStats, memStats);
const loadStats = { const loadStats = {
// Not avail on BSD, yet. // Not avail on BSD, yet.
average : parseFloat(_.get(sysInfo, 'currentLoad.avgLoad', 0).toFixed(2)), average: parseFloat(
current : parseFloat(_.get(sysInfo, 'currentLoad.currentLoad', 0).toFixed(2)), _.get(sysInfo, 'currentLoad.avgLoad', 0).toFixed(2)
),
current: parseFloat(
_.get(sysInfo, 'currentLoad.currentLoad', 0).toFixed(2)
),
}; };
this.setNonPersistentSystemStat(SysProps.SystemLoadStats, loadStats); this.setNonPersistentSystemStat(SysProps.SystemLoadStats, loadStats);
@ -401,13 +403,23 @@ class StatLog {
} }
_refreshUserStat(client, statName, ttlSeconds) { _refreshUserStat(client, statName, ttlSeconds) {
switch(statName) { switch (statName) {
case UserProps.NewPrivateMailCount: case UserProps.NewPrivateMailCount:
this._wrapUserRefreshWithCachedTTL(client, statName, this._refreshUserPrivateMailCount, ttlSeconds); this._wrapUserRefreshWithCachedTTL(
client,
statName,
this._refreshUserPrivateMailCount,
ttlSeconds
);
break; break;
case UserProps.NewAddressedToMessageCount: case UserProps.NewAddressedToMessageCount:
this._wrapUserRefreshWithCachedTTL(client, statName, this._refreshUserNewAddressedToMessageCount, ttlSeconds); this._wrapUserRefreshWithCachedTTL(
client,
statName,
this._refreshUserNewAddressedToMessageCount,
ttlSeconds
);
break; break;
} }
} }
@ -427,17 +439,21 @@ class StatLog {
_refreshUserPrivateMailCount(client) { _refreshUserPrivateMailCount(client) {
const MsgArea = require('./message_area'); const MsgArea = require('./message_area');
MsgArea.getNewMessageCountInAreaForUser(client.user.userId, Message.WellKnownAreaTags.Private, (err, count) => { MsgArea.getNewMessageCountInAreaForUser(
if (!err) { client.user.userId,
client.user.setProperty(UserProps.NewPrivateMailCount, count); Message.WellKnownAreaTags.Private,
(err, count) => {
if (!err) {
client.user.setProperty(UserProps.NewPrivateMailCount, count);
}
} }
}); );
} }
_refreshUserNewAddressedToMessageCount(client) { _refreshUserNewAddressedToMessageCount(client) {
const MsgArea = require('./message_area'); const MsgArea = require('./message_area');
MsgArea.getNewMessageCountAddressedToUser(client, (err, count) => { MsgArea.getNewMessageCountAddressedToUser(client, (err, count) => {
if(!err) { if (!err) {
client.user.setProperty(UserProps.NewAddressedToMessageCount, count); client.user.setProperty(UserProps.NewAddressedToMessageCount, count);
} }
}); });
@ -445,21 +461,19 @@ class StatLog {
_findLogEntries(logTable, filter, cb) { _findLogEntries(logTable, filter, cb) {
filter = filter || {}; filter = filter || {};
if(!_.isString(filter.logName)) { if (!_.isString(filter.logName)) {
return cb(Errors.MissingParam('filter.logName is required')); return cb(Errors.MissingParam('filter.logName is required'));
} }
filter.resultType = filter.resultType || 'obj'; filter.resultType = filter.resultType || 'obj';
filter.order = filter.order || 'timestamp'; filter.order = filter.order || 'timestamp';
let sql; let sql;
if('count' === filter.resultType) { if ('count' === filter.resultType) {
sql = sql = `SELECT COUNT() AS count
`SELECT COUNT() AS count
FROM ${logTable}`; FROM ${logTable}`;
} else { } else {
sql = sql = `SELECT timestamp, log_value
`SELECT timestamp, log_value
FROM ${logTable}`; FROM ${logTable}`;
} }
@ -473,40 +487,42 @@ class StatLog {
sql += ` AND session_id = ${filter.sessionId}`; sql += ` AND session_id = ${filter.sessionId}`;
} }
if(filter.date) { if (filter.date) {
filter.date = moment(filter.date); filter.date = moment(filter.date);
sql += ` AND DATE(timestamp, "localtime") = DATE("${filter.date.format('YYYY-MM-DD')}")`; sql += ` AND DATE(timestamp, "localtime") = DATE("${filter.date.format(
'YYYY-MM-DD'
)}")`;
} }
if('count' !== filter.resultType) { if ('count' !== filter.resultType) {
switch(filter.order) { switch (filter.order) {
case 'timestamp' : case 'timestamp':
case 'timestamp_asc' : case 'timestamp_asc':
sql += ' ORDER BY timestamp ASC'; sql += ' ORDER BY timestamp ASC';
break; break;
case 'timestamp_desc' : case 'timestamp_desc':
sql += ' ORDER BY timestamp DESC'; sql += ' ORDER BY timestamp DESC';
break; break;
case 'random' : case 'random':
sql += ' ORDER BY RANDOM()'; sql += ' ORDER BY RANDOM()';
break; break;
} }
} }
if(_.isNumber(filter.limit) && 0 !== filter.limit) { if (_.isNumber(filter.limit) && 0 !== filter.limit) {
sql += ` LIMIT ${filter.limit}`; sql += ` LIMIT ${filter.limit}`;
} }
sql += ';'; sql += ';';
if('count' === filter.resultType) { if ('count' === filter.resultType) {
sysDb.get(sql, [ filter.logName ], (err, row) => { sysDb.get(sql, [filter.logName], (err, row) => {
return cb(err, row ? row.count : 0); return cb(err, row ? row.count : 0);
}); });
} else { } else {
sysDb.all(sql, [ filter.logName ], (err, rows) => { sysDb.all(sql, [filter.logName], (err, rows) => {
return cb(err, rows); return cb(err, rows);
}); });
} }

View File

@ -379,9 +379,9 @@ function formatByteSizeAbbr(byteSize) {
} }
function formatByteSize(byteSize, withAbbr = false, decimals = 2) { function formatByteSize(byteSize, withAbbr = false, decimals = 2) {
const i = 0 === byteSize ? byteSize : Math.floor(Math.log(byteSize) / Math.log(1024)); const i = 0 === byteSize ? byteSize : Math.floor(Math.log(byteSize) / Math.log(1024));
let result = parseFloat((byteSize / Math.pow(1024, i)).toFixed(decimals)).toString(); let result = parseFloat((byteSize / Math.pow(1024, i)).toFixed(decimals)).toString();
if(withAbbr) { if (withAbbr) {
result += ` ${BYTE_SIZE_ABBRS[i]}`; result += ` ${BYTE_SIZE_ABBRS[i]}`;
} }
return result; return result;

View File

@ -1,9 +1,9 @@
/* jslint node: true */ /* jslint node: true */
'use strict'; 'use strict';
const Events = require('./events.js'); const Events = require('./events.js');
const LogNames = require('./user_log_name.js'); const LogNames = require('./user_log_name.js');
const SysProps = require('./system_property.js'); const SysProps = require('./system_property.js');
const DefaultKeepForDays = 365; const DefaultKeepForDays = 365;

View File

@ -5,6 +5,6 @@
// Common SYSTEM/global log keys // Common SYSTEM/global log keys
// //
module.exports = { module.exports = {
UserAddedRumorz : 'system_rumorz', UserAddedRumorz: 'system_rumorz',
UserLoginHistory : 'user_login_history', // JSON object UserLoginHistory: 'user_login_history', // JSON object
}; };

View File

@ -8,9 +8,9 @@
// their own! // their own!
// //
module.exports = { module.exports = {
LoginCount : 'login_count', LoginCount: 'login_count',
LoginsToday : 'logins_today', // non-persistent LoginsToday: 'logins_today', // non-persistent
LastLogin : 'last_login', // object { userId, sessionId, userName, userRealName, timestamp }; non-persistent LastLogin: 'last_login', // object { userId, sessionId, userName, userRealName, timestamp }; non-persistent
FileBaseAreaStats: 'file_base_area_stats', // object - see file_base_area.js::getAreaStats FileBaseAreaStats: 'file_base_area_stats', // object - see file_base_area.js::getAreaStats
FileUlTotalCount: 'ul_total_count', FileUlTotalCount: 'ul_total_count',
@ -18,26 +18,26 @@ module.exports = {
FileDlTotalCount: 'dl_total_count', FileDlTotalCount: 'dl_total_count',
FileDlTotalBytes: 'dl_total_bytes', FileDlTotalBytes: 'dl_total_bytes',
FileUlTodayCount : 'ul_today_count', // non-persistent FileUlTodayCount: 'ul_today_count', // non-persistent
FileUlTodayBytes : 'ul_today_bytes', // non-persistent FileUlTodayBytes: 'ul_today_bytes', // non-persistent
FileDlTodayCount : 'dl_today_count', // non-persistent FileDlTodayCount: 'dl_today_count', // non-persistent
FileDlTodayBytes : 'dl_today_bytes', // non-persistent FileDlTodayBytes: 'dl_today_bytes', // non-persistent
MessageTotalCount : 'message_post_total_count', // total non-private messages on the system; non-persistent MessageTotalCount: 'message_post_total_count', // total non-private messages on the system; non-persistent
MessagesToday : 'message_post_today', // non-private messages posted/imported today; non-persistent MessagesToday: 'message_post_today', // non-private messages posted/imported today; non-persistent
SysOpUsername : 'sysop_username', // non-persistent SysOpUsername: 'sysop_username', // non-persistent
SysOpRealName : 'sysop_real_name', // non-persistent SysOpRealName: 'sysop_real_name', // non-persistent
SysOpLocation : 'sysop_location', // non-persistent SysOpLocation: 'sysop_location', // non-persistent
SysOpAffiliations : 'sysop_affiliation', // non-persistent SysOpAffiliations: 'sysop_affiliation', // non-persistent
SysOpSex : 'sysop_sex', // non-persistent SysOpSex: 'sysop_sex', // non-persistent
SysOpEmailAddress : 'sysop_email_address', // non-persistent SysOpEmailAddress: 'sysop_email_address', // non-persistent
NextRandomRumor : 'random_rumor', NextRandomRumor: 'random_rumor',
SystemMemoryStats : 'system_memory_stats', // object { totalBytes, freeBytes }; non-persistent SystemMemoryStats: 'system_memory_stats', // object { totalBytes, freeBytes }; non-persistent
SystemLoadStats : 'system_load_stats', // object { average, current }; non-persistent SystemLoadStats: 'system_load_stats', // object { average, current }; non-persistent
TotalUserCount : 'user_total_count', // non-persistent TotalUserCount: 'user_total_count', // non-persistent
NewUsersTodayCount : 'user_new_today_count', // non-persistent NewUsersTodayCount: 'user_new_today_count', // non-persistent
}; };

View File

@ -217,10 +217,15 @@ exports.getModule = class TelnetBridgeModule extends MenuModule {
}); });
telnetConnection.on('end', err => { telnetConnection.on('end', err => {
self.client.removeListener('key press', connectionKeyPressHandler); self.client.removeListener(
'key press',
connectionKeyPressHandler
);
if(err) { if (err) {
self.client.log.warn(`Telnet bridge connection error: ${err.message}`); self.client.log.warn(
`Telnet bridge connection error: ${err.message}`
);
} }
callback( callback(

View File

@ -364,17 +364,30 @@ exports.ThemeManager = class ThemeManager {
const format = Config().theme.timeFormat[style] || 'h:mm a'; const format = Config().theme.timeFormat[style] || 'h:mm a';
return _.get(theme, `customization.defaults.timeFormat.${style}`, format); return _.get(theme, `customization.defaults.timeFormat.${style}`, format);
}, },
getDateTimeFormat : function(style = 'short') { getDateTimeFormat: function (style = 'short') {
const format = Config().theme.dateTimeFormat[style] || 'MM/DD/YYYY h:mm a'; const format =
return _.get(theme, `customization.defaults.dateTimeFormat.${style}`, format); Config().theme.dateTimeFormat[style] || 'MM/DD/YYYY h:mm a';
return _.get(
theme,
`customization.defaults.dateTimeFormat.${style}`,
format
);
}, },
getStatusAvailIndicators : function() { getStatusAvailIndicators: function () {
const format = Config().theme.statusAvailableIndicators || [ 'Y', 'N' ]; const format = Config().theme.statusAvailableIndicators || ['Y', 'N'];
return _.get(theme, 'customization.defaults.statusAvailableIndicators', format); return _.get(
theme,
'customization.defaults.statusAvailableIndicators',
format
);
}, },
getStatusVisibleIndicators : function() { getStatusVisibleIndicators: function () {
const format = Config().theme.statusVisibleIndicators || [ 'Y', 'N' ]; const format = Config().theme.statusVisibleIndicators || ['Y', 'N'];
return _.get(theme, 'customization.defaults.statusVisibleIndicators', format); return _.get(
theme,
'customization.defaults.statusVisibleIndicators',
format
);
}, },
}; };
} }

View File

@ -12,22 +12,22 @@ const Log = require('./logger.js').log;
const StatLog = require('./stat_log.js'); const StatLog = require('./stat_log.js');
// deps // deps
const crypto = require('crypto'); const crypto = require('crypto');
const assert = require('assert'); const assert = require('assert');
const async = require('async'); const async = require('async');
const _ = require('lodash'); const _ = require('lodash');
const moment = require('moment'); const moment = require('moment');
const sanatizeFilename = require('sanitize-filename'); const sanatizeFilename = require('sanitize-filename');
const ssh2 = require('ssh2'); const ssh2 = require('ssh2');
module.exports = class User { module.exports = class User {
constructor() { constructor() {
this.userId = 0; this.userId = 0;
this.username = ''; this.username = '';
this.properties = {}; // name:value this.properties = {}; // name:value
this.groups = []; // group membership(s) this.groups = []; // group membership(s)
this.authFactor = User.AuthFactors.None; this.authFactor = User.AuthFactors.None;
this.statusFlags = User.StatusFlags.None; this.statusFlags = User.StatusFlags.None;
} }
// static property accessors // static property accessors
@ -72,10 +72,10 @@ module.exports = class User {
static get StatusFlags() { static get StatusFlags() {
return { return {
None : 0x00000000, None: 0x00000000,
NotAvailable : 0x00000001, // Not currently available for chat, message, page, etc. NotAvailable: 0x00000001, // Not currently available for chat, message, page, etc.
NotVisible : 0x00000002, // Invisible -- does not show online, last callers, etc. NotVisible: 0x00000002, // Invisible -- does not show online, last callers, etc.
} };
} }
isAuthenticated() { isAuthenticated() {
@ -736,21 +736,27 @@ module.exports = class User {
if (!cb && _.isFunction(propsList)) { if (!cb && _.isFunction(propsList)) {
cb = propsList; cb = propsList;
propsList = [ propsList = [
UserProps.RealName, UserProps.Sex, UserProps.EmailAddress, UserProps.RealName,
UserProps.Location, UserProps.Affiliations, UserProps.Sex,
UserProps.EmailAddress,
UserProps.Location,
UserProps.Affiliations,
]; ];
} }
async.waterfall( async.waterfall(
[ [
(callback) => { callback => {
return User.getUserName(userId, callback); return User.getUserName(userId, callback);
}, },
(userName, callback) => { (userName, callback) => {
User.loadProperties(userId, { names : propsList }, (err, props) => { User.loadProperties(userId, { names: propsList }, (err, props) => {
return callback(err, Object.assign({}, props, { user_name : userName })); return callback(
err,
Object.assign({}, props, { user_name: userName })
);
}); });
} },
], ],
(err, userProps) => { (err, userProps) => {
if (err) { if (err) {
@ -904,7 +910,7 @@ module.exports = class User {
`SELECT count() AS user_count `SELECT count() AS user_count
FROM user;`, FROM user;`,
(err, row) => { (err, row) => {
if(err) { if (err) {
return cb(err); return cb(err);
} }
return cb(null, row.user_count); return cb(null, row.user_count);

View File

@ -140,20 +140,30 @@ exports.getModule = class UserConfigModule extends MenuModule {
return self.prevMenu(cb); return self.prevMenu(cb);
} }
self.client.log.info(`User "${self.client.user.username}" updated configuration`); self.client.log.info(
`User "${self.client.user.username}" updated configuration`
);
// //
// New password if it's not empty // New password if it's not empty
// //
if(formData.value.password.length > 0) { if (formData.value.password.length > 0) {
self.client.user.setNewAuthCredentials(formData.value.password, err => { self.client.user.setNewAuthCredentials(
if(err) { formData.value.password,
self.client.log.error( { err : err }, 'Failed storing new authentication credentials'); err => {
} else { if (err) {
self.client.log.info(`User "${self.client.user.username}" updated authentication credentials`); self.client.log.error(
{ err: err },
'Failed storing new authentication credentials'
);
} else {
self.client.log.info(
`User "${self.client.user.username}" updated authentication credentials`
);
}
return self.prevMenu(cb);
} }
return self.prevMenu(cb); );
});
} else { } else {
return self.prevMenu(cb); return self.prevMenu(cb);
} }

View File

@ -25,13 +25,15 @@ module.exports = class UserInterruptQueue {
} else if (opts.omit) { } else if (opts.omit) {
omitNodes = [opts.omit]; omitNodes = [opts.omit];
} }
omitNodes = omitNodes.map(n => _.isNumber(n) ? n : n.node); omitNodes = omitNodes.map(n => (_.isNumber(n) ? n : n.node));
const connOpts = { const connOpts = {
authUsersOnly: true, authUsersOnly: true,
visibleOnly: true, visibleOnly: true,
availOnly: true, availOnly: true,
}; };
opts.clients = getActiveConnections(connOpts).filter(ac => !omitNodes.includes(ac.node)); opts.clients = getActiveConnections(connOpts).filter(
ac => !omitNodes.includes(ac.node)
);
} }
if (!Array.isArray(opts.clients)) { if (!Array.isArray(opts.clients)) {
opts.clients = [opts.clients]; opts.clients = [opts.clients];

View File

@ -21,10 +21,10 @@ const {
const { getFileAreaByTag, getDefaultFileAreaTag } = require('./file_base_area.js'); const { getFileAreaByTag, getDefaultFileAreaTag } = require('./file_base_area.js');
// deps // deps
const async = require('async'); const async = require('async');
const _ = require('lodash'); const _ = require('lodash');
const assert = require('assert'); const assert = require('assert');
const moment = require('moment'); const moment = require('moment');
exports.userLogin = userLogin; exports.userLogin = userLogin;
exports.recordLogin = recordLogin; exports.recordLogin = recordLogin;
@ -38,8 +38,11 @@ function userLogin(client, username, password, options, cb) {
const config = Config(); const config = Config();
if(config.users.badUserNames.includes(username.toLowerCase())) { if (config.users.badUserNames.includes(username.toLowerCase())) {
client.log.info( { username, ip : client.remoteAddress }, `Attempt to login with banned username "${username}"`); client.log.info(
{ username, ip: client.remoteAddress },
`Attempt to login with banned username "${username}"`
);
// slow down a bit to thwart brute force attacks // slow down a bit to thwart brute force attacks
return setTimeout(() => { return setTimeout(() => {
@ -75,7 +78,7 @@ function userLogin(client, username, password, options, cb) {
); // ...but same user ); // ...but same user
}); });
if(existingClientConnection) { if (existingClientConnection) {
client.log.warn( client.log.warn(
{ {
existingNodeId: existingClientConnection.node, existingNodeId: existingClientConnection.node,
@ -193,8 +196,13 @@ function recordLogin(client, cb) {
StatLog.incrementNonPersistentSystemStat(SysProps.LoginsToday, 1); StatLog.incrementNonPersistentSystemStat(SysProps.LoginsToday, 1);
return StatLog.incrementSystemStat(SysProps.LoginCount, 1, callback); return StatLog.incrementSystemStat(SysProps.LoginCount, 1, callback);
}, },
(callback) => { callback => {
return StatLog.setUserStat(user, UserProps.LastLoginTs, loginTimestamp, callback); return StatLog.setUserStat(
user,
UserProps.LastLoginTs,
loginTimestamp,
callback
);
}, },
callback => { callback => {
return StatLog.incrementUserStat(user, UserProps.LoginCount, 1, callback); return StatLog.incrementUserStat(user, UserProps.LoginCount, 1, callback);
@ -214,24 +222,24 @@ function recordLogin(client, cb) {
callback callback
); );
}, },
(callback) => { callback => {
// Update live last login information which includes additional // Update live last login information which includes additional
// (pre-resolved) information such as user name/etc. // (pre-resolved) information such as user name/etc.
const lastLogin = { const lastLogin = {
userId : user.userId, userId: user.userId,
sessionId : user.sessionId, sessionId: user.sessionId,
userName : user.username, userName: user.username,
realName : user.getProperty(UserProps.RealName), realName: user.getProperty(UserProps.RealName),
affiliation : user.getProperty(UserProps.Affiliations), affiliation: user.getProperty(UserProps.Affiliations),
emailAddress : user.getProperty(UserProps.EmailAddress), emailAddress: user.getProperty(UserProps.EmailAddress),
sex : user.getProperty(UserProps.Sex), sex: user.getProperty(UserProps.Sex),
location : user.getProperty(UserProps.Location), location: user.getProperty(UserProps.Location),
timestamp : moment(loginTimestamp), timestamp: moment(loginTimestamp),
}; };
StatLog.setNonPersistentSystemStat(SysProps.LastLogin, lastLogin); StatLog.setNonPersistentSystemStat(SysProps.LastLogin, lastLogin);
return callback(null); return callback(null);
} },
], ],
err => { err => {
return cb(err); return cb(err);
@ -247,6 +255,9 @@ function transformLoginError(err, client, username) {
err = Errors.BadLogin('To many failed login attempts', ErrorReasons.TooMany); err = Errors.BadLogin('To many failed login attempts', ErrorReasons.TooMany);
} }
client.log.warn( { username, ip : client.remoteAddress, reason : err.message }, `Failed login attempt for user "${username}", ${client.friendlyRemoteAddress()}`); client.log.warn(
{ username, ip: client.remoteAddress, reason: err.message },
`Failed login attempt for user "${username}", ${client.friendlyRemoteAddress()}`
);
return err; return err;
} }

View File

@ -59,8 +59,8 @@ module.exports = {
MinutesOnlineTotalCount: 'minutes_online_total_count', MinutesOnlineTotalCount: 'minutes_online_total_count',
NewPrivateMailCount : 'new_private_mail_count', // non-persistent NewPrivateMailCount: 'new_private_mail_count', // non-persistent
NewAddressedToMessageCount : 'new_addr_to_msg_count', // non-persistent NewAddressedToMessageCount: 'new_addr_to_msg_count', // non-persistent
SSHPubKey: 'ssh_public_key', // OpenSSH format (ssh-keygen, etc.) SSHPubKey: 'ssh_public_key', // OpenSSH format (ssh-keygen, etc.)
AuthFactor1Types: 'auth_factor1_types', // List of User.AuthFactor1Types value(s) AuthFactor1Types: 'auth_factor1_types', // List of User.AuthFactor1Types value(s)
AuthFactor2OTP: 'auth_factor2_otp', // If present, OTP type for 2FA. See OTPTypes AuthFactor2OTP: 'auth_factor2_otp', // If present, OTP type for 2FA. See OTPTypes

View File

@ -9,16 +9,16 @@ const formatString = require('./string_format');
const pipeToAnsi = require('./color_codes.js').pipeToAnsi; const pipeToAnsi = require('./color_codes.js').pipeToAnsi;
// deps // deps
const util = require('util'); const util = require('util');
const _ = require('lodash'); const _ = require('lodash');
const { throws } = require('assert'); const { throws } = require('assert');
exports.VerticalMenuView = VerticalMenuView; exports.VerticalMenuView = VerticalMenuView;
function VerticalMenuView(options) { function VerticalMenuView(options) {
options.cursor = options.cursor || 'hide'; options.cursor = options.cursor || 'hide';
options.justify = options.justify || 'left'; options.justify = options.justify || 'left';
this.focusItemAtTop = true; this.focusItemAtTop = true;
MenuView.call(this, options); MenuView.call(this, options);
@ -53,8 +53,8 @@ function VerticalMenuView(options) {
const topIndex = (this.focusItemAtTop ? throws.focusedItemIndex : 0) || 0; const topIndex = (this.focusItemAtTop ? throws.focusedItemIndex : 0) || 0;
self.viewWindow = { self.viewWindow = {
top : topIndex, top: topIndex,
bottom : Math.min(topIndex + self.maxVisibleItems, self.items.length) - 1, bottom: Math.min(topIndex + self.maxVisibleItems, self.items.length) - 1,
}; };
}; };
@ -109,14 +109,17 @@ function VerticalMenuView(options) {
this.setRenderCacheItem(index, text, item.focused); this.setRenderCacheItem(index, text, item.focused);
}; };
this.drawRemovedItem = function(index) { this.drawRemovedItem = function (index) {
if (index <= this.items.length - 1) { if (index <= this.items.length - 1) {
return; return;
} }
const row = this.position.row + index; const row = this.position.row + index;
this.client.term.rawWrite(`${ansi.goto(row, this.position.col)}${ansi.normal()}${this.fillChar.repeat(this.dimens.width)}`) this.client.term.rawWrite(
`${ansi.goto(row, this.position.col)}${ansi.normal()}${this.fillChar.repeat(
this.dimens.width
)}`
);
}; };
} }
util.inherits(VerticalMenuView, MenuView); util.inherits(VerticalMenuView, MenuView);
@ -188,15 +191,15 @@ VerticalMenuView.prototype.setFocus = function (focused) {
VerticalMenuView.prototype.setFocusItemIndex = function (index) { VerticalMenuView.prototype.setFocusItemIndex = function (index) {
VerticalMenuView.super_.prototype.setFocusItemIndex.call(this, index); // sets this.focusedItemIndex VerticalMenuView.super_.prototype.setFocusItemIndex.call(this, index); // sets this.focusedItemIndex
const remainAfterFocus = this.focusItemAtTop ? const remainAfterFocus = this.focusItemAtTop
this.items.length - index : ? this.items.length - index
this.items.length; : this.items.length;
if(remainAfterFocus >= this.maxVisibleItems) { if (remainAfterFocus >= this.maxVisibleItems) {
const topIndex = (this.focusItemAtTop ? throws.focusedItemIndex : 0) || 0; const topIndex = (this.focusItemAtTop ? throws.focusedItemIndex : 0) || 0;
this.viewWindow = { this.viewWindow = {
top : topIndex, top: topIndex,
bottom : Math.min(topIndex + this.maxVisibleItems, this.items.length) - 1 bottom: Math.min(topIndex + this.maxVisibleItems, this.items.length) - 1,
}; };
this.positionCacheExpired = false; // skip standard behavior this.positionCacheExpired = false; // skip standard behavior
@ -407,7 +410,7 @@ VerticalMenuView.prototype.setItemSpacing = function (itemSpacing) {
this.positionCacheExpired = true; this.positionCacheExpired = true;
}; };
VerticalMenuView.prototype.setPropertyValue = function(propName, value) { VerticalMenuView.prototype.setPropertyValue = function (propName, value) {
if (propName === 'focusItemAtTop' && _.isBoolean(value)) { if (propName === 'focusItemAtTop' && _.isBoolean(value)) {
this.focusItemAtTop = value; this.focusItemAtTop = value;
} }

View File

@ -130,7 +130,7 @@ View.prototype.setPosition = function (pos) {
// //
// Allow the following forms: [row, col], { row : r, col : c }, or (row, col) // Allow the following forms: [row, col], { row : r, col : c }, or (row, col)
// //
if(Array.isArray(pos)) { if (Array.isArray(pos)) {
this.position.row = pos[0]; this.position.row = pos[0];
this.position.col = pos[1]; this.position.col = pos[1];
} else if (_.isNumber(pos.row) && _.isNumber(pos.col)) { } else if (_.isNumber(pos.row) && _.isNumber(pos.col)) {
@ -285,7 +285,7 @@ View.prototype.setFocusProperty = function (focused) {
this.hasFocus = focused; this.hasFocus = focused;
}; };
View.prototype.setFocus = function(focused) { View.prototype.setFocus = function (focused) {
// Call separate method to differentiate between a value set as a // Call separate method to differentiate between a value set as a
// property vs focus programmatically called. // property vs focus programmatically called.
this.setFocusProperty(focused); this.setFocusProperty(focused);

View File

@ -257,56 +257,59 @@ function ViewController(options) {
} }
}; };
this.applyViewConfig = function(config, cb) { this.applyViewConfig = function (config, cb) {
let highestId = 1; let highestId = 1;
let submitId; let submitId;
let initialFocusId = 1; let initialFocusId = 1;
async.each(Object.keys(config.mci || {}), function entry(mci, nextItem) { async.each(
const mciMatch = mci.match(MCI_REGEXP); // :TODO: How to handle auto-generated IDs???? Object.keys(config.mci || {}),
if(null === mciMatch) { function entry(mci, nextItem) {
self.client.log.warn( { mci : mci }, 'Unable to parse MCI code'); const mciMatch = mci.match(MCI_REGEXP); // :TODO: How to handle auto-generated IDs????
return; if (null === mciMatch) {
} self.client.log.warn({ mci: mci }, 'Unable to parse MCI code');
return;
const viewId = parseInt(mciMatch[2]);
assert(!isNaN(viewId), 'Cannot parse view ID: ' + mciMatch[2]); // shouldn't be possible with RegExp used
if(viewId > highestId) {
highestId = viewId;
}
const view = self.getView(viewId);
if(!view) {
return nextItem(null);
}
const mciConf = config.mci[mci];
self.setViewPropertiesFromMCIConf(view, mciConf);
if(mciConf.focus) {
initialFocusId = viewId;
}
if(true === view.submit) {
submitId = viewId;
}
nextItem(null);
},
err => {
// default to highest ID if no 'submit' entry present
if(!submitId) {
const highestIdView = self.getView(highestId);
if(highestIdView) {
highestIdView.submit = true;
} }
}
return cb(err, { initialFocusId : initialFocusId } ); const viewId = parseInt(mciMatch[2]);
}); assert(!isNaN(viewId), 'Cannot parse view ID: ' + mciMatch[2]); // shouldn't be possible with RegExp used
if (viewId > highestId) {
highestId = viewId;
}
const view = self.getView(viewId);
if (!view) {
return nextItem(null);
}
const mciConf = config.mci[mci];
self.setViewPropertiesFromMCIConf(view, mciConf);
if (mciConf.focus) {
initialFocusId = viewId;
}
if (true === view.submit) {
submitId = viewId;
}
nextItem(null);
},
err => {
// default to highest ID if no 'submit' entry present
if (!submitId) {
const highestIdView = self.getView(highestId);
if (highestIdView) {
highestIdView.submit = true;
}
}
return cb(err, { initialFocusId: initialFocusId });
}
);
}; };
// method for comparing submitted form data to configuration entries // method for comparing submitted form data to configuration entries

View File

@ -2,10 +2,7 @@
const { MenuModule } = require('./menu_module'); const { MenuModule } = require('./menu_module');
const stringFormat = require('./string_format'); const stringFormat = require('./string_format');
const { const { getActiveConnectionList, AllConnections } = require('./client_connections');
getActiveConnectionList,
AllConnections
} = require('./client_connections');
const StatLog = require('./stat_log'); const StatLog = require('./stat_log');
const SysProps = require('./system_property'); const SysProps = require('./system_property');
const UserProps = require('./user_property'); const UserProps = require('./user_property');
@ -19,9 +16,9 @@ const moment = require('moment');
const bunyan = require('bunyan'); const bunyan = require('bunyan');
exports.moduleInfo = { exports.moduleInfo = {
name : 'WFC', name: 'WFC',
desc : 'Semi-Traditional Waiting For Caller', desc: 'Semi-Traditional Waiting For Caller',
author : 'NuSkooler', author: 'NuSkooler',
}; };
const FormIds = { const FormIds = {
@ -31,13 +28,13 @@ const FormIds = {
}; };
const MciViewIds = { const MciViewIds = {
main : { main: {
nodeStatus : 1, nodeStatus: 1,
quickLogView : 2, quickLogView: 2,
nodeStatusSelection : 3, nodeStatusSelection: 3,
customRangeStart : 10, customRangeStart: 10,
} },
}; };
// Secure + 2FA + root user + 'wfc' group. // Secure + 2FA + root user + 'wfc' group.
@ -49,30 +46,32 @@ exports.getModule = class WaitingForCallerModule extends MenuModule {
constructor(options) { constructor(options) {
super(options); super(options);
this.config = Object.assign({}, _.get(options, 'menuConfig.config'), { extraArgs : options.extraArgs }); this.config = Object.assign({}, _.get(options, 'menuConfig.config'), {
extraArgs: options.extraArgs,
});
this.config.acs = this.config.acs || DefaultACS; this.config.acs = this.config.acs || DefaultACS;
if (!this.config.acs.includes('SC')) { if (!this.config.acs.includes('SC')) {
this.config.acs = 'SC' + this.config.acs; // secure connection at the very least this.config.acs = 'SC' + this.config.acs; // secure connection at the very least
} }
this.selectedNodeStatusIndex = -1; // no selection this.selectedNodeStatusIndex = -1; // no selection
this.menuMethods = { this.menuMethods = {
toggleAvailable : (formData, extraArgs, cb) => { toggleAvailable: (formData, extraArgs, cb) => {
const avail = this.client.user.isAvailable(); const avail = this.client.user.isAvailable();
this.client.user.setAvailability(!avail); this.client.user.setAvailability(!avail);
return this._refreshAll(cb); return this._refreshAll(cb);
}, },
toggleVisible : (formData, extraArgs, cb) => { toggleVisible: (formData, extraArgs, cb) => {
const visible = this.client.user.isVisible(); const visible = this.client.user.isVisible();
this.client.user.setVisibility(!visible); this.client.user.setVisibility(!visible);
return this._refreshAll(cb); return this._refreshAll(cb);
}, },
displayHelp : (formData, extraArgs, cb) => { displayHelp: (formData, extraArgs, cb) => {
return this._displayHelpPage(cb); return this._displayHelpPage(cb);
}, },
setNodeStatusSelection : (formData, extraArgs, cb) => { setNodeStatusSelection: (formData, extraArgs, cb) => {
const nodeStatusView = this.getView('main', MciViewIds.main.nodeStatus); const nodeStatusView = this.getView('main', MciViewIds.main.nodeStatus);
if (!nodeStatusView) { if (!nodeStatusView) {
return cb(null); return cb(null);
@ -89,19 +88,19 @@ exports.getModule = class WaitingForCallerModule extends MenuModule {
this._selectNodeByIndex(nodeStatusView, this.selectedNodeStatusIndex); this._selectNodeByIndex(nodeStatusView, this.selectedNodeStatusIndex);
} }
return cb(null); return cb(null);
} },
} };
} }
initSequence() { initSequence() {
async.series( async.series(
[ [
(callback) => { callback => {
return this.beforeArt(callback); return this.beforeArt(callback);
}, },
(callback) => { callback => {
return this._displayMainPage(false, callback); return this._displayMainPage(false, callback);
} },
], ],
() => { () => {
this.finishedLoading(); this.finishedLoading();
@ -112,7 +111,7 @@ exports.getModule = class WaitingForCallerModule extends MenuModule {
_displayMainPage(clearScreen, cb) { _displayMainPage(clearScreen, cb) {
async.series( async.series(
[ [
(callback) => { callback => {
return this.displayArtAndPrepViewController( return this.displayArtAndPrepViewController(
'main', 'main',
FormIds.main, FormIds.main,
@ -120,34 +119,49 @@ exports.getModule = class WaitingForCallerModule extends MenuModule {
callback callback
); );
}, },
(callback) => { callback => {
const quickLogView = this.getView('main', MciViewIds.main.quickLogView); const quickLogView = this.getView(
'main',
MciViewIds.main.quickLogView
);
if (!quickLogView) { if (!quickLogView) {
return callback(null); return callback(null);
} }
if (!this.logRingBuffer) { if (!this.logRingBuffer) {
const logLevel = this.config.quickLogLevel || // WFC specific const logLevel =
_.get(Config(), 'logging.rotatingFile.level') || // ...or system setting this.config.quickLogLevel || // WFC specific
'info'; // ...or default to info _.get(Config(), 'logging.rotatingFile.level') || // ...or system setting
'info'; // ...or default to info
this.logRingBuffer = new bunyan.RingBuffer({ limit : quickLogView.dimens.height || 24 }); this.logRingBuffer = new bunyan.RingBuffer({
limit: quickLogView.dimens.height || 24,
});
Log.log.addStream({ Log.log.addStream({
name : 'wfc-ringbuffer', name: 'wfc-ringbuffer',
type : 'raw', type: 'raw',
level : logLevel, level: logLevel,
stream : this.logRingBuffer stream: this.logRingBuffer,
}); });
} }
const nodeStatusView = this.getView('main', MciViewIds.main.nodeStatus); const nodeStatusView = this.getView(
const nodeStatusSelectionView = this.getView('main', MciViewIds.main.nodeStatusSelection); 'main',
const nodeStatusSelectionFormat = this.config.nodeStatusSelectionFormat || '{text}'; MciViewIds.main.nodeStatus
);
const nodeStatusSelectionView = this.getView(
'main',
MciViewIds.main.nodeStatusSelection
);
const nodeStatusSelectionFormat =
this.config.nodeStatusSelectionFormat || '{text}';
if (nodeStatusView && nodeStatusSelectionView) { if (nodeStatusView && nodeStatusSelectionView) {
nodeStatusView.on('index update', index => { nodeStatusView.on('index update', index => {
const item = nodeStatusView.getItems()[index]; const item = nodeStatusView.getItems()[index];
if (item) { if (item) {
nodeStatusSelectionView.setText(stringFormat(nodeStatusSelectionFormat, item)); nodeStatusSelectionView.setText(
stringFormat(nodeStatusSelectionFormat, item)
);
// :TODO: Update view // :TODO: Update view
// :TODO: this is not triggered by key-presses (1, 2, ...) -- we need to handle that as well // :TODO: this is not triggered by key-presses (1, 2, ...) -- we need to handle that as well
} }
@ -156,9 +170,9 @@ exports.getModule = class WaitingForCallerModule extends MenuModule {
return callback(null); return callback(null);
}, },
(callback) => { callback => {
return this._refreshAll(callback); return this._refreshAll(callback);
} },
], ],
err => { err => {
if (!err) { if (!err) {
@ -172,15 +186,11 @@ exports.getModule = class WaitingForCallerModule extends MenuModule {
_displayHelpPage(cb) { _displayHelpPage(cb) {
this._stopRefreshing(); this._stopRefreshing();
this.displayAsset( this.displayAsset(this.menuConfig.config.art.help, { clearScreen: true }, () => {
this.menuConfig.config.art.help, this.client.waitForKeyPress(() => {
{ clearScreen : true }, return this._displayMainPage(true, cb);
() => { });
this.client.waitForKeyPress( () => { });
return this._displayMainPage(true, cb);
});
}
);
} }
enter() { enter() {
@ -207,11 +217,15 @@ exports.getModule = class WaitingForCallerModule extends MenuModule {
const vis = this.config.opVisibility || 'current'; const vis = this.config.opVisibility || 'current';
switch (vis) { switch (vis) {
case 'hidden' : this.client.user.setVisibility(false); break; case 'hidden':
case 'visible' : this.client.user.setVisibility(true); break; this.client.user.setVisibility(false);
default : break; break;
case 'visible':
this.client.user.setVisibility(true);
break;
default:
break;
} }
} }
_restoreOpVisibility() { _restoreOpVisibility() {
@ -223,7 +237,7 @@ exports.getModule = class WaitingForCallerModule extends MenuModule {
this._stopRefreshing(); this._stopRefreshing();
} }
this.mainRefreshTimer = setInterval( () => { this.mainRefreshTimer = setInterval(() => {
this._refreshAll(); this._refreshAll();
}, MainStatRefreshTimeMs); }, MainStatRefreshTimeMs);
} }
@ -238,23 +252,23 @@ exports.getModule = class WaitingForCallerModule extends MenuModule {
_refreshAll(cb) { _refreshAll(cb) {
async.series( async.series(
[ [
(callback) => { callback => {
return this._refreshStats(callback); return this._refreshStats(callback);
}, },
(callback) => { callback => {
return this._refreshNodeStatus(callback); return this._refreshNodeStatus(callback);
}, },
(callback) => { callback => {
return this._refreshQuickLog(callback); return this._refreshQuickLog(callback);
}, },
(callback) => { callback => {
this.updateCustomViewTextsWithFilter( this.updateCustomViewTextsWithFilter(
'main', 'main',
MciViewIds.main.customRangeStart, MciViewIds.main.customRangeStart,
this.stats this.stats
); );
return callback(null); return callback(null);
} },
], ],
err => { err => {
if (cb) { if (cb) {
@ -265,46 +279,47 @@ exports.getModule = class WaitingForCallerModule extends MenuModule {
} }
_getStatusStrings(isAvailable, isVisible) { _getStatusStrings(isAvailable, isVisible) {
const availIndicators = Array.isArray(this.config.statusAvailableIndicators) ? const availIndicators = Array.isArray(this.config.statusAvailableIndicators)
this.config.statusAvailableIndicators : ? this.config.statusAvailableIndicators
this.client.currentTheme.helpers.getStatusAvailableIndicators(); : this.client.currentTheme.helpers.getStatusAvailableIndicators();
const visIndicators = Array.isArray(this.config.statusVisibleIndicators) ? const visIndicators = Array.isArray(this.config.statusVisibleIndicators)
this.config.statusVisibleIndicators : ? this.config.statusVisibleIndicators
this.client.currentTheme.helpers.getStatusVisibleIndicators(); : this.client.currentTheme.helpers.getStatusVisibleIndicators();
return [ return [
isAvailable ? (availIndicators[1] || 'Y') : (availIndicators[0] || 'N'), isAvailable ? availIndicators[1] || 'Y' : availIndicators[0] || 'N',
isVisible ? (visIndicators[1] || 'Y') : (visIndicators[0] || 'N'), isVisible ? visIndicators[1] || 'Y' : visIndicators[0] || 'N',
]; ];
} }
_refreshStats(cb) { _refreshStats(cb) {
const fileAreaStats = StatLog.getSystemStat(SysProps.FileBaseAreaStats) || {}; const fileAreaStats = StatLog.getSystemStat(SysProps.FileBaseAreaStats) || {};
const sysMemStats = StatLog.getSystemStat(SysProps.SystemMemoryStats) || {}; const sysMemStats = StatLog.getSystemStat(SysProps.SystemMemoryStats) || {};
const sysLoadStats = StatLog.getSystemStat(SysProps.SystemLoadStats) || {}; const sysLoadStats = StatLog.getSystemStat(SysProps.SystemLoadStats) || {};
const lastLoginStats = StatLog.getSystemStat(SysProps.LastLogin); const lastLoginStats = StatLog.getSystemStat(SysProps.LastLogin);
const now = moment(); const now = moment();
const [availIndicator, visIndicator] = this._getStatusStrings( const [availIndicator, visIndicator] = this._getStatusStrings(
this.client.user.isAvailable(), this.client.user.isVisible() this.client.user.isAvailable(),
this.client.user.isVisible()
); );
this.stats = { this.stats = {
// Date/Time // Date/Time
nowDate : now.format(this.getDateFormat()), nowDate: now.format(this.getDateFormat()),
nowTime : now.format(this.getTimeFormat()), nowTime: now.format(this.getTimeFormat()),
now : now.format(this._dateTimeFormat('now')), now: now.format(this._dateTimeFormat('now')),
// Current process (our Node.js service) // Current process (our Node.js service)
processUptimeSeconds : process.uptime(), processUptimeSeconds: process.uptime(),
// Totals // Totals
totalCalls : StatLog.getSystemStatNum(SysProps.LoginCount), totalCalls: StatLog.getSystemStatNum(SysProps.LoginCount),
totalPosts : StatLog.getSystemStatNum(SysProps.MessageTotalCount), totalPosts: StatLog.getSystemStatNum(SysProps.MessageTotalCount),
totalUsers : StatLog.getSystemStatNum(SysProps.TotalUserCount), totalUsers: StatLog.getSystemStatNum(SysProps.TotalUserCount),
totalFiles : fileAreaStats.totalFiles || 0, totalFiles: fileAreaStats.totalFiles || 0,
totalFileBytes : fileAreaStats.totalFileBytes || 0, totalFileBytes: fileAreaStats.totalFileBytes || 0,
// totalUploads : // totalUploads :
// totalUploadBytes : // totalUploadBytes :
@ -312,30 +327,42 @@ exports.getModule = class WaitingForCallerModule extends MenuModule {
// totalDownloadBytes : // totalDownloadBytes :
// Today's Stats // Today's Stats
callsToday : StatLog.getSystemStatNum(SysProps.LoginsToday), callsToday: StatLog.getSystemStatNum(SysProps.LoginsToday),
postsToday : StatLog.getSystemStatNum(SysProps.MessagesToday), postsToday: StatLog.getSystemStatNum(SysProps.MessagesToday),
uploadsToday : StatLog.getSystemStatNum(SysProps.FileUlTodayCount), uploadsToday: StatLog.getSystemStatNum(SysProps.FileUlTodayCount),
uploadBytesToday : StatLog.getSystemStatNum(SysProps.FileUlTodayBytes), uploadBytesToday: StatLog.getSystemStatNum(SysProps.FileUlTodayBytes),
downloadsToday : StatLog.getSystemStatNum(SysProps.FileDlTodayCount), downloadsToday: StatLog.getSystemStatNum(SysProps.FileDlTodayCount),
downloadBytesToday : StatLog.getSystemStatNum(SysProps.FileDlTodayBytes), downloadBytesToday: StatLog.getSystemStatNum(SysProps.FileDlTodayBytes),
newUsersToday : StatLog.getSystemStatNum(SysProps.NewUsersTodayCount), newUsersToday: StatLog.getSystemStatNum(SysProps.NewUsersTodayCount),
// Current // Current
currentUserName : this.client.user.username, currentUserName: this.client.user.username,
currentUserRealName : this.client.user.getProperty(UserProps.RealName) || this.client.user.username, currentUserRealName:
availIndicator : availIndicator, this.client.user.getProperty(UserProps.RealName) ||
visIndicator : visIndicator, this.client.user.username,
lastLoginUserName : lastLoginStats.userName, availIndicator: availIndicator,
lastLoginRealName : lastLoginStats.realName, visIndicator: visIndicator,
lastLoginDate : moment(lastLoginStats.timestamp).format(this.getDateFormat()), lastLoginUserName: lastLoginStats.userName,
lastLoginTime : moment(lastLoginStats.timestamp).format(this.getTimeFormat()), lastLoginRealName: lastLoginStats.realName,
lastLogin : moment(lastLoginStats.timestamp).format(this._dateTimeFormat('lastLogin')), lastLoginDate: moment(lastLoginStats.timestamp).format(this.getDateFormat()),
totalMemoryBytes : sysMemStats.totalBytes || 0, lastLoginTime: moment(lastLoginStats.timestamp).format(this.getTimeFormat()),
freeMemoryBytes : sysMemStats.freeBytes || 0, lastLogin: moment(lastLoginStats.timestamp).format(
systemAvgLoad : sysLoadStats.average || 0, this._dateTimeFormat('lastLogin')
systemCurrentLoad : sysLoadStats.current || 0, ),
newPrivateMail : StatLog.getUserStatNumByClient(this.client, UserProps.NewPrivateMailCount, MailCountTTLSeconds), totalMemoryBytes: sysMemStats.totalBytes || 0,
newMessagesAddrTo : StatLog.getUserStatNumByClient(this.client, UserProps.NewAddressedToMessageCount, MailCountTTLSeconds), freeMemoryBytes: sysMemStats.freeBytes || 0,
systemAvgLoad: sysLoadStats.average || 0,
systemCurrentLoad: sysLoadStats.current || 0,
newPrivateMail: StatLog.getUserStatNumByClient(
this.client,
UserProps.NewPrivateMailCount,
MailCountTTLSeconds
),
newMessagesAddrTo: StatLog.getUserStatNumByClient(
this.client,
UserProps.NewAddressedToMessageCount,
MailCountTTLSeconds
),
}; };
return cb(null); return cb(null);
@ -364,32 +391,37 @@ exports.getModule = class WaitingForCallerModule extends MenuModule {
.map(ac => { .map(ac => {
// Handle pre-authenticated // Handle pre-authenticated
if (!ac.authenticated) { if (!ac.authenticated) {
ac.text = ac.userName = '*Pre Auth*'; ac.text = ac.userName = '*Pre Auth*';
ac.action = 'Logging In'; ac.action = 'Logging In';
} }
const [availIndicator, visIndicator] = this._getStatusStrings(ac.isAvailable, ac.isVisible); const [availIndicator, visIndicator] = this._getStatusStrings(
ac.isAvailable,
ac.isVisible
);
const timeOn = ac.timeOn || moment.duration(0); const timeOn = ac.timeOn || moment.duration(0);
return Object.assign(ac, { return Object.assign(ac, {
availIndicator, availIndicator,
visIndicator, visIndicator,
timeOnMinutes : timeOn.asMinutes(), timeOnMinutes: timeOn.asMinutes(),
timeOn : _.upperFirst(timeOn.humanize()), // make friendly timeOn: _.upperFirst(timeOn.humanize()), // make friendly
affils : ac.affils || 'N/A', affils: ac.affils || 'N/A',
realName : ac.realName || 'N/A', realName: ac.realName || 'N/A',
}); });
}); });
// :TODO: Currently this always redraws due to setItems(). We really need painters alg.; The alternative now is to compare items... yuk. // :TODO: Currently this always redraws due to setItems(). We really need painters alg.; The alternative now is to compare items... yuk.
nodeStatusView.setItems(nodeStatusItems); nodeStatusView.setItems(nodeStatusItems);
this._selectNodeByIndex(nodeStatusView, this.selectedNodeStatusIndex); // redraws this._selectNodeByIndex(nodeStatusView, this.selectedNodeStatusIndex); // redraws
return cb(null); return cb(null);
} }
_refreshQuickLog(cb) { _refreshQuickLog(cb) {
const quickLogView = this.viewControllers.main.getView(MciViewIds.main.quickLogView); const quickLogView = this.viewControllers.main.getView(
MciViewIds.main.quickLogView
);
if (!quickLogView) { if (!quickLogView) {
return cb(null); return cb(null);
} }
@ -407,25 +439,23 @@ exports.getModule = class WaitingForCallerModule extends MenuModule {
} }
const quickLogTimestampFormat = const quickLogTimestampFormat =
this.config.quickLogTimestampFormat || this.config.quickLogTimestampFormat || this.getDateTimeFormat('short');
this.getDateTimeFormat('short');
const levelIndicators = this.config.quickLogLevelIndicators || const levelIndicators = this.config.quickLogLevelIndicators || {
{ trace: 'T',
trace : 'T', debug: 'D',
debug : 'D', info: 'I',
info : 'I', warn: 'W',
warn : 'W', error: 'E',
error : 'E', fatal: 'F',
fatal : 'F', };
};
const makeLevelIndicator = level => {
const makeLevelIndicator = (level) => {
return levelIndicators[level] || '?'; return levelIndicators[level] || '?';
}; };
const quickLogLevelMessagePrefixes = this.config.quickLogLevelMessagePrefixes || {}; const quickLogLevelMessagePrefixes =
this.config.quickLogLevelMessagePrefixes || {};
const prefixMssage = (message, level) => { const prefixMssage = (message, level) => {
const prefix = quickLogLevelMessagePrefixes[level] || ''; const prefix = quickLogLevelMessagePrefixes[level] || '';
return `${prefix}${message}`; return `${prefix}${message}`;
@ -434,12 +464,12 @@ exports.getModule = class WaitingForCallerModule extends MenuModule {
const logItems = records.map(rec => { const logItems = records.map(rec => {
const level = bunyan.nameFromLevel[rec.level]; const level = bunyan.nameFromLevel[rec.level];
return { return {
timestamp : moment(rec.time).format(quickLogTimestampFormat), timestamp: moment(rec.time).format(quickLogTimestampFormat),
level : rec.level, level: rec.level,
levelIndicator : makeLevelIndicator(level), levelIndicator: makeLevelIndicator(level),
nodeId : rec.nodeId || '*', nodeId: rec.nodeId || '*',
sessionId : rec.sessionId || '', sessionId: rec.sessionId || '',
message : prefixMssage(rec.msg, level), message: prefixMssage(rec.msg, level),
}; };
}); });
@ -454,4 +484,3 @@ exports.getModule = class WaitingForCallerModule extends MenuModule {
return format || this.getDateFormat(); return format || this.getDateFormat();
} }
}; };

View File

@ -2,11 +2,12 @@
'use strict'; 'use strict';
// ENiGMA½ // ENiGMA½
const { MenuModule } = require('./menu_module.js'); const { MenuModule } = require('./menu_module.js');
const { const {
getActiveConnectionList, getActiveConnectionList,
UserVisibleConnections } = require('./client_connections.js'); UserVisibleConnections,
const { Errors } = require('./enig_error.js'); } = require('./client_connections.js');
const { Errors } = require('./enig_error.js');
// deps // deps
const async = require('async'); const async = require('async');
@ -51,9 +52,14 @@ exports.getModule = class WhosOnlineModule extends MenuModule {
); );
} }
const onlineList = getActiveConnectionList(UserVisibleConnections).slice(0, onlineListView.height).map( const onlineList = getActiveConnectionList(UserVisibleConnections)
oe => Object.assign(oe, { text : oe.userName, timeOn : _.upperFirst(oe.timeOn.humanize()) }) .slice(0, onlineListView.height)
); .map(oe =>
Object.assign(oe, {
text: oe.userName,
timeOn: _.upperFirst(oe.timeOn.humanize()),
})
);
onlineListView.setItems(onlineList); onlineListView.setItems(onlineList);
onlineListView.redraw(); onlineListView.redraw();

View File

@ -1,72 +1,72 @@
{ {
"name": "enigma-bbs", "name": "enigma-bbs",
"version": "0.0.13-beta", "version": "0.0.13-beta",
"description": "ENiGMA½ Bulletin Board System", "description": "ENiGMA½ Bulletin Board System",
"author": "Bryan Ashby <bryan@l33t.codes>", "author": "Bryan Ashby <bryan@l33t.codes>",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"scripts": { "scripts": {
"start": "node main.js" "start": "node main.js"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/NuSkooler/enigma-bbs.git" "url": "https://github.com/NuSkooler/enigma-bbs.git"
}, },
"homepage": "https://github.com/NuSkooler/enigma-bbs", "homepage": "https://github.com/NuSkooler/enigma-bbs",
"bugs": { "bugs": {
"url": "https://github.com/NuSkooler/enigma-bbs/issues" "url": "https://github.com/NuSkooler/enigma-bbs/issues"
}, },
"keywords": [ "keywords": [
"bbs", "bbs",
"telnet", "telnet",
"ssh", "ssh",
"retro" "retro"
], ],
"dependencies": { "dependencies": {
"@breejs/later": "4.1.0", "@breejs/later": "4.1.0",
"async": "^3.2.3", "async": "^3.2.3",
"binary-parser": "2.0.2", "binary-parser": "2.0.2",
"buffers": "github:NuSkooler/node-buffers", "buffers": "github:NuSkooler/node-buffers",
"bunyan": "1.8.15", "bunyan": "1.8.15",
"deepdash": "^5.3.9", "deepdash": "^5.3.9",
"exiftool": "^0.0.3", "exiftool": "^0.0.3",
"fs-extra": "^10.0.1", "fs-extra": "^10.0.1",
"glob": "^7.2.0", "glob": "^7.2.0",
"graceful-fs": "^4.2.10", "graceful-fs": "^4.2.10",
"hashids": "^2.2.10", "hashids": "^2.2.10",
"hjson": "3.2.2", "hjson": "3.2.2",
"iconv-lite": "0.6.3", "iconv-lite": "0.6.3",
"ini-config-parser": "^1.0.4", "ini-config-parser": "^1.0.4",
"inquirer": "^8.2.2", "inquirer": "^8.2.2",
"lodash": "4.17.21", "lodash": "4.17.21",
"lru-cache": "^7.8.0", "lru-cache": "^7.8.0",
"mime-types": "^2.1.35", "mime-types": "^2.1.35",
"minimist": "^1.2.6", "minimist": "^1.2.6",
"moment": "^2.29.2", "moment": "^2.29.2",
"nntp-server": "^1.0.3", "nntp-server": "^1.0.3",
"node-pty": "0.10.1", "node-pty": "0.10.1",
"nodemailer": "^6.7.3", "nodemailer": "^6.7.3",
"otplib": "11.0.1", "otplib": "11.0.1",
"qrcode-generator": "^1.4.4", "qrcode-generator": "^1.4.4",
"rlogin": "^1.0.0", "rlogin": "^1.0.0",
"sane": "5.0.1", "sane": "5.0.1",
"sanitize-filename": "^1.6.3", "sanitize-filename": "^1.6.3",
"sqlite3": "^4.2.0", "sqlite3": "^4.2.0",
"sqlite3-trans": "^1.2.2", "sqlite3-trans": "^1.2.2",
"ssh2": "^1.9.0", "ssh2": "^1.9.0",
"telnet-socket": "^0.2.3", "telnet-socket": "^0.2.3",
"temptmp": "^1.1.0", "temptmp": "^1.1.0",
"uuid": "8.3.2", "uuid": "8.3.2",
"uuid-parse": "1.1.0", "uuid-parse": "1.1.0",
"ws": "7.4.3", "ws": "7.4.3",
"yazl": "^2.5.1", "yazl": "^2.5.1",
"systeminformation" : "^5.11.14" "systeminformation": "^5.11.14"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^8.13.0", "eslint": "^8.13.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
"prettier": "2.6.2" "prettier": "2.6.2"
}, },
"engines": { "engines": {
"node": ">=14" "node": ">=14"
} }
} }