2014-10-17 02:21:06 +00:00
|
|
|
/* jslint node: true */
|
|
|
|
'use strict';
|
|
|
|
|
2018-06-23 03:26:46 +00:00
|
|
|
// ENiGMA½
|
|
|
|
const Config = require('./config.js').get;
|
2018-07-21 20:32:06 +00:00
|
|
|
const Log = require('./logger.js').log;
|
2018-12-03 02:33:07 +00:00
|
|
|
const {
|
|
|
|
Errors,
|
|
|
|
ErrorReasons
|
|
|
|
} = require('./enig_error.js');
|
2015-04-21 04:50:58 +00:00
|
|
|
|
2018-06-23 03:26:46 +00:00
|
|
|
// deps
|
|
|
|
const fs = require('graceful-fs');
|
|
|
|
const paths = require('path');
|
|
|
|
const _ = require('lodash');
|
|
|
|
const assert = require('assert');
|
|
|
|
const async = require('async');
|
2018-07-21 20:32:06 +00:00
|
|
|
const glob = require('glob');
|
2014-10-17 02:21:06 +00:00
|
|
|
|
2018-06-23 03:26:46 +00:00
|
|
|
// exports
|
|
|
|
exports.loadModuleEx = loadModuleEx;
|
|
|
|
exports.loadModule = loadModule;
|
|
|
|
exports.loadModulesForCategory = loadModulesForCategory;
|
|
|
|
exports.getModulePaths = getModulePaths;
|
2018-07-21 20:32:06 +00:00
|
|
|
exports.initializeModules = initializeModules;
|
2014-10-17 02:21:06 +00:00
|
|
|
|
2015-04-21 04:50:58 +00:00
|
|
|
function loadModuleEx(options, cb) {
|
2018-06-22 05:15:04 +00:00
|
|
|
assert(_.isObject(options));
|
|
|
|
assert(_.isString(options.name));
|
|
|
|
assert(_.isString(options.path));
|
|
|
|
|
|
|
|
const modConfig = _.isObject(Config[options.category]) ? Config[options.category][options.name] : null;
|
|
|
|
|
|
|
|
if(_.isObject(modConfig) && false === modConfig.enabled) {
|
2018-12-03 02:33:07 +00:00
|
|
|
return cb(Errors.AccessDenied(`Module "${options.name}" is disabled`, ErrorReasons.Disabled));
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
2018-06-23 03:26:46 +00:00
|
|
|
// Modules are allowed to live in /path/to/<moduleName>/<moduleName>.js or
|
|
|
|
// simply in /path/to/<moduleName>.js. This allows for more advanced modules
|
|
|
|
// to have their own containing folder, package.json & dependencies, etc.
|
2018-06-22 05:15:04 +00:00
|
|
|
//
|
|
|
|
let mod;
|
2018-06-23 03:26:46 +00:00
|
|
|
let modPath = paths.join(options.path, `${options.name}.js`); // general case first
|
2018-06-22 05:15:04 +00:00
|
|
|
try {
|
|
|
|
mod = require(modPath);
|
|
|
|
} catch(e) {
|
|
|
|
if('MODULE_NOT_FOUND' === e.code) {
|
|
|
|
modPath = paths.join(options.path, options.name, `${options.name}.js`);
|
|
|
|
try {
|
|
|
|
mod = require(modPath);
|
|
|
|
} catch(e) {
|
|
|
|
return cb(e);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return cb(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!_.isObject(mod.moduleInfo)) {
|
2018-12-03 02:33:07 +00:00
|
|
|
return cb(Errors.Invalid(`No exported "moduleInfo" block for module ${modPath}!`));
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(!_.isFunction(mod.getModule)) {
|
2018-12-03 02:33:07 +00:00
|
|
|
return cb(Errors.Invalid(`No exported "getModule" method for module ${modPath}!`));
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return cb(null, mod);
|
2015-03-19 05:08:23 +00:00
|
|
|
}
|
2014-10-17 02:21:06 +00:00
|
|
|
|
2015-04-21 04:50:58 +00:00
|
|
|
function loadModule(name, category, cb) {
|
2018-06-22 05:15:04 +00:00
|
|
|
const path = Config().paths[category];
|
2015-04-21 04:50:58 +00:00
|
|
|
|
2018-06-22 05:15:04 +00:00
|
|
|
if(!_.isString(path)) {
|
2018-12-03 02:33:07 +00:00
|
|
|
return cb(Errors.DoesNotExist(`Not sure where to look for module "${name}" of category "${category}"`));
|
2018-06-22 05:15:04 +00:00
|
|
|
}
|
2015-04-21 04:50:58 +00:00
|
|
|
|
2018-06-22 05:15:04 +00:00
|
|
|
loadModuleEx( { name : name, path : path, category : category }, function loaded(err, mod) {
|
|
|
|
return cb(err, mod);
|
|
|
|
});
|
2015-04-21 04:50:58 +00:00
|
|
|
}
|
|
|
|
|
2016-02-17 05:11:55 +00:00
|
|
|
function loadModulesForCategory(category, iterator, complete) {
|
2017-06-12 02:44:34 +00:00
|
|
|
|
2018-06-22 05:15:04 +00:00
|
|
|
fs.readdir(Config().paths[category], (err, files) => {
|
|
|
|
if(err) {
|
|
|
|
return iterator(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
const jsModules = files.filter(file => {
|
|
|
|
return '.js' === paths.extname(file);
|
|
|
|
});
|
|
|
|
|
|
|
|
async.each(jsModules, (file, next) => {
|
|
|
|
loadModule(paths.basename(file, '.js'), category, (err, mod) => {
|
|
|
|
iterator(err, mod);
|
|
|
|
return next();
|
|
|
|
});
|
|
|
|
}, err => {
|
|
|
|
if(complete) {
|
|
|
|
return complete(err);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2015-03-19 05:08:23 +00:00
|
|
|
}
|
2017-06-12 02:44:34 +00:00
|
|
|
|
|
|
|
function getModulePaths() {
|
2018-06-22 05:15:04 +00:00
|
|
|
const config = Config();
|
|
|
|
return [
|
|
|
|
config.paths.mods,
|
|
|
|
config.paths.loginServers,
|
|
|
|
config.paths.contentServers,
|
|
|
|
config.paths.scannerTossers,
|
|
|
|
];
|
2017-06-12 02:44:34 +00:00
|
|
|
}
|
2018-07-21 20:32:06 +00:00
|
|
|
|
|
|
|
function initializeModules(cb) {
|
|
|
|
const Events = require('./events.js');
|
|
|
|
|
|
|
|
const modulePaths = getModulePaths().concat(__dirname);
|
|
|
|
|
|
|
|
async.each(modulePaths, (modulePath, nextPath) => {
|
|
|
|
glob('*{.js,/*.js}', { cwd : modulePath }, (err, files) => {
|
|
|
|
if(err) {
|
|
|
|
return nextPath(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
const ourPath = paths.join(__dirname, __filename);
|
|
|
|
|
|
|
|
async.each(files, (moduleName, nextModule) => {
|
|
|
|
const fullModulePath = paths.join(modulePath, moduleName);
|
|
|
|
if(ourPath === fullModulePath) {
|
|
|
|
return nextModule(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
const mod = require(fullModulePath);
|
|
|
|
|
|
|
|
if(_.isFunction(mod.moduleInitialize)) {
|
|
|
|
const initInfo = {
|
|
|
|
events : Events,
|
|
|
|
};
|
|
|
|
|
|
|
|
mod.moduleInitialize(initInfo, err => {
|
|
|
|
if(err) {
|
|
|
|
Log.warn( { error : err.message, modulePath : fullModulePath }, 'Error during "moduleInitialize"');
|
|
|
|
}
|
|
|
|
return nextModule(null);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
return nextModule(null);
|
|
|
|
}
|
|
|
|
} catch(e) {
|
2018-12-02 00:00:07 +00:00
|
|
|
Log.warn( { error : e.message, fullModulePath }, 'Exception during "moduleInitialize"');
|
2018-07-21 20:32:06 +00:00
|
|
|
return nextModule(null);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
err => {
|
|
|
|
return nextPath(err);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
err => {
|
|
|
|
return cb(err);
|
|
|
|
});
|
|
|
|
}
|