Table output, more fields, and --sort for './oputil.js user list'
This commit is contained in:
parent
5b1c11b1bc
commit
b971876795
|
@ -69,6 +69,18 @@ Actions:
|
||||||
info arguments:
|
info arguments:
|
||||||
--security Include security information in output
|
--security Include security information in output
|
||||||
|
|
||||||
|
list arguments:
|
||||||
|
--sort SORT_BY Specify field to sort by
|
||||||
|
|
||||||
|
Valid SORT_BY values:
|
||||||
|
id : User ID
|
||||||
|
username : Username
|
||||||
|
realname : Real name
|
||||||
|
status : Account status
|
||||||
|
created : Account creation date
|
||||||
|
lastlogin : Last login timestamp
|
||||||
|
logins : Login count
|
||||||
|
|
||||||
2fa-otp arguments:
|
2fa-otp arguments:
|
||||||
--qr-type TYPE Specify QR code type
|
--qr-type TYPE Specify QR code type
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ const async = require('async');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
|
const Table = require('easy-table');
|
||||||
|
|
||||||
exports.handleUserCommand = handleUserCommand;
|
exports.handleUserCommand = handleUserCommand;
|
||||||
|
|
||||||
|
@ -340,7 +341,7 @@ function showUserInfo(user) {
|
||||||
|
|
||||||
const statusDesc = () => {
|
const statusDesc = () => {
|
||||||
const status = user.properties[UserProps.AccountStatus];
|
const status = user.properties[UserProps.AccountStatus];
|
||||||
return _.invert(User.AccountStatus)[status] || 'unknown';
|
return _.invert(User.AccountStatus)[status] || 'N/A';
|
||||||
};
|
};
|
||||||
|
|
||||||
const created = () => {
|
const created = () => {
|
||||||
|
@ -508,7 +509,6 @@ function listUsers() {
|
||||||
// :TODO: --created-since SPEC and --last-called SPEC
|
// :TODO: --created-since SPEC and --last-called SPEC
|
||||||
// --created-since SPEC
|
// --created-since SPEC
|
||||||
// SPEC can be TIMESTAMP or e.g. "-1hour" or "-90days"
|
// SPEC can be TIMESTAMP or e.g. "-1hour" or "-90days"
|
||||||
// :TODO: --sort name|id
|
|
||||||
let listWhat;
|
let listWhat;
|
||||||
if (argv._.length > 2) {
|
if (argv._.length > 2) {
|
||||||
listWhat = argv._[argv._.length - 1];
|
listWhat = argv._[argv._.length - 1];
|
||||||
|
@ -516,6 +516,8 @@ function listUsers() {
|
||||||
listWhat = 'all';
|
listWhat = 'all';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sortBy = (argv.sort || 'id').toLowerCase();
|
||||||
|
|
||||||
const User = require('../../core/user');
|
const User = require('../../core/user');
|
||||||
if (!['all'].concat(Object.keys(User.AccountStatus)).includes(listWhat)) {
|
if (!['all'].concat(Object.keys(User.AccountStatus)).includes(listWhat)) {
|
||||||
return printUsageAndSetExitCode(getHelpFor('User'), ExitCodes.ERROR);
|
return printUsageAndSetExitCode(getHelpFor('User'), ExitCodes.ERROR);
|
||||||
|
@ -527,7 +529,13 @@ function listUsers() {
|
||||||
const UserProps = require('../../core/user_property');
|
const UserProps = require('../../core/user_property');
|
||||||
|
|
||||||
const userListOpts = {
|
const userListOpts = {
|
||||||
properties: [UserProps.AccountStatus],
|
properties: [
|
||||||
|
UserProps.RealName,
|
||||||
|
UserProps.AccountStatus,
|
||||||
|
UserProps.AccountCreated,
|
||||||
|
UserProps.LastLoginTs,
|
||||||
|
UserProps.LoginCount,
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
User.getUserList(userListOpts, (err, userList) => {
|
User.getUserList(userListOpts, (err, userList) => {
|
||||||
|
@ -550,9 +558,93 @@ function listUsers() {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
(userList, callback) => {
|
(userList, callback) => {
|
||||||
userList.forEach(user => {
|
// default sort: by ID
|
||||||
console.info(`${user.userId}: ${user.userName}`);
|
const sortById = (left, right) => {
|
||||||
|
return left.userId - right.userId;
|
||||||
|
};
|
||||||
|
|
||||||
|
const sortByLogin = prop => (left, right) => {
|
||||||
|
return parseInt(right[prop]) - parseInt(left[prop]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const sortByString = prop => (left, right) => {
|
||||||
|
return left[prop].localeCompare(right[prop], {
|
||||||
|
sensitivity: false,
|
||||||
|
numeric: true,
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const sortByTimestamp = prop => (left, right) => {
|
||||||
|
return moment(left[prop]) - moment(right[prop]);
|
||||||
|
};
|
||||||
|
|
||||||
|
let sorter;
|
||||||
|
switch (sortBy) {
|
||||||
|
case 'username':
|
||||||
|
sorter = sortByString('userName');
|
||||||
|
break;
|
||||||
|
case 'realname':
|
||||||
|
sorter = sortByString(UserProps.RealName);
|
||||||
|
break;
|
||||||
|
case 'status':
|
||||||
|
sorter = sortByString(UserProps.AccountStatus);
|
||||||
|
break;
|
||||||
|
case 'created':
|
||||||
|
sorter = sortByTimestamp(UserProps.AccountCreated);
|
||||||
|
break;
|
||||||
|
case 'lastlogin':
|
||||||
|
sorter = sortByTimestamp(UserProps.LastLoginTs);
|
||||||
|
break;
|
||||||
|
case 'logins':
|
||||||
|
sorter = sortByLogin(UserProps.LoginCount);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'id':
|
||||||
|
default:
|
||||||
|
sorter = sortById;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
userList.sort(sorter);
|
||||||
|
|
||||||
|
const StatusNames = _.invert(User.AccountStatus);
|
||||||
|
|
||||||
|
const propOrNA = (user, prop) => {
|
||||||
|
return user[prop] || 'N/A';
|
||||||
|
};
|
||||||
|
|
||||||
|
const timestampOrNA = (user, prop, format) => {
|
||||||
|
let ts = user[prop];
|
||||||
|
return ts ? moment(ts).format(format) : 'N/A';
|
||||||
|
};
|
||||||
|
|
||||||
|
const makeAccountStatus = status => {
|
||||||
|
return StatusNames[status] || 'N/A';
|
||||||
|
};
|
||||||
|
|
||||||
|
const table = new Table();
|
||||||
|
userList.forEach(user => {
|
||||||
|
table.cell('ID', user.userId);
|
||||||
|
table.cell('Username', user.userName);
|
||||||
|
table.cell('Real Name', user[UserProps.RealName]);
|
||||||
|
table.cell(
|
||||||
|
'Status',
|
||||||
|
makeAccountStatus(user[UserProps.AccountStatus])
|
||||||
|
);
|
||||||
|
table.cell(
|
||||||
|
'Created',
|
||||||
|
timestampOrNA(user, UserProps.AccountCreated, 'YYYY-MM-DD')
|
||||||
|
);
|
||||||
|
table.cell(
|
||||||
|
'Last Login',
|
||||||
|
timestampOrNA(user, UserProps.LastLoginTs, 'YYYY-MM-DD HH::mm')
|
||||||
|
);
|
||||||
|
table.cell('Logins', propOrNA(user, UserProps.LoginCount));
|
||||||
|
|
||||||
|
table.newRow();
|
||||||
|
});
|
||||||
|
|
||||||
|
console.info(table.toString());
|
||||||
|
|
||||||
return callback(null);
|
return callback(null);
|
||||||
},
|
},
|
||||||
|
|
|
@ -59,7 +59,8 @@
|
||||||
"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",
|
||||||
|
"easy-table": "^1.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "8.21.0",
|
"eslint": "8.21.0",
|
||||||
|
|
|
@ -602,6 +602,15 @@ dtrace-provider@~0.8:
|
||||||
dependencies:
|
dependencies:
|
||||||
nan "^2.10.0"
|
nan "^2.10.0"
|
||||||
|
|
||||||
|
easy-table@^1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/easy-table/-/easy-table-1.2.0.tgz#ba9225d7138fee307bfd4f0b5bc3c04bdc7c54eb"
|
||||||
|
integrity sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww==
|
||||||
|
dependencies:
|
||||||
|
ansi-regex "^5.0.1"
|
||||||
|
optionalDependencies:
|
||||||
|
wcwidth "^1.0.1"
|
||||||
|
|
||||||
emoji-regex@^8.0.0:
|
emoji-regex@^8.0.0:
|
||||||
version "8.0.0"
|
version "8.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
||||||
|
|
Loading…
Reference in New Issue