* Split out oputil stuff into modules based on <command>
* oputil: better syntax * oputil: allow areaTag@storageTag for scan, e.g. oputil fb scan <args> retro_pc@some_specific_storage
This commit is contained in:
parent
d41fbf6911
commit
d0511d5d74
|
@ -28,6 +28,7 @@ Upgrading from GitHub is easy:
|
||||||
```bash
|
```bash
|
||||||
cd /path/to/enigma-bbs
|
cd /path/to/enigma-bbs
|
||||||
git pull
|
git pull
|
||||||
|
rm -rf npm_modules # do this any time you update Node.js itself
|
||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ exports.isInternalArea = isInternalArea;
|
||||||
exports.getAvailableFileAreas = getAvailableFileAreas;
|
exports.getAvailableFileAreas = getAvailableFileAreas;
|
||||||
exports.getSortedAvailableFileAreas = getSortedAvailableFileAreas;
|
exports.getSortedAvailableFileAreas = getSortedAvailableFileAreas;
|
||||||
exports.getAreaDefaultStorageDirectory = getAreaDefaultStorageDirectory;
|
exports.getAreaDefaultStorageDirectory = getAreaDefaultStorageDirectory;
|
||||||
|
exports.getAreaStorageLocations = getAreaStorageLocations;
|
||||||
exports.getDefaultFileAreaTag = getDefaultFileAreaTag;
|
exports.getDefaultFileAreaTag = getDefaultFileAreaTag;
|
||||||
exports.getFileAreaByTag = getFileAreaByTag;
|
exports.getFileAreaByTag = getFileAreaByTag;
|
||||||
exports.getFileEntryPath = getFileEntryPath;
|
exports.getFileEntryPath = getFileEntryPath;
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
/* jslint node: true */
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const resolvePath = require('../misc_util.js').resolvePath;
|
||||||
|
|
||||||
|
const config = require('../../core/config.js');
|
||||||
|
const db = require('../../core/database.js');
|
||||||
|
|
||||||
|
const _ = require('lodash');
|
||||||
|
const async = require('async');
|
||||||
|
|
||||||
|
exports.printUsageAndSetExitCode = printUsageAndSetExitCode;
|
||||||
|
exports.getDefaultConfigPath = getDefaultConfigPath;
|
||||||
|
exports.initConfigAndDatabases = initConfigAndDatabases;
|
||||||
|
exports.getAreaAndStorage = getAreaAndStorage;
|
||||||
|
|
||||||
|
const exitCodes = exports.ExitCodes = {
|
||||||
|
SUCCESS : 0,
|
||||||
|
ERROR : -1,
|
||||||
|
BAD_COMMAND : -2,
|
||||||
|
BAD_ARGS : -3,
|
||||||
|
};
|
||||||
|
|
||||||
|
const argv = exports.argv = require('minimist')(process.argv.slice(2));
|
||||||
|
|
||||||
|
function printUsageAndSetExitCode(errMsg, exitCode) {
|
||||||
|
if(_.isUndefined(exitCode)) {
|
||||||
|
exitCode = exitCodes.ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exitCode = exitCode;
|
||||||
|
|
||||||
|
if(errMsg) {
|
||||||
|
console.error(errMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDefaultConfigPath() {
|
||||||
|
return resolvePath('~/.config/enigma-bbs/config.hjson');
|
||||||
|
}
|
||||||
|
|
||||||
|
function initConfig(cb) {
|
||||||
|
const configPath = argv.config ? argv.config : config.getDefaultPath();
|
||||||
|
|
||||||
|
config.init(configPath, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initConfigAndDatabases(cb) {
|
||||||
|
async.series(
|
||||||
|
[
|
||||||
|
function init(callback) {
|
||||||
|
initConfig(callback);
|
||||||
|
},
|
||||||
|
function initDb(callback) {
|
||||||
|
db.initializeDatabases(callback);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
err => {
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAreaAndStorage(tags) {
|
||||||
|
return tags.map(tag => {
|
||||||
|
const parts = tag.split('@');
|
||||||
|
const entry = {
|
||||||
|
areaTag : parts[0],
|
||||||
|
};
|
||||||
|
if(parts[1]) {
|
||||||
|
entry.storageTag = parts[1];
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,258 @@
|
||||||
|
/* jslint node: true */
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// ENiGMA½
|
||||||
|
const resolvePath = require('../../core/misc_util.js').resolvePath;
|
||||||
|
const printUsageAndSetExitCode = require('./oputil_common.js').printUsageAndSetExitCode;
|
||||||
|
const ExitCodes = require('./oputil_common.js').ExitCodes;
|
||||||
|
const argv = require('./oputil_common.js').argv;
|
||||||
|
const getDefaultConfigPath = require('./oputil_common.js').getDefaultConfigPath;
|
||||||
|
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('fs');
|
||||||
|
const hjson = require('hjson');
|
||||||
|
const paths = require('path');
|
||||||
|
|
||||||
|
exports.handleConfigCommand = handleConfigCommand;
|
||||||
|
|
||||||
|
|
||||||
|
function getAnswers(questions, cb) {
|
||||||
|
inq.prompt(questions).then( answers => {
|
||||||
|
return cb(answers);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const QUESTIONS = {
|
||||||
|
Intro : [
|
||||||
|
{
|
||||||
|
name : 'createNewConfig',
|
||||||
|
message : 'Create a new configuration?',
|
||||||
|
type : 'confirm',
|
||||||
|
default : false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'configPath',
|
||||||
|
message : 'Configuration path:',
|
||||||
|
default : argv.config ? argv.config : getDefaultConfigPath(),
|
||||||
|
when : answers => answers.createNewConfig
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
OverwriteConfig : [
|
||||||
|
{
|
||||||
|
name : 'overwriteConfig',
|
||||||
|
message : 'Config file exists. Overwrite?',
|
||||||
|
type : 'confirm',
|
||||||
|
default : false,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
Basic : [
|
||||||
|
{
|
||||||
|
name : 'boardName',
|
||||||
|
message : 'BBS name:',
|
||||||
|
default : 'New ENiGMA½ BBS',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
Misc : [
|
||||||
|
{
|
||||||
|
name : 'loggingLevel',
|
||||||
|
message : 'Logging level:',
|
||||||
|
type : 'list',
|
||||||
|
choices : [ 'Error', 'Warn', 'Info', 'Debug', 'Trace' ],
|
||||||
|
default : 2,
|
||||||
|
filter : s => s.toLowerCase(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'sevenZipExe',
|
||||||
|
message : '7-Zip executable:',
|
||||||
|
type : 'list',
|
||||||
|
choices : [ '7z', '7za', 'None' ]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
MessageConfAndArea : [
|
||||||
|
{
|
||||||
|
name : 'msgConfName',
|
||||||
|
message : 'First message conference:',
|
||||||
|
default : 'Local',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'msgConfDesc',
|
||||||
|
message : 'Conference description:',
|
||||||
|
default : 'Local Areas',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'msgAreaName',
|
||||||
|
message : 'First area in message conference:',
|
||||||
|
default : 'General',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name : 'msgAreaDesc',
|
||||||
|
message : 'Area description:',
|
||||||
|
default : 'General chit-chat',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
function makeMsgConfAreaName(s) {
|
||||||
|
return s.toLowerCase().replace(/\s+/g, '_');
|
||||||
|
}
|
||||||
|
|
||||||
|
function askNewConfigQuestions(cb) {
|
||||||
|
|
||||||
|
const ui = new inq.ui.BottomBar();
|
||||||
|
|
||||||
|
let configPath;
|
||||||
|
let config;
|
||||||
|
|
||||||
|
async.waterfall(
|
||||||
|
[
|
||||||
|
function intro(callback) {
|
||||||
|
getAnswers(QUESTIONS.Intro, answers => {
|
||||||
|
if(!answers.createNewConfig) {
|
||||||
|
return callback('exit');
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjust for ~ and the like
|
||||||
|
configPath = resolvePath(answers.configPath);
|
||||||
|
|
||||||
|
const configDir = paths.dirname(configPath);
|
||||||
|
mkdirsSync(configDir);
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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) {
|
||||||
|
ui.log.write(`${configPath} cannot be written to`);
|
||||||
|
callback('exit');
|
||||||
|
} else if('ENOENT' === err.code) {
|
||||||
|
callback(null, false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callback(null, true); // exists + writable
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function promptOverwrite(needPrompt, callback) {
|
||||||
|
if(needPrompt) {
|
||||||
|
getAnswers(QUESTIONS.OverwriteConfig, answers => {
|
||||||
|
callback(answers.overwriteConfig ? null : 'exit');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
callback(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function basic(callback) {
|
||||||
|
getAnswers(QUESTIONS.Basic, answers => {
|
||||||
|
config = {
|
||||||
|
general : {
|
||||||
|
boardName : answers.boardName,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
callback(null);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function msgConfAndArea(callback) {
|
||||||
|
getAnswers(QUESTIONS.MessageConfAndArea, answers => {
|
||||||
|
config.messageConferences = {};
|
||||||
|
|
||||||
|
const confName = makeMsgConfAreaName(answers.msgConfName);
|
||||||
|
const areaName = makeMsgConfAreaName(answers.msgAreaName);
|
||||||
|
|
||||||
|
config.messageConferences[confName] = {
|
||||||
|
name : answers.msgConfName,
|
||||||
|
desc : answers.msgConfDesc,
|
||||||
|
sort : 1,
|
||||||
|
default : true,
|
||||||
|
};
|
||||||
|
|
||||||
|
config.messageConferences.another_sample_conf = {
|
||||||
|
name : 'Another Sample Conference',
|
||||||
|
desc : 'Another conference example. Change me!',
|
||||||
|
sort : 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
config.messageConferences[confName].areas = {};
|
||||||
|
config.messageConferences[confName].areas[areaName] = {
|
||||||
|
name : answers.msgAreaName,
|
||||||
|
desc : answers.msgAreaDesc,
|
||||||
|
sort : 1,
|
||||||
|
default : true,
|
||||||
|
};
|
||||||
|
|
||||||
|
config.messageConferences.another_sample_conf = {
|
||||||
|
name : 'Another Sample Conference',
|
||||||
|
desc : 'Another conf sample. Change me!',
|
||||||
|
|
||||||
|
areas : {
|
||||||
|
another_sample_area : {
|
||||||
|
name : 'Another Sample Area',
|
||||||
|
desc : 'Another area example. Change me!',
|
||||||
|
sort : 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
callback(null);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function misc(callback) {
|
||||||
|
getAnswers(QUESTIONS.Misc, answers => {
|
||||||
|
if('None' !== answers.sevenZipExe) {
|
||||||
|
config.archivers = {
|
||||||
|
zip : {
|
||||||
|
compressCmd : answers.sevenZipExe,
|
||||||
|
decompressCmd : answers.sevenZipExe,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
config.logging = {
|
||||||
|
level : answers.loggingLevel,
|
||||||
|
};
|
||||||
|
|
||||||
|
callback(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
],
|
||||||
|
err => {
|
||||||
|
cb(err, configPath, config);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleConfigCommand() {
|
||||||
|
if(true === argv.help) {
|
||||||
|
return printUsageAndSetExitCode(getHelpFor('Config'), ExitCodes.ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(argv.new) {
|
||||||
|
askNewConfigQuestions( (err, configPath, config) => {
|
||||||
|
if(err) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
config = hjson.stringify(config, { bracesSameLine : true, spaces : '\t' } );
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(configPath, config, 'utf8');
|
||||||
|
console.info('Configuration generated');
|
||||||
|
} catch(e) {
|
||||||
|
console.error('Exception attempting to create config: ' + e.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return printUsageAndSetExitCode(getHelpFor('Config'), ExitCodes.ERROR);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
/* jslint node: true */
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const printUsageAndSetExitCode = require('./oputil_common.js').printUsageAndSetExitCode;
|
||||||
|
const ExitCodes = require('./oputil_common.js').ExitCodes;
|
||||||
|
const argv = require('./oputil_common.js').argv;
|
||||||
|
const initConfigAndDatabases = require('./oputil_common.js').initConfigAndDatabases;
|
||||||
|
const getHelpFor = require('./oputil_help.js').getHelpFor;
|
||||||
|
const getAreaAndStorage = require('./oputil_common.js').getAreaAndStorage;
|
||||||
|
|
||||||
|
|
||||||
|
const async = require('async');
|
||||||
|
const fs = require('fs');
|
||||||
|
const paths = require('path');
|
||||||
|
|
||||||
|
exports.handleFileBaseCommand = handleFileBaseCommand;
|
||||||
|
|
||||||
|
/*
|
||||||
|
:TODO:
|
||||||
|
|
||||||
|
Global options:
|
||||||
|
--yes: assume yes
|
||||||
|
--no-prompt: try to avoid user input
|
||||||
|
|
||||||
|
Prompt for import and description before scan
|
||||||
|
* Only after finding duplicate-by-path
|
||||||
|
* Default to filename -> desc if auto import
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
let fileArea; // required during init
|
||||||
|
|
||||||
|
function scanFileAreaForChanges(areaInfo, options, cb) {
|
||||||
|
|
||||||
|
const storageLocations = fileArea.getAreaStorageLocations(areaInfo).filter(sl => {
|
||||||
|
return options.areaAndStorageInfo.find(asi => {
|
||||||
|
return !asi.storageTag || sl.storageTag === asi.storageTag;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
async.eachSeries(storageLocations, (storageLoc, nextLocation) => {
|
||||||
|
async.series(
|
||||||
|
[
|
||||||
|
function scanPhysFiles(callback) {
|
||||||
|
const physDir = storageLoc.dir;
|
||||||
|
|
||||||
|
fs.readdir(physDir, (err, files) => {
|
||||||
|
if(err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
async.eachSeries(files, (fileName, nextFile) => {
|
||||||
|
const fullPath = paths.join(physDir, fileName);
|
||||||
|
|
||||||
|
fs.stat(fullPath, (err, stats) => {
|
||||||
|
if(err) {
|
||||||
|
// :TODO: Log me!
|
||||||
|
return nextFile(null); // always try next file
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!stats.isFile()) {
|
||||||
|
return nextFile(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.stdout.write(`* Scanning ${fullPath}... `);
|
||||||
|
|
||||||
|
fileArea.scanFile(
|
||||||
|
fullPath,
|
||||||
|
{
|
||||||
|
areaTag : areaInfo.areaTag,
|
||||||
|
storageTag : storageLoc.storageTag
|
||||||
|
},
|
||||||
|
(err, fileEntry, dupeEntries) => {
|
||||||
|
if(err) {
|
||||||
|
// :TODO: Log me!!!
|
||||||
|
console.info(`Error: ${err.message}`);
|
||||||
|
return nextFile(null); // try next anyway
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(dupeEntries.length > 0) {
|
||||||
|
// :TODO: Handle duplidates -- what to do here???
|
||||||
|
console.info('Dupe');
|
||||||
|
return nextFile(null);
|
||||||
|
} else {
|
||||||
|
console.info('Done!');
|
||||||
|
if(Array.isArray(options.tags)) {
|
||||||
|
options.tags.forEach(tag => {
|
||||||
|
fileEntry.hashTags.add(tag);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fileEntry.persist( err => {
|
||||||
|
return nextFile(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}, err => {
|
||||||
|
return callback(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function scanDbEntries(callback) {
|
||||||
|
// :TODO: Look @ db entries for area that were *not* processed above
|
||||||
|
return callback(null);
|
||||||
|
}
|
||||||
|
],
|
||||||
|
err => {
|
||||||
|
return nextLocation(err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
err => {
|
||||||
|
return cb(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function scanFileAreas() {
|
||||||
|
const options = {};
|
||||||
|
|
||||||
|
const tags = argv.tags;
|
||||||
|
if(tags) {
|
||||||
|
options.tags = tags.split(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
options.areaAndStorageInfo = getAreaAndStorage(argv._.slice(2));
|
||||||
|
|
||||||
|
async.series(
|
||||||
|
[
|
||||||
|
function init(callback) {
|
||||||
|
return initConfigAndDatabases(callback);
|
||||||
|
},
|
||||||
|
function scanAreas(callback) {
|
||||||
|
fileArea = require('../../core/file_base_area.js');
|
||||||
|
|
||||||
|
async.eachSeries(options.areaAndStorageInfo, (areaAndStorage, nextAreaTag) => {
|
||||||
|
const areaInfo = fileArea.getFileAreaByTag(areaAndStorage.areaTag);
|
||||||
|
if(!areaInfo) {
|
||||||
|
return nextAreaTag(new Error(`Invalid file base area tag: ${areaAndStorage.areaTag}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
console.info(`Processing area "${areaInfo.name}":`);
|
||||||
|
|
||||||
|
scanFileAreaForChanges(areaInfo, options, err => {
|
||||||
|
return callback(err);
|
||||||
|
});
|
||||||
|
}, err => {
|
||||||
|
return callback(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
],
|
||||||
|
err => {
|
||||||
|
if(err) {
|
||||||
|
process.exitCode = ExitCodes.ERROR;
|
||||||
|
console.error(err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFileBaseCommand() {
|
||||||
|
if(true === argv.help) {
|
||||||
|
return printUsageAndSetExitCode(getHelpFor('FileBase'), ExitCodes.ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
const action = argv._[1];
|
||||||
|
|
||||||
|
switch(action) {
|
||||||
|
case 'scan' : return scanFileAreas();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/* jslint node: true */
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const getDefaultConfigPath = require('./oputil_common.js').getDefaultConfigPath;
|
||||||
|
|
||||||
|
exports.getHelpFor = getHelpFor;
|
||||||
|
|
||||||
|
const usageHelp = exports.USAGE_HELP = {
|
||||||
|
General :
|
||||||
|
`usage: optutil.js [--version] [--help]
|
||||||
|
<command> [<args>]
|
||||||
|
|
||||||
|
global args:
|
||||||
|
--config PATH : specify config path (${getDefaultConfigPath()})
|
||||||
|
|
||||||
|
where <command> is one of:
|
||||||
|
user : user utilities
|
||||||
|
config : config file management
|
||||||
|
file-base
|
||||||
|
fb : file base management
|
||||||
|
|
||||||
|
`,
|
||||||
|
User :
|
||||||
|
`usage: optutil.js user --user USERNAME <args>
|
||||||
|
|
||||||
|
valid args:
|
||||||
|
--user USERNAME : specify username for further actions
|
||||||
|
--password PASS : set new password
|
||||||
|
--delete : delete user
|
||||||
|
--activate : activate user
|
||||||
|
--deactivate : deactivate user
|
||||||
|
`,
|
||||||
|
|
||||||
|
Config :
|
||||||
|
`usage: optutil.js config <args>
|
||||||
|
|
||||||
|
valid args:
|
||||||
|
--new : generate a new/initial configuration
|
||||||
|
`,
|
||||||
|
FileBase :
|
||||||
|
`usage: oputil.js file-base <action> [<args>] [<action_specific>]
|
||||||
|
|
||||||
|
where <action> is one of:
|
||||||
|
scan <args> AREA_TAG : (re)scan area specified by AREA_TAG for new files
|
||||||
|
multiple area tags can be specified in form of AREA_TAG1 AREA_TAG2 ...
|
||||||
|
|
||||||
|
valid scan <args>:
|
||||||
|
--tags TAG1,TAG2,... : specify tag(s) to assign to discovered entries
|
||||||
|
`
|
||||||
|
};
|
||||||
|
|
||||||
|
function getHelpFor(command) {
|
||||||
|
return usageHelp[command];
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/* jslint node: true */
|
||||||
|
/* 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 handleConfigCommand = require('./oputil_config.js').handleConfigCommand;
|
||||||
|
const getHelpFor = require('./oputil_help.js').getHelpFor;
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = function() {
|
||||||
|
|
||||||
|
process.exitCode = ExitCodes.SUCCESS;
|
||||||
|
|
||||||
|
if(true === argv.version) {
|
||||||
|
return console.info(require('../package.json').version);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(0 === argv._.length ||
|
||||||
|
'help' === argv._[0])
|
||||||
|
{
|
||||||
|
printUsageAndSetExitCode(getHelpFor('General'), ExitCodes.SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(argv._[0]) {
|
||||||
|
case 'user' :
|
||||||
|
handleUserCommand();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'config' :
|
||||||
|
handleConfigCommand();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'file-base' :
|
||||||
|
case 'fb' :
|
||||||
|
handleFileBaseCommand();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return printUsageAndSetExitCode('', ExitCodes.BAD_COMMAND);
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,112 @@
|
||||||
|
/* jslint node: true */
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const printUsageAndSetExitCode = require('./oputil_common.js').printUsageAndSetExitCode;
|
||||||
|
const ExitCodes = require('./oputil_common.js').ExitCodes;
|
||||||
|
const argv = require('./oputil_common.js').argv;
|
||||||
|
const initConfigAndDatabases = require('./oputil_common.js').initConfigAndDatabases;
|
||||||
|
|
||||||
|
|
||||||
|
const async = require('async');
|
||||||
|
|
||||||
|
exports.handleUserCommand = handleUserCommand;
|
||||||
|
|
||||||
|
function handleUserCommand() {
|
||||||
|
if(true === argv.help || !_.isString(argv.user) || 0 === argv.user.length) {
|
||||||
|
return printUsageAndSetExitCode('User', ExitCodes.ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_.isString(argv.password)) {
|
||||||
|
if(0 === argv.password.length) {
|
||||||
|
process.exitCode = ExitCodes.BAD_ARGS;
|
||||||
|
return console.error('Invalid password');
|
||||||
|
}
|
||||||
|
|
||||||
|
async.waterfall(
|
||||||
|
[
|
||||||
|
function init(callback) {
|
||||||
|
initAndGetUser(argv.user, callback);
|
||||||
|
},
|
||||||
|
function setNewPass(user, callback) {
|
||||||
|
user.setNewAuthCredentials(argv.password, function credsSet(err) {
|
||||||
|
if(err) {
|
||||||
|
process.exitCode = ExitCodes.ERROR;
|
||||||
|
callback(new Error('Failed setting password'));
|
||||||
|
} else {
|
||||||
|
callback(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
],
|
||||||
|
function complete(err) {
|
||||||
|
if(err) {
|
||||||
|
console.error(err.message);
|
||||||
|
} else {
|
||||||
|
console.info('Password set');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else if(argv.activate) {
|
||||||
|
setAccountStatus(argv.user, true);
|
||||||
|
} else if(argv.deactivate) {
|
||||||
|
setAccountStatus(argv.user, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUser(userName, cb) {
|
||||||
|
const user = require('./core/user.js');
|
||||||
|
user.getUserIdAndName(argv.user, function userNameAndId(err, userId) {
|
||||||
|
if(err) {
|
||||||
|
process.exitCode = ExitCodes.BAD_ARGS;
|
||||||
|
return cb(new Error('Failed to retrieve user'));
|
||||||
|
} else {
|
||||||
|
let u = new user.User();
|
||||||
|
u.userId = userId;
|
||||||
|
return cb(null, u);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initAndGetUser(userName, cb) {
|
||||||
|
async.waterfall(
|
||||||
|
[
|
||||||
|
function init(callback) {
|
||||||
|
initConfigAndDatabases(callback);
|
||||||
|
},
|
||||||
|
function getUserObject(callback) {
|
||||||
|
getUser(argv.user, (err, user) => {
|
||||||
|
if(err) {
|
||||||
|
process.exitCode = ExitCodes.BAD_ARGS;
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
return callback(null, user);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
],
|
||||||
|
(err, user) => {
|
||||||
|
return cb(err, user);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAccountStatus(userName, active) {
|
||||||
|
async.waterfall(
|
||||||
|
[
|
||||||
|
function init(callback) {
|
||||||
|
initAndGetUser(argv.user, callback);
|
||||||
|
},
|
||||||
|
function activateUser(user, callback) {
|
||||||
|
const AccountStatus = require('./core/user.js').User.AccountStatus;
|
||||||
|
user.persistProperty('account_status', active ? AccountStatus.active : AccountStatus.inactive, callback);
|
||||||
|
}
|
||||||
|
],
|
||||||
|
err => {
|
||||||
|
if(err) {
|
||||||
|
console.error(err.message);
|
||||||
|
} else {
|
||||||
|
console.info('User ' + ((true === active) ? 'activated' : 'deactivated'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ The following archivers are pre-configured in ENiGMA½ as of this writing. Remem
|
||||||
#### Lha
|
#### Lha
|
||||||
* Formats: <a href="https://en.wikipedia.org/wiki/LHA_(file_format)">LHA</a> files such as .lzh.
|
* Formats: <a href="https://en.wikipedia.org/wiki/LHA_(file_format)">LHA</a> files such as .lzh.
|
||||||
* Key: `Lha`
|
* Key: `Lha`
|
||||||
* Homepage/package: `lhasa` on most *nix environments. See also https://fragglet.github.io/lhasa/
|
* Homepage/package: `lhasa` on most *nix environments. See also https://fragglet.github.io/lhasa/ and http://www2m.biglobe.ne.jp/~dolphin/lha/lha-unix.htm
|
||||||
|
|
||||||
#### Arj
|
#### Arj
|
||||||
* Formats: .arj
|
* Formats: .arj
|
||||||
|
|
553
oputil.js
553
oputil.js
|
@ -4,555 +4,4 @@
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// ENiGMA½
|
require('./core/oputil/oputil_main.js')();
|
||||||
const config = require('./core/config.js');
|
|
||||||
const db = require('./core/database.js');
|
|
||||||
const resolvePath = require('./core/misc_util.js').resolvePath;
|
|
||||||
|
|
||||||
// deps
|
|
||||||
const _ = require('lodash');
|
|
||||||
const async = require('async');
|
|
||||||
const inq = require('inquirer');
|
|
||||||
const mkdirsSync = require('fs-extra').mkdirsSync;
|
|
||||||
const fs = require('fs');
|
|
||||||
const hjson = require('hjson');
|
|
||||||
const paths = require('path');
|
|
||||||
|
|
||||||
const argv = require('minimist')(process.argv.slice(2));
|
|
||||||
|
|
||||||
const ExitCodes = {
|
|
||||||
SUCCESS : 0,
|
|
||||||
ERROR : -1,
|
|
||||||
BAD_COMMAND : -2,
|
|
||||||
BAD_ARGS : -3,
|
|
||||||
};
|
|
||||||
|
|
||||||
const USAGE_HELP = {
|
|
||||||
General :
|
|
||||||
`usage: optutil.js [--version] [--help]
|
|
||||||
<command> [<args>]
|
|
||||||
|
|
||||||
global args:
|
|
||||||
--config PATH : specify config path (${getDefaultConfigPath()})
|
|
||||||
|
|
||||||
where <command> is one of:
|
|
||||||
user : user utilities
|
|
||||||
config : config file management
|
|
||||||
file-base
|
|
||||||
fb : file base management
|
|
||||||
|
|
||||||
`,
|
|
||||||
User :
|
|
||||||
`usage: optutil.js user --user USERNAME <args>
|
|
||||||
|
|
||||||
valid args:
|
|
||||||
--user USERNAME : specify username for further actions
|
|
||||||
--password PASS : set new password
|
|
||||||
--delete : delete user
|
|
||||||
--activate : activate user
|
|
||||||
--deactivate : deactivate user
|
|
||||||
`,
|
|
||||||
|
|
||||||
Config :
|
|
||||||
`usage: optutil.js config <args>
|
|
||||||
|
|
||||||
valid args:
|
|
||||||
--new : generate a new/initial configuration
|
|
||||||
`,
|
|
||||||
FileBase :
|
|
||||||
`usage: oputil.js file-base <action> [<args>] [<action_specific>]
|
|
||||||
|
|
||||||
where <action> is one of:
|
|
||||||
scan AREA_TAG : (re)scan area specified by AREA_TAG for new files
|
|
||||||
multiple area tags can be specified in form of AREA_TAG1 AREA_TAG2 ...
|
|
||||||
|
|
||||||
scan args:
|
|
||||||
--tags TAG1,TAG2,... : specify tag(s) to assign to discovered entries
|
|
||||||
`
|
|
||||||
};
|
|
||||||
|
|
||||||
function printUsageAndSetExitCode(command, exitCode) {
|
|
||||||
if(_.isUndefined(exitCode)) {
|
|
||||||
exitCode = ExitCodes.ERROR;
|
|
||||||
}
|
|
||||||
process.exitCode = exitCode;
|
|
||||||
const errMsg = USAGE_HELP[command];
|
|
||||||
if(errMsg) {
|
|
||||||
console.error(errMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initConfig(cb) {
|
|
||||||
const configPath = argv.config ? argv.config : config.getDefaultPath();
|
|
||||||
|
|
||||||
config.init(configPath, cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
function initConfigAndDatabases(cb) {
|
|
||||||
async.series(
|
|
||||||
[
|
|
||||||
function init(callback) {
|
|
||||||
initConfig(callback);
|
|
||||||
},
|
|
||||||
function initDb(callback) {
|
|
||||||
db.initializeDatabases(callback);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
err => {
|
|
||||||
return cb(err);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getUser(userName, cb) {
|
|
||||||
const user = require('./core/user.js');
|
|
||||||
user.getUserIdAndName(argv.user, function userNameAndId(err, userId) {
|
|
||||||
if(err) {
|
|
||||||
process.exitCode = ExitCodes.BAD_ARGS;
|
|
||||||
return cb(new Error('Failed to retrieve user'));
|
|
||||||
} else {
|
|
||||||
let u = new user.User();
|
|
||||||
u.userId = userId;
|
|
||||||
return cb(null, u);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function initAndGetUser(userName, cb) {
|
|
||||||
async.waterfall(
|
|
||||||
[
|
|
||||||
function init(callback) {
|
|
||||||
initConfigAndDatabases(callback);
|
|
||||||
},
|
|
||||||
function getUserObject(callback) {
|
|
||||||
getUser(argv.user, (err, user) => {
|
|
||||||
if(err) {
|
|
||||||
process.exitCode = ExitCodes.BAD_ARGS;
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
return callback(null, user);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
],
|
|
||||||
(err, user) => {
|
|
||||||
return cb(err, user);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setAccountStatus(userName, active) {
|
|
||||||
async.waterfall(
|
|
||||||
[
|
|
||||||
function init(callback) {
|
|
||||||
initAndGetUser(argv.user, callback);
|
|
||||||
},
|
|
||||||
function activateUser(user, callback) {
|
|
||||||
const AccountStatus = require('./core/user.js').User.AccountStatus;
|
|
||||||
user.persistProperty('account_status', active ? AccountStatus.active : AccountStatus.inactive, callback);
|
|
||||||
}
|
|
||||||
],
|
|
||||||
err => {
|
|
||||||
if(err) {
|
|
||||||
console.error(err.message);
|
|
||||||
} else {
|
|
||||||
console.info('User ' + ((true === active) ? 'activated' : 'deactivated'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleUserCommand() {
|
|
||||||
if(true === argv.help || !_.isString(argv.user) || 0 === argv.user.length) {
|
|
||||||
return printUsageAndSetExitCode('User', ExitCodes.ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(_.isString(argv.password)) {
|
|
||||||
if(0 === argv.password.length) {
|
|
||||||
process.exitCode = ExitCodes.BAD_ARGS;
|
|
||||||
return console.error('Invalid password');
|
|
||||||
}
|
|
||||||
|
|
||||||
async.waterfall(
|
|
||||||
[
|
|
||||||
function init(callback) {
|
|
||||||
initAndGetUser(argv.user, callback);
|
|
||||||
},
|
|
||||||
function setNewPass(user, callback) {
|
|
||||||
user.setNewAuthCredentials(argv.password, function credsSet(err) {
|
|
||||||
if(err) {
|
|
||||||
process.exitCode = ExitCodes.ERROR;
|
|
||||||
callback(new Error('Failed setting password'));
|
|
||||||
} else {
|
|
||||||
callback(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
],
|
|
||||||
function complete(err) {
|
|
||||||
if(err) {
|
|
||||||
console.error(err.message);
|
|
||||||
} else {
|
|
||||||
console.info('Password set');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else if(argv.activate) {
|
|
||||||
setAccountStatus(argv.user, true);
|
|
||||||
} else if(argv.deactivate) {
|
|
||||||
setAccountStatus(argv.user, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAnswers(questions, cb) {
|
|
||||||
inq.prompt(questions).then( answers => {
|
|
||||||
return cb(answers);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDefaultConfigPath() {
|
|
||||||
return resolvePath('~/.config/enigma-bbs/config.hjson');
|
|
||||||
}
|
|
||||||
|
|
||||||
const QUESTIONS = {
|
|
||||||
Intro : [
|
|
||||||
{
|
|
||||||
name : 'createNewConfig',
|
|
||||||
message : 'Create a new configuration?',
|
|
||||||
type : 'confirm',
|
|
||||||
default : false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name : 'configPath',
|
|
||||||
message : 'Configuration path:',
|
|
||||||
default : argv.config ? argv.config : getDefaultConfigPath(),
|
|
||||||
when : answers => answers.createNewConfig
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
OverwriteConfig : [
|
|
||||||
{
|
|
||||||
name : 'overwriteConfig',
|
|
||||||
message : 'Config file exists. Overwrite?',
|
|
||||||
type : 'confirm',
|
|
||||||
default : false,
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
Basic : [
|
|
||||||
{
|
|
||||||
name : 'boardName',
|
|
||||||
message : 'BBS name:',
|
|
||||||
default : 'New ENiGMA½ BBS',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
Misc : [
|
|
||||||
{
|
|
||||||
name : 'loggingLevel',
|
|
||||||
message : 'Logging level:',
|
|
||||||
type : 'list',
|
|
||||||
choices : [ 'Error', 'Warn', 'Info', 'Debug', 'Trace' ],
|
|
||||||
default : 2,
|
|
||||||
filter : s => s.toLowerCase(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name : 'sevenZipExe',
|
|
||||||
message : '7-Zip executable:',
|
|
||||||
type : 'list',
|
|
||||||
choices : [ '7z', '7za', 'None' ]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
MessageConfAndArea : [
|
|
||||||
{
|
|
||||||
name : 'msgConfName',
|
|
||||||
message : 'First message conference:',
|
|
||||||
default : 'Local',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name : 'msgConfDesc',
|
|
||||||
message : 'Conference description:',
|
|
||||||
default : 'Local Areas',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name : 'msgAreaName',
|
|
||||||
message : 'First area in message conference:',
|
|
||||||
default : 'General',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name : 'msgAreaDesc',
|
|
||||||
message : 'Area description:',
|
|
||||||
default : 'General chit-chat',
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
function makeMsgConfAreaName(s) {
|
|
||||||
return s.toLowerCase().replace(/\s+/g, '_');
|
|
||||||
}
|
|
||||||
|
|
||||||
function askNewConfigQuestions(cb) {
|
|
||||||
|
|
||||||
const ui = new inq.ui.BottomBar();
|
|
||||||
|
|
||||||
let configPath;
|
|
||||||
let config;
|
|
||||||
|
|
||||||
async.waterfall(
|
|
||||||
[
|
|
||||||
function intro(callback) {
|
|
||||||
getAnswers(QUESTIONS.Intro, answers => {
|
|
||||||
if(!answers.createNewConfig) {
|
|
||||||
return callback('exit');
|
|
||||||
}
|
|
||||||
|
|
||||||
// adjust for ~ and the like
|
|
||||||
configPath = resolvePath(answers.configPath);
|
|
||||||
|
|
||||||
const configDir = paths.dirname(configPath);
|
|
||||||
mkdirsSync(configDir);
|
|
||||||
|
|
||||||
//
|
|
||||||
// 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) {
|
|
||||||
ui.log.write(`${configPath} cannot be written to`);
|
|
||||||
callback('exit');
|
|
||||||
} else if('ENOENT' === err.code) {
|
|
||||||
callback(null, false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
callback(null, true); // exists + writable
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function promptOverwrite(needPrompt, callback) {
|
|
||||||
if(needPrompt) {
|
|
||||||
getAnswers(QUESTIONS.OverwriteConfig, answers => {
|
|
||||||
callback(answers.overwriteConfig ? null : 'exit');
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
callback(null);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function basic(callback) {
|
|
||||||
getAnswers(QUESTIONS.Basic, answers => {
|
|
||||||
config = {
|
|
||||||
general : {
|
|
||||||
boardName : answers.boardName,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
callback(null);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function msgConfAndArea(callback) {
|
|
||||||
getAnswers(QUESTIONS.MessageConfAndArea, answers => {
|
|
||||||
config.messageConferences = {};
|
|
||||||
|
|
||||||
const confName = makeMsgConfAreaName(answers.msgConfName);
|
|
||||||
const areaName = makeMsgConfAreaName(answers.msgAreaName);
|
|
||||||
|
|
||||||
config.messageConferences[confName] = {
|
|
||||||
name : answers.msgConfName,
|
|
||||||
desc : answers.msgConfDesc,
|
|
||||||
sort : 1,
|
|
||||||
default : true,
|
|
||||||
};
|
|
||||||
|
|
||||||
config.messageConferences.another_sample_conf = {
|
|
||||||
name : 'Another Sample Conference',
|
|
||||||
desc : 'Another conference example. Change me!',
|
|
||||||
sort : 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
config.messageConferences[confName].areas = {};
|
|
||||||
config.messageConferences[confName].areas[areaName] = {
|
|
||||||
name : answers.msgAreaName,
|
|
||||||
desc : answers.msgAreaDesc,
|
|
||||||
sort : 1,
|
|
||||||
default : true,
|
|
||||||
};
|
|
||||||
|
|
||||||
config.messageConferences.another_sample_conf = {
|
|
||||||
name : 'Another Sample Conference',
|
|
||||||
desc : 'Another conf sample. Change me!',
|
|
||||||
|
|
||||||
areas : {
|
|
||||||
another_sample_area : {
|
|
||||||
name : 'Another Sample Area',
|
|
||||||
desc : 'Another area example. Change me!',
|
|
||||||
sort : 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
callback(null);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function misc(callback) {
|
|
||||||
getAnswers(QUESTIONS.Misc, answers => {
|
|
||||||
if('None' !== answers.sevenZipExe) {
|
|
||||||
config.archivers = {
|
|
||||||
zip : {
|
|
||||||
compressCmd : answers.sevenZipExe,
|
|
||||||
decompressCmd : answers.sevenZipExe,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
config.logging = {
|
|
||||||
level : answers.loggingLevel,
|
|
||||||
};
|
|
||||||
|
|
||||||
callback(null);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
],
|
|
||||||
err => {
|
|
||||||
cb(err, configPath, config);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleConfigCommand() {
|
|
||||||
if(true === argv.help) {
|
|
||||||
return printUsageAndSetExitCode('Config', ExitCodes.ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(argv.new) {
|
|
||||||
askNewConfigQuestions( (err, configPath, config) => {
|
|
||||||
if(err) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
config = hjson.stringify(config, { bracesSameLine : true, spaces : '\t' } );
|
|
||||||
|
|
||||||
try {
|
|
||||||
fs.writeFileSync(configPath, config, 'utf8');
|
|
||||||
console.info('Configuration generated');
|
|
||||||
} catch(e) {
|
|
||||||
console.error('Exception attempting to create config: ' + e.toString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return printUsageAndSetExitCode('Config', ExitCodes.ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function scanFileBaseArea(areaTag, options, iterator, cb) {
|
|
||||||
async.waterfall(
|
|
||||||
[
|
|
||||||
function getFileArea(callback) {
|
|
||||||
const fileAreaMod = require('./core/file_base_area.js');
|
|
||||||
|
|
||||||
const areaInfo = fileAreaMod.getFileAreaByTag(areaTag);
|
|
||||||
if(!areaInfo) {
|
|
||||||
return callback(new Error(`Invalid file base area tag: ${areaTag}`));
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback(null, fileAreaMod, areaInfo);
|
|
||||||
},
|
|
||||||
function performScan(fileAreaMod, areaInfo, callback) {
|
|
||||||
fileAreaMod.scanFileAreaForChanges(areaInfo, options, iterator, err => {
|
|
||||||
return callback(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
],
|
|
||||||
err => {
|
|
||||||
return cb(err);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function fileAreaScan() {
|
|
||||||
const options = {};
|
|
||||||
|
|
||||||
const tags = argv.tags;
|
|
||||||
if(tags) {
|
|
||||||
options.tags = tags.split(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
const areaTags = argv._.slice(2);
|
|
||||||
|
|
||||||
function scanFileIterator(stepInfo, nextScanStep) {
|
|
||||||
if('start' === stepInfo.step) {
|
|
||||||
console.info(`Scanning ${stepInfo.filePath}...`);
|
|
||||||
}
|
|
||||||
return nextScanStep(null);
|
|
||||||
// :TODO: add 'finished' step when avail
|
|
||||||
}
|
|
||||||
|
|
||||||
async.series(
|
|
||||||
[
|
|
||||||
function init(callback) {
|
|
||||||
return initConfigAndDatabases(callback);
|
|
||||||
},
|
|
||||||
function scanAreas(callback) {
|
|
||||||
async.eachSeries(areaTags, (areaTag, nextAreaTag) => {
|
|
||||||
scanFileBaseArea(areaTag, options, scanFileIterator, err => {
|
|
||||||
return nextAreaTag(err);
|
|
||||||
});
|
|
||||||
}, err => {
|
|
||||||
return callback(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
],
|
|
||||||
err => {
|
|
||||||
if(err) {
|
|
||||||
process.exitCode = ExitCodes.ERROR;
|
|
||||||
console.error(err.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleFileBaseCommand() {
|
|
||||||
if(true === argv.help) {
|
|
||||||
return printUsageAndSetExitCode('FileBase', ExitCodes.ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
const action = argv._[1];
|
|
||||||
|
|
||||||
switch(action) {
|
|
||||||
case 'scan' : return fileAreaScan();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function main() {
|
|
||||||
|
|
||||||
process.exitCode = ExitCodes.SUCCESS;
|
|
||||||
|
|
||||||
if(true === argv.version) {
|
|
||||||
return console.info(require('./package.json').version);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(0 === argv._.length ||
|
|
||||||
'help' === argv._[0])
|
|
||||||
{
|
|
||||||
printUsageAndSetExitCode('General', ExitCodes.SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(argv._[0]) {
|
|
||||||
case 'user' :
|
|
||||||
handleUserCommand();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'config' :
|
|
||||||
handleConfigCommand();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'file-base' :
|
|
||||||
case 'fb' :
|
|
||||||
handleFileBaseCommand();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
printUsageAndSetExitCode('', ExitCodes.BAD_COMMAND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
|
|
Loading…
Reference in New Issue