First pass formatting with Prettier
* Added .prettierrc.json * Added .prettierignore * Formatted
This commit is contained in:
@@ -2,59 +2,59 @@
|
||||
/* eslint-disable no-console */
|
||||
'use strict';
|
||||
|
||||
const config = require('../../core/config.js');
|
||||
const db = require('../../core/database.js');
|
||||
const config = require('../../core/config.js');
|
||||
const db = require('../../core/database.js');
|
||||
|
||||
const _ = require('lodash');
|
||||
const async = require('async');
|
||||
const inq = require('inquirer');
|
||||
const fs = require('fs');
|
||||
const hjson = require('hjson');
|
||||
const _ = require('lodash');
|
||||
const async = require('async');
|
||||
const inq = require('inquirer');
|
||||
const fs = require('fs');
|
||||
const hjson = require('hjson');
|
||||
|
||||
const packageJson = require('../../package.json');
|
||||
const packageJson = require('../../package.json');
|
||||
|
||||
exports.printUsageAndSetExitCode = printUsageAndSetExitCode;
|
||||
exports.getDefaultConfigPath = getDefaultConfigPath;
|
||||
exports.getConfigPath = getConfigPath;
|
||||
exports.initConfigAndDatabases = initConfigAndDatabases;
|
||||
exports.getAreaAndStorage = getAreaAndStorage;
|
||||
exports.looksLikePattern = looksLikePattern;
|
||||
exports.getAnswers = getAnswers;
|
||||
exports.writeConfig = writeConfig;
|
||||
exports.printUsageAndSetExitCode = printUsageAndSetExitCode;
|
||||
exports.getDefaultConfigPath = getDefaultConfigPath;
|
||||
exports.getConfigPath = getConfigPath;
|
||||
exports.initConfigAndDatabases = initConfigAndDatabases;
|
||||
exports.getAreaAndStorage = getAreaAndStorage;
|
||||
exports.looksLikePattern = looksLikePattern;
|
||||
exports.getAnswers = getAnswers;
|
||||
exports.writeConfig = writeConfig;
|
||||
|
||||
const HJSONStringifyCommonOpts = exports.HJSONStringifyCommonOpts = {
|
||||
emitRootBraces : true,
|
||||
bracesSameLine : true,
|
||||
space : 4,
|
||||
keepWsc : true,
|
||||
quotes : 'min',
|
||||
eol : '\n',
|
||||
};
|
||||
|
||||
const exitCodes = exports.ExitCodes = {
|
||||
SUCCESS : 0,
|
||||
ERROR : -1,
|
||||
BAD_COMMAND : -2,
|
||||
BAD_ARGS : -3,
|
||||
};
|
||||
|
||||
const argv = exports.argv = require('minimist')(process.argv.slice(2), {
|
||||
alias : {
|
||||
h : 'help',
|
||||
v : 'version',
|
||||
c : 'config',
|
||||
n : 'no-prompt',
|
||||
}
|
||||
const HJSONStringifyCommonOpts = (exports.HJSONStringifyCommonOpts = {
|
||||
emitRootBraces: true,
|
||||
bracesSameLine: true,
|
||||
space: 4,
|
||||
keepWsc: true,
|
||||
quotes: 'min',
|
||||
eol: '\n',
|
||||
});
|
||||
|
||||
const exitCodes = (exports.ExitCodes = {
|
||||
SUCCESS: 0,
|
||||
ERROR: -1,
|
||||
BAD_COMMAND: -2,
|
||||
BAD_ARGS: -3,
|
||||
});
|
||||
|
||||
const argv = (exports.argv = require('minimist')(process.argv.slice(2), {
|
||||
alias: {
|
||||
h: 'help',
|
||||
v: 'version',
|
||||
c: 'config',
|
||||
n: 'no-prompt',
|
||||
},
|
||||
}));
|
||||
|
||||
function printUsageAndSetExitCode(errMsg, exitCode) {
|
||||
if(_.isUndefined(exitCode)) {
|
||||
if (_.isUndefined(exitCode)) {
|
||||
exitCode = exitCodes.ERROR;
|
||||
}
|
||||
|
||||
process.exitCode = exitCode;
|
||||
|
||||
if(errMsg) {
|
||||
if (errMsg) {
|
||||
console.error(errMsg);
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,7 @@ function getConfigPath() {
|
||||
function initConfig(cb) {
|
||||
const configPath = getConfigPath();
|
||||
|
||||
config.Config.create(configPath, { keepWsc : true, hotReload : false }, cb);
|
||||
config.Config.create(configPath, { keepWsc: true, hotReload: false }, cb);
|
||||
}
|
||||
|
||||
function initConfigAndDatabases(cb) {
|
||||
@@ -85,9 +85,9 @@ function initConfigAndDatabases(cb) {
|
||||
},
|
||||
function initArchiveUtil(callback) {
|
||||
// ensure we init ArchiveUtil without events
|
||||
require('../../core/archive_util').getInstance(false); // false=hotReload
|
||||
require('../../core/archive_util').getInstance(false); // false=hotReload
|
||||
return callback(null);
|
||||
}
|
||||
},
|
||||
],
|
||||
err => {
|
||||
return cb(err);
|
||||
@@ -99,10 +99,10 @@ function getAreaAndStorage(tags) {
|
||||
return tags.map(tag => {
|
||||
const parts = tag.toString().split('@');
|
||||
const entry = {
|
||||
areaTag : parts[0],
|
||||
areaTag: parts[0],
|
||||
};
|
||||
entry.pattern = entry.areaTag; // handy
|
||||
if(parts[1]) {
|
||||
entry.pattern = entry.areaTag; // handy
|
||||
if (parts[1]) {
|
||||
entry.storageTag = parts[1];
|
||||
}
|
||||
return entry;
|
||||
@@ -111,7 +111,7 @@ function getAreaAndStorage(tags) {
|
||||
|
||||
function looksLikePattern(tag) {
|
||||
// globs can start with @
|
||||
if(tag.indexOf('@') > 0) {
|
||||
if (tag.indexOf('@') > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -119,20 +119,21 @@ function looksLikePattern(tag) {
|
||||
}
|
||||
|
||||
function getAnswers(questions, cb) {
|
||||
inq.prompt(questions).then( answers => {
|
||||
inq.prompt(questions).then(answers => {
|
||||
return cb(answers);
|
||||
});
|
||||
}
|
||||
|
||||
function writeConfig(config, path) {
|
||||
config = hjson.stringify(config, HJSONStringifyCommonOpts)
|
||||
.replace(/%ENIG_VERSION%/g, packageJson.version)
|
||||
.replace(/%HJSON_VERSION%/g, hjson.version);
|
||||
config = hjson
|
||||
.stringify(config, HJSONStringifyCommonOpts)
|
||||
.replace(/%ENIG_VERSION%/g, packageJson.version)
|
||||
.replace(/%HJSON_VERSION%/g, hjson.version);
|
||||
|
||||
try {
|
||||
fs.writeFileSync(path, config, 'utf8');
|
||||
return true;
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
'use strict';
|
||||
|
||||
// ENiGMA½
|
||||
const resolvePath = require('../../core/misc_util.js').resolvePath;
|
||||
const resolvePath = require('../../core/misc_util.js').resolvePath;
|
||||
const {
|
||||
printUsageAndSetExitCode,
|
||||
getConfigPath,
|
||||
@@ -12,25 +12,28 @@ const {
|
||||
getAnswers,
|
||||
writeConfig,
|
||||
HJSONStringifyCommonOpts,
|
||||
} = require('./oputil_common.js');
|
||||
const getHelpFor = require('./oputil_help.js').getHelpFor;
|
||||
} = require('./oputil_common.js');
|
||||
const getHelpFor = require('./oputil_help.js').getHelpFor;
|
||||
|
||||
// deps
|
||||
const async = require('async');
|
||||
const inq = require('inquirer');
|
||||
const mkdirsSync = require('fs-extra').mkdirsSync;
|
||||
const fs = require('graceful-fs');
|
||||
const hjson = require('hjson');
|
||||
const paths = require('path');
|
||||
const _ = require('lodash');
|
||||
const sanatizeFilename = require('sanitize-filename');
|
||||
const async = require('async');
|
||||
const inq = require('inquirer');
|
||||
const mkdirsSync = require('fs-extra').mkdirsSync;
|
||||
const fs = require('graceful-fs');
|
||||
const hjson = require('hjson');
|
||||
const paths = require('path');
|
||||
const _ = require('lodash');
|
||||
const sanatizeFilename = require('sanitize-filename');
|
||||
|
||||
exports.handleConfigCommand = handleConfigCommand;
|
||||
exports.handleConfigCommand = handleConfigCommand;
|
||||
|
||||
const ConfigIncludeKeys = [
|
||||
'theme',
|
||||
'users.preAuthIdleLogoutSeconds', 'users.idleLogoutSeconds',
|
||||
'users.newUserNames', 'users.failedLogin', 'users.unlockAtEmailPwReset',
|
||||
'users.preAuthIdleLogoutSeconds',
|
||||
'users.idleLogoutSeconds',
|
||||
'users.newUserNames',
|
||||
'users.failedLogin',
|
||||
'users.unlockAtEmailPwReset',
|
||||
'paths.logs',
|
||||
'loginServers',
|
||||
'contentServers',
|
||||
@@ -39,71 +42,71 @@ const ConfigIncludeKeys = [
|
||||
];
|
||||
|
||||
const QUESTIONS = {
|
||||
Intro : [
|
||||
Intro: [
|
||||
{
|
||||
name : 'createNewConfig',
|
||||
message : 'Create a new configuration?',
|
||||
type : 'confirm',
|
||||
default : false,
|
||||
name: 'createNewConfig',
|
||||
message: 'Create a new configuration?',
|
||||
type: 'confirm',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
name : 'configPath',
|
||||
message : 'Configuration path:',
|
||||
default : getConfigPath(),
|
||||
when : answers => answers.createNewConfig
|
||||
name: 'configPath',
|
||||
message: 'Configuration path:',
|
||||
default: getConfigPath(),
|
||||
when: answers => answers.createNewConfig,
|
||||
},
|
||||
],
|
||||
|
||||
OverwriteConfig : [
|
||||
OverwriteConfig: [
|
||||
{
|
||||
name : 'overwriteConfig',
|
||||
message : 'Config file exists. Overwrite?',
|
||||
type : 'confirm',
|
||||
default : false,
|
||||
}
|
||||
],
|
||||
|
||||
Basic : [
|
||||
{
|
||||
name : 'boardName',
|
||||
message : 'BBS name:',
|
||||
default : 'New ENiGMA½ BBS',
|
||||
name: 'overwriteConfig',
|
||||
message: 'Config file exists. Overwrite?',
|
||||
type: 'confirm',
|
||||
default: false,
|
||||
},
|
||||
],
|
||||
|
||||
Misc : [
|
||||
Basic: [
|
||||
{
|
||||
name : 'loggingLevel',
|
||||
message : 'Logging level:',
|
||||
type : 'list',
|
||||
choices : [ 'Error', 'Warn', 'Info', 'Debug', 'Trace' ],
|
||||
default : 2,
|
||||
filter : s => s.toLowerCase(),
|
||||
name: 'boardName',
|
||||
message: 'BBS name:',
|
||||
default: 'New ENiGMA½ BBS',
|
||||
},
|
||||
],
|
||||
|
||||
MessageConfAndArea : [
|
||||
Misc: [
|
||||
{
|
||||
name : 'msgConfName',
|
||||
message : 'First message conference:',
|
||||
default : 'Local',
|
||||
name: 'loggingLevel',
|
||||
message: 'Logging level:',
|
||||
type: 'list',
|
||||
choices: ['Error', 'Warn', 'Info', 'Debug', 'Trace'],
|
||||
default: 2,
|
||||
filter: s => s.toLowerCase(),
|
||||
},
|
||||
],
|
||||
|
||||
MessageConfAndArea: [
|
||||
{
|
||||
name: 'msgConfName',
|
||||
message: 'First message conference:',
|
||||
default: 'Local',
|
||||
},
|
||||
{
|
||||
name : 'msgConfDesc',
|
||||
message : 'Conference description:',
|
||||
default : 'Local Areas',
|
||||
name: 'msgConfDesc',
|
||||
message: 'Conference description:',
|
||||
default: 'Local Areas',
|
||||
},
|
||||
{
|
||||
name : 'msgAreaName',
|
||||
message : 'First area in message conference:',
|
||||
default : 'General',
|
||||
name: 'msgAreaName',
|
||||
message: 'First area in message conference:',
|
||||
default: 'General',
|
||||
},
|
||||
{
|
||||
name : 'msgAreaDesc',
|
||||
message : 'Area description:',
|
||||
default : 'General chit-chat',
|
||||
}
|
||||
]
|
||||
name: 'msgAreaDesc',
|
||||
message: 'Area description:',
|
||||
default: 'General chit-chat',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
function makeMsgConfAreaName(s) {
|
||||
@@ -111,7 +114,6 @@ function makeMsgConfAreaName(s) {
|
||||
}
|
||||
|
||||
function askNewConfigQuestions(cb) {
|
||||
|
||||
const ui = new inq.ui.BottomBar();
|
||||
|
||||
let configPath;
|
||||
@@ -121,7 +123,7 @@ function askNewConfigQuestions(cb) {
|
||||
[
|
||||
function intro(callback) {
|
||||
getAnswers(QUESTIONS.Intro, answers => {
|
||||
if(!answers.createNewConfig) {
|
||||
if (!answers.createNewConfig) {
|
||||
return callback('exit');
|
||||
}
|
||||
|
||||
@@ -135,21 +137,21 @@ function askNewConfigQuestions(cb) {
|
||||
// Check if the file exists and can be written to
|
||||
//
|
||||
fs.access(configPath, fs.F_OK | fs.W_OK, err => {
|
||||
if(err) {
|
||||
if('EACCES' === err.code) {
|
||||
if (err) {
|
||||
if ('EACCES' === err.code) {
|
||||
ui.log.write(`${configPath} cannot be written to`);
|
||||
callback('exit');
|
||||
} else if('ENOENT' === err.code) {
|
||||
} else if ('ENOENT' === err.code) {
|
||||
callback(null, false);
|
||||
}
|
||||
} else {
|
||||
callback(null, true); // exists + writable
|
||||
callback(null, true); // exists + writable
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
function promptOverwrite(needPrompt, callback) {
|
||||
if(needPrompt) {
|
||||
if (needPrompt) {
|
||||
getAnswers(QUESTIONS.OverwriteConfig, answers => {
|
||||
return callback(answers.overwriteConfig ? null : 'exit');
|
||||
});
|
||||
@@ -162,7 +164,12 @@ function askNewConfigQuestions(cb) {
|
||||
const defaultConfig = require('../../core/config_default')();
|
||||
|
||||
// start by plopping in values we want directly from config.js
|
||||
const template = hjson.rt.parse(fs.readFileSync(paths.join(__dirname, '../../misc/config_template.in.hjson'), 'utf8'));
|
||||
const template = hjson.rt.parse(
|
||||
fs.readFileSync(
|
||||
paths.join(__dirname, '../../misc/config_template.in.hjson'),
|
||||
'utf8'
|
||||
)
|
||||
);
|
||||
|
||||
const direct = {};
|
||||
_.each(ConfigIncludeKeys, keyPath => {
|
||||
@@ -179,22 +186,22 @@ function askNewConfigQuestions(cb) {
|
||||
},
|
||||
function msgConfAndArea(callback) {
|
||||
getAnswers(QUESTIONS.MessageConfAndArea, answers => {
|
||||
const confName = makeMsgConfAreaName(answers.msgConfName);
|
||||
const areaName = makeMsgConfAreaName(answers.msgAreaName);
|
||||
const confName = makeMsgConfAreaName(answers.msgConfName);
|
||||
const areaName = makeMsgConfAreaName(answers.msgAreaName);
|
||||
|
||||
config.messageConferences[confName] = {
|
||||
name : answers.msgConfName,
|
||||
desc : answers.msgConfDesc,
|
||||
sort : 1,
|
||||
default : true,
|
||||
name: answers.msgConfName,
|
||||
desc: answers.msgConfDesc,
|
||||
sort: 1,
|
||||
default: true,
|
||||
};
|
||||
|
||||
config.messageConferences[confName].areas = {};
|
||||
config.messageConferences[confName].areas[areaName] = {
|
||||
name : answers.msgAreaName,
|
||||
desc : answers.msgAreaDesc,
|
||||
sort : 1,
|
||||
default : true,
|
||||
name: answers.msgAreaName,
|
||||
desc: answers.msgAreaDesc,
|
||||
sort: 1,
|
||||
default: true,
|
||||
};
|
||||
|
||||
return callback(null);
|
||||
@@ -206,7 +213,7 @@ function askNewConfigQuestions(cb) {
|
||||
|
||||
return callback(null);
|
||||
});
|
||||
}
|
||||
},
|
||||
],
|
||||
err => {
|
||||
return cb(err, configPath, config);
|
||||
@@ -217,14 +224,14 @@ function askNewConfigQuestions(cb) {
|
||||
const copyFileSyncSilent = (to, from, flags) => {
|
||||
try {
|
||||
fs.copyFileSync(to, from, flags);
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
/* absorb! */
|
||||
}
|
||||
};
|
||||
|
||||
function buildNewConfig() {
|
||||
askNewConfigQuestions( (err, configPath, config) => {
|
||||
if(err) {
|
||||
askNewConfigQuestions((err, configPath, config) => {
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -232,7 +239,7 @@ function buildNewConfig() {
|
||||
mkdirsSync(paths.join(__dirname, '../../config/menus'));
|
||||
|
||||
const boardName = sanatizeFilename(config.general.boardName)
|
||||
.replace(/[^a-z0-9_-]/ig, '_')
|
||||
.replace(/[^a-z0-9_-]/gi, '_')
|
||||
.replace(/_+/g, '_')
|
||||
.toLowerCase();
|
||||
|
||||
@@ -258,8 +265,12 @@ function buildNewConfig() {
|
||||
});
|
||||
|
||||
// We really only need includes to be replaced
|
||||
const mainTemplate = fs.readFileSync(paths.join(__dirname, '../../misc/menu_templates/main.in.hjson'), 'utf8')
|
||||
.replace(/%INCLUDE_FILES%/g, includeFiles.join('\n\t\t')); // cheesy, but works!
|
||||
const mainTemplate = fs
|
||||
.readFileSync(
|
||||
paths.join(__dirname, '../../misc/menu_templates/main.in.hjson'),
|
||||
'utf8'
|
||||
)
|
||||
.replace(/%INCLUDE_FILES%/g, includeFiles.join('\n\t\t')); // cheesy, but works!
|
||||
|
||||
const menuFile = `${boardName}-main.hjson`;
|
||||
fs.writeFileSync(
|
||||
@@ -270,7 +281,7 @@ function buildNewConfig() {
|
||||
|
||||
config.general.menuFile = paths.join(__dirname, '../../config/menus/', menuFile);
|
||||
|
||||
if(writeConfig(config, configPath)) {
|
||||
if (writeConfig(config, configPath)) {
|
||||
console.info('Configuration generated');
|
||||
} else {
|
||||
console.error('Failed writing configuration');
|
||||
@@ -280,15 +291,15 @@ function buildNewConfig() {
|
||||
|
||||
function catCurrentConfig() {
|
||||
try {
|
||||
const config = hjson.rt.parse(fs.readFileSync(getConfigPath(), 'utf8'));
|
||||
const config = hjson.rt.parse(fs.readFileSync(getConfigPath(), 'utf8'));
|
||||
const hjsonOpts = Object.assign({}, HJSONStringifyCommonOpts, {
|
||||
colors : false === argv.colors ? false : true,
|
||||
keepWsc : false === argv.comments ? false : true,
|
||||
colors: false === argv.colors ? false : true,
|
||||
keepWsc: false === argv.comments ? false : true,
|
||||
});
|
||||
|
||||
console.log(hjson.stringify(config, hjsonOpts));
|
||||
} catch(e) {
|
||||
if('ENOENT' == e.code) {
|
||||
} catch (e) {
|
||||
if ('ENOENT' == e.code) {
|
||||
console.error(`File not found: ${getConfigPath()}`);
|
||||
} else {
|
||||
console.error(e);
|
||||
@@ -297,16 +308,19 @@ function catCurrentConfig() {
|
||||
}
|
||||
|
||||
function handleConfigCommand() {
|
||||
if(true === argv.help) {
|
||||
if (true === argv.help) {
|
||||
return printUsageAndSetExitCode(getHelpFor('Config'), ExitCodes.ERROR);
|
||||
}
|
||||
|
||||
const action = argv._[1];
|
||||
|
||||
switch(action) {
|
||||
case 'new' : return buildNewConfig();
|
||||
case 'cat' : return catCurrentConfig();
|
||||
switch (action) {
|
||||
case 'new':
|
||||
return buildNewConfig();
|
||||
case 'cat':
|
||||
return catCurrentConfig();
|
||||
|
||||
default : return printUsageAndSetExitCode(getHelpFor('Config'), ExitCodes.ERROR);
|
||||
default:
|
||||
return printUsageAndSetExitCode(getHelpFor('Config'), ExitCodes.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,13 +2,12 @@
|
||||
/* eslint-disable no-console */
|
||||
'use strict';
|
||||
|
||||
const getDefaultConfigPath = require('./oputil_common.js').getDefaultConfigPath;
|
||||
const getDefaultConfigPath = require('./oputil_common.js').getDefaultConfigPath;
|
||||
|
||||
exports.getHelpFor = getHelpFor;
|
||||
exports.getHelpFor = getHelpFor;
|
||||
|
||||
const usageHelp = exports.USAGE_HELP = {
|
||||
General :
|
||||
`usage: oputil.js [--version] [--help]
|
||||
const usageHelp = (exports.USAGE_HELP = {
|
||||
General: `usage: oputil.js [--version] [--help]
|
||||
<command> [<arguments>]
|
||||
|
||||
Global arguments:
|
||||
@@ -22,8 +21,7 @@ Commands:
|
||||
fb File base management
|
||||
mb Message base management
|
||||
`,
|
||||
User :
|
||||
`usage: oputil.js user <action> [<arguments>]
|
||||
User: `usage: oputil.js user <action> [<arguments>]
|
||||
|
||||
Actions:
|
||||
info USERNAME Display information about a user
|
||||
@@ -83,8 +81,7 @@ info arguments:
|
||||
--out PATH Path to write QR code to. defaults to stdout
|
||||
`,
|
||||
|
||||
Config :
|
||||
`usage: oputil.js config <action> [<arguments>]
|
||||
Config: `usage: oputil.js config <action> [<arguments>]
|
||||
|
||||
Actions:
|
||||
new Generate a new / default configuration
|
||||
@@ -95,8 +92,7 @@ cat arguments:
|
||||
--no-color Disable color
|
||||
--no-comments Strip any comments
|
||||
`,
|
||||
FileBase :
|
||||
`usage: oputil.js fb <action> [<arguments>]
|
||||
FileBase: `usage: oputil.js fb <action> [<arguments>]
|
||||
|
||||
Actions:
|
||||
scan AREA_TAG[@STORAGE_TAG] Scan specified area
|
||||
@@ -160,8 +156,7 @@ import-areas arguments:
|
||||
|
||||
--create-dirs Also create backing storage directories
|
||||
`,
|
||||
FileOpsInfo :
|
||||
`
|
||||
FileOpsInfo: `
|
||||
General Information:
|
||||
Generally an area tag can also include an optional storage tag. For example, the
|
||||
area of 'bbswarez' stored using 'bbswarez_main': bbswarez@bbswarez_main
|
||||
@@ -172,8 +167,7 @@ General Information:
|
||||
|
||||
File ID's are those found in file.sqlite3.
|
||||
`,
|
||||
MessageBase :
|
||||
`usage: oputil.js mb <action> [<arguments>]
|
||||
MessageBase: `usage: oputil.js mb <action> [<arguments>]
|
||||
|
||||
Actions:
|
||||
areafix CMD1 CMD2 ... ADDR Sends an AreaFix NetMail
|
||||
@@ -202,8 +196,8 @@ qwk-export arguments:
|
||||
TIMESTAMP.
|
||||
--no-qwke Disable QWKE extensions.
|
||||
--no-synchronet Disable Synchronet style extensions.
|
||||
`
|
||||
};
|
||||
`,
|
||||
});
|
||||
|
||||
function getHelpFor(command) {
|
||||
return usageHelp[command];
|
||||
|
||||
@@ -2,35 +2,37 @@
|
||||
/* eslint-disable no-console */
|
||||
'use strict';
|
||||
|
||||
const ExitCodes = require('./oputil_common.js').ExitCodes;
|
||||
const argv = require('./oputil_common.js').argv;
|
||||
const printUsageAndSetExitCode = require('./oputil_common.js').printUsageAndSetExitCode;
|
||||
const handleUserCommand = require('./oputil_user.js').handleUserCommand;
|
||||
const handleFileBaseCommand = require('./oputil_file_base.js').handleFileBaseCommand;
|
||||
const handleMessageBaseCommand = require('./oputil_message_base.js').handleMessageBaseCommand;
|
||||
const handleConfigCommand = require('./oputil_config.js').handleConfigCommand;
|
||||
const getHelpFor = require('./oputil_help.js').getHelpFor;
|
||||
|
||||
|
||||
module.exports = function() {
|
||||
const ExitCodes = require('./oputil_common.js').ExitCodes;
|
||||
const argv = require('./oputil_common.js').argv;
|
||||
const printUsageAndSetExitCode = require('./oputil_common.js').printUsageAndSetExitCode;
|
||||
const handleUserCommand = require('./oputil_user.js').handleUserCommand;
|
||||
const handleFileBaseCommand = require('./oputil_file_base.js').handleFileBaseCommand;
|
||||
const handleMessageBaseCommand =
|
||||
require('./oputil_message_base.js').handleMessageBaseCommand;
|
||||
const handleConfigCommand = require('./oputil_config.js').handleConfigCommand;
|
||||
const getHelpFor = require('./oputil_help.js').getHelpFor;
|
||||
|
||||
module.exports = function () {
|
||||
process.exitCode = ExitCodes.SUCCESS;
|
||||
|
||||
if(true === argv.version) {
|
||||
if (true === argv.version) {
|
||||
return console.info(require('../../package.json').version);
|
||||
}
|
||||
|
||||
if(0 === argv._.length ||
|
||||
'help' === argv._[0])
|
||||
{
|
||||
if (0 === argv._.length || 'help' === argv._[0]) {
|
||||
return printUsageAndSetExitCode(getHelpFor('General'), ExitCodes.SUCCESS);
|
||||
}
|
||||
|
||||
switch(argv._[0]) {
|
||||
case 'user' : return handleUserCommand();
|
||||
case 'config' : return handleConfigCommand();
|
||||
case 'fb' : return handleFileBaseCommand();
|
||||
case 'mb' : return handleMessageBaseCommand();
|
||||
default : return printUsageAndSetExitCode(getHelpFor('General'), ExitCodes.BAD_COMMAND);
|
||||
switch (argv._[0]) {
|
||||
case 'user':
|
||||
return handleUserCommand();
|
||||
case 'config':
|
||||
return handleConfigCommand();
|
||||
case 'fb':
|
||||
return handleFileBaseCommand();
|
||||
case 'mb':
|
||||
return handleMessageBaseCommand();
|
||||
default:
|
||||
return printUsageAndSetExitCode(getHelpFor('General'), ExitCodes.BAD_COMMAND);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -12,29 +12,26 @@ const {
|
||||
writeConfig,
|
||||
} = require('./oputil_common.js');
|
||||
|
||||
const getHelpFor = require('./oputil_help.js').getHelpFor;
|
||||
const Address = require('../ftn_address.js');
|
||||
const Errors = require('../enig_error.js').Errors;
|
||||
const getHelpFor = require('./oputil_help.js').getHelpFor;
|
||||
const Address = require('../ftn_address.js');
|
||||
const Errors = require('../enig_error.js').Errors;
|
||||
|
||||
// deps
|
||||
const async = require('async');
|
||||
const paths = require('path');
|
||||
const fs = require('fs');
|
||||
const hjson = require('hjson');
|
||||
const _ = require('lodash');
|
||||
const moment = require('moment');
|
||||
const async = require('async');
|
||||
const paths = require('path');
|
||||
const fs = require('fs');
|
||||
const hjson = require('hjson');
|
||||
const _ = require('lodash');
|
||||
const moment = require('moment');
|
||||
|
||||
exports.handleMessageBaseCommand = handleMessageBaseCommand;
|
||||
exports.handleMessageBaseCommand = handleMessageBaseCommand;
|
||||
|
||||
function areaFix() {
|
||||
//
|
||||
// oputil mb areafix CMD1 CMD2 ... ADDR [--password PASS]
|
||||
//
|
||||
if(argv._.length < 3) {
|
||||
return printUsageAndSetExitCode(
|
||||
getHelpFor('MessageBase'),
|
||||
ExitCodes.ERROR
|
||||
);
|
||||
if (argv._.length < 3) {
|
||||
return printUsageAndSetExitCode(getHelpFor('MessageBase'), ExitCodes.ERROR);
|
||||
}
|
||||
|
||||
async.waterfall(
|
||||
@@ -46,8 +43,10 @@ function areaFix() {
|
||||
const addrArg = argv._.slice(-1)[0];
|
||||
const ftnAddr = Address.fromString(addrArg);
|
||||
|
||||
if(!ftnAddr) {
|
||||
return callback(Errors.Invalid(`"${addrArg}" is not a valid FTN address`));
|
||||
if (!ftnAddr) {
|
||||
return callback(
|
||||
Errors.Invalid(`"${addrArg}" is not a valid FTN address`)
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -65,9 +64,9 @@ function areaFix() {
|
||||
//
|
||||
const User = require('../user.js');
|
||||
|
||||
if(argv.from) {
|
||||
if (argv.from) {
|
||||
User.getUserIdAndNameByLookup(argv.from, (err, userId, fromName) => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
return callback(null, ftnAddr, argv.from, 0);
|
||||
}
|
||||
|
||||
@@ -76,7 +75,12 @@ function areaFix() {
|
||||
});
|
||||
} else {
|
||||
User.getUserName(User.RootUserID, (err, fromName) => {
|
||||
return callback(null, ftnAddr, fromName || 'SysOp', err ? 0 : User.RootUserID);
|
||||
return callback(
|
||||
null,
|
||||
ftnAddr,
|
||||
fromName || 'SysOp',
|
||||
err ? 0 : User.RootUserID
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -88,27 +92,31 @@ function areaFix() {
|
||||
// in the case of e.g. removing an area: "-SOME_AREA" would end
|
||||
// up confusing minimist, therefor they must be quoted: "'-SOME_AREA'"
|
||||
//
|
||||
const messageBody = argv._.slice(2, -1).map(arg => {
|
||||
return arg.replace(/["']/g, '');
|
||||
}).join('\r\n') + '\n';
|
||||
const messageBody =
|
||||
argv._.slice(2, -1)
|
||||
.map(arg => {
|
||||
return arg.replace(/["']/g, '');
|
||||
})
|
||||
.join('\r\n') + '\n';
|
||||
|
||||
const Message = require('../message.js');
|
||||
|
||||
const message = new Message({
|
||||
toUserName : argv.to || 'AreaFix',
|
||||
fromUserName : fromName,
|
||||
subject : argv.password || '',
|
||||
message : messageBody,
|
||||
areaTag : Message.WellKnownAreaTags.Private, // mark private
|
||||
meta : {
|
||||
System : {
|
||||
[ Message.SystemMetaNames.RemoteToUser ] : ftnAddr.toString(), // where to send it
|
||||
[ Message.SystemMetaNames.ExternalFlavor ] : Message.AddressFlavor.FTN, // on FTN-style network
|
||||
}
|
||||
}
|
||||
toUserName: argv.to || 'AreaFix',
|
||||
fromUserName: fromName,
|
||||
subject: argv.password || '',
|
||||
message: messageBody,
|
||||
areaTag: Message.WellKnownAreaTags.Private, // mark private
|
||||
meta: {
|
||||
System: {
|
||||
[Message.SystemMetaNames.RemoteToUser]: ftnAddr.toString(), // where to send it
|
||||
[Message.SystemMetaNames.ExternalFlavor]:
|
||||
Message.AddressFlavor.FTN, // on FTN-style network
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if(0 !== fromUserId) {
|
||||
if (0 !== fromUserId) {
|
||||
message.setLocalFromUserId(fromUserId);
|
||||
}
|
||||
|
||||
@@ -116,15 +124,17 @@ function areaFix() {
|
||||
},
|
||||
function persistMessage(message, callback) {
|
||||
message.persist(err => {
|
||||
if(!err) {
|
||||
console.log('AreaFix message persisted and will be exported at next scheduled scan');
|
||||
if (!err) {
|
||||
console.log(
|
||||
'AreaFix message persisted and will be exported at next scheduled scan'
|
||||
);
|
||||
}
|
||||
return callback(err);
|
||||
});
|
||||
}
|
||||
},
|
||||
],
|
||||
err => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
process.exitCode = ExitCodes.ERROR;
|
||||
console.error(`${err.message}${err.reason ? ': ' + err.reason : ''}`);
|
||||
}
|
||||
@@ -142,7 +152,7 @@ function validateUplinks(uplinks) {
|
||||
}
|
||||
|
||||
function getMsgAreaImportType(path) {
|
||||
if(argv.type) {
|
||||
if (argv.type) {
|
||||
return argv.type.toLowerCase();
|
||||
}
|
||||
|
||||
@@ -151,20 +161,20 @@ function getMsgAreaImportType(path) {
|
||||
|
||||
function importAreas() {
|
||||
const importPath = argv._[argv._.length - 1];
|
||||
if(argv._.length < 3 || !importPath || 0 === importPath.length) {
|
||||
if (argv._.length < 3 || !importPath || 0 === importPath.length) {
|
||||
return printUsageAndSetExitCode(getHelpFor('Config'), ExitCodes.ERROR);
|
||||
}
|
||||
|
||||
const importType = getMsgAreaImportType(importPath);
|
||||
if('na' !== importType && 'bbs' !== importType) {
|
||||
if ('na' !== importType && 'bbs' !== importType) {
|
||||
return console.error(`"${importType}" is not a recognized import file type`);
|
||||
}
|
||||
|
||||
// optional data - we'll prompt if for anything not found
|
||||
let confTag = argv.conf;
|
||||
let networkName = argv.network;
|
||||
let uplinks = argv.uplinks;
|
||||
if(uplinks) {
|
||||
let confTag = argv.conf;
|
||||
let networkName = argv.network;
|
||||
let uplinks = argv.uplinks;
|
||||
if (uplinks) {
|
||||
uplinks = uplinks.split(/[\s,]+/);
|
||||
}
|
||||
|
||||
@@ -174,24 +184,24 @@ function importAreas() {
|
||||
[
|
||||
function readImportFile(callback) {
|
||||
fs.readFile(importPath, 'utf8', (err, importData) => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
importEntries = getImportEntries(importType, importData);
|
||||
if(0 === importEntries.length) {
|
||||
if (0 === importEntries.length) {
|
||||
return callback(Errors.Invalid('Invalid or empty import file'));
|
||||
}
|
||||
|
||||
// We should have enough to validate uplinks
|
||||
if('bbs' === importType) {
|
||||
for(let i = 0; i < importEntries.length; ++i) {
|
||||
if(!validateUplinks(importEntries[i].uplinks)) {
|
||||
if ('bbs' === importType) {
|
||||
for (let i = 0; i < importEntries.length; ++i) {
|
||||
if (!validateUplinks(importEntries[i].uplinks)) {
|
||||
return callback(Errors.Invalid('Invalid uplink(s)'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(!validateUplinks(uplinks || [])) {
|
||||
if (!validateUplinks(uplinks || [])) {
|
||||
return callback(Errors.Invalid('Invalid uplink(s)'));
|
||||
}
|
||||
}
|
||||
@@ -203,184 +213,222 @@ function importAreas() {
|
||||
return initConfigAndDatabases(callback);
|
||||
},
|
||||
function validateAndCollectInput(callback) {
|
||||
const msgArea = require('../../core/message_area.js');
|
||||
const sysConfig = require('../../core/config.js').get();
|
||||
const msgArea = require('../../core/message_area.js');
|
||||
const sysConfig = require('../../core/config.js').get();
|
||||
|
||||
let msgConfs = msgArea.getSortedAvailMessageConferences(null, { noClient : true } );
|
||||
if(!msgConfs) {
|
||||
return callback(Errors.DoesNotExist('No conferences exist in your configuration'));
|
||||
let msgConfs = msgArea.getSortedAvailMessageConferences(null, {
|
||||
noClient: true,
|
||||
});
|
||||
if (!msgConfs) {
|
||||
return callback(
|
||||
Errors.DoesNotExist('No conferences exist in your configuration')
|
||||
);
|
||||
}
|
||||
|
||||
msgConfs = msgConfs.map(mc => {
|
||||
return {
|
||||
name : mc.conf.name,
|
||||
value : mc.confTag,
|
||||
name: mc.conf.name,
|
||||
value: mc.confTag,
|
||||
};
|
||||
});
|
||||
|
||||
if(confTag && !msgConfs.find(mc => {
|
||||
return confTag === mc.value;
|
||||
}))
|
||||
{
|
||||
return callback(Errors.DoesNotExist(`Conference "${confTag}" does not exist`));
|
||||
if (
|
||||
confTag &&
|
||||
!msgConfs.find(mc => {
|
||||
return confTag === mc.value;
|
||||
})
|
||||
) {
|
||||
return callback(
|
||||
Errors.DoesNotExist(`Conference "${confTag}" does not exist`)
|
||||
);
|
||||
}
|
||||
|
||||
const existingNetworkNames = Object.keys(_.get(sysConfig, 'messageNetworks.ftn.networks', {}));
|
||||
const existingNetworkNames = Object.keys(
|
||||
_.get(sysConfig, 'messageNetworks.ftn.networks', {})
|
||||
);
|
||||
|
||||
if(networkName && !existingNetworkNames.find(net => networkName === net)) {
|
||||
return callback(Errors.DoesNotExist(`FTN style Network "${networkName}" does not exist`));
|
||||
if (
|
||||
networkName &&
|
||||
!existingNetworkNames.find(net => networkName === net)
|
||||
) {
|
||||
return callback(
|
||||
Errors.DoesNotExist(
|
||||
`FTN style Network "${networkName}" does not exist`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// can't use --uplinks without a network
|
||||
if(!networkName && 0 === existingNetworkNames.length && uplinks) {
|
||||
return callback(Errors.Invalid('Cannot use --uplinks without an FTN network to import to'));
|
||||
if (!networkName && 0 === existingNetworkNames.length && uplinks) {
|
||||
return callback(
|
||||
Errors.Invalid(
|
||||
'Cannot use --uplinks without an FTN network to import to'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
getAnswers([
|
||||
{
|
||||
name : 'confTag',
|
||||
message : 'Message conference:',
|
||||
type : 'list',
|
||||
choices : msgConfs,
|
||||
pageSize : 10,
|
||||
when : !confTag,
|
||||
},
|
||||
{
|
||||
name : 'networkName',
|
||||
message : 'FTN network name:',
|
||||
type : 'list',
|
||||
choices : [ '-None-' ].concat(existingNetworkNames),
|
||||
pageSize : 10,
|
||||
when : !networkName && existingNetworkNames.length > 0,
|
||||
filter : (choice) => {
|
||||
return '-None-' === choice ? undefined : choice;
|
||||
}
|
||||
},
|
||||
],
|
||||
answers => {
|
||||
confTag = confTag || answers.confTag;
|
||||
networkName = networkName || answers.networkName;
|
||||
uplinks = uplinks || answers.uplinks;
|
||||
getAnswers(
|
||||
[
|
||||
{
|
||||
name: 'confTag',
|
||||
message: 'Message conference:',
|
||||
type: 'list',
|
||||
choices: msgConfs,
|
||||
pageSize: 10,
|
||||
when: !confTag,
|
||||
},
|
||||
{
|
||||
name: 'networkName',
|
||||
message: 'FTN network name:',
|
||||
type: 'list',
|
||||
choices: ['-None-'].concat(existingNetworkNames),
|
||||
pageSize: 10,
|
||||
when: !networkName && existingNetworkNames.length > 0,
|
||||
filter: choice => {
|
||||
return '-None-' === choice ? undefined : choice;
|
||||
},
|
||||
},
|
||||
],
|
||||
answers => {
|
||||
confTag = confTag || answers.confTag;
|
||||
networkName = networkName || answers.networkName;
|
||||
uplinks = uplinks || answers.uplinks;
|
||||
|
||||
importEntries.forEach(ie => {
|
||||
ie.areaTag = ie.ftnTag.toLowerCase();
|
||||
});
|
||||
importEntries.forEach(ie => {
|
||||
ie.areaTag = ie.ftnTag.toLowerCase();
|
||||
});
|
||||
|
||||
return callback(null);
|
||||
});
|
||||
return callback(null);
|
||||
}
|
||||
);
|
||||
},
|
||||
function collectUplinks(callback) {
|
||||
if(!networkName || uplinks || 'bbs' === importType) {
|
||||
if (!networkName || uplinks || 'bbs' === importType) {
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
getAnswers([
|
||||
{
|
||||
name : 'uplinks',
|
||||
message : 'Uplink(s) (comma separated):',
|
||||
type : 'input',
|
||||
validate : (input) => {
|
||||
const inputUplinks = input.split(/[\s,]+/);
|
||||
return validateUplinks(inputUplinks) ? true : 'Invalid uplink(s)';
|
||||
getAnswers(
|
||||
[
|
||||
{
|
||||
name: 'uplinks',
|
||||
message: 'Uplink(s) (comma separated):',
|
||||
type: 'input',
|
||||
validate: input => {
|
||||
const inputUplinks = input.split(/[\s,]+/);
|
||||
return validateUplinks(inputUplinks)
|
||||
? true
|
||||
: 'Invalid uplink(s)';
|
||||
},
|
||||
},
|
||||
],
|
||||
answers => {
|
||||
uplinks = answers.uplinks;
|
||||
return callback(null);
|
||||
}
|
||||
],
|
||||
answers => {
|
||||
uplinks = answers.uplinks;
|
||||
return callback(null);
|
||||
});
|
||||
);
|
||||
},
|
||||
function confirmWithUser(callback) {
|
||||
const sysConfig = require('../../core/config.js').get();
|
||||
const sysConfig = require('../../core/config.js').get();
|
||||
|
||||
console.info(`Importing the following for "${confTag}"`);
|
||||
console.info(`(${sysConfig.messageConferences[confTag].name} - ${sysConfig.messageConferences[confTag].desc})`);
|
||||
console.info(
|
||||
`(${sysConfig.messageConferences[confTag].name} - ${sysConfig.messageConferences[confTag].desc})`
|
||||
);
|
||||
console.info('');
|
||||
importEntries.forEach(ie => {
|
||||
console.info(` ${ie.ftnTag} - ${ie.name}`);
|
||||
});
|
||||
|
||||
if(networkName) {
|
||||
if (networkName) {
|
||||
console.info('');
|
||||
console.info(`For FTN network: ${networkName}`);
|
||||
console.info(`Uplinks: ${uplinks}`);
|
||||
console.info('');
|
||||
console.info('Importing will NOT create required FTN network configurations.');
|
||||
console.info('If you have not yet done this, you will need to complete additional steps after importing.');
|
||||
console.info(
|
||||
'Importing will NOT create required FTN network configurations.'
|
||||
);
|
||||
console.info(
|
||||
'If you have not yet done this, you will need to complete additional steps after importing.'
|
||||
);
|
||||
console.info('See Message Networks docs for details.');
|
||||
console.info('');
|
||||
}
|
||||
|
||||
getAnswers([
|
||||
{
|
||||
name : 'proceed',
|
||||
message : 'Proceed?',
|
||||
type : 'confirm',
|
||||
getAnswers(
|
||||
[
|
||||
{
|
||||
name: 'proceed',
|
||||
message: 'Proceed?',
|
||||
type: 'confirm',
|
||||
},
|
||||
],
|
||||
answers => {
|
||||
return callback(
|
||||
answers.proceed ? null : Errors.General('User canceled')
|
||||
);
|
||||
}
|
||||
],
|
||||
answers => {
|
||||
return callback(answers.proceed ? null : Errors.General('User canceled'));
|
||||
});
|
||||
|
||||
);
|
||||
},
|
||||
function loadConfigHjson(callback) {
|
||||
const configPath = getConfigPath();
|
||||
fs.readFile(configPath, 'utf8', (err, confData) => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
let config;
|
||||
try {
|
||||
config = hjson.parse(confData, { keepWsc : true } );
|
||||
} catch(e) {
|
||||
config = hjson.parse(confData, { keepWsc: true });
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
return callback(null, config);
|
||||
|
||||
});
|
||||
},
|
||||
function performImport(config, callback) {
|
||||
const confAreas = { messageConferences : {} };
|
||||
confAreas.messageConferences[confTag] = { areas : {} };
|
||||
const confAreas = { messageConferences: {} };
|
||||
confAreas.messageConferences[confTag] = { areas: {} };
|
||||
|
||||
const msgNetworks = { messageNetworks : { ftn : { areas : {} } } };
|
||||
const msgNetworks = { messageNetworks: { ftn: { areas: {} } } };
|
||||
|
||||
importEntries.forEach(ie => {
|
||||
const specificUplinks = ie.uplinks || uplinks; // AREAS.BBS has specific uplinks per area
|
||||
const specificUplinks = ie.uplinks || uplinks; // AREAS.BBS has specific uplinks per area
|
||||
|
||||
confAreas.messageConferences[confTag].areas[ie.areaTag] = {
|
||||
name : ie.name,
|
||||
desc : ie.name,
|
||||
name: ie.name,
|
||||
desc: ie.name,
|
||||
};
|
||||
|
||||
if(networkName) {
|
||||
if (networkName) {
|
||||
msgNetworks.messageNetworks.ftn.areas[ie.areaTag] = {
|
||||
network : networkName,
|
||||
tag : ie.ftnTag,
|
||||
uplinks : specificUplinks
|
||||
network: networkName,
|
||||
tag: ie.ftnTag,
|
||||
uplinks: specificUplinks,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const newConfig = _.defaultsDeep(config, confAreas, msgNetworks);
|
||||
const configPath = getConfigPath();
|
||||
|
||||
if(!writeConfig(newConfig, configPath)) {
|
||||
return callback(Errors.UnexpectedState('Failed writing configuration'));
|
||||
if (!writeConfig(newConfig, configPath)) {
|
||||
return callback(
|
||||
Errors.UnexpectedState('Failed writing configuration')
|
||||
);
|
||||
}
|
||||
|
||||
return callback(null);
|
||||
}
|
||||
},
|
||||
],
|
||||
err => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
console.error(err.reason ? err.reason : err.message);
|
||||
} else {
|
||||
const addFieldUpd = 'bbs' === importType ? '"name" and "desc"' : '"desc"';
|
||||
console.info('Import complete.');
|
||||
console.info(`You may wish to validate changes made to ${getConfigPath()}`);
|
||||
console.info(
|
||||
`You may wish to validate changes made to ${getConfigPath()}`
|
||||
);
|
||||
console.info(`as well as update ${addFieldUpd} fields, sorting, etc.`);
|
||||
console.info('');
|
||||
}
|
||||
@@ -391,7 +439,7 @@ function importAreas() {
|
||||
function getImportEntries(importType, importData) {
|
||||
let importEntries = [];
|
||||
|
||||
if('na' === importType) {
|
||||
if ('na' === importType) {
|
||||
//
|
||||
// parse out
|
||||
// TAG DESC
|
||||
@@ -399,10 +447,10 @@ function getImportEntries(importType, importData) {
|
||||
const re = /^([^\s]+)\s+([^\r\n]+)/gm;
|
||||
let m;
|
||||
|
||||
while( (m = re.exec(importData) )) {
|
||||
while ((m = re.exec(importData))) {
|
||||
importEntries.push({
|
||||
ftnTag : m[1].trim(),
|
||||
name : m[2].trim(),
|
||||
ftnTag: m[1].trim(),
|
||||
name: m[2].trim(),
|
||||
});
|
||||
}
|
||||
} else if ('bbs' === importType) {
|
||||
@@ -422,13 +470,13 @@ function getImportEntries(importType, importData) {
|
||||
//
|
||||
const re = /^[^\s]+\s+([^\s]+)\s+([^\n]+)$/gm;
|
||||
let m;
|
||||
while ( (m = re.exec(importData) )) {
|
||||
while ((m = re.exec(importData))) {
|
||||
const tag = m[1].trim();
|
||||
|
||||
importEntries.push({
|
||||
ftnTag : tag,
|
||||
name : `Area: ${tag}`,
|
||||
uplinks : m[2].trim().split(/[\s,]+/),
|
||||
ftnTag: tag,
|
||||
name: `Area: ${tag}`,
|
||||
uplinks: m[2].trim().split(/[\s,]+/),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -438,16 +486,16 @@ function getImportEntries(importType, importData) {
|
||||
|
||||
function dumpQWKPacket() {
|
||||
const packetPath = argv._[argv._.length - 1];
|
||||
if(argv._.length < 3 || !packetPath || 0 === packetPath.length) {
|
||||
if (argv._.length < 3 || !packetPath || 0 === packetPath.length) {
|
||||
return printUsageAndSetExitCode(getHelpFor('MessageBase'), ExitCodes.ERROR);
|
||||
}
|
||||
|
||||
async.waterfall(
|
||||
[
|
||||
(callback) => {
|
||||
callback => {
|
||||
return initConfigAndDatabases(callback);
|
||||
},
|
||||
(callback) => {
|
||||
callback => {
|
||||
const { QWKPacketReader } = require('../qwk_mail_packet');
|
||||
const reader = new QWKPacketReader(packetPath);
|
||||
|
||||
@@ -477,7 +525,7 @@ function dumpQWKPacket() {
|
||||
});
|
||||
|
||||
reader.read();
|
||||
}
|
||||
},
|
||||
],
|
||||
err => {
|
||||
if (err) {
|
||||
@@ -489,7 +537,7 @@ function dumpQWKPacket() {
|
||||
|
||||
function exportQWKPacket() {
|
||||
let packetPath = argv._[argv._.length - 1];
|
||||
if(argv._.length < 3 || !packetPath || 0 === packetPath.length) {
|
||||
if (argv._.length < 3 || !packetPath || 0 === packetPath.length) {
|
||||
return printUsageAndSetExitCode(getHelpFor('MessageBase'), ExitCodes.ERROR);
|
||||
}
|
||||
|
||||
@@ -524,19 +572,19 @@ function exportQWKPacket() {
|
||||
const userName = argv.user || '-';
|
||||
|
||||
const writerOptions = {
|
||||
enableQWKE : !(false === argv.qwke),
|
||||
enableHeadersExtension : !(false === argv.synchronet),
|
||||
enableAtKludges : !(false === argv.synchronet),
|
||||
archiveFormat : argv.format || 'application/zip'
|
||||
enableQWKE: !(false === argv.qwke),
|
||||
enableHeadersExtension: !(false === argv.synchronet),
|
||||
enableAtKludges: !(false === argv.synchronet),
|
||||
archiveFormat: argv.format || 'application/zip',
|
||||
};
|
||||
|
||||
let totalExported = 0;
|
||||
async.waterfall(
|
||||
[
|
||||
(callback) => {
|
||||
callback => {
|
||||
return initConfigAndDatabases(callback);
|
||||
},
|
||||
(callback) => {
|
||||
callback => {
|
||||
const User = require('../../core/user.js');
|
||||
|
||||
User.getUserIdAndName(userName, (err, userId) => {
|
||||
@@ -555,7 +603,7 @@ function exportQWKPacket() {
|
||||
// if they were not explicitly supplied
|
||||
if (!areaTags.length) {
|
||||
const {
|
||||
getAllAvailableMessageAreaTags
|
||||
getAllAvailableMessageAreaTags,
|
||||
} = require('../../core/message_area');
|
||||
|
||||
areaTags = getAllAvailableMessageAreaTags();
|
||||
@@ -566,8 +614,8 @@ function exportQWKPacket() {
|
||||
const Message = require('../message');
|
||||
|
||||
const filter = {
|
||||
resultType : 'id',
|
||||
areaTag : areaTags,
|
||||
resultType: 'id',
|
||||
areaTag: areaTags,
|
||||
newerThanTimestamp,
|
||||
};
|
||||
|
||||
@@ -581,34 +629,46 @@ function exportQWKPacket() {
|
||||
filter.privateTagUserId = user.userId;
|
||||
|
||||
Message.findMessages(filter, (err, privateMessageIds) => {
|
||||
return callback(err, user, Message, privateMessageIds.concat(publicMessageIds));
|
||||
return callback(
|
||||
err,
|
||||
user,
|
||||
Message,
|
||||
privateMessageIds.concat(publicMessageIds)
|
||||
);
|
||||
});
|
||||
});
|
||||
},
|
||||
(user, Message, messageIds, callback) => {
|
||||
const { QWKPacketWriter } = require('../qwk_mail_packet');
|
||||
const writer = new QWKPacketWriter(Object.assign(writerOptions, {
|
||||
bbsID,
|
||||
user,
|
||||
}));
|
||||
const writer = new QWKPacketWriter(
|
||||
Object.assign(writerOptions, {
|
||||
bbsID,
|
||||
user,
|
||||
})
|
||||
);
|
||||
|
||||
writer.on('ready', () => {
|
||||
async.eachSeries(messageIds, (messageId, nextMessageId) => {
|
||||
const message = new Message();
|
||||
message.load( { messageId }, err => {
|
||||
if (!err) {
|
||||
writer.appendMessage(message);
|
||||
++totalExported;
|
||||
async.eachSeries(
|
||||
messageIds,
|
||||
(messageId, nextMessageId) => {
|
||||
const message = new Message();
|
||||
message.load({ messageId }, err => {
|
||||
if (!err) {
|
||||
writer.appendMessage(message);
|
||||
++totalExported;
|
||||
}
|
||||
return nextMessageId(err);
|
||||
});
|
||||
},
|
||||
err => {
|
||||
writer.finish(packetPath);
|
||||
if (err) {
|
||||
console.error(
|
||||
`Failed to write one or more messages: ${err.message}`
|
||||
);
|
||||
}
|
||||
return nextMessageId(err);
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
writer.finish(packetPath);
|
||||
if (err) {
|
||||
console.error(`Failed to write one or more messages: ${err.message}`);
|
||||
}
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
writer.on('warning', err => {
|
||||
@@ -620,10 +680,10 @@ function exportQWKPacket() {
|
||||
});
|
||||
|
||||
writer.init();
|
||||
}
|
||||
},
|
||||
],
|
||||
err => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
return console.error(err.reason ? err.reason : err.message);
|
||||
}
|
||||
|
||||
@@ -633,24 +693,22 @@ function exportQWKPacket() {
|
||||
}
|
||||
|
||||
function handleMessageBaseCommand() {
|
||||
|
||||
function errUsage() {
|
||||
return printUsageAndSetExitCode(
|
||||
getHelpFor('MessageBase'),
|
||||
ExitCodes.ERROR
|
||||
);
|
||||
return printUsageAndSetExitCode(getHelpFor('MessageBase'), ExitCodes.ERROR);
|
||||
}
|
||||
|
||||
if(true === argv.help) {
|
||||
if (true === argv.help) {
|
||||
return errUsage();
|
||||
}
|
||||
|
||||
const action = argv._[1];
|
||||
|
||||
return({
|
||||
areafix : areaFix,
|
||||
'import-areas' : importAreas,
|
||||
'qwk-dump' : dumpQWKPacket,
|
||||
'qwk-export' : exportQWKPacket,
|
||||
}[action] || errUsage)();
|
||||
}
|
||||
return (
|
||||
{
|
||||
areafix: areaFix,
|
||||
'import-areas': importAreas,
|
||||
'qwk-dump': dumpQWKPacket,
|
||||
'qwk-export': exportQWKPacket,
|
||||
}[action] || errUsage
|
||||
)();
|
||||
}
|
||||
|
||||
@@ -7,18 +7,18 @@ const {
|
||||
getAnswers,
|
||||
ExitCodes,
|
||||
argv,
|
||||
initConfigAndDatabases
|
||||
} = require('./oputil_common.js');
|
||||
const getHelpFor = require('./oputil_help.js').getHelpFor;
|
||||
const Errors = require('../enig_error.js').Errors;
|
||||
const UserProps = require('../user_property.js');
|
||||
initConfigAndDatabases,
|
||||
} = require('./oputil_common.js');
|
||||
const getHelpFor = require('./oputil_help.js').getHelpFor;
|
||||
const Errors = require('../enig_error.js').Errors;
|
||||
const UserProps = require('../user_property.js');
|
||||
|
||||
const async = require('async');
|
||||
const _ = require('lodash');
|
||||
const moment = require('moment');
|
||||
const fs = require('fs-extra');
|
||||
const async = require('async');
|
||||
const _ = require('lodash');
|
||||
const moment = require('moment');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
exports.handleUserCommand = handleUserCommand;
|
||||
exports.handleUserCommand = handleUserCommand;
|
||||
|
||||
function initAndGetUser(userName, cb) {
|
||||
async.waterfall(
|
||||
@@ -29,7 +29,7 @@ function initAndGetUser(userName, cb) {
|
||||
function getUserObject(callback) {
|
||||
const User = require('../../core/user.js');
|
||||
User.getUserIdAndName(userName, (err, userId) => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
// try user ID if number was supplied
|
||||
if (_.isNumber(userName)) {
|
||||
return User.getUser(parseInt(userName), callback);
|
||||
@@ -38,7 +38,7 @@ function initAndGetUser(userName, cb) {
|
||||
}
|
||||
return User.getUser(userId, callback);
|
||||
});
|
||||
}
|
||||
},
|
||||
],
|
||||
(err, user) => {
|
||||
return cb(err, user);
|
||||
@@ -47,36 +47,36 @@ function initAndGetUser(userName, cb) {
|
||||
}
|
||||
|
||||
function setAccountStatus(user, status) {
|
||||
if(argv._.length < 3) {
|
||||
if (argv._.length < 3) {
|
||||
return printUsageAndSetExitCode(getHelpFor('User'), ExitCodes.ERROR);
|
||||
}
|
||||
|
||||
const AccountStatus = require('../../core/user.js').AccountStatus;
|
||||
|
||||
status = {
|
||||
activate : AccountStatus.active,
|
||||
deactivate : AccountStatus.inactive,
|
||||
disable : AccountStatus.disabled,
|
||||
lock : AccountStatus.locked,
|
||||
activate: AccountStatus.active,
|
||||
deactivate: AccountStatus.inactive,
|
||||
disable: AccountStatus.disabled,
|
||||
lock: AccountStatus.locked,
|
||||
}[status];
|
||||
|
||||
const statusDesc = _.invert(AccountStatus)[status];
|
||||
|
||||
async.series(
|
||||
[
|
||||
(callback) => {
|
||||
callback => {
|
||||
return user.persistProperty(UserProps.AccountStatus, status, callback);
|
||||
},
|
||||
(callback) => {
|
||||
if(AccountStatus.active !== status) {
|
||||
callback => {
|
||||
if (AccountStatus.active !== status) {
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
return user.unlockAccount(callback);
|
||||
}
|
||||
},
|
||||
],
|
||||
err => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
process.exitCode = ExitCodes.ERROR;
|
||||
console.error(err.message);
|
||||
} else {
|
||||
@@ -87,7 +87,7 @@ function setAccountStatus(user, status) {
|
||||
}
|
||||
|
||||
function setUserPassword(user) {
|
||||
if(argv._.length < 4) {
|
||||
if (argv._.length < 4) {
|
||||
return printUsageAndSetExitCode(getHelpFor('User'), ExitCodes.ERROR);
|
||||
}
|
||||
|
||||
@@ -96,22 +96,22 @@ function setUserPassword(user) {
|
||||
function validate(callback) {
|
||||
// :TODO: prompt if no password provided (more secure, no history, etc.)
|
||||
const password = argv._[argv._.length - 1];
|
||||
if(0 === password.length) {
|
||||
if (0 === password.length) {
|
||||
return callback(Errors.Invalid('Invalid password'));
|
||||
}
|
||||
return callback(null, password);
|
||||
},
|
||||
function set(password, callback) {
|
||||
user.setNewAuthCredentials(password, err => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
process.exitCode = ExitCodes.BAD_ARGS;
|
||||
}
|
||||
return callback(err);
|
||||
});
|
||||
}
|
||||
},
|
||||
],
|
||||
err => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
console.error(err.message);
|
||||
} else {
|
||||
console.info('New password set');
|
||||
@@ -125,7 +125,7 @@ function removeUserRecordsFromDbAndTable(dbName, tableName, userId, col, cb) {
|
||||
db.run(
|
||||
`DELETE FROM ${tableName}
|
||||
WHERE ${col} = ?;`,
|
||||
[ userId ],
|
||||
[userId],
|
||||
err => {
|
||||
return cb(err);
|
||||
}
|
||||
@@ -135,96 +135,118 @@ function removeUserRecordsFromDbAndTable(dbName, tableName, userId, col, cb) {
|
||||
function removeUser(user) {
|
||||
async.series(
|
||||
[
|
||||
(callback) => {
|
||||
if(user.isRoot()) {
|
||||
callback => {
|
||||
if (user.isRoot()) {
|
||||
return callback(Errors.Invalid('Cannot delete root/SysOp user!'));
|
||||
}
|
||||
|
||||
return callback(null);
|
||||
},
|
||||
(callback) => {
|
||||
if(false === argv.prompt) {
|
||||
callback => {
|
||||
if (false === argv.prompt) {
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
console.info('About to permanently delete the following user:');
|
||||
console.info(`Username : ${user.username}`);
|
||||
console.info(`Real name: ${user.properties[UserProps.RealName] || 'N/A'}`);
|
||||
console.info(
|
||||
`Real name: ${user.properties[UserProps.RealName] || 'N/A'}`
|
||||
);
|
||||
console.info(`User ID : ${user.userId}`);
|
||||
console.info('WARNING: This cannot be undone!');
|
||||
getAnswers([
|
||||
{
|
||||
name : 'proceed',
|
||||
message : `Proceed in deleting ${user.username}?`,
|
||||
type : 'confirm',
|
||||
getAnswers(
|
||||
[
|
||||
{
|
||||
name: 'proceed',
|
||||
message: `Proceed in deleting ${user.username}?`,
|
||||
type: 'confirm',
|
||||
},
|
||||
],
|
||||
answers => {
|
||||
if (answers.proceed) {
|
||||
return callback(null);
|
||||
}
|
||||
return callback(Errors.General('User canceled'));
|
||||
}
|
||||
],
|
||||
answers => {
|
||||
if(answers.proceed) {
|
||||
return callback(null);
|
||||
}
|
||||
return callback(Errors.General('User canceled'));
|
||||
});
|
||||
);
|
||||
},
|
||||
(callback) => {
|
||||
callback => {
|
||||
// op has confirmed they are wanting ready to proceed (or passed --no-prompt)
|
||||
const DeleteFrom = {
|
||||
message : [ 'user_message_area_last_read' ],
|
||||
system : [ 'user_event_log', ],
|
||||
user : [ 'user_group_member', 'user' ],
|
||||
file : [ 'file_user_rating']
|
||||
message: ['user_message_area_last_read'],
|
||||
system: ['user_event_log'],
|
||||
user: ['user_group_member', 'user'],
|
||||
file: ['file_user_rating'],
|
||||
};
|
||||
|
||||
async.eachSeries(Object.keys(DeleteFrom), (dbName, nextDbName) => {
|
||||
const tables = DeleteFrom[dbName];
|
||||
async.eachSeries(tables, (tableName, nextTableName) => {
|
||||
const col = ('user' === dbName && 'user' === tableName) ? 'id' : 'user_id';
|
||||
removeUserRecordsFromDbAndTable(dbName, tableName, user.userId, col, err => {
|
||||
return nextTableName(err);
|
||||
});
|
||||
},
|
||||
err => {
|
||||
return nextDbName(err);
|
||||
});
|
||||
},
|
||||
err => {
|
||||
return callback(err);
|
||||
});
|
||||
},
|
||||
(callback) => {
|
||||
//
|
||||
// Clean up *private* messages *to* this user
|
||||
//
|
||||
const Message = require('../../core/message.js');
|
||||
const MsgDb = require('../../core/database.js').dbs.message;
|
||||
|
||||
const filter = {
|
||||
resultType : 'id',
|
||||
privateTagUserId : user.userId,
|
||||
};
|
||||
Message.findMessages(filter, (err, ids) => {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
async.eachSeries(ids, (messageId, nextMessageId) => {
|
||||
MsgDb.run(
|
||||
`DELETE FROM message
|
||||
WHERE message_id = ?;`,
|
||||
[ messageId ],
|
||||
async.eachSeries(
|
||||
Object.keys(DeleteFrom),
|
||||
(dbName, nextDbName) => {
|
||||
const tables = DeleteFrom[dbName];
|
||||
async.eachSeries(
|
||||
tables,
|
||||
(tableName, nextTableName) => {
|
||||
const col =
|
||||
'user' === dbName && 'user' === tableName
|
||||
? 'id'
|
||||
: 'user_id';
|
||||
removeUserRecordsFromDbAndTable(
|
||||
dbName,
|
||||
tableName,
|
||||
user.userId,
|
||||
col,
|
||||
err => {
|
||||
return nextTableName(err);
|
||||
}
|
||||
);
|
||||
},
|
||||
err => {
|
||||
return nextMessageId(err);
|
||||
return nextDbName(err);
|
||||
}
|
||||
);
|
||||
},
|
||||
err => {
|
||||
return callback(err);
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
callback => {
|
||||
//
|
||||
// Clean up *private* messages *to* this user
|
||||
//
|
||||
const Message = require('../../core/message.js');
|
||||
const MsgDb = require('../../core/database.js').dbs.message;
|
||||
|
||||
const filter = {
|
||||
resultType: 'id',
|
||||
privateTagUserId: user.userId,
|
||||
};
|
||||
Message.findMessages(filter, (err, ids) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
async.eachSeries(
|
||||
ids,
|
||||
(messageId, nextMessageId) => {
|
||||
MsgDb.run(
|
||||
`DELETE FROM message
|
||||
WHERE message_id = ?;`,
|
||||
[messageId],
|
||||
err => {
|
||||
return nextMessageId(err);
|
||||
}
|
||||
);
|
||||
},
|
||||
err => {
|
||||
return callback(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
],
|
||||
err => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
return console.error(err.reason ? err.reason : err.message);
|
||||
}
|
||||
|
||||
@@ -234,7 +256,7 @@ function removeUser(user) {
|
||||
}
|
||||
|
||||
function renameUser(user) {
|
||||
if(argv._.length < 3) {
|
||||
if (argv._.length < 3) {
|
||||
return printUsageAndSetExitCode(getHelpFor('User'), ExitCodes.ERROR);
|
||||
}
|
||||
|
||||
@@ -242,25 +264,27 @@ function renameUser(user) {
|
||||
|
||||
async.series(
|
||||
[
|
||||
(callback) => {
|
||||
const { validateUserNameAvail } = require('../../core/system_view_validate.js');
|
||||
callback => {
|
||||
const {
|
||||
validateUserNameAvail,
|
||||
} = require('../../core/system_view_validate.js');
|
||||
return validateUserNameAvail(newUserName, callback);
|
||||
},
|
||||
(callback) => {
|
||||
callback => {
|
||||
const userDb = require('../../core/database.js').dbs.user;
|
||||
userDb.run(
|
||||
`UPDATE user
|
||||
SET user_name = ?
|
||||
WHERE id = ?;`,
|
||||
[ newUserName, user.userId, ],
|
||||
[newUserName, user.userId],
|
||||
err => {
|
||||
return callback(err);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
],
|
||||
err => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
return console.error(err.reason ? err.reason : err.message);
|
||||
}
|
||||
return console.info(`User "${user.username}" renamed to "${newUserName}"`);
|
||||
@@ -269,33 +293,33 @@ function renameUser(user) {
|
||||
}
|
||||
|
||||
function modUserGroups(user) {
|
||||
if(argv._.length < 3) {
|
||||
if (argv._.length < 3) {
|
||||
return printUsageAndSetExitCode(getHelpFor('User'), ExitCodes.ERROR);
|
||||
}
|
||||
|
||||
let groupName = argv._[argv._.length - 1].toString().replace(/["']/g, ''); // remove any quotes - necessary to allow "-foo"
|
||||
let action = groupName[0]; // + or -
|
||||
let groupName = argv._[argv._.length - 1].toString().replace(/["']/g, ''); // remove any quotes - necessary to allow "-foo"
|
||||
let action = groupName[0]; // + or -
|
||||
|
||||
if('-' === action || '+' === action || '~' === action) {
|
||||
if ('-' === action || '+' === action || '~' === action) {
|
||||
groupName = groupName.substr(1);
|
||||
}
|
||||
|
||||
action = action || '+';
|
||||
|
||||
if(0 === groupName.length) {
|
||||
if (0 === groupName.length) {
|
||||
return printUsageAndSetExitCode(getHelpFor('User'), ExitCodes.ERROR);
|
||||
}
|
||||
|
||||
//
|
||||
// Groups are currently arbitrary, so do a slight validation
|
||||
//
|
||||
if(!/[A-Za-z0-9]+/.test(groupName)) {
|
||||
if (!/[A-Za-z0-9]+/.test(groupName)) {
|
||||
process.exitCode = ExitCodes.BAD_ARGS;
|
||||
return console.error('Bad group name');
|
||||
}
|
||||
|
||||
function done(err) {
|
||||
if(err) {
|
||||
if (err) {
|
||||
process.exitCode = ExitCodes.BAD_ARGS;
|
||||
console.error(err.message);
|
||||
} else {
|
||||
@@ -304,7 +328,7 @@ function modUserGroups(user) {
|
||||
}
|
||||
|
||||
const UserGroup = require('../../core/user_group.js');
|
||||
if('-' === action || '~' === action) {
|
||||
if ('-' === action || '~' === action) {
|
||||
UserGroup.removeUserFromGroup(user.userId, groupName, done);
|
||||
} else {
|
||||
UserGroup.addUserToGroup(user.userId, groupName, done);
|
||||
@@ -312,7 +336,6 @@ function modUserGroups(user) {
|
||||
}
|
||||
|
||||
function showUserInfo(user) {
|
||||
|
||||
const User = require('../../core/user.js');
|
||||
|
||||
const statusDesc = () => {
|
||||
@@ -352,14 +375,15 @@ Email : ${propOrNA(UserProps.EmailAddress)}
|
||||
Location : ${propOrNA(UserProps.Location)}
|
||||
Affiliations : ${propOrNA(UserProps.Affiliations)}`;
|
||||
let secInfo = '';
|
||||
if(argv.security) {
|
||||
if (argv.security) {
|
||||
const otp = user.getProperty(UserProps.AuthFactor2OTP);
|
||||
if(otp) {
|
||||
if (otp) {
|
||||
const backupCodesOrNa = () => {
|
||||
try
|
||||
{
|
||||
return JSON.parse(user.getProperty(UserProps.AuthFactor2OTPBackupCodes)).join(', ');
|
||||
} catch(e) {
|
||||
try {
|
||||
return JSON.parse(
|
||||
user.getProperty(UserProps.AuthFactor2OTPBackupCodes)
|
||||
).join(', ');
|
||||
} catch (e) {
|
||||
return 'N/A';
|
||||
}
|
||||
};
|
||||
@@ -373,7 +397,7 @@ OTP Backup : ${backupCodesOrNa()}`;
|
||||
}
|
||||
|
||||
function twoFactorAuthOTP(user) {
|
||||
if(argv._.length < 4) {
|
||||
if (argv._.length < 4) {
|
||||
return printUsageAndSetExitCode(getHelpFor('User'), ExitCodes.ERROR);
|
||||
}
|
||||
|
||||
@@ -386,14 +410,14 @@ function twoFactorAuthOTP(user) {
|
||||
let otpType = argv._[argv._.length - 1];
|
||||
|
||||
// shortcut for removal
|
||||
if('disable' === otpType) {
|
||||
if ('disable' === otpType) {
|
||||
const props = [
|
||||
UserProps.AuthFactor2OTP,
|
||||
UserProps.AuthFactor2OTPSecret,
|
||||
UserProps.AuthFactor2OTPBackupCodes,
|
||||
];
|
||||
return user.removeProperties(props, err => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
console.error(err.message);
|
||||
} else {
|
||||
console.info(`2FA OTP disabled for ${user.username}`);
|
||||
@@ -406,30 +430,37 @@ function twoFactorAuthOTP(user) {
|
||||
function validate(callback) {
|
||||
// :TODO: Prompt for if not supplied
|
||||
// allow aliases for OTP types
|
||||
otpType = {
|
||||
google : OTPTypes.GoogleAuthenticator,
|
||||
hotp : OTPTypes.RFC4266_HOTP,
|
||||
totp : OTPTypes.RFC6238_TOTP,
|
||||
}[otpType] || otpType;
|
||||
otpType =
|
||||
{
|
||||
google: OTPTypes.GoogleAuthenticator,
|
||||
hotp: OTPTypes.RFC4266_HOTP,
|
||||
totp: OTPTypes.RFC6238_TOTP,
|
||||
}[otpType] || otpType;
|
||||
otpType = _.find(OTPTypes, t => {
|
||||
return t.toLowerCase() === otpType.toLowerCase();
|
||||
});
|
||||
if(!otpType) {
|
||||
if (!otpType) {
|
||||
return callback(Errors.Invalid('Invalid OTP type'));
|
||||
}
|
||||
return callback(null, otpType);
|
||||
},
|
||||
function prepare(otpType, callback) {
|
||||
const otpOpts = {
|
||||
username : user.username,
|
||||
qrType : argv['qr-type'] || 'ascii',
|
||||
username: user.username,
|
||||
qrType: argv['qr-type'] || 'ascii',
|
||||
};
|
||||
prepareOTP(otpType, otpOpts, (err, otpInfo) => {
|
||||
return callback(err, Object.assign(otpInfo, { otpType, backupCodes : createBackupCodes() }));
|
||||
return callback(
|
||||
err,
|
||||
Object.assign(otpInfo, {
|
||||
otpType,
|
||||
backupCodes: createBackupCodes(),
|
||||
})
|
||||
);
|
||||
});
|
||||
},
|
||||
function storeOrDisplayQR(otpInfo, callback) {
|
||||
if(!argv.out || !otpInfo.qr) {
|
||||
if (!argv.out || !otpInfo.qr) {
|
||||
return callback(null, otpInfo);
|
||||
}
|
||||
|
||||
@@ -439,25 +470,27 @@ function twoFactorAuthOTP(user) {
|
||||
},
|
||||
function persist(otpInfo, callback) {
|
||||
const props = {
|
||||
[ UserProps.AuthFactor2OTP ] : otpInfo.otpType,
|
||||
[ UserProps.AuthFactor2OTPSecret ] : otpInfo.secret,
|
||||
[ UserProps.AuthFactor2OTPBackupCodes ] : JSON.stringify(otpInfo.backupCodes),
|
||||
[UserProps.AuthFactor2OTP]: otpInfo.otpType,
|
||||
[UserProps.AuthFactor2OTPSecret]: otpInfo.secret,
|
||||
[UserProps.AuthFactor2OTPBackupCodes]: JSON.stringify(
|
||||
otpInfo.backupCodes
|
||||
),
|
||||
};
|
||||
user.persistProperties(props, err => {
|
||||
return callback(err, otpInfo);
|
||||
});
|
||||
}
|
||||
},
|
||||
],
|
||||
(err, otpInfo) => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
console.error(err.message);
|
||||
} else {
|
||||
console.info(`OTP enabled for : ${user.username}`);
|
||||
console.info(`Secret : ${otpInfo.secret}`);
|
||||
console.info(`Backup codes : ${otpInfo.backupCodes.join(', ')}`);
|
||||
|
||||
if(otpInfo.qr) {
|
||||
if(!argv.out) {
|
||||
if (otpInfo.qr) {
|
||||
if (!argv.out) {
|
||||
console.info('--- Begin QR ---');
|
||||
console.info(otpInfo.qr);
|
||||
console.info('--- End QR ---');
|
||||
@@ -484,19 +517,17 @@ function listUsers() {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
async.waterfall(
|
||||
[
|
||||
(callback) => {
|
||||
callback => {
|
||||
const UserProps = require('../../core/user_property');
|
||||
|
||||
const userListOpts = {
|
||||
properties : [
|
||||
UserProps.AccountStatus,
|
||||
],
|
||||
properties: [UserProps.AccountStatus],
|
||||
};
|
||||
|
||||
User.getUserList(userListOpts, (err, userList) => {
|
||||
@@ -510,9 +541,12 @@ function listUsers() {
|
||||
|
||||
const accountStatusFilter = User.AccountStatus[listWhat].toString();
|
||||
|
||||
return callback(null, userList.filter(user => {
|
||||
return user[UserProps.AccountStatus] === accountStatusFilter;
|
||||
}));
|
||||
return callback(
|
||||
null,
|
||||
userList.filter(user => {
|
||||
return user[UserProps.AccountStatus] === accountStatusFilter;
|
||||
})
|
||||
);
|
||||
});
|
||||
},
|
||||
(userList, callback) => {
|
||||
@@ -524,7 +558,7 @@ function listUsers() {
|
||||
},
|
||||
],
|
||||
err => {
|
||||
if(err) {
|
||||
if (err) {
|
||||
return console.error(err.reason ? err.reason : err.message);
|
||||
}
|
||||
}
|
||||
@@ -532,63 +566,72 @@ function listUsers() {
|
||||
}
|
||||
|
||||
function handleUserCommand() {
|
||||
function errUsage() {
|
||||
function errUsage() {
|
||||
return printUsageAndSetExitCode(getHelpFor('User'), ExitCodes.ERROR);
|
||||
}
|
||||
|
||||
if(true === argv.help) {
|
||||
if (true === argv.help) {
|
||||
return errUsage();
|
||||
}
|
||||
|
||||
const action = argv._[1];
|
||||
const userRequired = ![ 'list' ].includes(action);
|
||||
const userRequired = !['list'].includes(action);
|
||||
|
||||
let userName;
|
||||
if (userRequired) {
|
||||
const usernameIdx = [
|
||||
'pw', 'pass', 'passwd', 'password',
|
||||
'pw',
|
||||
'pass',
|
||||
'passwd',
|
||||
'password',
|
||||
'group',
|
||||
'mv', 'rename',
|
||||
'2fa-otp', 'otp'
|
||||
].includes(action) ? argv._.length - 2 : argv._.length - 1;
|
||||
'mv',
|
||||
'rename',
|
||||
'2fa-otp',
|
||||
'otp',
|
||||
].includes(action)
|
||||
? argv._.length - 2
|
||||
: argv._.length - 1;
|
||||
userName = argv._[usernameIdx];
|
||||
}
|
||||
|
||||
if(!userName && userRequired) {
|
||||
if (!userName && userRequired) {
|
||||
return errUsage();
|
||||
}
|
||||
|
||||
initAndGetUser(userName, (err, user) => {
|
||||
if(userName && err) {
|
||||
if (userName && err) {
|
||||
process.exitCode = ExitCodes.ERROR;
|
||||
return console.error(err.message);
|
||||
}
|
||||
|
||||
return ({
|
||||
pw : setUserPassword,
|
||||
passwd : setUserPassword,
|
||||
password : setUserPassword,
|
||||
return (
|
||||
{
|
||||
pw: setUserPassword,
|
||||
passwd: setUserPassword,
|
||||
password: setUserPassword,
|
||||
|
||||
rm : removeUser,
|
||||
remove : removeUser,
|
||||
del : removeUser,
|
||||
delete : removeUser,
|
||||
rm: removeUser,
|
||||
remove: removeUser,
|
||||
del: removeUser,
|
||||
delete: removeUser,
|
||||
|
||||
mv : renameUser,
|
||||
rename : renameUser,
|
||||
mv: renameUser,
|
||||
rename: renameUser,
|
||||
|
||||
activate : setAccountStatus,
|
||||
deactivate : setAccountStatus,
|
||||
disable : setAccountStatus,
|
||||
lock : setAccountStatus,
|
||||
activate: setAccountStatus,
|
||||
deactivate: setAccountStatus,
|
||||
disable: setAccountStatus,
|
||||
lock: setAccountStatus,
|
||||
|
||||
group : modUserGroups,
|
||||
group: modUserGroups,
|
||||
|
||||
info : showUserInfo,
|
||||
info: showUserInfo,
|
||||
|
||||
'2fa-otp' : twoFactorAuthOTP,
|
||||
otp : twoFactorAuthOTP,
|
||||
list : listUsers,
|
||||
}[action] || errUsage)(user, action);
|
||||
'2fa-otp': twoFactorAuthOTP,
|
||||
otp: twoFactorAuthOTP,
|
||||
list: listUsers,
|
||||
}[action] || errUsage
|
||||
)(user, action);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user