mirror of
https://github.com/Hopiu/webapi-eca.git
synced 2026-03-16 22:10:31 +00:00
cleansing, js-coffee moved to js where it is supposed to be. legacy js files removed. README updated
This commit is contained in:
parent
21ff603f36
commit
745dddf04f
33 changed files with 1907 additions and 3676 deletions
56
README.md
56
README.md
|
|
@ -1,11 +1,9 @@
|
|||
README: webapi-eca
|
||||
==================
|
||||
> A Modular ECA Engine Server which acts as a middleware between WebAPI's.
|
||||
> This folder continues examples of an ECA engine and how certain use cases
|
||||
> could be implemented together with a rules language.
|
||||
>
|
||||
> The server is started through the [server.js](server.html) module by calling
|
||||
> `node rule_server.js`.
|
||||
> The server is started through the [webapi-eca.js](webapi-eca.html) module by calling
|
||||
> `node js/webapi-eca.js`.
|
||||
|
||||
|
||||
Getting started
|
||||
|
|
@ -29,26 +27,29 @@ Download and install dependencies:
|
|||
cd webapi-eca
|
||||
npm install
|
||||
|
||||
Get your [redis](http://redis.io/) instance up and running (and find the port for the config file below) or create your own `js/db_interface.js`.
|
||||
Get your [redis](http://redis.io/) instance up and running (and find the port for the config file below) or create your own `js/persistence.js`.
|
||||
|
||||
Edit the configuration file:
|
||||
|
||||
vi config/config.json
|
||||
vi config/system.json
|
||||
|
||||
Apply your settings, for example:
|
||||
# TODO Remake
|
||||
|
||||
|
||||
{
|
||||
"http_port": 8125,
|
||||
"db_port": 6379,
|
||||
"crypto_key": "[your key]",
|
||||
"session_secret": "[your secret]"
|
||||
"http-port": 8125, # The port on which the system listens for requests
|
||||
"db-port": 6379, # The db-port where your redis instance is listening
|
||||
"log": { ### logging configurations
|
||||
"mode": "development", # if set to productive no expensive origin lookup is performed and logged
|
||||
"io-level": "info", # the log-level for the std I/O stream
|
||||
"file-level": "info", # the log-level for the log file
|
||||
"file-path": "server.log" # log file path, relative to cwd
|
||||
( add "nolog": "true" if no log shall be generated at all. Mainly used for unit tests )
|
||||
}
|
||||
}
|
||||
|
||||
Start the server:
|
||||
|
||||
run_server.sh
|
||||
run_engine.sh
|
||||
|
||||
*Congratulations, your own WebAPI based ECA engine server is now up and running!*
|
||||
|
||||
|
|
@ -69,32 +70,3 @@ Create the doc *(to be accessed via the webserver, e.g.: localhost:8125/doc/)*:
|
|||
Run test suite:
|
||||
|
||||
run_tests.sh
|
||||
|
||||
_
|
||||
|
||||
TODO
|
||||
----
|
||||
|
||||
* Redis queue
|
||||
* user handling (personal credentials)
|
||||
* security in terms of users (private key, login)
|
||||
* vm for modules, only give few libraries (no fs!)
|
||||
* rules generator (provide webpage that is used to create rules dependent on the existing modues)
|
||||
* geo location module, test on smartphone.
|
||||
|
||||
_
|
||||
|
||||
TODO per module
|
||||
---------------
|
||||
|
||||
Testing | clean documentation | Clean error handling (Especially in loading of modules and their credentials):
|
||||
|
||||
* DB Interface
|
||||
* Engine
|
||||
* Event Poller
|
||||
* HTTP Listener
|
||||
* Logging
|
||||
* Module Loader
|
||||
* Module Manager
|
||||
* Server
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
echo "Started listening on file changes to compile them!..."
|
||||
coffee -wc -o $DIR/js-coffee $DIR/coffee/
|
||||
coffee -wc -o $DIR/js $DIR/coffee/
|
||||
|
|
|
|||
41
gen_doc.sh
41
gen_doc.sh
|
|
@ -4,48 +4,15 @@
|
|||
* Create the documentation to be displayed through the webserver.
|
||||
*/
|
||||
//
|
||||
require('groc').CLI(
|
||||
require( 'groc' ).CLI(
|
||||
[
|
||||
"README.md",
|
||||
"LICENSE.md",
|
||||
"coffee/*.coffee",
|
||||
"mod_actions/**/*.js",
|
||||
"mod_events/**/*.js",
|
||||
"-o./webpages/public/doc"
|
||||
],
|
||||
function(err) {
|
||||
if (err) console.error(err);
|
||||
else console.log('Done!');
|
||||
function( err ) {
|
||||
if ( err ) console.error( err );
|
||||
else console.log( 'Done!' );
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* # docco Documentation
|
||||
* Create the documentation to be displayed through the webserver.
|
||||
*/
|
||||
// var glob = require("glob"),
|
||||
// docco = require('docco'),
|
||||
// opt = ["", "", "--output", "webpages/doc"],
|
||||
// files = [
|
||||
// "README.md",
|
||||
// "LICENSE.md",
|
||||
// "create_doc.js",
|
||||
// "coffee/*.coffee",
|
||||
// "mod_actions/**/*.js",
|
||||
// "mod_events/**/*.js"
|
||||
// ];
|
||||
//
|
||||
// var semaphore = files.length;
|
||||
// for(var i = 0; i < files.length; i++) {
|
||||
// glob(files[i], null, function (er, files) {
|
||||
// if(!er) {
|
||||
// opt = opt.concat(files);
|
||||
// } else {
|
||||
// console.error(er);
|
||||
// }
|
||||
// if(--semaphore === 0) {
|
||||
// docco.run(opt);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,152 +0,0 @@
|
|||
// Generated by CoffeeScript 1.7.1
|
||||
|
||||
/*
|
||||
|
||||
Configuration
|
||||
=============
|
||||
> Loads the configuration file and acts as an interface to it.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var exports, fetchProp, fs, loadConfigFile, path;
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
path = require('path');
|
||||
|
||||
|
||||
/*
|
||||
Module call
|
||||
-----------
|
||||
|
||||
Calling the module as a function will act as a constructor and load the config file.
|
||||
It is possible to hand an args object with the properties nolog (true if no outputs shall
|
||||
be generated) and configPath for a custom configuration file path.
|
||||
|
||||
@param {Object} args
|
||||
*/
|
||||
|
||||
exports = module.exports = (function(_this) {
|
||||
return function(args) {
|
||||
args = args != null ? args : {};
|
||||
if (args.nolog) {
|
||||
_this.nolog = true;
|
||||
}
|
||||
if (args.configPath) {
|
||||
loadConfigFile(args.configPath);
|
||||
} else {
|
||||
loadConfigFile(path.join('config', 'system.json'));
|
||||
}
|
||||
return module.exports;
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Tries to load a configuration file from the path relative to this module's parent folder.
|
||||
Reads the config file synchronously from the file system and try to parse it.
|
||||
|
||||
@private loadConfigFile
|
||||
@param {String} configPath
|
||||
*/
|
||||
|
||||
loadConfigFile = (function(_this) {
|
||||
return function(configPath) {
|
||||
var confProperties, e, prop, _i, _len;
|
||||
_this.config = null;
|
||||
confProperties = ['log', 'http-port', 'db-port'];
|
||||
try {
|
||||
_this.config = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', configPath)));
|
||||
_this.isReady = true;
|
||||
for (_i = 0, _len = confProperties.length; _i < _len; _i++) {
|
||||
prop = confProperties[_i];
|
||||
if (!_this.config[prop]) {
|
||||
_this.isReady = false;
|
||||
}
|
||||
}
|
||||
if (!_this.isReady && !_this.nolog) {
|
||||
return console.error("Missing property in config file, requires:\n" + (" - " + (confProperties.join("\n - "))));
|
||||
}
|
||||
} catch (_error) {
|
||||
e = _error;
|
||||
_this.isReady = false;
|
||||
if (!_this.nolog) {
|
||||
return console.error("Failed loading config file: " + e.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Fetch a property from the configuration
|
||||
|
||||
@private fetchProp( *prop* )
|
||||
@param {String} prop
|
||||
*/
|
||||
|
||||
fetchProp = (function(_this) {
|
||||
return function(prop) {
|
||||
var _ref;
|
||||
return (_ref = _this.config) != null ? _ref[prop] : void 0;
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
***Returns*** true if the config file is ready, else false
|
||||
|
||||
@public isReady()
|
||||
*/
|
||||
|
||||
exports.isReady = (function(_this) {
|
||||
return function() {
|
||||
return _this.isReady;
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
***Returns*** the HTTP port
|
||||
|
||||
@public getHttpPort()
|
||||
*/
|
||||
|
||||
exports.getHttpPort = function() {
|
||||
return fetchProp('http-port');
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
***Returns*** the DB port*
|
||||
|
||||
@public getDBPort()
|
||||
*/
|
||||
|
||||
exports.getDbPort = function() {
|
||||
return fetchProp('db-port');
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
***Returns*** the log conf object
|
||||
|
||||
@public getLogConf()
|
||||
*/
|
||||
|
||||
exports.getLogConf = function() {
|
||||
return fetchProp('log');
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
***Returns*** the crypto key
|
||||
|
||||
@public getCryptoKey()
|
||||
*/
|
||||
|
||||
exports.getKeygenPassphrase = function() {
|
||||
return fetchProp('keygen-passphrase');
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
// Generated by CoffeeScript 1.7.1
|
||||
|
||||
/*
|
||||
|
||||
Dynamic Modules
|
||||
===============
|
||||
> Compiles CoffeeScript modules and loads JS modules in a VM, together
|
||||
> with only a few allowed node.js modules.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var cryptico, cs, db, exports, issueApiCall, needle, vm;
|
||||
|
||||
db = require('./persistence');
|
||||
|
||||
vm = require('vm');
|
||||
|
||||
needle = require('needle');
|
||||
|
||||
cs = require('coffee-script');
|
||||
|
||||
cryptico = require('my-cryptico');
|
||||
|
||||
|
||||
/*
|
||||
Module call
|
||||
-----------
|
||||
Initializes the dynamic module handler.
|
||||
|
||||
@param {Object} args
|
||||
*/
|
||||
|
||||
exports = module.exports = (function(_this) {
|
||||
return function(args) {
|
||||
var numBits, passPhrase;
|
||||
_this.log = args.logger;
|
||||
if (!_this.strPublicKey && args['keygen']) {
|
||||
db(args);
|
||||
passPhrase = args['keygen'];
|
||||
numBits = 1024;
|
||||
_this.oPrivateRSAkey = cryptico.generateRSAKey(passPhrase, numBits);
|
||||
_this.strPublicKey = cryptico.publicKeyString(_this.oPrivateRSAkey);
|
||||
_this.log.info("DM | Public Key generated: " + _this.strPublicKey);
|
||||
}
|
||||
return module.exports;
|
||||
};
|
||||
})(this);
|
||||
|
||||
exports.getPublicKey = (function(_this) {
|
||||
return function() {
|
||||
return _this.strPublicKey;
|
||||
};
|
||||
})(this);
|
||||
|
||||
issueApiCall = (function(_this) {
|
||||
return function(method, url, credentials, cb) {
|
||||
var err, func;
|
||||
try {
|
||||
if (method === 'get') {
|
||||
func = needle.get;
|
||||
} else {
|
||||
func = needle.post;
|
||||
}
|
||||
return func(url, credentials, function(err, resp, body) {
|
||||
if (!err) {
|
||||
return cb(body);
|
||||
} else {
|
||||
return cb();
|
||||
}
|
||||
});
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
return _this.log.info('DM | Error even before calling!');
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Try to run a JS module from a string, together with the
|
||||
given parameters. If it is written in CoffeeScript we
|
||||
compile it first into JS.
|
||||
|
||||
@public compileString ( *src, id, params, lang* )
|
||||
@param {String} src
|
||||
@param {String} id
|
||||
@param {Object} params
|
||||
@param {String} lang
|
||||
*/
|
||||
|
||||
exports.compileString = (function(_this) {
|
||||
return function(src, userId, ruleId, modId, lang, dbMod, cb) {
|
||||
var answ, err, fTryToLoad, logFunction;
|
||||
answ = {
|
||||
code: 200,
|
||||
message: 'Successfully compiled'
|
||||
};
|
||||
if (lang === 'CoffeeScript') {
|
||||
try {
|
||||
src = cs.compile(src);
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
answ.code = 400;
|
||||
answ.message = 'Compilation of CoffeeScript failed at line ' + err.location.first_line;
|
||||
}
|
||||
}
|
||||
logFunction = function(uId, rId, mId) {
|
||||
return function(msg) {
|
||||
return db.appendLog(uId, rId, mId, msg);
|
||||
};
|
||||
};
|
||||
db.resetLog(userId, ruleId);
|
||||
fTryToLoad = function(params) {
|
||||
var oDecrypted, sandbox;
|
||||
if (params) {
|
||||
try {
|
||||
oDecrypted = cryptico.decrypt(params, _this.oPrivateRSAkey);
|
||||
params = JSON.parse(oDecrypted.plaintext);
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
_this.log.warn("DM | Error during parsing of user defined params for " + userId + ", " + ruleId + ", " + modId);
|
||||
params = {};
|
||||
}
|
||||
} else {
|
||||
params = {};
|
||||
}
|
||||
sandbox = {
|
||||
id: userId + '.' + modId + '.vm',
|
||||
params: params,
|
||||
apicall: issueApiCall,
|
||||
log: logFunction(userId, ruleId, modId),
|
||||
exports: {}
|
||||
};
|
||||
try {
|
||||
vm.runInNewContext(src, sandbox, sandbox.id);
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
console.log(err);
|
||||
answ.code = 400;
|
||||
answ.message = 'Loading Module failed: ' + err.message;
|
||||
}
|
||||
return cb({
|
||||
answ: answ,
|
||||
module: sandbox.exports
|
||||
});
|
||||
};
|
||||
if (dbMod) {
|
||||
return dbMod.getUserParams(modId, userId, function(err, obj) {
|
||||
return fTryToLoad(obj);
|
||||
});
|
||||
} else {
|
||||
return fTryToLoad();
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
|
||||
}).call(this);
|
||||
|
|
@ -1,293 +0,0 @@
|
|||
// Generated by CoffeeScript 1.7.1
|
||||
|
||||
/*
|
||||
|
||||
Engine
|
||||
==================
|
||||
> The heart of the WebAPI ECA System. The engine loads action invoker modules
|
||||
> corresponding to active rules actions and invokes them if an appropriate event
|
||||
> is retrieved.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var db, dynmod, exports, isRunning, jsonQuery, listUserRules, pollQueue, processEvent, updateActionModules, validConditions;
|
||||
|
||||
db = require('./persistence');
|
||||
|
||||
dynmod = require('./dynamic-modules');
|
||||
|
||||
jsonQuery = require('js-select');
|
||||
|
||||
|
||||
/*
|
||||
This is ging to have a structure like:
|
||||
An object of users with their active rules and the required action modules
|
||||
"user-1":
|
||||
"rule-1":
|
||||
"rule": oRule-1
|
||||
"actions":
|
||||
"action-1": oAction-1
|
||||
"action-2": oAction-2
|
||||
"rule-2":
|
||||
"rule": oRule-2
|
||||
"actions":
|
||||
"action-1": oAction-1
|
||||
"user-2":
|
||||
"rule-3":
|
||||
"rule": oRule-3
|
||||
"actions":
|
||||
"action-3": oAction-3
|
||||
*/
|
||||
|
||||
listUserRules = {};
|
||||
|
||||
isRunning = false;
|
||||
|
||||
|
||||
/*
|
||||
Module call
|
||||
-----------
|
||||
Initializes the Engine and starts polling the event queue for new events.
|
||||
|
||||
@param {Object} args
|
||||
*/
|
||||
|
||||
exports = module.exports = (function(_this) {
|
||||
return function(args) {
|
||||
if (!isRunning) {
|
||||
isRunning = true;
|
||||
_this.log = args.logger;
|
||||
db(args);
|
||||
dynmod(args);
|
||||
setTimeout(pollQueue, 10);
|
||||
return module.exports;
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
This is a helper function for the unit tests so we can verify that action
|
||||
modules are loaded correctly
|
||||
*TODO we should change this to functions returning true or false rather than returning
|
||||
*the whole list
|
||||
@public getListUserRules ()
|
||||
*/
|
||||
|
||||
exports.getListUserRules = function() {
|
||||
return listUserRules;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
An event associated to rules happened and is captured here. Such events
|
||||
are basically CRUD on rules.
|
||||
|
||||
@public internalEvent ( *evt* )
|
||||
@param {Object} evt
|
||||
*/
|
||||
|
||||
exports.internalEvent = (function(_this) {
|
||||
return function(evt) {
|
||||
var oRule, oUser;
|
||||
if (!listUserRules[evt.user] && evt.event !== 'del') {
|
||||
listUserRules[evt.user] = {};
|
||||
}
|
||||
oUser = listUserRules[evt.user];
|
||||
oRule = evt.rule;
|
||||
if (evt.event === 'new' || (evt.event === 'init' && !oUser[oRule.id])) {
|
||||
oUser[oRule.id] = {
|
||||
rule: oRule,
|
||||
actions: {}
|
||||
};
|
||||
updateActionModules(oRule.id);
|
||||
}
|
||||
if (evt.event === 'del' && oUser) {
|
||||
delete oUser[evt.ruleId];
|
||||
}
|
||||
if (JSON.stringify(oUser) === "{}") {
|
||||
return delete listUserRules[evt.user];
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
As soon as changes were made to the rule set we need to ensure that the aprropriate action
|
||||
invoker modules are loaded, updated or deleted.
|
||||
|
||||
@private updateActionModules ( *updatedRuleId* )
|
||||
@param {Object} updatedRuleId
|
||||
*/
|
||||
|
||||
updateActionModules = function(updatedRuleId) {
|
||||
var fAddRequired, fRemoveNotRequired, name, oUser, userName, _results;
|
||||
fRemoveNotRequired = function(oUser) {
|
||||
var action, fRequired, _results;
|
||||
fRequired = function(actionName) {
|
||||
var action, _i, _len, _ref;
|
||||
_ref = oUser[updatedRuleId].rule.actions;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
action = _ref[_i];
|
||||
if ((action.split(' -> '))[0] === actionName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
_results = [];
|
||||
for (action in oUser[updatedRuleId].rule.actions) {
|
||||
if (!fRequired(action)) {
|
||||
_results.push(delete oUser[updatedRuleId].actions[action]);
|
||||
} else {
|
||||
_results.push(void 0);
|
||||
}
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
for (name in listUserRules) {
|
||||
oUser = listUserRules[name];
|
||||
fRemoveNotRequired(oUser);
|
||||
}
|
||||
fAddRequired = function(userName, oUser) {
|
||||
var fCheckRules, nmRl, oRl, _results;
|
||||
fCheckRules = function(oMyRule) {
|
||||
var action, fAddIfNewOrNotExisting, _i, _len, _ref, _results;
|
||||
fAddIfNewOrNotExisting = function(actionName) {
|
||||
var moduleName;
|
||||
moduleName = (actionName.split(' -> '))[0];
|
||||
if (!oMyRule.actions[moduleName] || oMyRule.rule.id === updatedRuleId) {
|
||||
return db.actionInvokers.getModule(moduleName, function(err, obj) {
|
||||
return dynmod.compileString(obj.data, userName, oMyRule.rule.id, moduleName, obj.lang, db.actionInvokers, function(result) {
|
||||
if (!result.answ === 200) {
|
||||
this.log.error("EN | Compilation of code failed! " + userName + ", " + oMyRule.rule.id + ", " + moduleName);
|
||||
}
|
||||
return oMyRule.actions[moduleName] = result.module;
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
_ref = oMyRule.rule.actions;
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
action = _ref[_i];
|
||||
_results.push(fAddIfNewOrNotExisting(action));
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
_results = [];
|
||||
for (nmRl in oUser) {
|
||||
oRl = oUser[nmRl];
|
||||
_results.push(fCheckRules(oRl));
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
_results = [];
|
||||
for (userName in listUserRules) {
|
||||
oUser = listUserRules[userName];
|
||||
_results.push(fAddRequired(userName, oUser));
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
|
||||
pollQueue = function() {
|
||||
if (isRunning) {
|
||||
return db.popEvent(function(err, obj) {
|
||||
if (!err && obj) {
|
||||
processEvent(obj);
|
||||
}
|
||||
return setTimeout(pollQueue, 50);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Checks whether all conditions of the rule are met by the event.
|
||||
|
||||
@private validConditions ( *evt, rule* )
|
||||
@param {Object} evt
|
||||
@param {Object} rule
|
||||
*/
|
||||
|
||||
validConditions = function(evt, rule) {
|
||||
var prop, _i, _len, _ref;
|
||||
if (rule.conditions.length === 0) {
|
||||
return true;
|
||||
}
|
||||
_ref = rule.conditions;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
prop = _ref[_i];
|
||||
if (jsonQuery(evt, prop).nodes().length === 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Handles retrieved events.
|
||||
|
||||
@private processEvent ( *evt* )
|
||||
@param {Object} evt
|
||||
*/
|
||||
|
||||
processEvent = (function(_this) {
|
||||
return function(evt) {
|
||||
var action, arr, fSearchAndInvokeAction, oMyRule, oUser, ruleName, userName, _results;
|
||||
fSearchAndInvokeAction = function(node, arrPath, evt, depth) {
|
||||
var err;
|
||||
if (!node) {
|
||||
this.log.error("EN | Didn't find property in user rule list: " + arrPath.join(', ' + " at depth " + depth));
|
||||
return;
|
||||
}
|
||||
if (depth === arrPath.length) {
|
||||
try {
|
||||
return node(evt.payload);
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
return this.log.info("EN | ERROR IN ACTION INVOKER: " + err.message);
|
||||
}
|
||||
} else {
|
||||
return fSearchAndInvokeAction(node[arrPath[depth]], arrPath, evt, depth + 1);
|
||||
}
|
||||
};
|
||||
_this.log.info('EN | processing event: ' + evt.event + '(' + evt.eventid + ')');
|
||||
_results = [];
|
||||
for (userName in listUserRules) {
|
||||
oUser = listUserRules[userName];
|
||||
_results.push((function() {
|
||||
var _results1;
|
||||
_results1 = [];
|
||||
for (ruleName in oUser) {
|
||||
oMyRule = oUser[ruleName];
|
||||
if (evt.event === oMyRule.rule.event && validConditions(evt, oMyRule.rule)) {
|
||||
this.log.info('EN | EVENT FIRED: ' + evt.event + '(' + evt.eventid + ') for rule ' + ruleName);
|
||||
_results1.push((function() {
|
||||
var _i, _len, _ref, _results2;
|
||||
_ref = oMyRule.rule.actions;
|
||||
_results2 = [];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
action = _ref[_i];
|
||||
arr = action.split(' -> ');
|
||||
_results2.push(fSearchAndInvokeAction(listUserRules, [userName, ruleName, 'actions', arr[0], arr[1]], evt, 0));
|
||||
}
|
||||
return _results2;
|
||||
})());
|
||||
} else {
|
||||
_results1.push(void 0);
|
||||
}
|
||||
}
|
||||
return _results1;
|
||||
}).call(_this));
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
})(this);
|
||||
|
||||
exports.shutDown = function() {
|
||||
return isRunning = false;
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
// Generated by CoffeeScript 1.7.1
|
||||
|
||||
/*
|
||||
|
||||
HTTP Listener
|
||||
=============
|
||||
> Receives the HTTP requests to the server at the given port. The requests
|
||||
> (bound to a method) are then redirected to the appropriate handler which
|
||||
> takes care of the request.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var app, exports, express, initRouting, path, qs, requestHandler;
|
||||
|
||||
requestHandler = require('./request-handler');
|
||||
|
||||
path = require('path');
|
||||
|
||||
qs = require('querystring');
|
||||
|
||||
express = require('express');
|
||||
|
||||
app = express();
|
||||
|
||||
|
||||
/*
|
||||
Module call
|
||||
-----------
|
||||
Initializes the HTTP listener and its request handler.
|
||||
|
||||
@param {Object} args
|
||||
*/
|
||||
|
||||
exports = module.exports = (function(_this) {
|
||||
return function(args) {
|
||||
_this.log = args.logger;
|
||||
_this.shutDownSystem = args['shutdown-function'];
|
||||
requestHandler(args);
|
||||
initRouting(args['http-port']);
|
||||
return module.exports;
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Initializes the request routing and starts listening on the given port.
|
||||
|
||||
@param {int} port
|
||||
@private initRouting( *fShutDown* )
|
||||
*/
|
||||
|
||||
initRouting = (function(_this) {
|
||||
return function(port) {
|
||||
var server, sess_sec;
|
||||
app.use(express.cookieParser());
|
||||
sess_sec = "149u*y8C:@kmN/520Gt\\v'+KFBnQ!\\r<>5X/xRI`sT<Iw";
|
||||
app.use(express.session({
|
||||
secret: sess_sec
|
||||
}));
|
||||
_this.log.info('HL | no session backbone');
|
||||
app.use('/', express["static"](path.resolve(__dirname, '..', 'webpages', 'public')));
|
||||
app.get('/admin', requestHandler.handleAdmin);
|
||||
app.get('/forge', requestHandler.handleForge);
|
||||
app.post('/event', requestHandler.handleEvent);
|
||||
app.post('/login', requestHandler.handleLogin);
|
||||
app.post('/logout', requestHandler.handleLogout);
|
||||
app.post('/usercommand', requestHandler.handleUserCommand);
|
||||
app.post('/admincommand', requestHandler.handleAdminCommand);
|
||||
server = app.listen(parseInt(port) || 8111);
|
||||
server.on('listening', function() {
|
||||
var addr;
|
||||
addr = server.address();
|
||||
if (addr.port !== port) {
|
||||
return _this.shutDownSystem();
|
||||
}
|
||||
});
|
||||
return server.on('error', function(err) {
|
||||
|
||||
/*
|
||||
Error handling of the express port listener requires special attention,
|
||||
thus we have to catch the error, which is issued if the port is already in use.
|
||||
*/
|
||||
switch (err.errno) {
|
||||
case 'EADDRINUSE':
|
||||
_this.log.error(err, 'HL | http-port already in use, shutting down!');
|
||||
break;
|
||||
case 'EACCES':
|
||||
_this.log.error(err, 'HL | http-port not accessible, shutting down!');
|
||||
break;
|
||||
default:
|
||||
_this.log.error(err, 'HL | Error in server, shutting down!');
|
||||
}
|
||||
return _this.shutDownSystem();
|
||||
});
|
||||
};
|
||||
})(this);
|
||||
|
||||
}).call(this);
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
// Generated by CoffeeScript 1.7.1
|
||||
(function() {
|
||||
var bunyan, fs, path;
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
path = require('path');
|
||||
|
||||
bunyan = require('bunyan');
|
||||
|
||||
|
||||
/*
|
||||
Returns a bunyan logger according to the given arguments.
|
||||
|
||||
@public getLogger( *args* )
|
||||
@param {Object} args
|
||||
*/
|
||||
|
||||
exports.getLogger = (function(_this) {
|
||||
return function(args) {
|
||||
var e, emptylog, opt;
|
||||
emptylog = {
|
||||
trace: function() {},
|
||||
debug: function() {},
|
||||
info: function() {},
|
||||
warn: function() {},
|
||||
error: function() {},
|
||||
fatal: function() {}
|
||||
};
|
||||
args = args != null ? args : {};
|
||||
if (args.nolog) {
|
||||
return emptylog;
|
||||
} else {
|
||||
try {
|
||||
opt = {
|
||||
name: "webapi-eca"
|
||||
};
|
||||
if (args['mode'] === 'development') {
|
||||
opt.src = true;
|
||||
}
|
||||
if (args['file-path']) {
|
||||
_this.logPath = path.resolve(args['file-path']);
|
||||
} else {
|
||||
_this.logPath = path.resolve(__dirname, '..', 'logs', 'server.log');
|
||||
}
|
||||
try {
|
||||
fs.writeFileSync(_this.logPath + '.temp', 'temp');
|
||||
fs.unlinkSync(_this.logPath + '.temp');
|
||||
} catch (_error) {
|
||||
e = _error;
|
||||
console.error("Log folder '" + _this.logPath + "' is not writable");
|
||||
return emptylog;
|
||||
}
|
||||
opt.streams = [
|
||||
{
|
||||
level: args['io-level'],
|
||||
stream: process.stdout
|
||||
}, {
|
||||
level: args['file-level'],
|
||||
path: _this.logPath
|
||||
}
|
||||
];
|
||||
return bunyan.createLogger(opt);
|
||||
} catch (_error) {
|
||||
e = _error;
|
||||
console.error(e);
|
||||
return emptylog;
|
||||
}
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
|
||||
}).call(this);
|
||||
|
|
@ -1,966 +0,0 @@
|
|||
// Generated by CoffeeScript 1.7.1
|
||||
|
||||
/*
|
||||
|
||||
Persistence
|
||||
============
|
||||
> Handles the connection to the database and provides functionalities for event pollers,
|
||||
> action invokers, rules and the (hopefully encrypted) storing of user-specific parameters
|
||||
> per module.
|
||||
> General functionality as a wrapper for the module holds initialization,
|
||||
> the retrieval of modules and shut down.
|
||||
>
|
||||
> The general structure for linked data is that the key is stored in a set.
|
||||
> By fetching all set entries we can then fetch all elements, which is
|
||||
> automated in this function.
|
||||
> For example, modules of the same group, e.g. action invokers are registered in an
|
||||
> unordered set in the database, from where they can be retrieved again. For example
|
||||
> a new action invoker has its ID (e.g 'probinder') first registered in the set
|
||||
> 'action-invokers' and then stored in the db with the key 'action-invoker:' + ID
|
||||
> (e.g. action-invoker:probinder).
|
||||
>
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var IndexedModules, exports, getSetRecords, redis, replyHandler,
|
||||
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
||||
|
||||
redis = require('redis');
|
||||
|
||||
|
||||
/*
|
||||
Module call
|
||||
-----------
|
||||
Initializes the DB connection with the given `db-port` property in the `args` object.
|
||||
|
||||
@param {Object} args
|
||||
*/
|
||||
|
||||
exports = module.exports = (function(_this) {
|
||||
return function(args) {
|
||||
if (!_this.db) {
|
||||
if (!args['db-port']) {
|
||||
args['db-port'] = 6379;
|
||||
}
|
||||
_this.log = args.logger;
|
||||
exports.eventPollers = new IndexedModules('event-poller', _this.log);
|
||||
exports.actionInvokers = new IndexedModules('action-invoker', _this.log);
|
||||
return exports.initPort(args['db-port']);
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
|
||||
exports.getLogger = (function(_this) {
|
||||
return function() {
|
||||
return _this.log;
|
||||
};
|
||||
})(this);
|
||||
|
||||
exports.initPort = (function(_this) {
|
||||
return function(port) {
|
||||
var _ref;
|
||||
_this.connRefused = false;
|
||||
if ((_ref = _this.db) != null) {
|
||||
_ref.quit();
|
||||
}
|
||||
_this.db = redis.createClient(port, 'localhost', {
|
||||
connect_timeout: 2000
|
||||
});
|
||||
_this.db.on('error', function(err) {
|
||||
if (err.message.indexOf('ECONNREFUSED') > -1) {
|
||||
_this.connRefused = true;
|
||||
return _this.log.error(err, 'DB | Wrong port?');
|
||||
}
|
||||
});
|
||||
exports.eventPollers.setDB(_this.db);
|
||||
return exports.actionInvokers.setDB(_this.db);
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Checks whether the db is connected and passes either an error on failure after
|
||||
ten attempts within five seconds, or nothing on success to the callback(err).
|
||||
|
||||
@public isConnected( *cb* )
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
exports.isConnected = (function(_this) {
|
||||
return function(cb) {
|
||||
var fCheckConnection, numAttempts;
|
||||
if (!_this.db) {
|
||||
return cb(new Error('DB | DB initialization did not occur or failed miserably!'));
|
||||
} else {
|
||||
if (_this.db.connected) {
|
||||
return cb();
|
||||
} else {
|
||||
numAttempts = 0;
|
||||
fCheckConnection = function() {
|
||||
var _ref;
|
||||
if (_this.connRefused) {
|
||||
if ((_ref = _this.db) != null) {
|
||||
_ref.quit();
|
||||
}
|
||||
return cb(new Error('DB | Connection refused! Wrong port?'));
|
||||
} else {
|
||||
if (_this.db.connected) {
|
||||
_this.log.info('DB | Successfully connected to DB!');
|
||||
return cb();
|
||||
} else if (numAttempts++ < 10) {
|
||||
return setTimeout(fCheckConnection, 100);
|
||||
} else {
|
||||
return cb(new Error('DB | Connection to DB failed!'));
|
||||
}
|
||||
}
|
||||
};
|
||||
return setTimeout(fCheckConnection, 100);
|
||||
}
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Abstracts logging for simple action replies from the DB.
|
||||
|
||||
@private replyHandler( *action* )
|
||||
@param {String} action
|
||||
*/
|
||||
|
||||
replyHandler = (function(_this) {
|
||||
return function(action) {
|
||||
return function(err, reply) {
|
||||
if (err) {
|
||||
return _this.log.warn(err, "during '" + action + "'");
|
||||
} else {
|
||||
return _this.log.info("DB | " + action + ": " + reply);
|
||||
}
|
||||
};
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Push an event into the event queue.
|
||||
|
||||
@public pushEvent( *oEvent* )
|
||||
@param {Object} oEvent
|
||||
*/
|
||||
|
||||
exports.pushEvent = (function(_this) {
|
||||
return function(oEvent) {
|
||||
if (oEvent) {
|
||||
_this.log.info("DB | Event pushed into the queue: '" + oEvent.eventid + "'");
|
||||
return _this.db.rpush('event_queue', JSON.stringify(oEvent));
|
||||
} else {
|
||||
return _this.log.warn('DB | Why would you give me an empty event...');
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Pop an event from the event queue and pass it to cb(err, obj).
|
||||
|
||||
@public popEvent( *cb* )
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
exports.popEvent = (function(_this) {
|
||||
return function(cb) {
|
||||
var makeObj;
|
||||
makeObj = function(pcb) {
|
||||
return function(err, obj) {
|
||||
return pcb(err, JSON.parse(obj));
|
||||
};
|
||||
};
|
||||
return _this.db.lpop('event_queue', makeObj(cb));
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Purge the event queue.
|
||||
|
||||
@public purgeEventQueue()
|
||||
*/
|
||||
|
||||
exports.purgeEventQueue = (function(_this) {
|
||||
return function() {
|
||||
return _this.db.del('event_queue', replyHandler('purging event queue'));
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Fetches all linked data set keys from a linking set, fetches the single
|
||||
data objects via the provided function and returns the results to cb(err, obj).
|
||||
|
||||
@private getSetRecords( *set, fSingle, cb* )
|
||||
@param {String} set the set name how it is stored in the DB
|
||||
@param {function} fSingle a function to retrieve a single data element
|
||||
per set entry
|
||||
@param {function} cb the callback(err, obj) function that receives all
|
||||
the retrieved data or an error
|
||||
*/
|
||||
|
||||
getSetRecords = (function(_this) {
|
||||
return function(set, fSingle, cb) {
|
||||
_this.log.info("DB | Fetching set records: '" + set + "'");
|
||||
return _this.db.smembers(set, function(err, arrReply) {
|
||||
var fCallback, objReplies, reply, semaphore, _i, _len, _results;
|
||||
if (err) {
|
||||
_this.log.warn(err, "DB | fetching '" + set + "'");
|
||||
return cb(err);
|
||||
} else if (arrReply.length === 0) {
|
||||
return cb();
|
||||
} else {
|
||||
semaphore = arrReply.length;
|
||||
objReplies = {};
|
||||
setTimeout(function() {
|
||||
if (semaphore > 0) {
|
||||
return cb(new Error("Timeout fetching '" + set + "'"));
|
||||
}
|
||||
}, 2000);
|
||||
fCallback = function(prop) {
|
||||
return function(err, data) {
|
||||
--semaphore;
|
||||
if (err) {
|
||||
_this.log.warn(err, "DB | fetching single element: '" + prop + "'");
|
||||
} else if (!data) {
|
||||
_this.log.warn(new Error("Empty key in DB: '" + prop + "'"));
|
||||
} else {
|
||||
objReplies[prop] = data;
|
||||
}
|
||||
if (semaphore === 0) {
|
||||
return cb(null, objReplies);
|
||||
}
|
||||
};
|
||||
};
|
||||
_results = [];
|
||||
for (_i = 0, _len = arrReply.length; _i < _len; _i++) {
|
||||
reply = arrReply[_i];
|
||||
_results.push(fSingle(reply, fCallback(reply)));
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
});
|
||||
};
|
||||
})(this);
|
||||
|
||||
IndexedModules = (function() {
|
||||
function IndexedModules(setname, log) {
|
||||
this.setname = setname;
|
||||
this.log = log;
|
||||
this.deleteUserParams = __bind(this.deleteUserParams, this);
|
||||
this.getUserParamsIds = __bind(this.getUserParamsIds, this);
|
||||
this.getUserParams = __bind(this.getUserParams, this);
|
||||
this.storeUserParams = __bind(this.storeUserParams, this);
|
||||
this.deleteModule = __bind(this.deleteModule, this);
|
||||
this.getModules = __bind(this.getModules, this);
|
||||
this.getModuleIds = __bind(this.getModuleIds, this);
|
||||
this.getAvailableModuleIds = __bind(this.getAvailableModuleIds, this);
|
||||
this.getModuleParams = __bind(this.getModuleParams, this);
|
||||
this.getModule = __bind(this.getModule, this);
|
||||
this.unpublish = __bind(this.unpublish, this);
|
||||
this.publish = __bind(this.publish, this);
|
||||
this.unlinkModule = __bind(this.unlinkModule, this);
|
||||
this.linkModule = __bind(this.linkModule, this);
|
||||
this.storeModule = __bind(this.storeModule, this);
|
||||
this.log.info("DB | (IdxedMods) Instantiated indexed modules for '" + this.setname + "'");
|
||||
}
|
||||
|
||||
IndexedModules.prototype.setDB = function(db) {
|
||||
this.db = db;
|
||||
return this.log.info("DB | (IdxedMods) Registered new DB connection for '" + this.setname + "'");
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Stores a module and links it to the user.
|
||||
|
||||
@private storeModule( *userId, oModule* )
|
||||
@param {String} userId
|
||||
@param {object} oModule
|
||||
*/
|
||||
|
||||
IndexedModules.prototype.storeModule = function(userId, oModule) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".storeModule( " + userId + ", oModule )");
|
||||
this.db.sadd("" + this.setname + "s", oModule.id, replyHandler("sadd '" + oModule.id + "' to '" + this.setname + "'"));
|
||||
this.db.hmset("" + this.setname + ":" + oModule.id, oModule, replyHandler("hmset properties in hash '" + this.setname + ":" + oModule.id + "'"));
|
||||
return this.linkModule(oModule.id, userId);
|
||||
};
|
||||
|
||||
IndexedModules.prototype.linkModule = function(mId, userId) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".linkModule( " + mId + ", " + userId + " )");
|
||||
this.db.sadd("" + this.setname + ":" + mId + ":users", userId, replyHandler("sadd " + userId + " to '" + this.setname + ":" + mId + ":users'"));
|
||||
return this.db.sadd("user:" + userId + ":" + this.setname + "s", mId, replyHandler("sadd " + mId + " to 'user:" + userId + ":" + this.setname + "s'"));
|
||||
};
|
||||
|
||||
IndexedModules.prototype.unlinkModule = function(mId, userId) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".unlinkModule( " + mId + ", " + userId + " )");
|
||||
this.db.srem("" + this.setname + ":" + mId + ":users", userId, replyHandler("srem " + userId + " from '" + this.setname + ":" + mId + ":users'"));
|
||||
return this.db.srem("user:" + userId + ":" + this.setname + "s", mId, replyHandler("srem " + mId + " from 'user:" + userId + ":" + this.setname + "s'"));
|
||||
};
|
||||
|
||||
IndexedModules.prototype.publish = function(mId) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".publish( " + mId + " )");
|
||||
return this.db.sadd("public-" + this.setname + "s", mId, replyHandler("sadd '" + mId + "' to 'public-" + this.setname + "s'"));
|
||||
};
|
||||
|
||||
IndexedModules.prototype.unpublish = function(mId) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".unpublish( " + mId + " )");
|
||||
return this.db.srem("public-" + this.setname + "s", mId, replyHandler("srem '" + mId + "' from 'public-" + this.setname + "s'"));
|
||||
};
|
||||
|
||||
IndexedModules.prototype.getModule = function(mId, cb) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".getModule( " + mId + " )");
|
||||
return this.db.hgetall("" + this.setname + ":" + mId, cb);
|
||||
};
|
||||
|
||||
IndexedModules.prototype.getModuleParams = function(mId, cb) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".getModuleParams( " + mId + " )");
|
||||
return this.db.hget("" + this.setname + ":" + mId, "params", cb);
|
||||
};
|
||||
|
||||
IndexedModules.prototype.getAvailableModuleIds = function(userId, cb) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".getPublicModuleIds( " + userId + " )");
|
||||
return this.db.sunion("public-" + this.setname + "s", "user:" + userId + ":" + this.setname + "s", cb);
|
||||
};
|
||||
|
||||
IndexedModules.prototype.getModuleIds = function(cb) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".getModuleIds()");
|
||||
return this.db.smembers("" + this.setname + "s", cb);
|
||||
};
|
||||
|
||||
IndexedModules.prototype.getModules = function(cb) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".getModules()");
|
||||
return getSetRecords("" + this.setname + "s", this.getModule, cb);
|
||||
};
|
||||
|
||||
IndexedModules.prototype.deleteModule = function(mId) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".deleteModule( " + mId + " )");
|
||||
this.db.srem("" + this.setname + "s", mId, replyHandler("srem '" + mId + "' from " + this.setname + "s"));
|
||||
this.db.del("" + this.setname + ":" + mId, replyHandler("del of '" + this.setname + ":" + mId + "'"));
|
||||
this.unpublish(mId);
|
||||
return this.db.smembers("" + this.setname + ":" + mId + ":users", (function(_this) {
|
||||
return function(err, obj) {
|
||||
var userId, _i, _j, _len, _len1, _results;
|
||||
for (_i = 0, _len = obj.length; _i < _len; _i++) {
|
||||
userId = obj[_i];
|
||||
_this.unlinkModule(mId, userId);
|
||||
}
|
||||
_results = [];
|
||||
for (_j = 0, _len1 = obj.length; _j < _len1; _j++) {
|
||||
userId = obj[_j];
|
||||
_results.push(_this.deleteUserParams(mId, userId));
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
})(this));
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Stores user params for a module. They are expected to be RSA encrypted with helps of
|
||||
the provided cryptico JS library and will only be decrypted right before the module is loaded!
|
||||
|
||||
@private storeUserParams( *mId, userId, encData* )
|
||||
@param {String} mId
|
||||
@param {String} userId
|
||||
@param {object} encData
|
||||
*/
|
||||
|
||||
IndexedModules.prototype.storeUserParams = function(mId, userId, encData) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".storeUserParams( " + mId + ", " + userId + ", encData )");
|
||||
this.db.sadd("" + this.setname + "-params", "" + mId + ":" + userId, replyHandler("sadd '" + mId + ":" + userId + "' to '" + this.setname + "-params'"));
|
||||
return this.db.set("" + this.setname + "-params:" + mId + ":" + userId, encData, replyHandler("set user params in '" + this.setname + "-params:" + mId + ":" + userId + "'"));
|
||||
};
|
||||
|
||||
IndexedModules.prototype.getUserParams = function(mId, userId, cb) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".getUserParams( " + mId + ", " + userId + " )");
|
||||
return this.db.get("" + this.setname + "-params:" + mId + ":" + userId, cb);
|
||||
};
|
||||
|
||||
IndexedModules.prototype.getUserParamsIds = function(cb) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".getUserParamsIds()");
|
||||
return this.db.smembers("" + this.setname + "-params", cb);
|
||||
};
|
||||
|
||||
IndexedModules.prototype.deleteUserParams = function(mId, userId) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".deleteUserParams(" + mId + ", " + userId + " )");
|
||||
this.db.srem("" + this.setname + "-params", "" + mId + ":" + userId, replyHandler("srem '" + mId + ":" + userId + "' from '" + this.setname + "-params'"));
|
||||
return this.db.del("" + this.setname + "-params:" + mId + ":" + userId, replyHandler("del '" + this.setname + "-params:" + mId + ":" + userId + "'"));
|
||||
};
|
||||
|
||||
return IndexedModules;
|
||||
|
||||
})();
|
||||
|
||||
|
||||
/*
|
||||
*# Rules
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Appends a log entry.
|
||||
|
||||
@public log( *userId, ruleId, message* )
|
||||
@param {String} userId
|
||||
@param {String} ruleId
|
||||
@param {String} message
|
||||
*/
|
||||
|
||||
exports.appendLog = (function(_this) {
|
||||
return function(userId, ruleId, moduleId, message) {
|
||||
return _this.db.append("" + userId + ":" + ruleId, "[" + ((new Date).toISOString()) + "] {" + moduleId + "} " + message + "\n");
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Retrieves a log entry.
|
||||
|
||||
@public getLog( *userId, ruleId* )
|
||||
@param {String} userId
|
||||
@param {String} ruleId
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
exports.getLog = (function(_this) {
|
||||
return function(userId, ruleId, cb) {
|
||||
return _this.db.get("" + userId + ":" + ruleId, cb);
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Resets a log entry.
|
||||
|
||||
@public resetLog( *userId, ruleId* )
|
||||
@param {String} userId
|
||||
@param {String} ruleId
|
||||
*/
|
||||
|
||||
exports.resetLog = (function(_this) {
|
||||
return function(userId, ruleId) {
|
||||
return _this.db.del("" + userId + ":" + ruleId, replyHandler("RESET LOG '" + userId + ":" + ruleId + "'"));
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Query the DB for a rule and pass it to cb(err, obj).
|
||||
|
||||
@public getRule( *ruleId, cb* )
|
||||
@param {String} ruleId
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
exports.getRule = (function(_this) {
|
||||
return function(ruleId, cb) {
|
||||
_this.log.info("DB | getRule: '" + ruleId + "'");
|
||||
return _this.db.get("rule:" + ruleId, cb);
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Fetch all rules and pass them to cb(err, obj).
|
||||
|
||||
@public getRules( *cb* )
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
exports.getRules = (function(_this) {
|
||||
return function(cb) {
|
||||
_this.log.info('DB | Fetching all Rules');
|
||||
return getSetRecords('rules', exports.getRule, cb);
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Fetch all rule IDs and hand it to cb(err, obj).
|
||||
|
||||
@public getRuleIds( *cb* )
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
exports.getRuleIds = (function(_this) {
|
||||
return function(cb) {
|
||||
_this.log.info('DB | Fetching all Rule IDs');
|
||||
return _this.db.smembers('rules', cb);
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Store a string representation of a rule in the DB.
|
||||
|
||||
@public storeRule( *ruleId, data* )
|
||||
@param {String} ruleId
|
||||
@param {String} data
|
||||
*/
|
||||
|
||||
exports.storeRule = (function(_this) {
|
||||
return function(ruleId, data) {
|
||||
_this.log.info("DB | storeRule: '" + ruleId + "'");
|
||||
_this.db.sadd('rules', "" + ruleId, replyHandler("storing rule key '" + ruleId + "'"));
|
||||
return _this.db.set("rule:" + ruleId, data, replyHandler("storing rule '" + ruleId + "'"));
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Delete a string representation of a rule.
|
||||
|
||||
@public deleteRule( *ruleId, userId* )
|
||||
@param {String} ruleId
|
||||
@param {String} userId
|
||||
*/
|
||||
|
||||
exports.deleteRule = (function(_this) {
|
||||
return function(ruleId) {
|
||||
_this.log.info("DB | deleteRule: '" + ruleId + "'");
|
||||
_this.db.srem("rules", ruleId, replyHandler("Deleting rule key '" + ruleId + "'"));
|
||||
_this.db.del("rule:" + ruleId, replyHandler("Deleting rule '" + ruleId + "'"));
|
||||
_this.db.smembers("rule:" + ruleId + ":users", function(err, obj) {
|
||||
var delLinkedUserRule, id, _i, _len, _results;
|
||||
delLinkedUserRule = function(userId) {
|
||||
return _this.db.srem("user:" + userId + ":rules", ruleId, replyHandler("Deleting rule key '" + ruleId + "' in linked user '" + userId + "'"));
|
||||
};
|
||||
_results = [];
|
||||
for (_i = 0, _len = obj.length; _i < _len; _i++) {
|
||||
id = obj[_i];
|
||||
_results.push(delLinkedUserRule(id));
|
||||
}
|
||||
return _results;
|
||||
});
|
||||
_this.db.del("rule:" + ruleId + ":users", replyHandler("Deleting rule '" + ruleId + "' users"));
|
||||
_this.db.smembers("rule:" + ruleId + ":active-users", function(err, obj) {
|
||||
var delActiveUserRule, id, _i, _len, _results;
|
||||
delActiveUserRule = function(userId) {
|
||||
return _this.db.srem("user:" + userId + ":active-rules", ruleId, replyHandler("Deleting rule key '" + ruleId + "' in active user '" + userId + "'"));
|
||||
};
|
||||
_results = [];
|
||||
for (_i = 0, _len = obj.length; _i < _len; _i++) {
|
||||
id = obj[_i];
|
||||
_results.push(delActiveUserRule(id));
|
||||
}
|
||||
return _results;
|
||||
});
|
||||
return _this.db.del("rule:" + ruleId + ":active-users", replyHandler("Deleting rule '" + ruleId + "' active users"));
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Associate a rule to a user.
|
||||
|
||||
@public linkRule( *ruleId, userId* )
|
||||
@param {String} ruleId
|
||||
@param {String} userId
|
||||
*/
|
||||
|
||||
exports.linkRule = (function(_this) {
|
||||
return function(ruleId, userId) {
|
||||
_this.log.info("DB | linkRule: '" + ruleId + "' for user '" + userId + "'");
|
||||
_this.db.sadd("rule:" + ruleId + ":users", userId, replyHandler("storing user '" + userId + "' for rule key '" + ruleId + "'"));
|
||||
return _this.db.sadd("user:" + userId + ":rules", ruleId, replyHandler("storing rule key '" + ruleId + "' for user '" + userId + "'"));
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Get rules linked to a user and hand it to cb(err, obj).
|
||||
|
||||
@public getUserLinkRule( *userId, cb* )
|
||||
@param {String} userId
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
exports.getUserLinkedRules = (function(_this) {
|
||||
return function(userId, cb) {
|
||||
_this.log.info("DB | getUserLinkedRules: for user '" + userId + "'");
|
||||
return _this.db.smembers("user:" + userId + ":rules", cb);
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Get users linked to a rule and hand it to cb(err, obj).
|
||||
|
||||
@public getRuleLinkedUsers( *ruleId, cb* )
|
||||
@param {String} ruleId
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
exports.getRuleLinkedUsers = (function(_this) {
|
||||
return function(ruleId, cb) {
|
||||
_this.log.info("DB | getRuleLinkedUsers: for rule '" + ruleId + "'");
|
||||
return _this.db.smembers("rule:" + ruleId + ":users", cb);
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Delete an association of a rule to a user.
|
||||
|
||||
@public unlinkRule( *ruleId, userId* )
|
||||
@param {String} ruleId
|
||||
@param {String} userId
|
||||
*/
|
||||
|
||||
exports.unlinkRule = (function(_this) {
|
||||
return function(ruleId, userId) {
|
||||
_this.log.info("DB | unlinkRule: '" + ruleId + ":" + userId + "'");
|
||||
_this.db.srem("rule:" + ruleId + ":users", userId, replyHandler("removing user '" + userId + "' for rule key '" + ruleId + "'"));
|
||||
return _this.db.srem("user:" + userId + ":rules", ruleId, replyHandler("removing rule key '" + ruleId + "' for user '" + userId + "'"));
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Activate a rule.
|
||||
|
||||
@public activateRule( *ruleId, userId* )
|
||||
@param {String} ruleId
|
||||
@param {String} userId
|
||||
*/
|
||||
|
||||
exports.activateRule = (function(_this) {
|
||||
return function(ruleId, userId) {
|
||||
_this.log.info("DB | activateRule: '" + ruleId + "' for '" + userId + "'");
|
||||
_this.db.sadd("rule:" + ruleId + ":active-users", userId, replyHandler("storing activated user '" + userId + "' in rule '" + ruleId + "'"));
|
||||
return _this.db.sadd("user:" + userId + ":active-rules", ruleId, replyHandler("storing activated rule '" + ruleId + "' in user '" + userId + "'"));
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Get rules activated for a user and hand it to cb(err, obj).
|
||||
|
||||
@public getUserLinkRule( *userId, cb* )
|
||||
@param {String} userId
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
exports.getUserActivatedRules = (function(_this) {
|
||||
return function(userId, cb) {
|
||||
_this.log.info("DB | getUserActivatedRules: for user '" + userId + "'");
|
||||
return _this.db.smembers("user:" + userId + ":active-rules", cb);
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Get users activated for a rule and hand it to cb(err, obj).
|
||||
|
||||
@public getRuleActivatedUsers ( *ruleId, cb* )
|
||||
@param {String} ruleId
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
exports.getRuleActivatedUsers = (function(_this) {
|
||||
return function(ruleId, cb) {
|
||||
_this.log.info("DB | getRuleActivatedUsers: for rule '" + ruleId + "'");
|
||||
return _this.db.smembers("rule:" + ruleId + ":active-users", cb);
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Deactivate a rule.
|
||||
|
||||
@public deactivateRule( *ruleId, userId* )
|
||||
@param {String} ruleId
|
||||
@param {String} userId
|
||||
*/
|
||||
|
||||
exports.deactivateRule = (function(_this) {
|
||||
return function(ruleId, userId) {
|
||||
_this.log.info("DB | deactivateRule: '" + ruleId + "' for '" + userId + "'");
|
||||
_this.db.srem("rule:" + ruleId + ":active-users", userId, replyHandler("removing activated user '" + userId + "' in rule '" + ruleId + "'"));
|
||||
return _this.db.srem("user:" + userId + ":active-rules", ruleId, replyHandler("removing activated rule '" + ruleId + "' in user '" + userId + "'"));
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Fetch all active ruleIds and pass them to cb(err, obj).
|
||||
|
||||
@public getAllActivatedRuleIds( *cb* )
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
exports.getAllActivatedRuleIdsPerUser = (function(_this) {
|
||||
return function(cb) {
|
||||
_this.log.info("DB | Fetching all active rules");
|
||||
return _this.db.smembers('users', function(err, obj) {
|
||||
var fFetchActiveUserRules, result, semaphore, user, _i, _len, _results;
|
||||
result = {};
|
||||
if (obj.length === 0) {
|
||||
return cb(null, result);
|
||||
} else {
|
||||
semaphore = obj.length;
|
||||
fFetchActiveUserRules = function(userId) {
|
||||
return _this.db.smembers("user:" + user + ":active-rules", function(err, obj) {
|
||||
if (obj.length > 0) {
|
||||
result[userId] = obj;
|
||||
}
|
||||
if (--semaphore === 0) {
|
||||
return cb(null, result);
|
||||
}
|
||||
});
|
||||
};
|
||||
_results = [];
|
||||
for (_i = 0, _len = obj.length; _i < _len; _i++) {
|
||||
user = obj[_i];
|
||||
_results.push(fFetchActiveUserRules(user));
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
});
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
*# Users
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Store a user object (needs to be a flat structure).
|
||||
The password should be hashed before it is passed to this function.
|
||||
|
||||
@public storeUser( *objUser* )
|
||||
@param {Object} objUser
|
||||
*/
|
||||
|
||||
exports.storeUser = (function(_this) {
|
||||
return function(objUser) {
|
||||
_this.log.info("DB | storeUser: '" + objUser.username + "'");
|
||||
if (objUser && objUser.username && objUser.password) {
|
||||
_this.db.sadd('users', objUser.username, replyHandler("storing user key '" + objUser.username + "'"));
|
||||
objUser.password = objUser.password;
|
||||
return _this.db.hmset("user:" + objUser.username, objUser, replyHandler("storing user properties '" + objUser.username + "'"));
|
||||
} else {
|
||||
return _this.log.warn(new Error('DB | username or password was missing'));
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Fetch all user IDs and pass them to cb(err, obj).
|
||||
|
||||
@public getUserIds( *cb* )
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
exports.getUserIds = (function(_this) {
|
||||
return function(cb) {
|
||||
_this.log.info("DB | getUserIds");
|
||||
return _this.db.smembers("users", cb);
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Fetch a user by id and pass it to cb(err, obj).
|
||||
|
||||
@public getUser( *userId, cb* )
|
||||
@param {String} userId
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
exports.getUser = (function(_this) {
|
||||
return function(userId, cb) {
|
||||
_this.log.info("DB | getUser: '" + userId + "'");
|
||||
return _this.db.hgetall("user:" + userId, cb);
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Deletes a user and all his associated linked and active rules.
|
||||
|
||||
@public deleteUser( *userId* )
|
||||
@param {String} userId
|
||||
*/
|
||||
|
||||
exports.deleteUser = (function(_this) {
|
||||
return function(userId) {
|
||||
_this.log.info("DB | deleteUser: '" + userId + "'");
|
||||
_this.db.srem("users", userId, replyHandler("Deleting user key '" + userId + "'"));
|
||||
_this.db.del("user:" + userId, replyHandler("Deleting user '" + userId + "'"));
|
||||
_this.db.smembers("user:" + userId + ":rules", function(err, obj) {
|
||||
var delLinkedRuleUser, id, _i, _len, _results;
|
||||
delLinkedRuleUser = function(ruleId) {
|
||||
return _this.db.srem("rule:" + ruleId + ":users", userId, replyHandler("Deleting user key '" + userId + "' in linked rule '" + ruleId + "'"));
|
||||
};
|
||||
_results = [];
|
||||
for (_i = 0, _len = obj.length; _i < _len; _i++) {
|
||||
id = obj[_i];
|
||||
_results.push(delLinkedRuleUser(id));
|
||||
}
|
||||
return _results;
|
||||
});
|
||||
_this.db.del("user:" + userId + ":rules", replyHandler("Deleting user '" + userId + "' rules"));
|
||||
_this.db.smembers("user:" + userId + ":active-rules", function(err, obj) {
|
||||
var delActivatedRuleUser, id, _i, _len, _results;
|
||||
delActivatedRuleUser = function(ruleId) {
|
||||
return _this.db.srem("rule:" + ruleId + ":active-users", userId, replyHandler("Deleting user key '" + userId + "' in active rule '" + ruleId + "'"));
|
||||
};
|
||||
_results = [];
|
||||
for (_i = 0, _len = obj.length; _i < _len; _i++) {
|
||||
id = obj[_i];
|
||||
_results.push(delActivatedRuleUser(id));
|
||||
}
|
||||
return _results;
|
||||
});
|
||||
_this.db.del("user:" + userId + ":active-rules", replyHandler("Deleting user '" + userId + "' rules"));
|
||||
_this.db.smembers("user:" + userId + ":roles", function(err, obj) {
|
||||
var delRoleUser, id, _i, _len, _results;
|
||||
delRoleUser = function(roleId) {
|
||||
return _this.db.srem("role:" + roleId + ":users", userId, replyHandler("Deleting user key '" + userId + "' in role '" + roleId + "'"));
|
||||
};
|
||||
_results = [];
|
||||
for (_i = 0, _len = obj.length; _i < _len; _i++) {
|
||||
id = obj[_i];
|
||||
_results.push(delRoleUser(id));
|
||||
}
|
||||
return _results;
|
||||
});
|
||||
return _this.db.del("user:" + userId + ":roles", replyHandler("Deleting user '" + userId + "' roles"));
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Checks the credentials and on success returns the user object to the
|
||||
callback(err, obj) function. The password has to be hashed (SHA-3-512)
|
||||
beforehand by the instance closest to the user that enters the password,
|
||||
because we only store hashes of passwords for security6 reasons.
|
||||
|
||||
@public loginUser( *userId, password, cb* )
|
||||
@param {String} userId
|
||||
@param {String} password
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
exports.loginUser = (function(_this) {
|
||||
return function(userId, password, cb) {
|
||||
var fCheck;
|
||||
_this.log.info("DB | User '" + userId + "' tries to log in");
|
||||
fCheck = function(pw) {
|
||||
return function(err, obj) {
|
||||
if (err) {
|
||||
return cb(err, null);
|
||||
} else if (obj && obj.password) {
|
||||
if (pw === obj.password) {
|
||||
_this.log.info("DB | User '" + obj.username + "' logged in!");
|
||||
return cb(null, obj);
|
||||
} else {
|
||||
return cb(new Error('Wrong credentials!'), null);
|
||||
}
|
||||
} else {
|
||||
return cb(new Error('User not found!'), null);
|
||||
}
|
||||
};
|
||||
};
|
||||
return _this.db.hgetall("user:" + userId, fCheck(password));
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
*# User Roles
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Associate a role with a user.
|
||||
|
||||
@public storeUserRole( *userId, role* )
|
||||
@param {String} userId
|
||||
@param {String} role
|
||||
*/
|
||||
|
||||
exports.storeUserRole = (function(_this) {
|
||||
return function(userId, role) {
|
||||
_this.log.info("DB | storeUserRole: '" + userId + ":" + role + "'");
|
||||
_this.db.sadd('roles', role, replyHandler("adding role '" + role + "' to role index set"));
|
||||
_this.db.sadd("user:" + userId + ":roles", role, replyHandler("adding role '" + role + "' to user '" + userId + "'"));
|
||||
return _this.db.sadd("role:" + role + ":users", userId, replyHandler("adding user '" + userId + "' to role '" + role + "'"));
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Fetch all roles of a user and pass them to cb(err, obj).
|
||||
|
||||
@public getUserRoles( *userId* )
|
||||
@param {String} userId
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
exports.getUserRoles = (function(_this) {
|
||||
return function(userId, cb) {
|
||||
_this.log.info("DB | getUserRoles: '" + userId + "'");
|
||||
return _this.db.smembers("user:" + userId + ":roles", cb);
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Fetch all users of a role and pass them to cb(err, obj).
|
||||
|
||||
@public getUserRoles( *role* )
|
||||
@param {String} role
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
exports.getRoleUsers = (function(_this) {
|
||||
return function(role, cb) {
|
||||
_this.log.info("DB | getRoleUsers: '" + role + "'");
|
||||
return _this.db.smembers("role:" + role + ":users", cb);
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Remove a role from a user.
|
||||
|
||||
@public removeRoleFromUser( *role, userId* )
|
||||
@param {String} role
|
||||
@param {String} userId
|
||||
*/
|
||||
|
||||
exports.removeUserRole = (function(_this) {
|
||||
return function(userId, role) {
|
||||
_this.log.info("DB | removeRoleFromUser: role '" + role + "', user '" + userId + "'");
|
||||
_this.db.srem("user:" + userId + ":roles", role, replyHandler("Removing role '" + role + "' from user '" + userId + "'"));
|
||||
return _this.db.srem("role:" + role + ":users", userId, replyHandler("Removing user '" + userId + "' from role '" + role + "'"));
|
||||
};
|
||||
})(this);
|
||||
|
||||
|
||||
/*
|
||||
Shuts down the db link.
|
||||
|
||||
@public shutDown()
|
||||
*/
|
||||
|
||||
exports.shutDown = (function(_this) {
|
||||
return function() {
|
||||
var _ref;
|
||||
return (_ref = _this.db) != null ? _ref.quit() : void 0;
|
||||
};
|
||||
})(this);
|
||||
|
||||
}).call(this);
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
// Generated by CoffeeScript 1.7.1
|
||||
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
/*
|
||||
|
||||
Components Manager
|
||||
|
|
@ -7,10 +6,12 @@ Components Manager
|
|||
> The components manager takes care of the dynamic JS modules and the rules.
|
||||
> Event Poller and Action Invoker modules are loaded as strings and stored in the database,
|
||||
> then compiled into node modules and rules and used in the engine and event poller.
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
var commandFunctions, db, dynmod, eventEmitter, events, exports, forgeModule, fs, getModuleParams, getModules, hasRequiredParams, path;
|
||||
var commandFunctions, db, dynmod, eventEmitter, events, exports, forgeModule, fs, getModuleParams, getModules, hasRequiredParams, path,
|
||||
_this = this;
|
||||
|
||||
db = require('./persistence');
|
||||
|
||||
|
|
@ -24,67 +25,61 @@ Components Manager
|
|||
|
||||
eventEmitter = new events.EventEmitter();
|
||||
|
||||
|
||||
/*
|
||||
Module call
|
||||
-----------
|
||||
Initializes the Components Manager and constructs a new Event Emitter.
|
||||
|
||||
@param {Object} args
|
||||
*/
|
||||
*/
|
||||
|
||||
exports = module.exports = (function(_this) {
|
||||
return function(args) {
|
||||
_this.log = args.logger;
|
||||
db(args);
|
||||
dynmod(args);
|
||||
return module.exports;
|
||||
};
|
||||
})(this);
|
||||
|
||||
exports = module.exports = function(args) {
|
||||
_this.log = args.logger;
|
||||
db(args);
|
||||
dynmod(args);
|
||||
return module.exports;
|
||||
};
|
||||
|
||||
/*
|
||||
Add an event handler (eh) that listens for rules.
|
||||
|
||||
@public addRuleListener ( *eh* )
|
||||
@param {function} eh
|
||||
*/
|
||||
*/
|
||||
|
||||
exports.addRuleListener = (function(_this) {
|
||||
return function(eh) {
|
||||
eventEmitter.addListener('rule', eh);
|
||||
return db.getAllActivatedRuleIdsPerUser(function(err, objUsers) {
|
||||
var fGoThroughUsers, rules, user, _results;
|
||||
fGoThroughUsers = function(user, rules) {
|
||||
var fFetchRule, rule, _i, _len, _results;
|
||||
fFetchRule = function(rule) {
|
||||
return db.getRule(rule, (function(_this) {
|
||||
return function(err, oRule) {
|
||||
return eventEmitter.emit('rule', {
|
||||
event: 'init',
|
||||
user: user,
|
||||
rule: JSON.parse(oRule)
|
||||
});
|
||||
};
|
||||
})(this));
|
||||
};
|
||||
_results = [];
|
||||
for (_i = 0, _len = rules.length; _i < _len; _i++) {
|
||||
rule = rules[_i];
|
||||
_results.push(fFetchRule(rule));
|
||||
}
|
||||
return _results;
|
||||
|
||||
exports.addRuleListener = function(eh) {
|
||||
eventEmitter.addListener('rule', eh);
|
||||
return db.getAllActivatedRuleIdsPerUser(function(err, objUsers) {
|
||||
var fGoThroughUsers, rules, user, _results;
|
||||
fGoThroughUsers = function(user, rules) {
|
||||
var fFetchRule, rule, _i, _len, _results;
|
||||
fFetchRule = function(rule) {
|
||||
var _this = this;
|
||||
return db.getRule(rule, function(err, oRule) {
|
||||
return eventEmitter.emit('rule', {
|
||||
event: 'init',
|
||||
user: user,
|
||||
rule: JSON.parse(oRule)
|
||||
});
|
||||
});
|
||||
};
|
||||
_results = [];
|
||||
for (user in objUsers) {
|
||||
rules = objUsers[user];
|
||||
_results.push(fGoThroughUsers(user, rules));
|
||||
for (_i = 0, _len = rules.length; _i < _len; _i++) {
|
||||
rule = rules[_i];
|
||||
_results.push(fFetchRule(rule));
|
||||
}
|
||||
return _results;
|
||||
});
|
||||
};
|
||||
})(this);
|
||||
|
||||
};
|
||||
_results = [];
|
||||
for (user in objUsers) {
|
||||
rules = objUsers[user];
|
||||
_results.push(fGoThroughUsers(user, rules));
|
||||
}
|
||||
return _results;
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Processes a user request coming through the request-handler.
|
||||
|
|
@ -99,7 +94,8 @@ Components Manager
|
|||
@param {Object} user
|
||||
@param {Object} oReq
|
||||
@param {function} callback
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
exports.processRequest = function(user, oReq, callback) {
|
||||
var dat, err;
|
||||
|
|
@ -144,7 +140,8 @@ Components Manager
|
|||
|
||||
getModules = function(user, oPayload, dbMod, callback) {
|
||||
return dbMod.getAvailableModuleIds(user.username, function(err, arrNames) {
|
||||
var answReq, fGetFunctions, id, oRes, sem, _i, _len, _results;
|
||||
var answReq, fGetFunctions, id, oRes, sem, _i, _len, _results,
|
||||
_this = this;
|
||||
oRes = {};
|
||||
answReq = function() {
|
||||
return callback({
|
||||
|
|
@ -156,18 +153,16 @@ Components Manager
|
|||
if (sem === 0) {
|
||||
return answReq();
|
||||
} else {
|
||||
fGetFunctions = (function(_this) {
|
||||
return function(id) {
|
||||
return dbMod.getModule(id, function(err, oModule) {
|
||||
if (oModule) {
|
||||
oRes[id] = JSON.parse(oModule.functions);
|
||||
}
|
||||
if (--sem === 0) {
|
||||
return answReq();
|
||||
}
|
||||
});
|
||||
};
|
||||
})(this);
|
||||
fGetFunctions = function(id) {
|
||||
return dbMod.getModule(id, function(err, oModule) {
|
||||
if (oModule) {
|
||||
oRes[id] = JSON.parse(oModule.functions);
|
||||
}
|
||||
if (--sem === 0) {
|
||||
return answReq();
|
||||
}
|
||||
});
|
||||
};
|
||||
_results = [];
|
||||
for (_i = 0, _len = arrNames.length; _i < _len; _i++) {
|
||||
id = arrNames[_i];
|
||||
|
|
@ -191,46 +186,44 @@ Components Manager
|
|||
}
|
||||
};
|
||||
|
||||
forgeModule = (function(_this) {
|
||||
return function(user, oPayload, dbMod, callback) {
|
||||
var answ;
|
||||
answ = hasRequiredParams(['id', 'params', 'lang', 'data'], oPayload);
|
||||
if (answ.code !== 200) {
|
||||
return callback(answ);
|
||||
} else {
|
||||
return dbMod.getModule(oPayload.id, function(err, mod) {
|
||||
var src;
|
||||
if (mod) {
|
||||
answ.code = 409;
|
||||
answ.message = 'Module name already existing: ' + oPayload.id;
|
||||
return callback(answ);
|
||||
} else {
|
||||
src = oPayload.data;
|
||||
return dynmod.compileString(src, user.username, 'dummyRule', oPayload.id, oPayload.lang, null, function(cm) {
|
||||
var funcs, id, name, _ref;
|
||||
answ = cm.answ;
|
||||
if (answ.code === 200) {
|
||||
funcs = [];
|
||||
_ref = cm.module;
|
||||
for (name in _ref) {
|
||||
id = _ref[name];
|
||||
funcs.push(name);
|
||||
}
|
||||
_this.log.info("CM | Storing new module with functions " + (funcs.join()));
|
||||
answ.message = "Event Poller module successfully stored! Found following function(s): " + funcs;
|
||||
oPayload.functions = JSON.stringify(funcs);
|
||||
dbMod.storeModule(user.username, oPayload);
|
||||
if (oPayload["public"] === 'true') {
|
||||
dbMod.publish(oPayload.id);
|
||||
}
|
||||
forgeModule = function(user, oPayload, dbMod, callback) {
|
||||
var answ;
|
||||
answ = hasRequiredParams(['id', 'params', 'lang', 'data'], oPayload);
|
||||
if (answ.code !== 200) {
|
||||
return callback(answ);
|
||||
} else {
|
||||
return dbMod.getModule(oPayload.id, function(err, mod) {
|
||||
var src;
|
||||
if (mod) {
|
||||
answ.code = 409;
|
||||
answ.message = 'Module name already existing: ' + oPayload.id;
|
||||
return callback(answ);
|
||||
} else {
|
||||
src = oPayload.data;
|
||||
return dynmod.compileString(src, user.username, 'dummyRule', oPayload.id, oPayload.lang, null, function(cm) {
|
||||
var funcs, id, name, _ref;
|
||||
answ = cm.answ;
|
||||
if (answ.code === 200) {
|
||||
funcs = [];
|
||||
_ref = cm.module;
|
||||
for (name in _ref) {
|
||||
id = _ref[name];
|
||||
funcs.push(name);
|
||||
}
|
||||
return callback(answ);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
_this.log.info("CM | Storing new module with functions " + (funcs.join()));
|
||||
answ.message = "Event Poller module successfully stored! Found following function(s): " + funcs;
|
||||
oPayload.functions = JSON.stringify(funcs);
|
||||
dbMod.storeModule(user.username, oPayload);
|
||||
if (oPayload["public"] === 'true') {
|
||||
dbMod.publish(oPayload.id);
|
||||
}
|
||||
}
|
||||
return callback(answ);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
commandFunctions = {
|
||||
get_public_key: function(user, oPayload, callback) {
|
||||
200
js/config.js
200
js/config.js
|
|
@ -1,79 +1,145 @@
|
|||
'use strict';
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
/*
|
||||
|
||||
var path = require('path'),
|
||||
log = require('./logging'),
|
||||
config;
|
||||
Configuration
|
||||
=============
|
||||
> Loads the configuration file and acts as an interface to it.
|
||||
*/
|
||||
|
||||
exports = module.exports = function(args) {
|
||||
args = args || {};
|
||||
log(args);
|
||||
if(typeof args.relPath === 'string') loadConfigFile(args.relPath);
|
||||
return module.exports;
|
||||
};
|
||||
|
||||
loadConfigFile(path.join('config', 'config.json'));
|
||||
(function() {
|
||||
var exports, fetchProp, fs, loadConfigFile, path,
|
||||
_this = this;
|
||||
|
||||
function loadConfigFile(relPath) {
|
||||
try {
|
||||
config = JSON.parse(require('fs').readFileSync(path.resolve(__dirname, '..', relPath)));
|
||||
if(config && config.http_port && config.db_port
|
||||
&& config.crypto_key && config.session_secret) {
|
||||
log.print('CF', 'config file loaded successfully!');
|
||||
} else {
|
||||
log.error('CF', new Error('Missing property in config file, requires:\n'
|
||||
+ ' - http_port\n'
|
||||
+ ' - db_port\n'
|
||||
+ ' - crypto_key\n'
|
||||
+ ' - session_secret'));
|
||||
fs = require('fs');
|
||||
|
||||
path = require('path');
|
||||
|
||||
/*
|
||||
Module call
|
||||
-----------
|
||||
|
||||
Calling the module as a function will act as a constructor and load the config file.
|
||||
It is possible to hand an args object with the properties nolog (true if no outputs shall
|
||||
be generated) and configPath for a custom configuration file path.
|
||||
|
||||
@param {Object} args
|
||||
*/
|
||||
|
||||
|
||||
exports = module.exports = function(args) {
|
||||
args = args != null ? args : {};
|
||||
if (args.nolog) {
|
||||
_this.nolog = true;
|
||||
}
|
||||
} catch (e) {
|
||||
e.addInfo = 'no config ready';
|
||||
log.error('CF', e);
|
||||
}
|
||||
}
|
||||
if (args.configPath) {
|
||||
loadConfigFile(args.configPath);
|
||||
} else {
|
||||
loadConfigFile(path.join('config', 'system.json'));
|
||||
}
|
||||
return module.exports;
|
||||
};
|
||||
|
||||
/**
|
||||
* Answer true if the config file is ready, else false
|
||||
*/
|
||||
exports.isReady = function() {
|
||||
if(config) return true;
|
||||
else return false;
|
||||
};
|
||||
/*
|
||||
Tries to load a configuration file from the path relative to this module's parent folder.
|
||||
Reads the config file synchronously from the file system and try to parse it.
|
||||
|
||||
@private loadConfigFile
|
||||
@param {String} configPath
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fetch a property from the configuration
|
||||
* @param {String} prop
|
||||
*/
|
||||
function fetchProp(prop) {
|
||||
if(config) return config[prop];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTTP port
|
||||
*/
|
||||
exports.getHttpPort = function() {
|
||||
return fetchProp('http_port');
|
||||
};
|
||||
loadConfigFile = function(configPath) {
|
||||
var confProperties, e, prop, _i, _len;
|
||||
_this.config = null;
|
||||
confProperties = ['log', 'http-port', 'db-port'];
|
||||
try {
|
||||
_this.config = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', configPath)));
|
||||
_this.isReady = true;
|
||||
for (_i = 0, _len = confProperties.length; _i < _len; _i++) {
|
||||
prop = confProperties[_i];
|
||||
if (!_this.config[prop]) {
|
||||
_this.isReady = false;
|
||||
}
|
||||
}
|
||||
if (!_this.isReady && !_this.nolog) {
|
||||
return console.error("Missing property in config file, requires:\n" + (" - " + (confProperties.join("\n - "))));
|
||||
}
|
||||
} catch (_error) {
|
||||
e = _error;
|
||||
_this.isReady = false;
|
||||
if (!_this.nolog) {
|
||||
return console.error("Failed loading config file: " + e.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the DB port
|
||||
*/
|
||||
exports.getDBPort = function() {
|
||||
return fetchProp('db_port');
|
||||
};
|
||||
/*
|
||||
Fetch a property from the configuration
|
||||
|
||||
@private fetchProp( *prop* )
|
||||
@param {String} prop
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the crypto key
|
||||
*/
|
||||
exports.getCryptoKey = function() {
|
||||
return fetchProp('crypto_key');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the session secret
|
||||
*/
|
||||
exports.getSessionSecret = function() {
|
||||
return fetchProp('session_secret');
|
||||
};
|
||||
fetchProp = function(prop) {
|
||||
var _ref;
|
||||
return (_ref = _this.config) != null ? _ref[prop] : void 0;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
***Returns*** true if the config file is ready, else false
|
||||
|
||||
@public isReady()
|
||||
*/
|
||||
|
||||
|
||||
exports.isReady = function() {
|
||||
return _this.isReady;
|
||||
};
|
||||
|
||||
/*
|
||||
***Returns*** the HTTP port
|
||||
|
||||
@public getHttpPort()
|
||||
*/
|
||||
|
||||
|
||||
exports.getHttpPort = function() {
|
||||
return fetchProp('http-port');
|
||||
};
|
||||
|
||||
/*
|
||||
***Returns*** the DB port*
|
||||
|
||||
@public getDBPort()
|
||||
*/
|
||||
|
||||
|
||||
exports.getDbPort = function() {
|
||||
return fetchProp('db-port');
|
||||
};
|
||||
|
||||
/*
|
||||
***Returns*** the log conf object
|
||||
|
||||
@public getLogConf()
|
||||
*/
|
||||
|
||||
|
||||
exports.getLogConf = function() {
|
||||
return fetchProp('log');
|
||||
};
|
||||
|
||||
/*
|
||||
***Returns*** the crypto key
|
||||
|
||||
@public getCryptoKey()
|
||||
*/
|
||||
|
||||
|
||||
exports.getKeygenPassphrase = function() {
|
||||
return fetchProp('keygen-passphrase');
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
|
|
|
|||
|
|
@ -1,322 +0,0 @@
|
|||
/**
|
||||
* # DB Interface
|
||||
* Handles the connection to the database and provides functionalities for
|
||||
* event/action modules, rules and the encrypted storing of authentication tokens.
|
||||
*
|
||||
* ## General
|
||||
* General functionality as a wrapper for the module holds initialization,
|
||||
* encryption/decryption, the retrieval of modules and shut down.
|
||||
* Modules of the same group, e.g. action modules are registered in an unordered
|
||||
* set in the database, from where they can be retrieved again. For example a new
|
||||
* action module has its ID (e.g 'probinder') first registered in the set
|
||||
* 'action_modules' and then stored in the db with the key 'action\_module\_' + ID
|
||||
* (e.g. action\_module\_probinder).
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var redis = require('redis'),
|
||||
crypto = require('crypto'),
|
||||
log = require('./logging'),
|
||||
crypto_key, db;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the DB connection. Requires a valid configuration file which contains
|
||||
* a db port and a crypto key.
|
||||
*
|
||||
*/
|
||||
exports = module.exports = function(args) {
|
||||
args = args || {};
|
||||
log(args);
|
||||
|
||||
var config = require('./config')(args);
|
||||
crypto_key = config.getCryptoKey();
|
||||
db = redis.createClient(config.getDBPort(), 'localhost', { connect_timeout: 2000 });
|
||||
db.on("error", function (err) {
|
||||
err.addInfo = 'message from DB';
|
||||
log.error('DB', err);
|
||||
});
|
||||
return module.exports;
|
||||
};
|
||||
|
||||
exports.isConnected = function(cb) {
|
||||
if(db.connected) cb(null);
|
||||
else setTimeout(function() {
|
||||
if(db.connected) {
|
||||
log.print('DB', 'Successfully connected to DB!');
|
||||
cb(null);
|
||||
} else {
|
||||
var e = new Error('Connection to DB failed!');
|
||||
log.error('DB', e);
|
||||
cb(e);
|
||||
}
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
/**
|
||||
* ### encrypt
|
||||
* this is used to decrypt
|
||||
* @param {String} plainText
|
||||
*/
|
||||
function encrypt(plainText) {
|
||||
if(!plainText) return null;
|
||||
try {
|
||||
var enciph = crypto.createCipher('aes-256-cbc', crypto_key);
|
||||
var et = enciph.update(plainText, 'utf8', 'base64') + enciph.final('base64');
|
||||
log.print('DB', 'Encrypted credentials into: ' + et);
|
||||
return et;
|
||||
} catch (err) {
|
||||
log.error('DB', 'in encrypting: ' + err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ### decrypt
|
||||
*/
|
||||
function decrypt(crypticText, id) {
|
||||
if(!crypticText) return null;
|
||||
try {
|
||||
var deciph = crypto.createDecipher('aes-256-cbc', crypto_key);
|
||||
return deciph.update(crypticText, 'base64', 'utf8') + deciph.final('utf8');
|
||||
} catch (err) {
|
||||
log.error('DB', 'in decrypting "' + id + '": ' + err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ### replyHandler
|
||||
* Abstraction answer handling for simple information replies from the DB.
|
||||
* @param {String} action the action to be displayed in the output string.
|
||||
*/
|
||||
function replyHandler(action) {
|
||||
return function(err, reply) {
|
||||
if(err) log.error('DB', ' during "' + action + '": ' + err);
|
||||
else log.print('DB', action + ': ' + reply);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* ### getSetRecords
|
||||
* The general structure for modules is that the key is stored in a set.
|
||||
* By fetching all set entries we can then fetch all modules, which is
|
||||
* automated in this function.
|
||||
*
|
||||
* @param {String} set the set name how it is stored in the DB
|
||||
* @param {function} funcSingle the function that fetches single entries from the DB
|
||||
* @param {function} cb the function to be called on success or error, receives
|
||||
* arguments (err, obj)
|
||||
*/
|
||||
function getSetRecords(set, funcSingle, cb) {
|
||||
if(db) db.smembers(set, function(err, reply) {
|
||||
if(err) log.error('DB', 'fetching ' + set + ': ' + err);
|
||||
else {
|
||||
if(reply.length === 0) {
|
||||
cb(null, null);
|
||||
} else {
|
||||
var semaphore = reply.length, objReplies = {};
|
||||
setTimeout(function() {
|
||||
if(semaphore > 0) {
|
||||
cb('Timeout fetching ' + set, null);
|
||||
}
|
||||
}, 1000);
|
||||
for(var i = 0; i < reply.length; i++){
|
||||
funcSingle(reply[i], function(prop) {
|
||||
return function(err, reply) {
|
||||
if(err) log.error('DB', ' fetching single element: ' + prop);
|
||||
else {
|
||||
objReplies[prop] = reply;
|
||||
if(--semaphore === 0) cb(null, objReplies);
|
||||
}
|
||||
};
|
||||
}(reply[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// @method shutDown()
|
||||
|
||||
// Shuts down the db link.
|
||||
exports.shutDown = function() { if(db) db.quit(); };
|
||||
|
||||
|
||||
// ## Action Modules
|
||||
|
||||
/**
|
||||
* ### storeActionModule
|
||||
* Store a string representation of an action module in the DB.
|
||||
* @param {String} id the unique identifier of the module
|
||||
* @param {String} data the string representation
|
||||
*/
|
||||
exports.storeActionModule = function(id, data) {
|
||||
if(db) {
|
||||
db.sadd('action_modules', id, replyHandler('storing action module key ' + id));
|
||||
db.set('action_module_' + id, data, replyHandler('storing action module ' + id));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* ### getActionModule(id, cb)
|
||||
* Query the DB for an action module.
|
||||
* @param {String} id the module id
|
||||
* @param {function} cb the cb to receive the answer (err, obj)
|
||||
*/
|
||||
exports.getActionModule = function(id, cb) {
|
||||
if(cb && db) db.get('action_module_' + id, cb);
|
||||
};
|
||||
|
||||
/**
|
||||
* ### getActionModules(cb)
|
||||
* Fetch all action modules.
|
||||
* @param {function} cb the cb to receive the answer (err, obj)
|
||||
*/
|
||||
exports.getActionModules = function(cb) {
|
||||
getSetRecords('action_modules', exports.getActionModule, cb);
|
||||
};
|
||||
|
||||
/**
|
||||
* storeActionModuleAuth(id, data)
|
||||
* Store a string representation of the authentication parameters for an action module.
|
||||
* @param {String} id the unique identifier of the module
|
||||
* @param {String} data the string representation
|
||||
*/
|
||||
exports.storeActionModuleAuth = function(id, data) {
|
||||
if(data && db) {
|
||||
db.sadd('action_modules_auth', id, replyHandler('storing action module auth key ' + id));
|
||||
db.set('action_module_' + id +'_auth', encrypt(data), replyHandler('storing action module auth ' + id));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* ### getActionModuleAuth(id, cb)
|
||||
* Query the DB for an action module authentication token.
|
||||
* @param {String} id the module id
|
||||
* @param {function} cb the cb to receive the answer (err, obj)
|
||||
*/
|
||||
exports.getActionModuleAuth = function(id, cb) {
|
||||
if(cb && db) db.get('action_module_' + id + '_auth', function(id) {
|
||||
return function(err, txt) { cb(err, decrypt(txt, 'action_module_' + id + '_auth')); };
|
||||
}(id));
|
||||
};
|
||||
|
||||
// ## Event Modules
|
||||
|
||||
/**
|
||||
* ### storeEventModule(id, data)
|
||||
* Store a string representation of an event module in the DB.
|
||||
* @param {String} id the unique identifier of the module
|
||||
* @param {String} data the string representation
|
||||
*/
|
||||
exports.storeEventModule = function(id, data) {
|
||||
if(db) {
|
||||
db.sadd('event_modules', id, replyHandler('storing event module key ' + id));
|
||||
db.set('event_module_' + id, data, replyHandler('storing event module ' + id));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* ### getEventModule(id, cb)
|
||||
* Query the DB for an event module.
|
||||
* @param {String} id the module id
|
||||
* @param {function} cb the cb to receive the answer (err, obj)
|
||||
*/
|
||||
exports.getEventModule = function(id, cb) {
|
||||
if(cb && db) db.get('event_module_' + id, cb);
|
||||
};
|
||||
|
||||
/**
|
||||
* ### getEventModules(cb)
|
||||
* Fetch all event modules.
|
||||
* @param {function} cb the cb that receives the arguments (err, obj)
|
||||
*/
|
||||
exports.getEventModules = function(cb) {
|
||||
getSetRecords('event_modules', exports.getEventModule, cb);
|
||||
};
|
||||
|
||||
/**
|
||||
* ### storeEventModuleAuth(id, data)
|
||||
* Store a string representation of he authentication parameters for an event module.
|
||||
* @param {String} id the unique identifier of the module
|
||||
* @param {String} data the string representation
|
||||
*/
|
||||
exports.storeEventModuleAuth = function(id, data) {
|
||||
if(data && db) {
|
||||
db.sadd('event_modules_auth', id, replyHandler('storing event module auth key ' + id));
|
||||
db.set('event_module_' + id +'_auth', encrypt(data), replyHandler('storing event module auth ' + id));
|
||||
}
|
||||
};
|
||||
|
||||
// @method getEventModuleAuth(id, cb)
|
||||
|
||||
// Query the DB for an event module authentication token.
|
||||
// @param {String} id the module id
|
||||
// @param {function} cb the cb to receive the answer (err, obj)
|
||||
exports.getEventModuleAuth = function(id, cb) {
|
||||
if(cb) db.get('event_module_' + id +'_auth', function(id) {
|
||||
return function(err, txt) { cb(err, decrypt(txt, 'event_module_' + id + '_auth')); };
|
||||
}(id));
|
||||
};
|
||||
|
||||
// ## Rules
|
||||
|
||||
// @method storeRule(id, data)
|
||||
|
||||
// Store a string representation of a rule in the DB.
|
||||
// @param {String} id the unique identifier of the rule
|
||||
// @param {String} data the string representation
|
||||
exports.storeRule = function(id, data) {
|
||||
if(db) {
|
||||
db.sadd('rules', id, replyHandler('storing rule key ' + id));
|
||||
db.set('rule_' + id, data, replyHandler('storing rule ' + id));
|
||||
}
|
||||
};
|
||||
|
||||
// @method getRule(id, cb)
|
||||
|
||||
// Query the DB for a rule.
|
||||
// @param {String} id the rule id
|
||||
// @param {function} cb the cb to receive the answer (err, obj)
|
||||
exports.getRule = function(id, cb) {
|
||||
if(db) db.get('rule_' + id, cb);
|
||||
};
|
||||
|
||||
// @method getRules(cb)
|
||||
|
||||
// Fetch all rules from the database.
|
||||
// @param {function} cb
|
||||
exports.getRules = function(cb) {
|
||||
getSetRecords('rules', exports.getRule, cb);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} objUser
|
||||
* @param {function} cb
|
||||
*/
|
||||
exports.storeUser = function(objUser, cb) {
|
||||
if(db && objUser && objUser.username && objUser.password) {
|
||||
db.sadd('users', objUser.username, replyHandler('storing user key ' + objUser.username));
|
||||
objUser.password = encrypt(objUser.password);
|
||||
db.set('user:' + objUser.username, objUser, replyHandler('storing user properties ' + objUser.username));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks the credentials and on success returns the user object.
|
||||
* @param {Object} objUser
|
||||
* @param {function} cb
|
||||
*/
|
||||
exports.loginUser = function(username, password, cb) {
|
||||
if(typeof cb !== 'function') return;
|
||||
if(db) db.get('user:' + username, function(p) {
|
||||
return function(err, obj) {
|
||||
if(err) cb(err);
|
||||
else if(encrypt(obj.password) === p) cb(null, obj);
|
||||
else cb(new Error('Wrong credentials!'));
|
||||
};
|
||||
}(password));
|
||||
else cb(new Error('No database link available!'));
|
||||
};
|
||||
150
js/dynamic-modules.js
Normal file
150
js/dynamic-modules.js
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
// Generated by CoffeeScript 1.6.3
|
||||
/*
|
||||
|
||||
Dynamic Modules
|
||||
===============
|
||||
> Compiles CoffeeScript modules and loads JS modules in a VM, together
|
||||
> with only a few allowed node.js modules.
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
var cryptico, cs, db, exports, issueApiCall, needle, vm,
|
||||
_this = this;
|
||||
|
||||
db = require('./persistence');
|
||||
|
||||
vm = require('vm');
|
||||
|
||||
needle = require('needle');
|
||||
|
||||
cs = require('coffee-script');
|
||||
|
||||
cryptico = require('my-cryptico');
|
||||
|
||||
/*
|
||||
Module call
|
||||
-----------
|
||||
Initializes the dynamic module handler.
|
||||
|
||||
@param {Object} args
|
||||
*/
|
||||
|
||||
|
||||
exports = module.exports = function(args) {
|
||||
var numBits, passPhrase;
|
||||
_this.log = args.logger;
|
||||
if (!_this.strPublicKey && args['keygen']) {
|
||||
db(args);
|
||||
passPhrase = args['keygen'];
|
||||
numBits = 1024;
|
||||
_this.oPrivateRSAkey = cryptico.generateRSAKey(passPhrase, numBits);
|
||||
_this.strPublicKey = cryptico.publicKeyString(_this.oPrivateRSAkey);
|
||||
_this.log.info("DM | Public Key generated: " + _this.strPublicKey);
|
||||
}
|
||||
return module.exports;
|
||||
};
|
||||
|
||||
exports.getPublicKey = function() {
|
||||
return _this.strPublicKey;
|
||||
};
|
||||
|
||||
issueApiCall = function(method, url, credentials, cb) {
|
||||
var err, func;
|
||||
try {
|
||||
if (method === 'get') {
|
||||
func = needle.get;
|
||||
} else {
|
||||
func = needle.post;
|
||||
}
|
||||
return func(url, credentials, function(err, resp, body) {
|
||||
if (!err) {
|
||||
return cb(body);
|
||||
} else {
|
||||
return cb();
|
||||
}
|
||||
});
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
return _this.log.info('DM | Error even before calling!');
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Try to run a JS module from a string, together with the
|
||||
given parameters. If it is written in CoffeeScript we
|
||||
compile it first into JS.
|
||||
|
||||
@public compileString ( *src, id, params, lang* )
|
||||
@param {String} src
|
||||
@param {String} id
|
||||
@param {Object} params
|
||||
@param {String} lang
|
||||
*/
|
||||
|
||||
|
||||
exports.compileString = function(src, userId, ruleId, modId, lang, dbMod, cb) {
|
||||
var answ, err, fTryToLoad, logFunction;
|
||||
answ = {
|
||||
code: 200,
|
||||
message: 'Successfully compiled'
|
||||
};
|
||||
if (lang === 'CoffeeScript') {
|
||||
try {
|
||||
src = cs.compile(src);
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
answ.code = 400;
|
||||
answ.message = 'Compilation of CoffeeScript failed at line ' + err.location.first_line;
|
||||
}
|
||||
}
|
||||
logFunction = function(uId, rId, mId) {
|
||||
return function(msg) {
|
||||
return db.appendLog(uId, rId, mId, msg);
|
||||
};
|
||||
};
|
||||
db.resetLog(userId, ruleId);
|
||||
fTryToLoad = function(params) {
|
||||
var oDecrypted, sandbox;
|
||||
if (params) {
|
||||
try {
|
||||
oDecrypted = cryptico.decrypt(params, _this.oPrivateRSAkey);
|
||||
params = JSON.parse(oDecrypted.plaintext);
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
_this.log.warn("DM | Error during parsing of user defined params for " + userId + ", " + ruleId + ", " + modId);
|
||||
params = {};
|
||||
}
|
||||
} else {
|
||||
params = {};
|
||||
}
|
||||
sandbox = {
|
||||
id: userId + '.' + modId + '.vm',
|
||||
params: params,
|
||||
apicall: issueApiCall,
|
||||
log: logFunction(userId, ruleId, modId),
|
||||
exports: {}
|
||||
};
|
||||
try {
|
||||
vm.runInNewContext(src, sandbox, sandbox.id);
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
console.log(err);
|
||||
answ.code = 400;
|
||||
answ.message = 'Loading Module failed: ' + err.message;
|
||||
}
|
||||
return cb({
|
||||
answ: answ,
|
||||
module: sandbox.exports
|
||||
});
|
||||
};
|
||||
if (dbMod) {
|
||||
return dbMod.getUserParams(modId, userId, function(err, obj) {
|
||||
return fTryToLoad(obj);
|
||||
});
|
||||
} else {
|
||||
return fTryToLoad();
|
||||
}
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
511
js/engine.js
511
js/engine.js
|
|
@ -1,245 +1,288 @@
|
|||
'use strict';
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
/*
|
||||
|
||||
var path = require('path'),
|
||||
regex = /\$X\.[\w\.\[\]]*/g, // find properties of $X
|
||||
listRules = {},
|
||||
listActionModules = {},
|
||||
isRunning = true,
|
||||
dynmod = require('./dynamic-modules'),
|
||||
db = require('./persistence'), log;
|
||||
|
||||
exports = module.exports = function( args ) {
|
||||
log = args.logger;
|
||||
db( args);
|
||||
dynmod(args);
|
||||
pollQueue();
|
||||
return module.exports;
|
||||
};
|
||||
|
||||
var updateActionModules = function() {
|
||||
for ( var user in listRules ) {
|
||||
if(!listActionModules[user]) listActionModules[user] = {};
|
||||
for ( var rule in listRules[user] ) {
|
||||
var actions = listRules[user][rule].actions;
|
||||
console.log(actions);
|
||||
for ( var module in actions ){
|
||||
for ( var i = 0; i < actions[module]['functions'].length; i++ ){
|
||||
db.actionInvokers.getModule(module, function( err, objAM ){
|
||||
db.actionInvokers.getUserParams(module, user, function( err, objParams ) {
|
||||
console.log (objAM);
|
||||
|
||||
//FIXME am name is called 'actions'???
|
||||
// if(objParams) { //TODO we don't need them for all modules
|
||||
var answ = dynmod.compileString(objAM.code, objAM.actions + "_" + user, objParams, objAM.lang);
|
||||
console.log('answ');
|
||||
console.log(answ);
|
||||
listActionModules[user][module] = answ.module;
|
||||
console.log('loaded ' + user + ': ' + module);
|
||||
console.log(listActionModules);
|
||||
// }
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.internalEvent = function( evt ) {
|
||||
try {
|
||||
// TODO do we need to determine init from newRule?
|
||||
console.log (evt);
|
||||
// console.log (data);
|
||||
// var obj = JSON.parse( data );
|
||||
// db.getRuleActivatedUsers(obj.id, function ( err, arrUsers ) {
|
||||
// console.log (arrUsers);
|
||||
// for(var i = 0; i < arrUsers.length; i++) {
|
||||
// if( !listRules[arrUsers[i]]) listRules[arrUsers[i]] = {};
|
||||
// listRules[arrUsers[i]][obj.id] = obj;
|
||||
// updateActionModules();
|
||||
// }
|
||||
// });
|
||||
} catch( err ) {
|
||||
console.log( err );
|
||||
}
|
||||
console.log('internal event handled');
|
||||
};
|
||||
Engine
|
||||
==================
|
||||
> The heart of the WebAPI ECA System. The engine loads action invoker modules
|
||||
> corresponding to active rules actions and invokes them if an appropriate event
|
||||
> is retrieved.
|
||||
*/
|
||||
|
||||
|
||||
function pollQueue() {
|
||||
if(isRunning) {
|
||||
db.popEvent(function (err, obj) {
|
||||
if(!err && obj) {
|
||||
processEvent(obj);
|
||||
}
|
||||
setTimeout(pollQueue, 50); //TODO adapt to load
|
||||
});
|
||||
}
|
||||
}
|
||||
(function() {
|
||||
var db, dynmod, exports, isRunning, jsonQuery, listUserRules, pollQueue, processEvent, updateActionModules, validConditions,
|
||||
_this = this;
|
||||
|
||||
/**
|
||||
* Handles correctly posted events
|
||||
* @param {Object} evt The event object
|
||||
*/
|
||||
function processEvent(evt) {
|
||||
log.info('EN', 'processing event: ' + evt.event + '(' + evt.eventid + ')');
|
||||
var actions = checkEvent(evt);
|
||||
console.log('found actions to invoke:');
|
||||
console.log(actions);
|
||||
for(var user in actions) {
|
||||
for(var module in actions[user]) {
|
||||
for(var i = 0; i < actions[user][module]['functions'].length; i++) {
|
||||
var act = {
|
||||
module: module,
|
||||
function: actions[user][module]['functions'][i]
|
||||
}
|
||||
invokeAction(evt, user, act);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
db = require('./persistence');
|
||||
|
||||
/**
|
||||
FIXME merge with processEvent
|
||||
dynmod = require('./dynamic-modules');
|
||||
|
||||
* Check an event against the rules repository and return the actions
|
||||
* if the conditons are met.
|
||||
* @param {Object} evt the event to check
|
||||
*/
|
||||
function checkEvent(evt) {
|
||||
var actions = {}, tEvt;
|
||||
for(var user in listRules) {
|
||||
actions[user] = {};
|
||||
for(var rule in listRules[user]) {
|
||||
//TODO this needs to get depth safe, not only data but eventually also
|
||||
// on one level above (eventid and other meta)
|
||||
tEvt = listRules[user][rule].event;
|
||||
if(tEvt.module + ' -> ' + tEvt.function === evt.event && validConditions(evt.payload, listRules[user][rule])) {
|
||||
log.info('EN', 'Rule "' + rule + '" fired');
|
||||
var oAct = listRules[user][rule].actions;
|
||||
console.log (oAct);
|
||||
for(var module in oAct) {
|
||||
if(!actions[user][module]) {
|
||||
actions[user][module] = {
|
||||
functions: []
|
||||
};
|
||||
}
|
||||
for(var i = 0; i < oAct[module]['functions'].length; i++ ){
|
||||
console.log ('processing action ' + i + ', ' + oAct[module]['functions'][i]);
|
||||
actions[user][module]['functions'].push(oAct[module]['functions'][i]);
|
||||
// if(actions[user].indexOf(arrAct[i]) === -1) actions[user].push(arrAct[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
jsonQuery = require('js-select');
|
||||
|
||||
// {
|
||||
// "event": "emailyak -> newMail",
|
||||
// "payload": {
|
||||
// "TextBody": "hello"
|
||||
// }
|
||||
// }
|
||||
|
||||
// exports.sendMail = ( args ) ->
|
||||
// url = 'https://api.emailyak.com/v1/ps1g59ndfcwg10w/json/send/email/'
|
||||
/*
|
||||
This is ging to have a structure like:
|
||||
An object of users with their active rules and the required action modules
|
||||
"user-1":
|
||||
"rule-1":
|
||||
"rule": oRule-1
|
||||
"actions":
|
||||
"action-1": oAction-1
|
||||
"action-2": oAction-2
|
||||
"rule-2":
|
||||
"rule": oRule-2
|
||||
"actions":
|
||||
"action-1": oAction-1
|
||||
"user-2":
|
||||
"rule-3":
|
||||
"rule": oRule-3
|
||||
"actions":
|
||||
"action-3": oAction-3
|
||||
*/
|
||||
|
||||
// data =
|
||||
// FromAddress: 'tester@mscliveweb.simpleyak.com'
|
||||
// ToAddress: 'dominic.bosch.db@gmail.com'
|
||||
// TextBody: 'test'
|
||||
|
||||
// needle.post url, JSON.stringify( data ), {json: true}, ( err, resp, body ) ->
|
||||
// log err
|
||||
// log body
|
||||
/**
|
||||
* Checks whether all conditions of the rule are met by the event.
|
||||
* @param {Object} evt the event to check
|
||||
* @param {Object} rule the rule with its conditions
|
||||
*/
|
||||
function validConditions(evt, rule) {
|
||||
for(var property in rule.conditions){
|
||||
if(!evt[property] || evt[property] != rule.condition[property]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
listUserRules = {};
|
||||
|
||||
/**
|
||||
* Invoke an action according to its type.
|
||||
* @param {Object} evt The event that invoked the action
|
||||
* @param {Object} action The action to be invoked
|
||||
*/
|
||||
function invokeAction( evt, user, action ) {
|
||||
console.log('invoking action');
|
||||
var actionargs = {};
|
||||
//FIXME internal events, such as loopback ha sno arrow
|
||||
//TODO this requires change. the module property will be the identifier
|
||||
// in the actions object (or shall we allow several times the same action?)
|
||||
console.log(action.module);
|
||||
console.log(listActionModules);
|
||||
var srvc = listActionModules[user][action.module];
|
||||
console.log(srvc);
|
||||
if(srvc && srvc[action.function]) {
|
||||
//FIXME preprocessing not only on data
|
||||
//FIXME no preprocessing at all, why don't we just pass the whole event to the action?'
|
||||
// preprocessActionArguments(evt.payload, action.arguments, actionargs);
|
||||
try {
|
||||
if(srvc[action.function]) srvc[action.function](evt.payload);
|
||||
} catch(err) {
|
||||
log.error('EN', 'during action execution: ' + err);
|
||||
}
|
||||
}
|
||||
else log.info('EN', 'No api interface found for: ' + action.module);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Action properties may contain event properties which need to be resolved beforehand.
|
||||
// * @param {Object} evt The event whose property values can be used in the rules action
|
||||
// * @param {Object} act The rules action arguments
|
||||
// * @param {Object} res The object to be used to enter the new properties
|
||||
// */
|
||||
// function preprocessActionArguments(evt, act, res) {
|
||||
// for(var prop in act) {
|
||||
// /*
|
||||
// * If the property is an object itself we go into recursion
|
||||
// */
|
||||
// if(typeof act[prop] === 'object') {
|
||||
// res[prop] = {};
|
||||
// preprocessActionArguments(evt, act[prop], res[prop]);
|
||||
// }
|
||||
// else {
|
||||
// var txt = act[prop];
|
||||
// var arr = txt.match(regex);
|
||||
|
||||
// * If rules action property holds event properties we resolve them and
|
||||
// * replace the original action property
|
||||
|
||||
// // console.log(evt);
|
||||
// if(arr) {
|
||||
// for(var i = 0; i < arr.length; i++) {
|
||||
// /*
|
||||
// * The first three characters are '$X.', followed by the property
|
||||
// */
|
||||
// var actionProp = arr[i].substring(3).toLowerCase();
|
||||
// // console.log(actionProp);
|
||||
// for(var eprop in evt) {
|
||||
// // our rules language doesn't care about upper or lower case
|
||||
// if(eprop.toLowerCase() === actionProp) {
|
||||
// txt = txt.replace(arr[i], evt[eprop]);
|
||||
// }
|
||||
// }
|
||||
// txt = txt.replace(arr[i], '[property not available]');
|
||||
// }
|
||||
// }
|
||||
// res[prop] = txt;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
exports.shutDown = function() {
|
||||
if(log) log.info('EN', 'Shutting down Poller and DB Link');
|
||||
isRunning = false;
|
||||
if(db) db.shutDown();
|
||||
};
|
||||
|
||||
/*
|
||||
Module call
|
||||
-----------
|
||||
Initializes the Engine and starts polling the event queue for new events.
|
||||
|
||||
@param {Object} args
|
||||
*/
|
||||
|
||||
|
||||
exports = module.exports = function(args) {
|
||||
if (!isRunning) {
|
||||
isRunning = true;
|
||||
_this.log = args.logger;
|
||||
db(args);
|
||||
dynmod(args);
|
||||
setTimeout(pollQueue, 10);
|
||||
return module.exports;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
This is a helper function for the unit tests so we can verify that action
|
||||
modules are loaded correctly
|
||||
#TODO we should change this to functions returning true or false rather than returning
|
||||
#the whole list
|
||||
@public getListUserRules ()
|
||||
*/
|
||||
|
||||
|
||||
exports.getListUserRules = function() {
|
||||
return listUserRules;
|
||||
};
|
||||
|
||||
/*
|
||||
An event associated to rules happened and is captured here. Such events
|
||||
are basically CRUD on rules.
|
||||
|
||||
@public internalEvent ( *evt* )
|
||||
@param {Object} evt
|
||||
*/
|
||||
|
||||
|
||||
exports.internalEvent = function(evt) {
|
||||
var oRule, oUser;
|
||||
if (!listUserRules[evt.user] && evt.event !== 'del') {
|
||||
listUserRules[evt.user] = {};
|
||||
}
|
||||
oUser = listUserRules[evt.user];
|
||||
oRule = evt.rule;
|
||||
if (evt.event === 'new' || (evt.event === 'init' && !oUser[oRule.id])) {
|
||||
oUser[oRule.id] = {
|
||||
rule: oRule,
|
||||
actions: {}
|
||||
};
|
||||
updateActionModules(oRule.id);
|
||||
}
|
||||
if (evt.event === 'del' && oUser) {
|
||||
delete oUser[evt.ruleId];
|
||||
}
|
||||
if (JSON.stringify(oUser) === "{}") {
|
||||
return delete listUserRules[evt.user];
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
As soon as changes were made to the rule set we need to ensure that the aprropriate action
|
||||
invoker modules are loaded, updated or deleted.
|
||||
|
||||
@private updateActionModules ( *updatedRuleId* )
|
||||
@param {Object} updatedRuleId
|
||||
*/
|
||||
|
||||
|
||||
updateActionModules = function(updatedRuleId) {
|
||||
var fAddRequired, fRemoveNotRequired, name, oUser, userName, _results;
|
||||
fRemoveNotRequired = function(oUser) {
|
||||
var action, fRequired, _results;
|
||||
fRequired = function(actionName) {
|
||||
var action, _i, _len, _ref;
|
||||
_ref = oUser[updatedRuleId].rule.actions;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
action = _ref[_i];
|
||||
if ((action.split(' -> '))[0] === actionName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
_results = [];
|
||||
for (action in oUser[updatedRuleId].rule.actions) {
|
||||
if (!fRequired(action)) {
|
||||
_results.push(delete oUser[updatedRuleId].actions[action]);
|
||||
} else {
|
||||
_results.push(void 0);
|
||||
}
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
for (name in listUserRules) {
|
||||
oUser = listUserRules[name];
|
||||
fRemoveNotRequired(oUser);
|
||||
}
|
||||
fAddRequired = function(userName, oUser) {
|
||||
var fCheckRules, nmRl, oRl, _results;
|
||||
fCheckRules = function(oMyRule) {
|
||||
var action, fAddIfNewOrNotExisting, _i, _len, _ref, _results;
|
||||
fAddIfNewOrNotExisting = function(actionName) {
|
||||
var moduleName;
|
||||
moduleName = (actionName.split(' -> '))[0];
|
||||
if (!oMyRule.actions[moduleName] || oMyRule.rule.id === updatedRuleId) {
|
||||
return db.actionInvokers.getModule(moduleName, function(err, obj) {
|
||||
return dynmod.compileString(obj.data, userName, oMyRule.rule.id, moduleName, obj.lang, db.actionInvokers, function(result) {
|
||||
if (!result.answ === 200) {
|
||||
this.log.error("EN | Compilation of code failed! " + userName + ", " + oMyRule.rule.id + ", " + moduleName);
|
||||
}
|
||||
return oMyRule.actions[moduleName] = result.module;
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
_ref = oMyRule.rule.actions;
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
action = _ref[_i];
|
||||
_results.push(fAddIfNewOrNotExisting(action));
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
_results = [];
|
||||
for (nmRl in oUser) {
|
||||
oRl = oUser[nmRl];
|
||||
_results.push(fCheckRules(oRl));
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
_results = [];
|
||||
for (userName in listUserRules) {
|
||||
oUser = listUserRules[userName];
|
||||
_results.push(fAddRequired(userName, oUser));
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
|
||||
pollQueue = function() {
|
||||
if (isRunning) {
|
||||
return db.popEvent(function(err, obj) {
|
||||
if (!err && obj) {
|
||||
processEvent(obj);
|
||||
}
|
||||
return setTimeout(pollQueue, 50);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Checks whether all conditions of the rule are met by the event.
|
||||
|
||||
@private validConditions ( *evt, rule* )
|
||||
@param {Object} evt
|
||||
@param {Object} rule
|
||||
*/
|
||||
|
||||
|
||||
validConditions = function(evt, rule) {
|
||||
var prop, _i, _len, _ref;
|
||||
if (rule.conditions.length === 0) {
|
||||
return true;
|
||||
}
|
||||
_ref = rule.conditions;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
prop = _ref[_i];
|
||||
if (jsonQuery(evt, prop).nodes().length === 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/*
|
||||
Handles retrieved events.
|
||||
|
||||
@private processEvent ( *evt* )
|
||||
@param {Object} evt
|
||||
*/
|
||||
|
||||
|
||||
processEvent = function(evt) {
|
||||
var action, arr, fSearchAndInvokeAction, oMyRule, oUser, ruleName, userName, _results;
|
||||
fSearchAndInvokeAction = function(node, arrPath, evt, depth) {
|
||||
var err;
|
||||
if (!node) {
|
||||
this.log.error("EN | Didn't find property in user rule list: " + arrPath.join(', ' + " at depth " + depth));
|
||||
return;
|
||||
}
|
||||
if (depth === arrPath.length) {
|
||||
try {
|
||||
return node(evt.payload);
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
return this.log.info("EN | ERROR IN ACTION INVOKER: " + err.message);
|
||||
}
|
||||
} else {
|
||||
return fSearchAndInvokeAction(node[arrPath[depth]], arrPath, evt, depth + 1);
|
||||
}
|
||||
};
|
||||
_this.log.info('EN | processing event: ' + evt.event + '(' + evt.eventid + ')');
|
||||
_results = [];
|
||||
for (userName in listUserRules) {
|
||||
oUser = listUserRules[userName];
|
||||
_results.push((function() {
|
||||
var _results1;
|
||||
_results1 = [];
|
||||
for (ruleName in oUser) {
|
||||
oMyRule = oUser[ruleName];
|
||||
if (evt.event === oMyRule.rule.event && validConditions(evt, oMyRule.rule)) {
|
||||
this.log.info('EN | EVENT FIRED: ' + evt.event + '(' + evt.eventid + ') for rule ' + ruleName);
|
||||
_results1.push((function() {
|
||||
var _i, _len, _ref, _results2;
|
||||
_ref = oMyRule.rule.actions;
|
||||
_results2 = [];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
action = _ref[_i];
|
||||
arr = action.split(' -> ');
|
||||
_results2.push(fSearchAndInvokeAction(listUserRules, [userName, ruleName, 'actions', arr[0], arr[1]], evt, 0));
|
||||
}
|
||||
return _results2;
|
||||
})());
|
||||
} else {
|
||||
_results1.push(void 0);
|
||||
}
|
||||
}
|
||||
return _results1;
|
||||
}).call(_this));
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
|
||||
exports.shutDown = function() {
|
||||
return isRunning = false;
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
// Generated by CoffeeScript 1.7.1
|
||||
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
/*
|
||||
|
||||
Dynamic Modules
|
||||
===============
|
||||
> Compiles CoffeeScript modules and loads JS modules in a VM, together
|
||||
> with only a few allowed node.js modules.
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
var db, dynmod, fLoadModule, isRunning, listUserModules, log, logconf, logger, pollLoop;
|
||||
|
|
@ -77,7 +77,7 @@ Dynamic Modules
|
|||
} else {
|
||||
return dynmod.compileString(obj.data, msg.user, msg.rule.id, arrName[0], obj.lang, db.eventPollers, function(result) {
|
||||
if (!result.answ === 200) {
|
||||
log.error("EP | Compilation of code failed! " + msg.user + ", " + msg.rule.id + ", " + arrName[0]);
|
||||
log.error("EP | Compilation of code failed! " + msg.user + ", " + msg.rule.id + ", " + arrName[0]);
|
||||
}
|
||||
if (!listUserModules[msg.user]) {
|
||||
listUserModules[msg.user] = {};
|
||||
|
|
@ -87,7 +87,7 @@ Dynamic Modules
|
|||
pollfunc: arrName[1],
|
||||
module: result.module
|
||||
};
|
||||
return log.info("EP | New event module loaded! " + msg.user + ", " + msg.rule.id + ", " + arrName[0]);
|
||||
return log.info("EP | New event module loaded! " + msg.user + ", " + msg.rule.id + ", " + arrName[0]);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -97,12 +97,12 @@ Dynamic Modules
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
This function will loop infinitely every 10 seconds until isRunning is set to false
|
||||
|
||||
@private pollLoop()
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
pollLoop = function() {
|
||||
var err, fPoll, fRegisterModuleReference, myRule, oRules, ruleName, userName;
|
||||
95
js/http-listener.js
Normal file
95
js/http-listener.js
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// Generated by CoffeeScript 1.6.3
|
||||
/*
|
||||
|
||||
HTTP Listener
|
||||
=============
|
||||
> Receives the HTTP requests to the server at the given port. The requests
|
||||
> (bound to a method) are then redirected to the appropriate handler which
|
||||
> takes care of the request.
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
var app, exports, express, initRouting, path, qs, requestHandler,
|
||||
_this = this;
|
||||
|
||||
requestHandler = require('./request-handler');
|
||||
|
||||
path = require('path');
|
||||
|
||||
qs = require('querystring');
|
||||
|
||||
express = require('express');
|
||||
|
||||
app = express();
|
||||
|
||||
/*
|
||||
Module call
|
||||
-----------
|
||||
Initializes the HTTP listener and its request handler.
|
||||
|
||||
@param {Object} args
|
||||
*/
|
||||
|
||||
|
||||
exports = module.exports = function(args) {
|
||||
_this.log = args.logger;
|
||||
_this.shutDownSystem = args['shutdown-function'];
|
||||
requestHandler(args);
|
||||
initRouting(args['http-port']);
|
||||
return module.exports;
|
||||
};
|
||||
|
||||
/*
|
||||
Initializes the request routing and starts listening on the given port.
|
||||
|
||||
@param {int} port
|
||||
@private initRouting( *fShutDown* )
|
||||
*/
|
||||
|
||||
|
||||
initRouting = function(port) {
|
||||
var server, sess_sec;
|
||||
app.use(express.cookieParser());
|
||||
sess_sec = "149u*y8C:@kmN/520Gt\\v'+KFBnQ!\\r<>5X/xRI`sT<Iw";
|
||||
app.use(express.session({
|
||||
secret: sess_sec
|
||||
}));
|
||||
_this.log.info('HL | no session backbone');
|
||||
app.use('/', express["static"](path.resolve(__dirname, '..', 'webpages', 'public')));
|
||||
app.get('/admin', requestHandler.handleAdmin);
|
||||
app.get('/forge', requestHandler.handleForge);
|
||||
app.post('/event', requestHandler.handleEvent);
|
||||
app.post('/login', requestHandler.handleLogin);
|
||||
app.post('/logout', requestHandler.handleLogout);
|
||||
app.post('/usercommand', requestHandler.handleUserCommand);
|
||||
app.post('/admincommand', requestHandler.handleAdminCommand);
|
||||
server = app.listen(parseInt(port) || 8111);
|
||||
server.on('listening', function() {
|
||||
var addr;
|
||||
addr = server.address();
|
||||
if (addr.port !== port) {
|
||||
return _this.shutDownSystem();
|
||||
}
|
||||
});
|
||||
return server.on('error', function(err) {
|
||||
/*
|
||||
Error handling of the express port listener requires special attention,
|
||||
thus we have to catch the error, which is issued if the port is already in use.
|
||||
*/
|
||||
|
||||
switch (err.errno) {
|
||||
case 'EADDRINUSE':
|
||||
_this.log.error(err, 'HL | http-port already in use, shutting down!');
|
||||
break;
|
||||
case 'EACCES':
|
||||
_this.log.error(err, 'HL | http-port not accessible, shutting down!');
|
||||
break;
|
||||
default:
|
||||
_this.log.error(err, 'HL | Error in server, shutting down!');
|
||||
}
|
||||
return _this.shutDownSystem();
|
||||
});
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
// HTTP Listener
|
||||
// =============
|
||||
//
|
||||
// Handles the HTTP requests to the server at the port specified by the [config](config.html) file.
|
||||
|
||||
'use strict';
|
||||
|
||||
var path = require('path'),
|
||||
express = require('express'),
|
||||
app = express(),
|
||||
RedisStore = require('connect-redis')(express),
|
||||
qs = require('querystring'),
|
||||
log = require('./logging'),
|
||||
sess_sec = '#C[>;j`@".TXm2TA;A2Tg)',
|
||||
db_port, http_port, server,
|
||||
eventHandler, userHandler;
|
||||
|
||||
/*
|
||||
* The module needs to be called as a function to initialize it.
|
||||
* After that it fetches the http\_port, db\_port & sess\_sec properties
|
||||
* from the configuration file.
|
||||
*/
|
||||
exports = module.exports = function(args) {
|
||||
args = args || {};
|
||||
log(args);
|
||||
var config = require('./config')(args);
|
||||
userHandler = require('./user_handler')(args);
|
||||
db_port = config.getDBPort(),
|
||||
sess_sec = config.getSessionSecret(),
|
||||
http_port = config.getHttpPort();
|
||||
return module.exports;
|
||||
};
|
||||
|
||||
exports.addHandlers = function(funcAdminHandler, funcEvtHandler) {
|
||||
if(!funcAdminHandler || !funcEvtHandler) {
|
||||
log.error('HL', 'ERROR: either adminHandler or eventHandler function not defined!');
|
||||
return;
|
||||
}
|
||||
userHandler.addHandler(funcAdminHandler);
|
||||
eventHandler = funcEvtHandler;
|
||||
// Add cookie support for session handling.
|
||||
app.use(express.cookieParser());
|
||||
app.use(express.session({secret: sess_sec}));
|
||||
log.print('HL', 'no session backbone');
|
||||
|
||||
// ^ TODO figure out why redis backbone doesn't work. eventually the db pass has to be set in the DB?
|
||||
// } session information seems to be stored in DB but not retrieved correctly
|
||||
// } if(db_port) {
|
||||
// } app.use(express.session({
|
||||
// } store: new RedisStore({
|
||||
// } host: 'localhost',
|
||||
// } port: db_port,
|
||||
// } db: 2
|
||||
// } ,
|
||||
// } pass: null
|
||||
// } }),
|
||||
// } secret: sess_sec
|
||||
// } }));
|
||||
// } log.print('HL', 'Added redis DB as session backbone');
|
||||
// } } else {
|
||||
// } app.use(express.session({secret: sess_sec}));
|
||||
// } log.print('HL', 'no session backbone');
|
||||
// } }
|
||||
|
||||
// Redirect the requests to the appropriate handler.
|
||||
app.use('/', express.static(path.resolve(__dirname, '..', 'webpages')));
|
||||
// app.use('/doc/', express.static(path.resolve(__dirname, '..', 'webpages', 'doc')));
|
||||
// app.get('/mobile', userHandler.handleRequest);
|
||||
app.get('/rulesforge', userHandler.handleRequest);
|
||||
// app.use('/mobile', express.static(path.resolve(__dirname, '..', 'webpages', 'mobile')));
|
||||
// } app.use('/rulesforge/', express.static(path.resolve(__dirname, '..', 'webpages', 'rulesforge')));
|
||||
app.get('/admin', userHandler.handleRequest);
|
||||
app.post('/login', userHandler.handleLogin);
|
||||
app.post('/push_event', onPushEvent);
|
||||
try {
|
||||
if(http_port) server = app.listen(http_port); // inbound event channel
|
||||
else log.error('HL', new Error('No HTTP port found!? Nothing to listen on!...'));
|
||||
} catch(e) {
|
||||
e.addInfo = 'port unavailable';
|
||||
log.error(e);
|
||||
funcAdminHandler({cmd: 'shutdown'});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* If a post request reaches the server, this function handles it and treats the request as a possible event.
|
||||
*/
|
||||
function onPushEvent(req, resp) {
|
||||
var body = '';
|
||||
req.on('data', function (data) { body += data; });
|
||||
req.on('end', function () {
|
||||
var obj = qs.parse(body);
|
||||
/* If required event properties are present we process the event */
|
||||
if(obj && obj.event && obj.eventid){
|
||||
resp.writeHead(200, { "Content-Type": "text/plain" });
|
||||
resp.write('Thank you for the event (' + obj.event + '[' + obj.eventid + '])!');
|
||||
eventHandler(obj);
|
||||
} else {
|
||||
resp.writeHead(400, { "Content-Type": "text/plain" });
|
||||
resp.write('Your event was missing important parameters!');
|
||||
}
|
||||
resp.end();
|
||||
});
|
||||
}
|
||||
|
||||
exports.loadUsers = function() {
|
||||
var users = JSON.parse(require('fs').readFileSync(path.resolve(__dirname, '..', relPath)));
|
||||
for(var name in users) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
exports.shutDown = function() {
|
||||
log.print('HL', 'Shutting down HTTP listener');
|
||||
process.exit(); // This is a bit brute force...
|
||||
};
|
||||
165
js/logging.js
165
js/logging.js
|
|
@ -1,107 +1,72 @@
|
|||
/*
|
||||
* Logging
|
||||
* =======
|
||||
* Functions to handle logging and errors.
|
||||
*
|
||||
* Valid log types are:
|
||||
*
|
||||
* - 0 standard I/O
|
||||
* - 1 file
|
||||
* - 2 silent
|
||||
*/
|
||||
//TODO dynamic log file names (especially to track unit test logs)
|
||||
var fs = require('fs'),
|
||||
wst = require('winston'),
|
||||
logTypes = [ flushToConsole, flushToFile, null],
|
||||
logFile = require('path').resolve(__dirname, '..', 'server.log'),
|
||||
logType = 0;
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
(function() {
|
||||
var bunyan, fs, path,
|
||||
_this = this;
|
||||
|
||||
exports = module.exports = function(args) {
|
||||
args = args || {};
|
||||
if(args.logType) logType = parseInt(args.logType) || 0;
|
||||
if(logType == 1) fs.truncateSync(logFile, 0);
|
||||
if(logType > logTypes.length - 1) logType = 0;
|
||||
// winston.add(winston.transports.File, { filename: 'somefile.log' });
|
||||
// winston.remove(winston.transports.Console);
|
||||
return module.exports;
|
||||
};
|
||||
fs = require('fs');
|
||||
|
||||
exports.getLogType = function() { return logType; };
|
||||
path = require('path');
|
||||
|
||||
function flush(err, msg) {
|
||||
if(typeof logTypes[logType] === 'function') logTypes[logType](err, msg);
|
||||
}
|
||||
bunyan = require('bunyan');
|
||||
|
||||
function flushToConsole(err, msg) {
|
||||
if(err) console.error("\033[31m" + msg + "\033[0m");
|
||||
else console.log(msg);
|
||||
// if(err) console.error(msg);
|
||||
// else console.log(msg);
|
||||
}
|
||||
/*
|
||||
Returns a bunyan logger according to the given arguments.
|
||||
|
||||
@public getLogger( *args* )
|
||||
@param {Object} args
|
||||
*/
|
||||
|
||||
function flushToFile(err, msg) {
|
||||
fs.appendFile(logFile, msg + '\n', function (err) {});
|
||||
}
|
||||
|
||||
// @function print(module, msg)
|
||||
exports.getLogger = function(args) {
|
||||
var e, emptylog, opt;
|
||||
emptylog = {
|
||||
trace: function() {},
|
||||
debug: function() {},
|
||||
info: function() {},
|
||||
warn: function() {},
|
||||
error: function() {},
|
||||
fatal: function() {}
|
||||
};
|
||||
args = args != null ? args : {};
|
||||
if (args.nolog) {
|
||||
return emptylog;
|
||||
} else {
|
||||
try {
|
||||
opt = {
|
||||
name: "webapi-eca"
|
||||
};
|
||||
if (args['mode'] === 'development') {
|
||||
opt.src = true;
|
||||
}
|
||||
if (args['file-path']) {
|
||||
_this.logPath = path.resolve(args['file-path']);
|
||||
} else {
|
||||
_this.logPath = path.resolve(__dirname, '..', 'logs', 'server.log');
|
||||
}
|
||||
try {
|
||||
fs.writeFileSync(_this.logPath + '.temp', 'temp');
|
||||
fs.unlinkSync(_this.logPath + '.temp');
|
||||
} catch (_error) {
|
||||
e = _error;
|
||||
console.error("Log folder '" + _this.logPath + "' is not writable");
|
||||
return emptylog;
|
||||
}
|
||||
opt.streams = [
|
||||
{
|
||||
level: args['io-level'],
|
||||
stream: process.stdout
|
||||
}, {
|
||||
level: args['file-level'],
|
||||
path: _this.logPath
|
||||
}
|
||||
];
|
||||
return bunyan.createLogger(opt);
|
||||
} catch (_error) {
|
||||
e = _error;
|
||||
console.error(e);
|
||||
return emptylog;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Prints a log to stdout.
|
||||
* @param {String} module
|
||||
* @param {String} msg
|
||||
*/
|
||||
exports.print = function(module, msg) {
|
||||
flush(false, (new Date()).toISOString() + ' | ' + module + ' | ' + msg);
|
||||
};
|
||||
|
||||
/**
|
||||
* Prints a log to stderr.
|
||||
* @param {String} module
|
||||
* @param {Error} err
|
||||
*/
|
||||
function printError(module, err, isSevere) {
|
||||
var ts = (new Date()).toISOString() + ' | ', ai = '';
|
||||
if(!err) {
|
||||
err = new Error('Unexpected error');
|
||||
isSevere = true;
|
||||
}
|
||||
if(typeof err === 'string') err = new Error(err);
|
||||
// if(module) flush(true, ts + module + ' | ERROR AND BAD HANDLING: ' + err + '\n' + e.stack);
|
||||
// else flush(true, ts + '!N/A! | ERROR, BAD HANDLING AND NO MODULE NAME: ' + err + '\n' + e.stack);
|
||||
// } else if(err) {
|
||||
if(err.addInfo) ai = ' (' + err.addInfo + ')';
|
||||
if(!err.message) err.message = 'UNKNOWN REASON!\n' + err.stack;
|
||||
if(module) {
|
||||
var msg = ts + module + ' | ERROR'+ai+': ' + err.message;
|
||||
if(isSevere) msg += '\n' + err.stack;
|
||||
flush(true, msg);
|
||||
} else flush(true, ts + '!N/A! | ERROR AND NO MODULE NAME'+ai+': ' + err.message + '\n' + err.stack);
|
||||
// } else {
|
||||
// var e = new Error('Unexpected error');
|
||||
// flush(true, e.message + ': \n' + e.stack);
|
||||
// }
|
||||
};
|
||||
|
||||
/**
|
||||
* Prints a message to stderr.
|
||||
* @param {String} module
|
||||
* @param {Error} err
|
||||
*/
|
||||
exports.error = function(module, err) {
|
||||
printError(module, err, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Prints a message with error stack to stderr
|
||||
* @param {String} module
|
||||
* @param {Error} err
|
||||
*/
|
||||
exports.severe = function(module, err) {
|
||||
printError(module, err, true);
|
||||
};
|
||||
|
||||
exports.obj = function (varname, obj) {
|
||||
var arrS = (new Error).stack.split('\n');
|
||||
console.log('Dumping object "' + varname + '"' + arrS[2]);
|
||||
console.log(obj);
|
||||
};
|
||||
}).call(this);
|
||||
|
|
|
|||
|
|
@ -1,86 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var cs = require('coffee-script');
|
||||
var fs = require('fs'),
|
||||
path = require('path'),
|
||||
log = require('./logging');
|
||||
|
||||
exports = module.exports = function(args) {
|
||||
args = args || {};
|
||||
log(args);
|
||||
return module.exports;
|
||||
};
|
||||
|
||||
exports.requireFromString = function(src, name, dir) {
|
||||
if(!dir) dir = __dirname;
|
||||
|
||||
// Allow coffee scripts!
|
||||
console.log(cs.compile(src));
|
||||
|
||||
var id = path.resolve(dir, name, name + '.vm');
|
||||
var vm = require('vm'),
|
||||
sandbox = {
|
||||
log: log,
|
||||
needle: require('needle')
|
||||
};
|
||||
|
||||
var m = vm.runInNewContext(src, sandbox, id + '.vm');
|
||||
console.log('module loader');
|
||||
console.log(m);
|
||||
// var m = new module.constructor(id, module);
|
||||
// m.paths = module.paths;
|
||||
// try {
|
||||
// m._compile(src);
|
||||
// } catch(err) {
|
||||
// err.addInfo = 'during compilation of module ' + name;
|
||||
// log.error('LM', err);
|
||||
// // log.error('LM', ' during compilation of ' + name + ': ' + err);
|
||||
// }
|
||||
return m.exports;
|
||||
};
|
||||
|
||||
exports.loadModule = function(directory, name, callback) {
|
||||
try {
|
||||
fs.readFile(path.resolve(__dirname, '..', directory, name, name + '.js'), 'utf8', function (err, data) {
|
||||
if (err) {
|
||||
log.error('LM', 'Loading module file!');
|
||||
return;
|
||||
}
|
||||
var mod = exports.requireFromString(data, name, directory);
|
||||
if(mod && fs.existsSync(path.resolve(__dirname, '..', directory, name, 'credentials.json'))) {
|
||||
fs.readFile(path.resolve(__dirname, '..', directory, name, 'credentials.json'), 'utf8', function (err, auth) {
|
||||
if (err) {
|
||||
log.error('LM', 'Loading credentials file for "' + name + '"!');
|
||||
callback(name, data, mod, null);
|
||||
return;
|
||||
}
|
||||
if(mod.loadCredentials) mod.loadCredentials(JSON.parse(auth));
|
||||
callback(name, data, mod, auth);
|
||||
});
|
||||
} else {
|
||||
// Hand back the name, the string contents and the compiled module
|
||||
callback(name, data, mod, null);
|
||||
}
|
||||
});
|
||||
} catch(err) {
|
||||
log.error('LM', 'Failed loading module "' + name + '"');
|
||||
}
|
||||
};
|
||||
|
||||
exports.loadModules = function(directory, callback) {
|
||||
fs.readdir(path.resolve(__dirname, '..', directory), function (err, list) {
|
||||
if (err) {
|
||||
log.error('LM', 'loading modules directory: ' + err);
|
||||
return;
|
||||
}
|
||||
log.print('LM', 'Loading ' + list.length + ' modules from "' + directory + '"');
|
||||
list.forEach(function (file) {
|
||||
fs.stat(path.resolve(__dirname, '..', directory, file), function (err, stat) {
|
||||
if (stat && stat.isDirectory()) {
|
||||
exports.loadModule(directory, file, callback);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
/*
|
||||
# Module Manager
|
||||
> The module manager takes care of the module and rules loading in the initialization
|
||||
> phase and on user request.
|
||||
|
||||
> Event and Action modules are loaded as strings and stored in the database,
|
||||
> then compiled into node modules and rules
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs'),
|
||||
path = require('path'),
|
||||
log = require('./logging'),
|
||||
ml, db, funcLoadAction, funcLoadRule;
|
||||
|
||||
exports = module.exports = function(args) {
|
||||
args = args || {};
|
||||
log(args);
|
||||
ml = require('./module_loader')(args);
|
||||
return module.exports;
|
||||
};
|
||||
|
||||
exports.addDBLink = function(db_link) {
|
||||
db = db_link;
|
||||
};
|
||||
|
||||
exports.storeEventModule = function (user, obj, answHandler) {
|
||||
log.print('MM', 'implement storeEventModule');
|
||||
answHandler.answerSuccess('Thank you for the event!');
|
||||
};
|
||||
|
||||
exports.storeActionModule = function (user, obj, answHandler) {
|
||||
log.print('MM', 'implement storeActionModule');
|
||||
answHandler.answerSuccess('Thank you for the action!');
|
||||
};
|
||||
|
||||
exports.storeRule = function (user, obj, answHandler) {
|
||||
log.print('MM', 'implement storeRule');
|
||||
answHandler.answerSuccess('Thank you for the rule!');
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Legacy file system loaders
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Load Rules from fs
|
||||
* ------------------
|
||||
*/
|
||||
exports.loadRulesFromFS = function(args, answHandler) {
|
||||
if(!args) args = {};
|
||||
if(!args.name) args.name = 'rules';
|
||||
if(!funcLoadRule) log.error('ML', 'no rule loader function available');
|
||||
else {
|
||||
fs.readFile(path.resolve(__dirname, '..', 'rules', args.name + '.json'), 'utf8', function (err, data) {
|
||||
if (err) {
|
||||
log.error('ML', 'Loading rules file: ' + args.name + '.json');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var arr = JSON.parse(data), txt = '';
|
||||
log.print('ML', 'Loading ' + arr.length + ' rules:');
|
||||
for(var i = 0; i < arr.length; i++) {
|
||||
txt += arr[i].id + ', ';
|
||||
db.storeRule(arr[i].id, JSON.stringify(arr[i]));
|
||||
// funcLoadRule(arr[i]);
|
||||
}
|
||||
answHandler.answerSuccess('Yep, loaded rules: ' + txt);
|
||||
} catch (e) {
|
||||
log.error('ML', 'rules file was corrupt! (' + args.name + '.json)');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Load Action Modules from fs
|
||||
* ---------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} name
|
||||
* @param {Object} data
|
||||
* @param {Object} mod
|
||||
* @param {String} [auth] The string representation of the auth json
|
||||
*/
|
||||
function loadActionCallback(name, data, mod, auth) {
|
||||
db.storeActionModule(name, data); // store module in db
|
||||
// funcLoadAction(name, mod); // hand back compiled module
|
||||
if(auth) db.storeActionModuleAuth(name, auth);
|
||||
}
|
||||
|
||||
exports.loadActionModuleFromFS = function (args, answHandler) {
|
||||
if(ml) {
|
||||
if(args && args.name) {
|
||||
answHandler.answerSuccess('Loading action module ' + args.name + '...');
|
||||
ml.loadModule('mod_actions', args.name, loadActionCallback);
|
||||
} else log.error('MM', 'Action Module name not provided!');
|
||||
}
|
||||
};
|
||||
|
||||
exports.loadActionModulesFromFS = function(args, answHandler) {
|
||||
if(ml) {
|
||||
answHandler.answerSuccess('Loading action modules...');
|
||||
ml.loadModules('mod_actions', loadActionCallback);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Load Event Modules from fs
|
||||
* --------------------------
|
||||
*/
|
||||
|
||||
function loadEventCallback(name, data, mod, auth) {
|
||||
if(db) {
|
||||
db.storeEventModule(name, data); // store module in db
|
||||
if(auth) db.storeEventModuleAuth(name, auth);
|
||||
}
|
||||
}
|
||||
|
||||
exports.loadEventModuleFromFS = function(args, answHandler) {
|
||||
if(ml) {
|
||||
if(args && args.name) {
|
||||
answHandler.answerSuccess('Loading event module ' + args.name + '...');
|
||||
ml.loadModule('mod_events', args.name, loadEventCallback);
|
||||
} else log.error('MM', 'Event Module name not provided!');
|
||||
}
|
||||
};
|
||||
|
||||
exports.loadEventModulesFromFS = function(args, answHandler) {
|
||||
answHandler.answerSuccess('Loading event moules...');
|
||||
ml.loadModules('mod_actions', loadEventCallback);
|
||||
};
|
||||
|
||||
894
js/persistence.js
Normal file
894
js/persistence.js
Normal file
|
|
@ -0,0 +1,894 @@
|
|||
// Generated by CoffeeScript 1.6.3
|
||||
/*
|
||||
|
||||
Persistence
|
||||
============
|
||||
> Handles the connection to the database and provides functionalities for event pollers,
|
||||
> action invokers, rules and the (hopefully encrypted) storing of user-specific parameters
|
||||
> per module.
|
||||
> General functionality as a wrapper for the module holds initialization,
|
||||
> the retrieval of modules and shut down.
|
||||
>
|
||||
> The general structure for linked data is that the key is stored in a set.
|
||||
> By fetching all set entries we can then fetch all elements, which is
|
||||
> automated in this function.
|
||||
> For example, modules of the same group, e.g. action invokers are registered in an
|
||||
> unordered set in the database, from where they can be retrieved again. For example
|
||||
> a new action invoker has its ID (e.g 'probinder') first registered in the set
|
||||
> 'action-invokers' and then stored in the db with the key 'action-invoker:' + ID
|
||||
> (e.g. action-invoker:probinder).
|
||||
>
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
var IndexedModules, exports, getSetRecords, redis, replyHandler,
|
||||
_this = this,
|
||||
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
||||
|
||||
redis = require('redis');
|
||||
|
||||
/*
|
||||
Module call
|
||||
-----------
|
||||
Initializes the DB connection with the given `db-port` property in the `args` object.
|
||||
|
||||
@param {Object} args
|
||||
*/
|
||||
|
||||
|
||||
exports = module.exports = function(args) {
|
||||
if (!_this.db) {
|
||||
if (!args['db-port']) {
|
||||
args['db-port'] = 6379;
|
||||
}
|
||||
_this.log = args.logger;
|
||||
exports.eventPollers = new IndexedModules('event-poller', _this.log);
|
||||
exports.actionInvokers = new IndexedModules('action-invoker', _this.log);
|
||||
return exports.initPort(args['db-port']);
|
||||
}
|
||||
};
|
||||
|
||||
exports.getLogger = function() {
|
||||
return _this.log;
|
||||
};
|
||||
|
||||
exports.initPort = function(port) {
|
||||
var _ref;
|
||||
_this.connRefused = false;
|
||||
if ((_ref = _this.db) != null) {
|
||||
_ref.quit();
|
||||
}
|
||||
_this.db = redis.createClient(port, 'localhost', {
|
||||
connect_timeout: 2000
|
||||
});
|
||||
_this.db.on('error', function(err) {
|
||||
if (err.message.indexOf('ECONNREFUSED') > -1) {
|
||||
_this.connRefused = true;
|
||||
return _this.log.error(err, 'DB | Wrong port?');
|
||||
}
|
||||
});
|
||||
exports.eventPollers.setDB(_this.db);
|
||||
return exports.actionInvokers.setDB(_this.db);
|
||||
};
|
||||
|
||||
/*
|
||||
Checks whether the db is connected and passes either an error on failure after
|
||||
ten attempts within five seconds, or nothing on success to the callback(err).
|
||||
|
||||
@public isConnected( *cb* )
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
|
||||
exports.isConnected = function(cb) {
|
||||
var fCheckConnection, numAttempts;
|
||||
if (!_this.db) {
|
||||
return cb(new Error('DB | DB initialization did not occur or failed miserably!'));
|
||||
} else {
|
||||
if (_this.db.connected) {
|
||||
return cb();
|
||||
} else {
|
||||
numAttempts = 0;
|
||||
fCheckConnection = function() {
|
||||
var _ref;
|
||||
if (_this.connRefused) {
|
||||
if ((_ref = _this.db) != null) {
|
||||
_ref.quit();
|
||||
}
|
||||
return cb(new Error('DB | Connection refused! Wrong port?'));
|
||||
} else {
|
||||
if (_this.db.connected) {
|
||||
_this.log.info('DB | Successfully connected to DB!');
|
||||
return cb();
|
||||
} else if (numAttempts++ < 10) {
|
||||
return setTimeout(fCheckConnection, 100);
|
||||
} else {
|
||||
return cb(new Error('DB | Connection to DB failed!'));
|
||||
}
|
||||
}
|
||||
};
|
||||
return setTimeout(fCheckConnection, 100);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Abstracts logging for simple action replies from the DB.
|
||||
|
||||
@private replyHandler( *action* )
|
||||
@param {String} action
|
||||
*/
|
||||
|
||||
|
||||
replyHandler = function(action) {
|
||||
return function(err, reply) {
|
||||
if (err) {
|
||||
return _this.log.warn(err, "during '" + action + "'");
|
||||
} else {
|
||||
return _this.log.info("DB | " + action + ": " + reply);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
Push an event into the event queue.
|
||||
|
||||
@public pushEvent( *oEvent* )
|
||||
@param {Object} oEvent
|
||||
*/
|
||||
|
||||
|
||||
exports.pushEvent = function(oEvent) {
|
||||
if (oEvent) {
|
||||
_this.log.info("DB | Event pushed into the queue: '" + oEvent.eventid + "'");
|
||||
return _this.db.rpush('event_queue', JSON.stringify(oEvent));
|
||||
} else {
|
||||
return _this.log.warn('DB | Why would you give me an empty event...');
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Pop an event from the event queue and pass it to cb(err, obj).
|
||||
|
||||
@public popEvent( *cb* )
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
|
||||
exports.popEvent = function(cb) {
|
||||
var makeObj;
|
||||
makeObj = function(pcb) {
|
||||
return function(err, obj) {
|
||||
return pcb(err, JSON.parse(obj));
|
||||
};
|
||||
};
|
||||
return _this.db.lpop('event_queue', makeObj(cb));
|
||||
};
|
||||
|
||||
/*
|
||||
Purge the event queue.
|
||||
|
||||
@public purgeEventQueue()
|
||||
*/
|
||||
|
||||
|
||||
exports.purgeEventQueue = function() {
|
||||
return _this.db.del('event_queue', replyHandler('purging event queue'));
|
||||
};
|
||||
|
||||
/*
|
||||
Fetches all linked data set keys from a linking set, fetches the single
|
||||
data objects via the provided function and returns the results to cb(err, obj).
|
||||
|
||||
@private getSetRecords( *set, fSingle, cb* )
|
||||
@param {String} set the set name how it is stored in the DB
|
||||
@param {function} fSingle a function to retrieve a single data element
|
||||
per set entry
|
||||
@param {function} cb the callback(err, obj) function that receives all
|
||||
the retrieved data or an error
|
||||
*/
|
||||
|
||||
|
||||
getSetRecords = function(set, fSingle, cb) {
|
||||
_this.log.info("DB | Fetching set records: '" + set + "'");
|
||||
return _this.db.smembers(set, function(err, arrReply) {
|
||||
var fCallback, objReplies, reply, semaphore, _i, _len, _results;
|
||||
if (err) {
|
||||
_this.log.warn(err, "DB | fetching '" + set + "'");
|
||||
return cb(err);
|
||||
} else if (arrReply.length === 0) {
|
||||
return cb();
|
||||
} else {
|
||||
semaphore = arrReply.length;
|
||||
objReplies = {};
|
||||
setTimeout(function() {
|
||||
if (semaphore > 0) {
|
||||
return cb(new Error("Timeout fetching '" + set + "'"));
|
||||
}
|
||||
}, 2000);
|
||||
fCallback = function(prop) {
|
||||
return function(err, data) {
|
||||
--semaphore;
|
||||
if (err) {
|
||||
_this.log.warn(err, "DB | fetching single element: '" + prop + "'");
|
||||
} else if (!data) {
|
||||
_this.log.warn(new Error("Empty key in DB: '" + prop + "'"));
|
||||
} else {
|
||||
objReplies[prop] = data;
|
||||
}
|
||||
if (semaphore === 0) {
|
||||
return cb(null, objReplies);
|
||||
}
|
||||
};
|
||||
};
|
||||
_results = [];
|
||||
for (_i = 0, _len = arrReply.length; _i < _len; _i++) {
|
||||
reply = arrReply[_i];
|
||||
_results.push(fSingle(reply, fCallback(reply)));
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
IndexedModules = (function() {
|
||||
function IndexedModules(setname, log) {
|
||||
this.setname = setname;
|
||||
this.log = log;
|
||||
this.deleteUserParams = __bind(this.deleteUserParams, this);
|
||||
this.getUserParamsIds = __bind(this.getUserParamsIds, this);
|
||||
this.getUserParams = __bind(this.getUserParams, this);
|
||||
this.storeUserParams = __bind(this.storeUserParams, this);
|
||||
this.deleteModule = __bind(this.deleteModule, this);
|
||||
this.getModules = __bind(this.getModules, this);
|
||||
this.getModuleIds = __bind(this.getModuleIds, this);
|
||||
this.getAvailableModuleIds = __bind(this.getAvailableModuleIds, this);
|
||||
this.getModuleParams = __bind(this.getModuleParams, this);
|
||||
this.getModule = __bind(this.getModule, this);
|
||||
this.unpublish = __bind(this.unpublish, this);
|
||||
this.publish = __bind(this.publish, this);
|
||||
this.unlinkModule = __bind(this.unlinkModule, this);
|
||||
this.linkModule = __bind(this.linkModule, this);
|
||||
this.storeModule = __bind(this.storeModule, this);
|
||||
this.log.info("DB | (IdxedMods) Instantiated indexed modules for '" + this.setname + "'");
|
||||
}
|
||||
|
||||
IndexedModules.prototype.setDB = function(db) {
|
||||
this.db = db;
|
||||
return this.log.info("DB | (IdxedMods) Registered new DB connection for '" + this.setname + "'");
|
||||
};
|
||||
|
||||
/*
|
||||
Stores a module and links it to the user.
|
||||
|
||||
@private storeModule( *userId, oModule* )
|
||||
@param {String} userId
|
||||
@param {object} oModule
|
||||
*/
|
||||
|
||||
|
||||
IndexedModules.prototype.storeModule = function(userId, oModule) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".storeModule( " + userId + ", oModule )");
|
||||
this.db.sadd("" + this.setname + "s", oModule.id, replyHandler("sadd '" + oModule.id + "' to '" + this.setname + "'"));
|
||||
this.db.hmset("" + this.setname + ":" + oModule.id, oModule, replyHandler("hmset properties in hash '" + this.setname + ":" + oModule.id + "'"));
|
||||
return this.linkModule(oModule.id, userId);
|
||||
};
|
||||
|
||||
IndexedModules.prototype.linkModule = function(mId, userId) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".linkModule( " + mId + ", " + userId + " )");
|
||||
this.db.sadd("" + this.setname + ":" + mId + ":users", userId, replyHandler("sadd " + userId + " to '" + this.setname + ":" + mId + ":users'"));
|
||||
return this.db.sadd("user:" + userId + ":" + this.setname + "s", mId, replyHandler("sadd " + mId + " to 'user:" + userId + ":" + this.setname + "s'"));
|
||||
};
|
||||
|
||||
IndexedModules.prototype.unlinkModule = function(mId, userId) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".unlinkModule( " + mId + ", " + userId + " )");
|
||||
this.db.srem("" + this.setname + ":" + mId + ":users", userId, replyHandler("srem " + userId + " from '" + this.setname + ":" + mId + ":users'"));
|
||||
return this.db.srem("user:" + userId + ":" + this.setname + "s", mId, replyHandler("srem " + mId + " from 'user:" + userId + ":" + this.setname + "s'"));
|
||||
};
|
||||
|
||||
IndexedModules.prototype.publish = function(mId) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".publish( " + mId + " )");
|
||||
return this.db.sadd("public-" + this.setname + "s", mId, replyHandler("sadd '" + mId + "' to 'public-" + this.setname + "s'"));
|
||||
};
|
||||
|
||||
IndexedModules.prototype.unpublish = function(mId) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".unpublish( " + mId + " )");
|
||||
return this.db.srem("public-" + this.setname + "s", mId, replyHandler("srem '" + mId + "' from 'public-" + this.setname + "s'"));
|
||||
};
|
||||
|
||||
IndexedModules.prototype.getModule = function(mId, cb) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".getModule( " + mId + " )");
|
||||
return this.db.hgetall("" + this.setname + ":" + mId, cb);
|
||||
};
|
||||
|
||||
IndexedModules.prototype.getModuleParams = function(mId, cb) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".getModuleParams( " + mId + " )");
|
||||
return this.db.hget("" + this.setname + ":" + mId, "params", cb);
|
||||
};
|
||||
|
||||
IndexedModules.prototype.getAvailableModuleIds = function(userId, cb) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".getPublicModuleIds( " + userId + " )");
|
||||
return this.db.sunion("public-" + this.setname + "s", "user:" + userId + ":" + this.setname + "s", cb);
|
||||
};
|
||||
|
||||
IndexedModules.prototype.getModuleIds = function(cb) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".getModuleIds()");
|
||||
return this.db.smembers("" + this.setname + "s", cb);
|
||||
};
|
||||
|
||||
IndexedModules.prototype.getModules = function(cb) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".getModules()");
|
||||
return getSetRecords("" + this.setname + "s", this.getModule, cb);
|
||||
};
|
||||
|
||||
IndexedModules.prototype.deleteModule = function(mId) {
|
||||
var _this = this;
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".deleteModule( " + mId + " )");
|
||||
this.db.srem("" + this.setname + "s", mId, replyHandler("srem '" + mId + "' from " + this.setname + "s"));
|
||||
this.db.del("" + this.setname + ":" + mId, replyHandler("del of '" + this.setname + ":" + mId + "'"));
|
||||
this.unpublish(mId);
|
||||
return this.db.smembers("" + this.setname + ":" + mId + ":users", function(err, obj) {
|
||||
var userId, _i, _j, _len, _len1, _results;
|
||||
for (_i = 0, _len = obj.length; _i < _len; _i++) {
|
||||
userId = obj[_i];
|
||||
_this.unlinkModule(mId, userId);
|
||||
}
|
||||
_results = [];
|
||||
for (_j = 0, _len1 = obj.length; _j < _len1; _j++) {
|
||||
userId = obj[_j];
|
||||
_results.push(_this.deleteUserParams(mId, userId));
|
||||
}
|
||||
return _results;
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Stores user params for a module. They are expected to be RSA encrypted with helps of
|
||||
the provided cryptico JS library and will only be decrypted right before the module is loaded!
|
||||
|
||||
@private storeUserParams( *mId, userId, encData* )
|
||||
@param {String} mId
|
||||
@param {String} userId
|
||||
@param {object} encData
|
||||
*/
|
||||
|
||||
|
||||
IndexedModules.prototype.storeUserParams = function(mId, userId, encData) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".storeUserParams( " + mId + ", " + userId + ", encData )");
|
||||
this.db.sadd("" + this.setname + "-params", "" + mId + ":" + userId, replyHandler("sadd '" + mId + ":" + userId + "' to '" + this.setname + "-params'"));
|
||||
return this.db.set("" + this.setname + "-params:" + mId + ":" + userId, encData, replyHandler("set user params in '" + this.setname + "-params:" + mId + ":" + userId + "'"));
|
||||
};
|
||||
|
||||
IndexedModules.prototype.getUserParams = function(mId, userId, cb) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".getUserParams( " + mId + ", " + userId + " )");
|
||||
return this.db.get("" + this.setname + "-params:" + mId + ":" + userId, cb);
|
||||
};
|
||||
|
||||
IndexedModules.prototype.getUserParamsIds = function(cb) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".getUserParamsIds()");
|
||||
return this.db.smembers("" + this.setname + "-params", cb);
|
||||
};
|
||||
|
||||
IndexedModules.prototype.deleteUserParams = function(mId, userId) {
|
||||
this.log.info("DB | (IdxedMods) " + this.setname + ".deleteUserParams(" + mId + ", " + userId + " )");
|
||||
this.db.srem("" + this.setname + "-params", "" + mId + ":" + userId, replyHandler("srem '" + mId + ":" + userId + "' from '" + this.setname + "-params'"));
|
||||
return this.db.del("" + this.setname + "-params:" + mId + ":" + userId, replyHandler("del '" + this.setname + "-params:" + mId + ":" + userId + "'"));
|
||||
};
|
||||
|
||||
return IndexedModules;
|
||||
|
||||
})();
|
||||
|
||||
/*
|
||||
## Rules
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Appends a log entry.
|
||||
|
||||
@public log( *userId, ruleId, message* )
|
||||
@param {String} userId
|
||||
@param {String} ruleId
|
||||
@param {String} message
|
||||
*/
|
||||
|
||||
|
||||
exports.appendLog = function(userId, ruleId, moduleId, message) {
|
||||
return _this.db.append("" + userId + ":" + ruleId, "[" + ((new Date).toISOString()) + "] {" + moduleId + "} " + message + "\n");
|
||||
};
|
||||
|
||||
/*
|
||||
Retrieves a log entry.
|
||||
|
||||
@public getLog( *userId, ruleId* )
|
||||
@param {String} userId
|
||||
@param {String} ruleId
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
|
||||
exports.getLog = function(userId, ruleId, cb) {
|
||||
return _this.db.get("" + userId + ":" + ruleId, cb);
|
||||
};
|
||||
|
||||
/*
|
||||
Resets a log entry.
|
||||
|
||||
@public resetLog( *userId, ruleId* )
|
||||
@param {String} userId
|
||||
@param {String} ruleId
|
||||
*/
|
||||
|
||||
|
||||
exports.resetLog = function(userId, ruleId) {
|
||||
return _this.db.del("" + userId + ":" + ruleId, replyHandler("RESET LOG '" + userId + ":" + ruleId + "'"));
|
||||
};
|
||||
|
||||
/*
|
||||
Query the DB for a rule and pass it to cb(err, obj).
|
||||
|
||||
@public getRule( *ruleId, cb* )
|
||||
@param {String} ruleId
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
|
||||
exports.getRule = function(ruleId, cb) {
|
||||
_this.log.info("DB | getRule: '" + ruleId + "'");
|
||||
return _this.db.get("rule:" + ruleId, cb);
|
||||
};
|
||||
|
||||
/*
|
||||
Fetch all rules and pass them to cb(err, obj).
|
||||
|
||||
@public getRules( *cb* )
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
|
||||
exports.getRules = function(cb) {
|
||||
_this.log.info('DB | Fetching all Rules');
|
||||
return getSetRecords('rules', exports.getRule, cb);
|
||||
};
|
||||
|
||||
/*
|
||||
Fetch all rule IDs and hand it to cb(err, obj).
|
||||
|
||||
@public getRuleIds( *cb* )
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
|
||||
exports.getRuleIds = function(cb) {
|
||||
_this.log.info('DB | Fetching all Rule IDs');
|
||||
return _this.db.smembers('rules', cb);
|
||||
};
|
||||
|
||||
/*
|
||||
Store a string representation of a rule in the DB.
|
||||
|
||||
@public storeRule( *ruleId, data* )
|
||||
@param {String} ruleId
|
||||
@param {String} data
|
||||
*/
|
||||
|
||||
|
||||
exports.storeRule = function(ruleId, data) {
|
||||
_this.log.info("DB | storeRule: '" + ruleId + "'");
|
||||
_this.db.sadd('rules', "" + ruleId, replyHandler("storing rule key '" + ruleId + "'"));
|
||||
return _this.db.set("rule:" + ruleId, data, replyHandler("storing rule '" + ruleId + "'"));
|
||||
};
|
||||
|
||||
/*
|
||||
Delete a string representation of a rule.
|
||||
|
||||
@public deleteRule( *ruleId, userId* )
|
||||
@param {String} ruleId
|
||||
@param {String} userId
|
||||
*/
|
||||
|
||||
|
||||
exports.deleteRule = function(ruleId) {
|
||||
_this.log.info("DB | deleteRule: '" + ruleId + "'");
|
||||
_this.db.srem("rules", ruleId, replyHandler("Deleting rule key '" + ruleId + "'"));
|
||||
_this.db.del("rule:" + ruleId, replyHandler("Deleting rule '" + ruleId + "'"));
|
||||
_this.db.smembers("rule:" + ruleId + ":users", function(err, obj) {
|
||||
var delLinkedUserRule, id, _i, _len, _results;
|
||||
delLinkedUserRule = function(userId) {
|
||||
return _this.db.srem("user:" + userId + ":rules", ruleId, replyHandler("Deleting rule key '" + ruleId + "' in linked user '" + userId + "'"));
|
||||
};
|
||||
_results = [];
|
||||
for (_i = 0, _len = obj.length; _i < _len; _i++) {
|
||||
id = obj[_i];
|
||||
_results.push(delLinkedUserRule(id));
|
||||
}
|
||||
return _results;
|
||||
});
|
||||
_this.db.del("rule:" + ruleId + ":users", replyHandler("Deleting rule '" + ruleId + "' users"));
|
||||
_this.db.smembers("rule:" + ruleId + ":active-users", function(err, obj) {
|
||||
var delActiveUserRule, id, _i, _len, _results;
|
||||
delActiveUserRule = function(userId) {
|
||||
return _this.db.srem("user:" + userId + ":active-rules", ruleId, replyHandler("Deleting rule key '" + ruleId + "' in active user '" + userId + "'"));
|
||||
};
|
||||
_results = [];
|
||||
for (_i = 0, _len = obj.length; _i < _len; _i++) {
|
||||
id = obj[_i];
|
||||
_results.push(delActiveUserRule(id));
|
||||
}
|
||||
return _results;
|
||||
});
|
||||
return _this.db.del("rule:" + ruleId + ":active-users", replyHandler("Deleting rule '" + ruleId + "' active users"));
|
||||
};
|
||||
|
||||
/*
|
||||
Associate a rule to a user.
|
||||
|
||||
@public linkRule( *ruleId, userId* )
|
||||
@param {String} ruleId
|
||||
@param {String} userId
|
||||
*/
|
||||
|
||||
|
||||
exports.linkRule = function(ruleId, userId) {
|
||||
_this.log.info("DB | linkRule: '" + ruleId + "' for user '" + userId + "'");
|
||||
_this.db.sadd("rule:" + ruleId + ":users", userId, replyHandler("storing user '" + userId + "' for rule key '" + ruleId + "'"));
|
||||
return _this.db.sadd("user:" + userId + ":rules", ruleId, replyHandler("storing rule key '" + ruleId + "' for user '" + userId + "'"));
|
||||
};
|
||||
|
||||
/*
|
||||
Get rules linked to a user and hand it to cb(err, obj).
|
||||
|
||||
@public getUserLinkRule( *userId, cb* )
|
||||
@param {String} userId
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
|
||||
exports.getUserLinkedRules = function(userId, cb) {
|
||||
_this.log.info("DB | getUserLinkedRules: for user '" + userId + "'");
|
||||
return _this.db.smembers("user:" + userId + ":rules", cb);
|
||||
};
|
||||
|
||||
/*
|
||||
Get users linked to a rule and hand it to cb(err, obj).
|
||||
|
||||
@public getRuleLinkedUsers( *ruleId, cb* )
|
||||
@param {String} ruleId
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
|
||||
exports.getRuleLinkedUsers = function(ruleId, cb) {
|
||||
_this.log.info("DB | getRuleLinkedUsers: for rule '" + ruleId + "'");
|
||||
return _this.db.smembers("rule:" + ruleId + ":users", cb);
|
||||
};
|
||||
|
||||
/*
|
||||
Delete an association of a rule to a user.
|
||||
|
||||
@public unlinkRule( *ruleId, userId* )
|
||||
@param {String} ruleId
|
||||
@param {String} userId
|
||||
*/
|
||||
|
||||
|
||||
exports.unlinkRule = function(ruleId, userId) {
|
||||
_this.log.info("DB | unlinkRule: '" + ruleId + ":" + userId + "'");
|
||||
_this.db.srem("rule:" + ruleId + ":users", userId, replyHandler("removing user '" + userId + "' for rule key '" + ruleId + "'"));
|
||||
return _this.db.srem("user:" + userId + ":rules", ruleId, replyHandler("removing rule key '" + ruleId + "' for user '" + userId + "'"));
|
||||
};
|
||||
|
||||
/*
|
||||
Activate a rule.
|
||||
|
||||
@public activateRule( *ruleId, userId* )
|
||||
@param {String} ruleId
|
||||
@param {String} userId
|
||||
*/
|
||||
|
||||
|
||||
exports.activateRule = function(ruleId, userId) {
|
||||
_this.log.info("DB | activateRule: '" + ruleId + "' for '" + userId + "'");
|
||||
_this.db.sadd("rule:" + ruleId + ":active-users", userId, replyHandler("storing activated user '" + userId + "' in rule '" + ruleId + "'"));
|
||||
return _this.db.sadd("user:" + userId + ":active-rules", ruleId, replyHandler("storing activated rule '" + ruleId + "' in user '" + userId + "'"));
|
||||
};
|
||||
|
||||
/*
|
||||
Get rules activated for a user and hand it to cb(err, obj).
|
||||
|
||||
@public getUserLinkRule( *userId, cb* )
|
||||
@param {String} userId
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
|
||||
exports.getUserActivatedRules = function(userId, cb) {
|
||||
_this.log.info("DB | getUserActivatedRules: for user '" + userId + "'");
|
||||
return _this.db.smembers("user:" + userId + ":active-rules", cb);
|
||||
};
|
||||
|
||||
/*
|
||||
Get users activated for a rule and hand it to cb(err, obj).
|
||||
|
||||
@public getRuleActivatedUsers ( *ruleId, cb* )
|
||||
@param {String} ruleId
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
|
||||
exports.getRuleActivatedUsers = function(ruleId, cb) {
|
||||
_this.log.info("DB | getRuleActivatedUsers: for rule '" + ruleId + "'");
|
||||
return _this.db.smembers("rule:" + ruleId + ":active-users", cb);
|
||||
};
|
||||
|
||||
/*
|
||||
Deactivate a rule.
|
||||
|
||||
@public deactivateRule( *ruleId, userId* )
|
||||
@param {String} ruleId
|
||||
@param {String} userId
|
||||
*/
|
||||
|
||||
|
||||
exports.deactivateRule = function(ruleId, userId) {
|
||||
_this.log.info("DB | deactivateRule: '" + ruleId + "' for '" + userId + "'");
|
||||
_this.db.srem("rule:" + ruleId + ":active-users", userId, replyHandler("removing activated user '" + userId + "' in rule '" + ruleId + "'"));
|
||||
return _this.db.srem("user:" + userId + ":active-rules", ruleId, replyHandler("removing activated rule '" + ruleId + "' in user '" + userId + "'"));
|
||||
};
|
||||
|
||||
/*
|
||||
Fetch all active ruleIds and pass them to cb(err, obj).
|
||||
|
||||
@public getAllActivatedRuleIds( *cb* )
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
|
||||
exports.getAllActivatedRuleIdsPerUser = function(cb) {
|
||||
_this.log.info("DB | Fetching all active rules");
|
||||
return _this.db.smembers('users', function(err, obj) {
|
||||
var fFetchActiveUserRules, result, semaphore, user, _i, _len, _results;
|
||||
result = {};
|
||||
if (obj.length === 0) {
|
||||
return cb(null, result);
|
||||
} else {
|
||||
semaphore = obj.length;
|
||||
fFetchActiveUserRules = function(userId) {
|
||||
return _this.db.smembers("user:" + user + ":active-rules", function(err, obj) {
|
||||
if (obj.length > 0) {
|
||||
result[userId] = obj;
|
||||
}
|
||||
if (--semaphore === 0) {
|
||||
return cb(null, result);
|
||||
}
|
||||
});
|
||||
};
|
||||
_results = [];
|
||||
for (_i = 0, _len = obj.length; _i < _len; _i++) {
|
||||
user = obj[_i];
|
||||
_results.push(fFetchActiveUserRules(user));
|
||||
}
|
||||
return _results;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
## Users
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Store a user object (needs to be a flat structure).
|
||||
The password should be hashed before it is passed to this function.
|
||||
|
||||
@public storeUser( *objUser* )
|
||||
@param {Object} objUser
|
||||
*/
|
||||
|
||||
|
||||
exports.storeUser = function(objUser) {
|
||||
_this.log.info("DB | storeUser: '" + objUser.username + "'");
|
||||
if (objUser && objUser.username && objUser.password) {
|
||||
_this.db.sadd('users', objUser.username, replyHandler("storing user key '" + objUser.username + "'"));
|
||||
objUser.password = objUser.password;
|
||||
return _this.db.hmset("user:" + objUser.username, objUser, replyHandler("storing user properties '" + objUser.username + "'"));
|
||||
} else {
|
||||
return _this.log.warn(new Error('DB | username or password was missing'));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Fetch all user IDs and pass them to cb(err, obj).
|
||||
|
||||
@public getUserIds( *cb* )
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
|
||||
exports.getUserIds = function(cb) {
|
||||
_this.log.info("DB | getUserIds");
|
||||
return _this.db.smembers("users", cb);
|
||||
};
|
||||
|
||||
/*
|
||||
Fetch a user by id and pass it to cb(err, obj).
|
||||
|
||||
@public getUser( *userId, cb* )
|
||||
@param {String} userId
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
|
||||
exports.getUser = function(userId, cb) {
|
||||
_this.log.info("DB | getUser: '" + userId + "'");
|
||||
return _this.db.hgetall("user:" + userId, cb);
|
||||
};
|
||||
|
||||
/*
|
||||
Deletes a user and all his associated linked and active rules.
|
||||
|
||||
@public deleteUser( *userId* )
|
||||
@param {String} userId
|
||||
*/
|
||||
|
||||
|
||||
exports.deleteUser = function(userId) {
|
||||
_this.log.info("DB | deleteUser: '" + userId + "'");
|
||||
_this.db.srem("users", userId, replyHandler("Deleting user key '" + userId + "'"));
|
||||
_this.db.del("user:" + userId, replyHandler("Deleting user '" + userId + "'"));
|
||||
_this.db.smembers("user:" + userId + ":rules", function(err, obj) {
|
||||
var delLinkedRuleUser, id, _i, _len, _results;
|
||||
delLinkedRuleUser = function(ruleId) {
|
||||
return _this.db.srem("rule:" + ruleId + ":users", userId, replyHandler("Deleting user key '" + userId + "' in linked rule '" + ruleId + "'"));
|
||||
};
|
||||
_results = [];
|
||||
for (_i = 0, _len = obj.length; _i < _len; _i++) {
|
||||
id = obj[_i];
|
||||
_results.push(delLinkedRuleUser(id));
|
||||
}
|
||||
return _results;
|
||||
});
|
||||
_this.db.del("user:" + userId + ":rules", replyHandler("Deleting user '" + userId + "' rules"));
|
||||
_this.db.smembers("user:" + userId + ":active-rules", function(err, obj) {
|
||||
var delActivatedRuleUser, id, _i, _len, _results;
|
||||
delActivatedRuleUser = function(ruleId) {
|
||||
return _this.db.srem("rule:" + ruleId + ":active-users", userId, replyHandler("Deleting user key '" + userId + "' in active rule '" + ruleId + "'"));
|
||||
};
|
||||
_results = [];
|
||||
for (_i = 0, _len = obj.length; _i < _len; _i++) {
|
||||
id = obj[_i];
|
||||
_results.push(delActivatedRuleUser(id));
|
||||
}
|
||||
return _results;
|
||||
});
|
||||
_this.db.del("user:" + userId + ":active-rules", replyHandler("Deleting user '" + userId + "' rules"));
|
||||
_this.db.smembers("user:" + userId + ":roles", function(err, obj) {
|
||||
var delRoleUser, id, _i, _len, _results;
|
||||
delRoleUser = function(roleId) {
|
||||
return _this.db.srem("role:" + roleId + ":users", userId, replyHandler("Deleting user key '" + userId + "' in role '" + roleId + "'"));
|
||||
};
|
||||
_results = [];
|
||||
for (_i = 0, _len = obj.length; _i < _len; _i++) {
|
||||
id = obj[_i];
|
||||
_results.push(delRoleUser(id));
|
||||
}
|
||||
return _results;
|
||||
});
|
||||
return _this.db.del("user:" + userId + ":roles", replyHandler("Deleting user '" + userId + "' roles"));
|
||||
};
|
||||
|
||||
/*
|
||||
Checks the credentials and on success returns the user object to the
|
||||
callback(err, obj) function. The password has to be hashed (SHA-3-512)
|
||||
beforehand by the instance closest to the user that enters the password,
|
||||
because we only store hashes of passwords for security6 reasons.
|
||||
|
||||
@public loginUser( *userId, password, cb* )
|
||||
@param {String} userId
|
||||
@param {String} password
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
|
||||
exports.loginUser = function(userId, password, cb) {
|
||||
var fCheck;
|
||||
_this.log.info("DB | User '" + userId + "' tries to log in");
|
||||
fCheck = function(pw) {
|
||||
return function(err, obj) {
|
||||
if (err) {
|
||||
return cb(err, null);
|
||||
} else if (obj && obj.password) {
|
||||
if (pw === obj.password) {
|
||||
_this.log.info("DB | User '" + obj.username + "' logged in!");
|
||||
return cb(null, obj);
|
||||
} else {
|
||||
return cb(new Error('Wrong credentials!'), null);
|
||||
}
|
||||
} else {
|
||||
return cb(new Error('User not found!'), null);
|
||||
}
|
||||
};
|
||||
};
|
||||
return _this.db.hgetall("user:" + userId, fCheck(password));
|
||||
};
|
||||
|
||||
/*
|
||||
## User Roles
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Associate a role with a user.
|
||||
|
||||
@public storeUserRole( *userId, role* )
|
||||
@param {String} userId
|
||||
@param {String} role
|
||||
*/
|
||||
|
||||
|
||||
exports.storeUserRole = function(userId, role) {
|
||||
_this.log.info("DB | storeUserRole: '" + userId + ":" + role + "'");
|
||||
_this.db.sadd('roles', role, replyHandler("adding role '" + role + "' to role index set"));
|
||||
_this.db.sadd("user:" + userId + ":roles", role, replyHandler("adding role '" + role + "' to user '" + userId + "'"));
|
||||
return _this.db.sadd("role:" + role + ":users", userId, replyHandler("adding user '" + userId + "' to role '" + role + "'"));
|
||||
};
|
||||
|
||||
/*
|
||||
Fetch all roles of a user and pass them to cb(err, obj).
|
||||
|
||||
@public getUserRoles( *userId* )
|
||||
@param {String} userId
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
|
||||
exports.getUserRoles = function(userId, cb) {
|
||||
_this.log.info("DB | getUserRoles: '" + userId + "'");
|
||||
return _this.db.smembers("user:" + userId + ":roles", cb);
|
||||
};
|
||||
|
||||
/*
|
||||
Fetch all users of a role and pass them to cb(err, obj).
|
||||
|
||||
@public getUserRoles( *role* )
|
||||
@param {String} role
|
||||
@param {function} cb
|
||||
*/
|
||||
|
||||
|
||||
exports.getRoleUsers = function(role, cb) {
|
||||
_this.log.info("DB | getRoleUsers: '" + role + "'");
|
||||
return _this.db.smembers("role:" + role + ":users", cb);
|
||||
};
|
||||
|
||||
/*
|
||||
Remove a role from a user.
|
||||
|
||||
@public removeRoleFromUser( *role, userId* )
|
||||
@param {String} role
|
||||
@param {String} userId
|
||||
*/
|
||||
|
||||
|
||||
exports.removeUserRole = function(userId, role) {
|
||||
_this.log.info("DB | removeRoleFromUser: role '" + role + "', user '" + userId + "'");
|
||||
_this.db.srem("user:" + userId + ":roles", role, replyHandler("Removing role '" + role + "' from user '" + userId + "'"));
|
||||
return _this.db.srem("role:" + role + ":users", userId, replyHandler("Removing user '" + userId + "' from role '" + role + "'"));
|
||||
};
|
||||
|
||||
/*
|
||||
Shuts down the db link.
|
||||
|
||||
@public shutDown()
|
||||
*/
|
||||
|
||||
|
||||
exports.shutDown = function() {
|
||||
var _ref;
|
||||
return (_ref = _this.db) != null ? _ref.quit() : void 0;
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
57
js/queue.js
57
js/queue.js
|
|
@ -1,57 +0,0 @@
|
|||
// *(will be replaced by a Redis DB queue)*
|
||||
|
||||
// Queue.js
|
||||
// ========
|
||||
//
|
||||
// *A function to represent a queue*
|
||||
|
||||
// *Created by Stephen Morley - http://code.stephenmorley.org/ - and released under
|
||||
// the terms of the CC0 1.0 Universal legal code:*
|
||||
|
||||
// *http://creativecommons.org/publicdomain/zero/1.0/legalcode*
|
||||
|
||||
// *items are added to the end of the queue and removed from the front.*
|
||||
|
||||
exports.Queue = function(){
|
||||
// initialise the queue and offset
|
||||
var queue = [];
|
||||
var offset = 0;
|
||||
this.getLength = function(){
|
||||
// return the length of the queue
|
||||
return (queue.length - offset);
|
||||
};
|
||||
|
||||
/* Returns true if the queue is empty, and false otherwise.
|
||||
*/
|
||||
this.isEmpty = function(){ return (queue.length == 0); };
|
||||
|
||||
/* Enqueues the specified item. The parameter is:
|
||||
* item - the item to enqueue
|
||||
*/
|
||||
this.enqueue = function(item){ queue.push(item); };
|
||||
|
||||
/* Dequeues an item and returns it. If the queue is empty then undefined is
|
||||
* returned.
|
||||
*/
|
||||
this.dequeue = function(){
|
||||
// if the queue is empty, return undefined
|
||||
if (queue.length == 0) return undefined;
|
||||
// store the item at the front of the queue
|
||||
var item = queue[offset];
|
||||
// increment the offset and remove the free space if necessary
|
||||
if (++ offset * 2 >= queue.length){
|
||||
queue = queue.slice(offset);
|
||||
offset = 0;
|
||||
}
|
||||
// return the dequeued item
|
||||
return item;
|
||||
};
|
||||
|
||||
/* Returns the item at the front of the queue (without dequeuing it). If the
|
||||
* queue is empty then undefined is returned.
|
||||
*/
|
||||
this.peek = function(){
|
||||
// return the item at the front of the queue
|
||||
return (queue.length > 0 ? queue[offset] : undefined);
|
||||
};
|
||||
};
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
// Generated by CoffeeScript 1.7.1
|
||||
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
/*
|
||||
|
||||
Request Handler
|
||||
|
|
@ -8,10 +7,12 @@ Request Handler
|
|||
> the [HTTP Listener](http-listener.html). It will handle user requests for
|
||||
> pages as well as POST requests such as user login, module storing, event
|
||||
> invocation and also admin commands.
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
var crypto, db, dirHandlers, exports, fs, getHandlerPath, getRemoteScripts, getScript, getTemplate, mustache, path, qs, renderPage;
|
||||
var crypto, db, dirHandlers, exports, fs, getHandlerPath, getRemoteScripts, getScript, getTemplate, mustache, path, qs, renderPage,
|
||||
_this = this;
|
||||
|
||||
db = require('./persistence');
|
||||
|
||||
|
|
@ -27,35 +28,32 @@ Request Handler
|
|||
|
||||
dirHandlers = path.resolve(__dirname, '..', 'webpages', 'handlers');
|
||||
|
||||
exports = module.exports = (function(_this) {
|
||||
return function(args) {
|
||||
var fStoreUser, user, users;
|
||||
_this.log = args.logger;
|
||||
_this.userRequestHandler = args['request-service'];
|
||||
_this.objAdminCmds = {
|
||||
shutdown: function(obj, cb) {
|
||||
var data;
|
||||
data = {
|
||||
code: 200,
|
||||
message: 'Shutting down... BYE!'
|
||||
};
|
||||
setTimeout(args['shutdown-function'], 500);
|
||||
return cb(null, data);
|
||||
}
|
||||
};
|
||||
db(args);
|
||||
users = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', 'config', 'users.json')));
|
||||
fStoreUser = function(username, oUser) {
|
||||
oUser.username = username;
|
||||
return db.storeUser(oUser);
|
||||
};
|
||||
for (user in users) {
|
||||
fStoreUser(user, users[user]);
|
||||
exports = module.exports = function(args) {
|
||||
var fStoreUser, user, users;
|
||||
_this.log = args.logger;
|
||||
_this.userRequestHandler = args['request-service'];
|
||||
_this.objAdminCmds = {
|
||||
shutdown: function(obj, cb) {
|
||||
var data;
|
||||
data = {
|
||||
code: 200,
|
||||
message: 'Shutting down... BYE!'
|
||||
};
|
||||
setTimeout(args['shutdown-function'], 500);
|
||||
return cb(null, data);
|
||||
}
|
||||
return module.exports;
|
||||
};
|
||||
})(this);
|
||||
|
||||
db(args);
|
||||
users = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', 'config', 'users.json')));
|
||||
fStoreUser = function(username, oUser) {
|
||||
oUser.username = username;
|
||||
return db.storeUser(oUser);
|
||||
};
|
||||
for (user in users) {
|
||||
fStoreUser(user, users[user]);
|
||||
}
|
||||
return module.exports;
|
||||
};
|
||||
|
||||
/*
|
||||
Handles possible events that were posted to this server and pushes them into the
|
||||
|
|
@ -67,7 +65,8 @@ Request Handler
|
|||
objects.*
|
||||
|
||||
@public handleEvent( *req, resp* )
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
exports.handleEvent = function(req, resp) {
|
||||
var body;
|
||||
|
|
@ -103,7 +102,6 @@ Request Handler
|
|||
});
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Associates the user object with the session if login is successful.
|
||||
|
||||
|
|
@ -113,34 +111,32 @@ Request Handler
|
|||
objects.*
|
||||
|
||||
@public handleLogin( *req, resp* )
|
||||
*/
|
||||
*/
|
||||
|
||||
exports.handleLogin = (function(_this) {
|
||||
return function(req, resp) {
|
||||
var body;
|
||||
body = '';
|
||||
req.on('data', function(data) {
|
||||
return body += data;
|
||||
});
|
||||
return req.on('end', function() {
|
||||
var obj;
|
||||
obj = qs.parse(body);
|
||||
return db.loginUser(obj.username, obj.password, function(err, usr) {
|
||||
if (err) {
|
||||
_this.log.warn("RH | AUTH-UH-OH ( " + obj.username + " ): " + err.message);
|
||||
} else {
|
||||
req.session.user = usr;
|
||||
}
|
||||
if (req.session.user) {
|
||||
return resp.send('OK!');
|
||||
} else {
|
||||
return resp.send(401, 'NO!');
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
})(this);
|
||||
|
||||
exports.handleLogin = function(req, resp) {
|
||||
var body;
|
||||
body = '';
|
||||
req.on('data', function(data) {
|
||||
return body += data;
|
||||
});
|
||||
return req.on('end', function() {
|
||||
var obj;
|
||||
obj = qs.parse(body);
|
||||
return db.loginUser(obj.username, obj.password, function(err, usr) {
|
||||
if (err) {
|
||||
_this.log.warn("RH | AUTH-UH-OH ( " + obj.username + " ): " + err.message);
|
||||
} else {
|
||||
req.session.user = usr;
|
||||
}
|
||||
if (req.session.user) {
|
||||
return resp.send('OK!');
|
||||
} else {
|
||||
return resp.send(401, 'NO!');
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
A post request retrieved on this handler causes the user object to be
|
||||
|
|
@ -152,7 +148,8 @@ Request Handler
|
|||
objects.*
|
||||
|
||||
@public handleLogout( *req, resp* )
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
exports.handleLogout = function(req, resp) {
|
||||
if (req.session) {
|
||||
|
|
@ -161,25 +158,25 @@ Request Handler
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Resolves the path to a handler webpage.
|
||||
|
||||
@private getHandlerPath( *name* )
|
||||
@param {String} name
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
getHandlerPath = function(name) {
|
||||
return path.join(dirHandlers, name + '.html');
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Fetches a template.
|
||||
|
||||
@private getTemplate( *name* )
|
||||
@param {String} name
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
getTemplate = function(name) {
|
||||
var pth;
|
||||
|
|
@ -187,13 +184,13 @@ Request Handler
|
|||
return fs.readFileSync(pth, 'utf8');
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Fetches a script.
|
||||
|
||||
@private getScript( *name* )
|
||||
@param {String} name
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
getScript = function(name) {
|
||||
var pth;
|
||||
|
|
@ -201,13 +198,13 @@ Request Handler
|
|||
return fs.readFileSync(pth, 'utf8');
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Fetches remote scripts snippets.
|
||||
|
||||
@private getRemoteScripts( *name* )
|
||||
@param {String} name
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
getRemoteScripts = function(name) {
|
||||
var pth;
|
||||
|
|
@ -215,7 +212,6 @@ Request Handler
|
|||
return fs.readFileSync(pth, 'utf8');
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Renders a page, with helps of mustache, depending on the user session and returns it.
|
||||
|
||||
|
|
@ -223,7 +219,8 @@ Request Handler
|
|||
@param {String} name
|
||||
@param {Object} sess
|
||||
@param {Object} msg
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
renderPage = function(name, req, resp, msg) {
|
||||
var code, content, data, err, menubar, page, pageElements, pathSkel, remote_scripts, script, skeleton;
|
||||
|
|
@ -262,7 +259,6 @@ Request Handler
|
|||
return resp.send(code, mustache.render(page, data));
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Present the desired forge page to the user.
|
||||
|
||||
|
|
@ -272,7 +268,8 @@ Request Handler
|
|||
objects.*
|
||||
|
||||
@public handleForge( *req, resp* )
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
exports.handleForge = function(req, resp) {
|
||||
var page;
|
||||
|
|
@ -283,7 +280,6 @@ Request Handler
|
|||
return renderPage(page, req, resp);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Handles the user command requests.
|
||||
|
||||
|
|
@ -293,29 +289,27 @@ Request Handler
|
|||
objects.*
|
||||
|
||||
@public handleUser( *req, resp* )
|
||||
*/
|
||||
*/
|
||||
|
||||
exports.handleUserCommand = (function(_this) {
|
||||
return function(req, resp) {
|
||||
var body;
|
||||
if (req.session && req.session.user) {
|
||||
body = '';
|
||||
req.on('data', function(data) {
|
||||
return body += data;
|
||||
});
|
||||
return req.on('end', function() {
|
||||
var obj;
|
||||
obj = qs.parse(body);
|
||||
return _this.userRequestHandler(req.session.user, obj, function(obj) {
|
||||
return resp.send(obj.code, obj);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return resp.send(401, 'Login first!');
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
|
||||
exports.handleUserCommand = function(req, resp) {
|
||||
var body;
|
||||
if (req.session && req.session.user) {
|
||||
body = '';
|
||||
req.on('data', function(data) {
|
||||
return body += data;
|
||||
});
|
||||
return req.on('end', function() {
|
||||
var obj;
|
||||
obj = qs.parse(body);
|
||||
return _this.userRequestHandler(req.session.user, obj, function(obj) {
|
||||
return resp.send(obj.code, obj);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return resp.send(401, 'Login first!');
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Present the admin console to the user if he's allowed to see it.
|
||||
|
|
@ -326,7 +320,8 @@ Request Handler
|
|||
objects.*
|
||||
|
||||
@public handleForge( *req, resp* )
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
exports.handleAdmin = function(req, resp) {
|
||||
var msg, page;
|
||||
|
|
@ -341,7 +336,6 @@ Request Handler
|
|||
return renderPage(page, req, resp, msg);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Handles the admin command requests.
|
||||
|
||||
|
|
@ -351,32 +345,31 @@ Request Handler
|
|||
objects.*
|
||||
|
||||
@public handleAdminCommand( *req, resp* )
|
||||
*/
|
||||
*/
|
||||
|
||||
exports.handleAdminCommand = (function(_this) {
|
||||
return function(req, resp) {
|
||||
var body;
|
||||
if (req.session && req.session.user && req.session.user.isAdmin === "true") {
|
||||
body = '';
|
||||
req.on('data', function(data) {
|
||||
return body += data;
|
||||
});
|
||||
return req.on('end', function() {
|
||||
var obj;
|
||||
obj = qs.parse(body);
|
||||
_this.log.info('RH | Received admin request: ' + obj.command);
|
||||
if (obj.command && _this.objAdminCmds[obj.command]) {
|
||||
return _this.objAdminCmds[obj.command](obj, function(err, obj) {
|
||||
return resp.send(obj.code, obj);
|
||||
});
|
||||
} else {
|
||||
return resp.send(404, 'Command unknown!');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return resp.send(401, 'You need to be logged in as admin!');
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
|
||||
exports.handleAdminCommand = function(req, resp) {
|
||||
var body;
|
||||
if (req.session && req.session.user && req.session.user.isAdmin === "true") {
|
||||
body = '';
|
||||
req.on('data', function(data) {
|
||||
return body += data;
|
||||
});
|
||||
return req.on('end', function() {
|
||||
var obj;
|
||||
obj = qs.parse(body);
|
||||
_this.log.info('RH | Received admin request: ' + obj.command);
|
||||
if (obj.command && _this.objAdminCmds[obj.command]) {
|
||||
return _this.objAdminCmds[obj.command](obj, function(err, obj) {
|
||||
return resp.send(obj.code, obj);
|
||||
});
|
||||
} else {
|
||||
return resp.send(404, 'Command unknown!');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return resp.send(401, 'You need to be logged in as admin!');
|
||||
}
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
158
js/server.js
158
js/server.js
|
|
@ -1,158 +0,0 @@
|
|||
/*
|
||||
* Rules Server
|
||||
* ============
|
||||
* >This is the main module that is used to run the whole server:
|
||||
* >
|
||||
* > node server [log_type http_port]
|
||||
* >
|
||||
* >Valid `log_type`'s are:
|
||||
* >
|
||||
* >- `0`: standard I/O output (default)
|
||||
* >- `1`: log file (server.log)
|
||||
* >- `2`: silent
|
||||
* >
|
||||
* >`http_port` can be set to use another port, than defined in the
|
||||
* >[config](config.html) file, to listen to, e.g. used by the test suite.
|
||||
* >
|
||||
* >---
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
// Grab all required modules
|
||||
var path = require('path'),
|
||||
log = require('./logging'),
|
||||
conf = require('./config'),
|
||||
http_listener = require('./http_listener'),
|
||||
mm = require('./module_manager'),
|
||||
db = require('./db_interface'),
|
||||
engine = require('./engine'),
|
||||
semaphore = 0,
|
||||
args = {},
|
||||
procCmds = {},
|
||||
// Prepare the admin commands that are issued via HTTP requests.
|
||||
adminCmds = {
|
||||
'loadrules': mm.loadRulesFromFS,
|
||||
'loadaction': mm.loadActionModuleFromFS,
|
||||
'loadactions': mm.loadActionModulesFromFS,
|
||||
'loadevent': mm.loadEventModuleFromFS,
|
||||
'loadevents': mm.loadEventModulesFromFS,
|
||||
'loadusers': http_listener.loadUsers,
|
||||
'shutdown': shutDown
|
||||
};
|
||||
|
||||
/*
|
||||
* Error handling of the express port listener requires special attention,
|
||||
* thus we have to catch the process error, which is issued if
|
||||
* the port is already in use.
|
||||
*/
|
||||
process.on('uncaughtException', function(err) {
|
||||
switch(err.errno) {
|
||||
case 'EADDRINUSE':
|
||||
err.addInfo = 'http_port already in use, shutting down!';
|
||||
log.error('RS', err);
|
||||
shutDown();
|
||||
break;
|
||||
default: log.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* ## Initialize the Server
|
||||
* This function is invoked right after the module is loaded and starts the server.
|
||||
*/
|
||||
function init() {
|
||||
log.print('RS', 'STARTING SERVER');
|
||||
// Check whether the config file is ready, which is required to start the server.
|
||||
if(!conf.isReady()) {
|
||||
log.error('RS', 'Config file not ready!');
|
||||
process.exit();
|
||||
}
|
||||
|
||||
// Fetch the `log_type` argument and post a log about which log type is used.
|
||||
if(process.argv.length > 2) {
|
||||
args.logType = parseInt(process.argv[2]) || 0;
|
||||
if(args.logType === 0) log.print('RS', 'Log type set to standard I/O output');
|
||||
else if(args.logType === 1) log.print('RS', 'Log type set to file output');
|
||||
else if(args.logType === 2) log.print('RS', 'Log type set to silent');
|
||||
else log.print('RS', 'Unknown log type, using standard I/O');
|
||||
log(args);
|
||||
} else log.print('RS', 'No log method passed, using standard I/O');
|
||||
|
||||
// Fetch the `http_port` argument
|
||||
if(process.argv.length > 3) args.http_port = parseInt(process.argv[3]);
|
||||
else log.print('RS', 'No HTTP port passed, using standard port from config file');
|
||||
|
||||
db(args);
|
||||
// We only proceed with the initialization if the DB is ready
|
||||
db.isConnected(function(err, result) {
|
||||
if(!err) {
|
||||
|
||||
// Initialize all required modules with the args object.
|
||||
log.print('RS', 'Initialzing engine');
|
||||
engine(args);
|
||||
log.print('RS', 'Initialzing http listener');
|
||||
http_listener(args);
|
||||
log.print('RS', 'Initialzing module manager');
|
||||
mm(args);
|
||||
log.print('RS', 'Initialzing DB');
|
||||
|
||||
// Distribute handlers between modules to link the application.
|
||||
log.print('RS', 'Passing handlers to engine');
|
||||
engine.addDBLinkAndLoadActionsAndRules(db);
|
||||
log.print('RS', 'Passing handlers to http listener');
|
||||
http_listener.addHandlers(db, handleAdminCommands, engine.pushEvent);
|
||||
log.print('RS', 'Passing handlers to module manager');
|
||||
mm.addHandlers(db, engine.loadActionModule, engine.addRule);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* admin commands handler receives all command arguments and an answerHandler
|
||||
* object that eases response handling to the HTTP request issuer.
|
||||
*/
|
||||
function handleAdminCommands(args, answHandler) {
|
||||
if(args && args.cmd) {
|
||||
var func = adminCmds[args.cmd];
|
||||
if(typeof func == 'function') func(args, answHandler);
|
||||
} else log.print('RS', 'No command in request');
|
||||
setTimeout(function(ah) {
|
||||
return function() {
|
||||
if(!ah.isAnswered()) ah.answerError('Not handeled...');
|
||||
};
|
||||
}(answHandler), 2000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down the server.
|
||||
* @param {Object} args
|
||||
* @param {Object} answHandler
|
||||
*/
|
||||
function shutDown(args, answHandler) {
|
||||
if(answHandler) answHandler.answerSuccess('Goodbye!');
|
||||
log.print('RS', 'Received shut down command!');
|
||||
if(engine) engine.shutDown();
|
||||
if(http_listener) http_listener.shutDown();
|
||||
}
|
||||
|
||||
/*
|
||||
* ### Process Commands
|
||||
*
|
||||
* When the server is run as a child process, this function handles messages
|
||||
* from the parent process (e.g. the testing suite)
|
||||
*/
|
||||
process.on('message', function(cmd) {
|
||||
if(typeof procCmds[cmd] === 'function') procCmds[cmd]();
|
||||
else console.error('err with command');
|
||||
});
|
||||
|
||||
/**
|
||||
* The die command redirects to the shutDown function.
|
||||
*/
|
||||
procCmds.die = shutDown;
|
||||
|
||||
/*
|
||||
* *Start initialization*
|
||||
*/
|
||||
init();
|
||||
|
|
@ -1,182 +0,0 @@
|
|||
var path = require('path'),
|
||||
qs = require('querystring'),
|
||||
log = require('./logging'),
|
||||
db = require('./db_interface'),
|
||||
mm = require('./module_manager'),
|
||||
// ### Prepare the admin command handlers that are issued via HTTP requests. ###
|
||||
objAdminCmds = {
|
||||
'loadrules': mm.loadRulesFromFS,
|
||||
'loadaction': mm.loadActionModuleFromFS,
|
||||
'loadactions': mm.loadActionModulesFromFS,
|
||||
'loadevent': mm.loadEventModuleFromFS,
|
||||
'loadevents': mm.loadEventModulesFromFS
|
||||
};
|
||||
|
||||
exports = module.exports = function(args) {
|
||||
args = args || {};
|
||||
log(args);
|
||||
db(args);
|
||||
mm(args);
|
||||
mm.addDBLink(db);
|
||||
var users = JSON.parse(require('fs').readFileSync(path.resolve(__dirname, '..', 'config', 'users.json')));
|
||||
for(var name in users) {
|
||||
db.storeUser(users[name]);
|
||||
}
|
||||
log.print('RS', 'Initialzing module manager');
|
||||
|
||||
return module.exports;
|
||||
};
|
||||
|
||||
exports.addHandler = function(fShutdown) {
|
||||
objAdminCmds.shutdown = fShutdown;
|
||||
};
|
||||
|
||||
exports.handleRequest = function(req, resp) {
|
||||
req.on('end', function () {
|
||||
resp.end();
|
||||
});
|
||||
if(req.session && req.session.user) {
|
||||
resp.send('You\'re logged in');
|
||||
} else resp.sendfile(path.resolve(__dirname, '..', 'webpages', 'handlers', 'login.html'));
|
||||
// resp.end();
|
||||
log.print('UH', 'last: '+ req.session.lastPage);
|
||||
req.session.lastPage = req.originalUrl;
|
||||
log.print('UH', 'last: '+ req.session.lastPage);
|
||||
log.print('UH', 'retrieved req: '+ req.originalUrl);
|
||||
// console.log(req);
|
||||
};
|
||||
|
||||
exports.handleLogin = function(req, resp) {
|
||||
var body = '';
|
||||
req.on('data', function (data) { body += data; });
|
||||
req.on('end', function () {
|
||||
if(!req.session || !req.session.user) {
|
||||
var obj = qs.parse(body);
|
||||
db.loginUser(obj.username, obj.password, function(err, obj) {
|
||||
if(!err) req.session.user = obj;
|
||||
if(req.session.user) {
|
||||
resp.write('Welcome ' + req.session.user.name + '!');
|
||||
} else {
|
||||
resp.writeHead(401, { "Content-Type": "text/plain" });
|
||||
resp.write('Login failed!');
|
||||
}
|
||||
resp.end();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function answerHandler(r) {
|
||||
var response = r, hasBeenAnswered = false;
|
||||
function postAnswer(msg) {
|
||||
if(!hasBeenAnswered) {
|
||||
response.write(msg);
|
||||
response.end();
|
||||
hasBeenAnswered = true;
|
||||
}
|
||||
}
|
||||
return {
|
||||
answerSuccess: function(msg) {
|
||||
if(!hasBeenAnswered) response.writeHead(200, { "Content-Type": "text/plain" });
|
||||
postAnswer(msg);
|
||||
},
|
||||
answerError: function(msg) {
|
||||
if(!hasBeenAnswered) response.writeHead(400, { "Content-Type": "text/plain" });
|
||||
postAnswer(msg);
|
||||
},
|
||||
isAnswered: function() { return hasBeenAnswered; }
|
||||
};
|
||||
};
|
||||
|
||||
//TODO add loadUsers as directive to admin commands
|
||||
exports.loadUsers = function () {
|
||||
var users = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', 'users.json')));
|
||||
for(var name in users) {
|
||||
db.storeUser(users[name]);
|
||||
}
|
||||
};
|
||||
|
||||
function onAdminCommand(request, response) {
|
||||
var q = request.query;
|
||||
log.print('HL', 'Received admin request: ' + request.originalUrl);
|
||||
if(q.cmd) {
|
||||
fAdminCommands(q, answerHandler(response));
|
||||
// answerSuccess(response, 'Thank you, we try our best!');
|
||||
} else answerError(response, 'I\'m not sure about what you want from me...');
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
admin commands handler receives all command arguments and an answerHandler
|
||||
object that eases response handling to the HTTP request issuer.
|
||||
|
||||
@private fAdminCommands( *args, answHandler* )
|
||||
*/
|
||||
|
||||
|
||||
function fAdminCommands(args, answHandler) {
|
||||
var fAnsw, _name;
|
||||
if (args && args.cmd) {
|
||||
if (typeof objAdminCmds[_name = args.cmd] === "function") {
|
||||
objAdminCmds[_name](args, answHandler);
|
||||
}
|
||||
} else {
|
||||
log.print('RS', 'No command in request');
|
||||
}
|
||||
/*
|
||||
The fAnsw function receives an answerHandler object as an argument when called
|
||||
and returns an anonymous function
|
||||
*/
|
||||
|
||||
fAnsw = function(ah) {
|
||||
/*
|
||||
The anonymous function checks whether the answerHandler was already used to
|
||||
issue an answer, if no answer was provided we answer with an error message
|
||||
*/
|
||||
|
||||
return function() {
|
||||
if (!ah.isAnswered()) {
|
||||
return ah.answerError('Not handled...');
|
||||
}
|
||||
};
|
||||
};
|
||||
/*
|
||||
Delayed function call of the anonymous function that checks the answer handler
|
||||
*/
|
||||
|
||||
return setTimeout(fAnsw(answHandler), 2000);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
###
|
||||
admin commands handler receives all command arguments and an answerHandler
|
||||
object that eases response handling to the HTTP request issuer.
|
||||
|
||||
@private fAdminCommands( *args, answHandler* )
|
||||
###
|
||||
fAdminCommands = (args, answHandler) ->
|
||||
if args and args.cmd
|
||||
adminCmds[args.cmd]? args, answHandler
|
||||
else
|
||||
log.print 'RS', 'No command in request'
|
||||
|
||||
###
|
||||
The fAnsw function receives an answerHandler object as an argument when called
|
||||
and returns an anonymous function
|
||||
###
|
||||
fAnsw = (ah) ->
|
||||
###
|
||||
The anonymous function checks whether the answerHandler was already used to
|
||||
issue an answer, if no answer was provided we answer with an error message
|
||||
###
|
||||
() ->
|
||||
if not ah.isAnswered()
|
||||
ah.answerError 'Not handled...'
|
||||
|
||||
###
|
||||
Delayed function call of the anonymous function that checks the answer handler
|
||||
###
|
||||
setTimeout fAnsw(answHandler), 2000
|
||||
*/
|
||||
81
js/users.js
81
js/users.js
|
|
@ -1,81 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var log = require('./logging'),
|
||||
objCmds = {
|
||||
addUser: addUser,
|
||||
getUser: getUser,
|
||||
delUser: delUser,
|
||||
addRule: addRule,
|
||||
getRules: getRules,
|
||||
delRule: delRule
|
||||
};
|
||||
|
||||
exports = module.exports = function(args) {
|
||||
args = args || {};
|
||||
log(args);
|
||||
return module.exports;
|
||||
};
|
||||
|
||||
exports.handleCommand = function(args, cb) {
|
||||
if(!args.cmd) {
|
||||
var e = new Error('No command defined!');
|
||||
if(typeof cb === 'function') cb(e);
|
||||
else log.error('US', e);
|
||||
} else {
|
||||
objCmds[args.cmd](args, cb);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} args
|
||||
* @param {function} cb
|
||||
*/
|
||||
function addUser(args, cb) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} args
|
||||
* @param {function} cb
|
||||
*/
|
||||
function getUser(args, cb) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} args
|
||||
* @param {function} cb
|
||||
*/
|
||||
function delUser(args, cb) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} args
|
||||
* @param {function} cb
|
||||
*/
|
||||
function addRule(args, cb) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} args
|
||||
* @param {function} cb
|
||||
*/
|
||||
function getRule(args, cb) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} args
|
||||
* @param {function} cb
|
||||
*/
|
||||
function delRule(args, cb) {
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
// Generated by CoffeeScript 1.7.1
|
||||
|
||||
// Generated by CoffeeScript 1.6.3
|
||||
/*
|
||||
|
||||
WebAPI-ECA Engine
|
||||
|
|
@ -10,10 +9,12 @@ WebAPI-ECA Engine
|
|||
> node webapi-eca [opt]
|
||||
>
|
||||
> See below in the optimist CLI preparation for allowed optional parameters `[opt]`.
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
var argv, cm, conf, cp, db, engine, fs, http, init, logconf, logger, nameEP, opt, optimist, path, procCmds, shutDown, usage;
|
||||
var argv, cm, conf, cp, db, engine, fs, http, init, logconf, logger, nameEP, opt, optimist, path, procCmds, shutDown, usage,
|
||||
_this = this;
|
||||
|
||||
logger = require('./logging');
|
||||
|
||||
|
|
@ -39,10 +40,10 @@ WebAPI-ECA Engine
|
|||
|
||||
procCmds = {};
|
||||
|
||||
|
||||
/*
|
||||
Let's prepare the optimist CLI optional arguments `[opt]`:
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
usage = 'This runs your webapi-based ECA engine';
|
||||
|
||||
|
|
@ -129,76 +130,72 @@ WebAPI-ECA Engine
|
|||
|
||||
this.log.info('RS | STARTING SERVER');
|
||||
|
||||
|
||||
/*
|
||||
This function is invoked right after the module is loaded and starts the server.
|
||||
|
||||
@private init()
|
||||
*/
|
||||
*/
|
||||
|
||||
init = (function(_this) {
|
||||
return function() {
|
||||
var args;
|
||||
args = {
|
||||
logger: _this.log,
|
||||
logconf: logconf
|
||||
};
|
||||
args['http-port'] = parseInt(argv.w || conf.getHttpPort());
|
||||
args['db-port'] = parseInt(argv.d || conf.getDbPort());
|
||||
args['keygen'] = conf.getKeygenPassphrase();
|
||||
_this.log.info('RS | Initialzing DB');
|
||||
db(args);
|
||||
return db.isConnected(function(err) {
|
||||
var cliArgs, poller;
|
||||
if (err) {
|
||||
_this.log.error('RS | No DB connection, shutting down system!');
|
||||
return shutDown();
|
||||
} else {
|
||||
_this.log.info('RS | Initialzing engine');
|
||||
engine(args);
|
||||
_this.log.info('RS | Forking a child process for the event poller');
|
||||
cliArgs = [args.logconf['mode'], args.logconf['io-level'], args.logconf['file-level'], args.logconf['file-path'], args.logconf['nolog']];
|
||||
poller = cp.fork(path.resolve(__dirname, nameEP), cliArgs);
|
||||
_this.log.info('RS | Initialzing module manager');
|
||||
cm(args);
|
||||
cm.addRuleListener(engine.internalEvent);
|
||||
cm.addRuleListener(function(evt) {
|
||||
return poller.send(evt);
|
||||
});
|
||||
_this.log.info('RS | Initialzing http listener');
|
||||
args['request-service'] = cm.processRequest;
|
||||
args['shutdown-function'] = shutDown;
|
||||
return http(args);
|
||||
}
|
||||
});
|
||||
|
||||
init = function() {
|
||||
var args;
|
||||
args = {
|
||||
logger: _this.log,
|
||||
logconf: logconf
|
||||
};
|
||||
})(this);
|
||||
|
||||
args['http-port'] = parseInt(argv.w || conf.getHttpPort());
|
||||
args['db-port'] = parseInt(argv.d || conf.getDbPort());
|
||||
args['keygen'] = conf.getKeygenPassphrase();
|
||||
_this.log.info('RS | Initialzing DB');
|
||||
db(args);
|
||||
return db.isConnected(function(err) {
|
||||
var cliArgs, poller;
|
||||
if (err) {
|
||||
_this.log.error('RS | No DB connection, shutting down system!');
|
||||
return shutDown();
|
||||
} else {
|
||||
_this.log.info('RS | Initialzing engine');
|
||||
engine(args);
|
||||
_this.log.info('RS | Forking a child process for the event poller');
|
||||
cliArgs = [args.logconf['mode'], args.logconf['io-level'], args.logconf['file-level'], args.logconf['file-path'], args.logconf['nolog']];
|
||||
poller = cp.fork(path.resolve(__dirname, nameEP), cliArgs);
|
||||
_this.log.info('RS | Initialzing module manager');
|
||||
cm(args);
|
||||
cm.addRuleListener(engine.internalEvent);
|
||||
cm.addRuleListener(function(evt) {
|
||||
return poller.send(evt);
|
||||
});
|
||||
_this.log.info('RS | Initialzing http listener');
|
||||
args['request-service'] = cm.processRequest;
|
||||
args['shutdown-function'] = shutDown;
|
||||
return http(args);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Shuts down the server.
|
||||
|
||||
@private shutDown()
|
||||
*/
|
||||
*/
|
||||
|
||||
shutDown = (function(_this) {
|
||||
return function() {
|
||||
_this.log.warn('RS | Received shut down command!');
|
||||
if (db != null) {
|
||||
db.shutDown();
|
||||
}
|
||||
engine.shutDown();
|
||||
return process.exit();
|
||||
};
|
||||
})(this);
|
||||
|
||||
shutDown = function() {
|
||||
_this.log.warn('RS | Received shut down command!');
|
||||
if (db != null) {
|
||||
db.shutDown();
|
||||
}
|
||||
engine.shutDown();
|
||||
return process.exit();
|
||||
};
|
||||
|
||||
/*
|
||||
*# Process Commands
|
||||
## Process Commands
|
||||
|
||||
When the server is run as a child process, this function handles messages
|
||||
from the parent process (e.g. the testing suite)
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
process.on('message', function(cmd) {
|
||||
return typeof procCmds[cmd] === "function" ? procCmds[cmd]() : void 0;
|
||||
4
node_modules/my-cryptico/index.js
generated
vendored
4
node_modules/my-cryptico/index.js
generated
vendored
|
|
@ -1,8 +1,8 @@
|
|||
var fs = require('fs');
|
||||
// We do this so we use the exact same librar in the browser and in node
|
||||
// We do this so we use the exact same library in the browser and in node
|
||||
// Read and eval library
|
||||
// Eval is evil...
|
||||
var filedata = fs.readFileSync('./webpages/public/js/cryptico.js','utf8');
|
||||
eval(filedata);
|
||||
|
||||
exports = module.exports = cryptico;
|
||||
exports = module.exports = cryptico;
|
||||
22
package.json
22
package.json
|
|
@ -2,7 +2,7 @@
|
|||
"name": "webapi-eca",
|
||||
"author": "Dominic Bosch",
|
||||
"description": "An ECA inference engine as a middleware between Web API's",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"private": true,
|
||||
"repository": {
|
||||
"type" : "git",
|
||||
|
|
@ -22,25 +22,5 @@
|
|||
"redis": "0.10.0",
|
||||
"request": "2.33.0",
|
||||
"optimist": "0.6.1"
|
||||
},
|
||||
"__comment": {
|
||||
"dependencies": {
|
||||
"glob" :"3.2.7",
|
||||
"docco": "0.6.2",
|
||||
"socket.io": "0.9.16",
|
||||
"contextify": "0.1.6",
|
||||
"dependencies_safe": {
|
||||
"connect-redis": "1.4.6",
|
||||
"crypto-js": "3.1.2",
|
||||
"express": "3.4.0",
|
||||
"groc": "0.6.1",
|
||||
"mustache": "0.7.3",
|
||||
"needle": "0.6.1",
|
||||
"nodeunit": "0.8.4",
|
||||
"redis": "0.9.0",
|
||||
"request": "2.27.0",
|
||||
"coffee-script": "1.6.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
#!/bin/bash
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
node $DIR/js-coffee/webapi-eca | $DIR/node_modules/bunyan/bin/bunyan
|
||||
node $DIR/js/webapi-eca | $DIR/node_modules/bunyan/bin/bunyan
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
var cs = require('coffee-script');
|
||||
var fs = require('fs');
|
||||
var csSrc = fs.readFileSync('coffee/config.coffee', { encoding: "utf-8" });
|
||||
// csSrc = "log = require './logging'";
|
||||
console.log(cs.compile(csSrc));
|
||||
|
|
@ -6,7 +6,7 @@ var fs = require( 'fs' ),
|
|||
db = require( './js-coffee/persistence' ),
|
||||
args = process.argv.slice( 2 ),
|
||||
fEnd = function() {
|
||||
console.log('Shutting down DB from unit_test.sh script...');
|
||||
console.log( 'Shutting down DB from unit_test.sh script...' );
|
||||
db.shutDown();
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue