Re-apply some Prettier formatting after merge
This commit is contained in:
parent
c93b8cda81
commit
9172fdda9d
|
@ -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) {
|
||||||
|
|
125
core/bbs.js
125
core/bbs.js
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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})`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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
|
||||||
);
|
);
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -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
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
|
@ -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})`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
12
core/nua.js
12
core/nua.js
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
108
core/stat_log.js
108
core/stat_log.js
|
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
54
core/user.js
54
core/user.js
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
299
core/wfc.js
299
core/wfc.js
|
@ -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();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
140
package.json
140
package.json
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue