bad commit, new architecture

This commit is contained in:
Dominic Bosch 2013-11-14 15:06:10 +01:00
parent b743a880e4
commit 11ced9a57e
27 changed files with 850 additions and 245 deletions

View file

@ -33,7 +33,8 @@ Insert your settings, for example:
{
"http_port": 8125,
"db_port": 6379,
"crypto_key": "[your key]"
"crypto_key": "[your key]",
"session_secret": "[your secret]"
}
Start the server:

View file

@ -1,20 +1,19 @@
/*
* # groc Documentation
* Create the documentation to be displayed through the webserver.
*/
require('groc').CLI([
"README.md",
"TODO.js",
"LICENSE.js",
"js/*",
"mod_actions/**/*.js",
"mod_events/**/*.js"
// ,
// "rules/*.json"
*/
require('groc').CLI(
[
"README.md",
"TODO.js",
"LICENSE.js",
"js/*",
"mod_actions/**/*.js",
"mod_events/**/*.js",
"-o./webpages/doc"
],
function(error) {
if (error) {
process.exit(1);
}
function(err) {
if (err) console.error(err);
else console.log('Done!');
}
);

View file

@ -1,78 +1,79 @@
'use strict';
/**
* command standard config file loading and pass certain property back to the callback
* @param {String} prop
* @param {function} cb
*/
function fetchProp(prop, cb) {
if(typeof cb === 'function') {
exports.getConfig(null, function(err, data) {
if(!err) cb(null, data[prop]);
else cb(err);
});
var path = require('path'), log, config;
exports = module.exports = function(relPath) {
if(typeof relPath !== 'string') relPath = path.join('config', 'config.json');
loadConfigFile(relPath);
};
exports.init = function(args, cb) {
args = args || {};
if(args.log) log = args.log;
else log = args.log = require('./logging');
loadConfigFile(path.join('config', 'config.json'));
if(typeof cb === 'function') cb();
};
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'));
}
} catch (e) {
e.addInfo = 'no config ready';
log.error('CF', e);
}
}
/**
*
* @param {String[]} relPath
* @param {Object} cb
* Fetch a property from the configuration
* @param {String} prop
*/
exports.getConfig = function(relPath, cb) {
var fs = require('fs'), path = require('path'), log = require('./logging');
if(!relPath) relPath = path.join('config', 'config.json');
fs.readFile(
path.resolve(__dirname, '..', relPath),
'utf8',
function (err, data) {
if (err) {
err.addInfo = 'config file loading';
if(typeof cb === 'function') cb(err);
else log.error('CF', err);
} else {
try {
var config = JSON.parse(data);
if(!config.http_port || !config.db_port || !config.crypto_key) {
var e = new Error('Missing property, requires:\n'
+ ' - http_port\n'
+ ' - db_port\n'
+ ' - crypto_key');
if(typeof cb === 'function') cb(e);
else log.error('CF', e);
} else {
if(typeof cb === 'function') cb(null, config);
else log.print('CF', 'config file loaded successfully but pointless since no callback defined...');
}
} catch(e) {
e.addInfo = 'config file parsing';
log.error('CF', e);
}
}
}
);
function fetchProp(prop) {
if(config) return config[prop];
}
/**
* Get the HTTP port
*/
exports.getHttpPort = function() {
return fetchProp('http_port');
};
/**
* Command config file loading and retrieve the http port via the callback.
* @param {function} cb
* Get the DB port
*/
exports.getHttpPort = function(cb) {
fetchProp('http_port', cb);
exports.getDBPort = function() {
return fetchProp('db_port');
};
/**
* Command config file loading and retrieve the DB port via the callback.
* @param {function} cb
* Get the crypto key
*/
exports.getDBPort = function(cb) {
fetchProp('db_port', cb);
exports.getCryptoKey = function() {
return fetchProp('crypto_key');
};
/**
* Command config file loading and retrieve the crypto key via the callback.
* @param {function} cb
* Get the session secret
*/
exports.getCryptoKey = function(cb) {
fetchProp('crypto_key', cb);
exports.getSessionSecret = function() {
return fetchProp('session_secret');
};
exports.die = function(cb) {
if(typeof cb === 'function') cb();
};

View file

@ -11,32 +11,32 @@
// '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 port where the DB listens to requests
* and a key that is used for encryptions.
* @param {int} db_port
var log, crypto_key, db,
redis = require('redis'),
crypto = require('crypto');
/**
* Initializes the DB connection. Requires a valid configuration file which contains
* a db port and a crypto key.
* @param args {Object}
* @param cb {function}
*/
exports.init = function(cb){
require('./config').getConfig(null, function(err, data) {
if(!err) {
crypto_key = data.crypto_key;
db = redis.createClient(data.db_port);
db.on("error", function (err) {
err.addInfo = 'message from DB';
log.error('DB', err);
});
if(typeof cb === 'function') cb();
} else {
err.addInfo = 'fetching db_port and crypto_key';
if(typeof cb === 'function') cb(err);
}
exports.init = function(args, cb) {
args = args || {};
if(args.log) log = args.log;
else log = args.log = require('./logging');
var config = require('./config');
config.init(args);
crypto_key = config.getCryptoKey();
db = redis.createClient(config.getDBPort());
db.on("error", function (err) {
err.addInfo = 'message from DB';
log.error('DB', err);
});
if(typeof cb === 'function') cb();
};
/**
@ -264,3 +264,19 @@ exports.getRules = function(callback) {
getSetRecords('rules', exports.getRule, callback);
};
/**
*
* @param {function} cb
* @param {Object} objUser
*/
exports.storeUser = function(cb, objUser) {
if(objUser && objUser.id) {
db.sadd('users', objUser.id, replyHandler('storing user key ' + objUser.id));
db.set('user:' + objUser.id, data, replyHandler('storing user properties ' + objUser.id));
}
};
exports.die = function(cb) {
if(typeof cb === 'function') cb();
};

View file

@ -1,9 +1,18 @@
'use strict';
var log, ml;
exports.init = function(args, cb) {
args = args || {};
if(args.log) log = args.log;
else log = args.log = require('./logging');
ml = require('./module_loader').init(args);
if(typeof cb === 'function') cb();
};
var path = require('path'),
cp = require('child_process'),
ml = require('./module_loader'),
log = require('./logging'),
poller, db, isRunning = true,
qEvents = new (require('./queue')).Queue(); // export queue into redis
@ -17,7 +26,7 @@ var regex = /\$X\.[\w\.\[\]]*/g, // find properties of $X
* @param {String} db_port the db port
* @param {String} crypto_key the key to be used for encryption on the db, max legnth 256
*/
function init(db_link) {
exports.addDBLink = function(db_link) {
db = db_link;
loadActions();
poller = cp.fork(path.resolve(__dirname, 'eventpoller'));
@ -25,11 +34,11 @@ function init(db_link) {
if(evt.event === 'ep_finished_loading') {
eventsLoaded = true;
tryToLoadRules();
} else pushEvent(evt);
} else exports.pushEvent(evt);
});
//start to poll the event queue
pollQueue();
}
};
function loadActions() {
db.getActionModules(function(err, obj) {
@ -64,7 +73,7 @@ function loadActions() {
function tryToLoadRules() {
if(eventsLoaded && actionsLoaded) {
db.getRules(function(err, obj) {
for(var el in obj) loadRule(JSON.parse(obj[el]));
for(var el in obj) exports.loadRule(JSON.parse(obj[el]));
});
}
}
@ -73,16 +82,16 @@ function tryToLoadRules() {
* Insert an action module into the list of available interfaces.
* @param {Object} objModule the action module object
*/
function loadActionModule(name, objModule) {
exports.loadActionModule = function(name, objModule) {
log.print('EN', 'Action module "' + name + '" loaded');
listActionModules[name] = objModule;
}
};
/**
* Insert a rule into the eca rules repository
* @param {Object} objRule the rule object
*/
function loadRule(objRule) {
exports.loadRule = function(objRule) {
//TODO validate rule
log.print('EN', 'Loading Rule: ' + objRule.id);
if(listRules[objRule.id]) log.print('EN', 'Replacing rule: ' + objRule.id);
@ -94,7 +103,7 @@ function loadRule(objRule) {
} catch (err) {
log.print('EN', 'Unable to inform poller about new active rule!');
}
}
};
function pollQueue() {
if(isRunning) {
@ -110,9 +119,9 @@ function pollQueue() {
* Stores correctly posted events in the queue
* @param {Object} evt The event object
*/
function pushEvent(evt) {
exports.pushEvent = function(evt) {
qEvents.enqueue(evt);
}
};
/**
* Handles correctly posted events
@ -225,29 +234,26 @@ function preprocessActionArguments(evt, act, res) {
}
}
function loadEventModule(args, answHandler) {
exports.loadEventModule = function(args, answHandler) {
if(args && args.name) {
answHandler.answerSuccess('Loading event module ' + args.name + '...');
poller.send('cmd|loadevent|'+args.name);
} else if(args) answHandler.answerError(args.name + ' not found');
}
};
function loadEventModules(args, answHandler) {
exports.loadEventModules = function(args, answHandler) {
answHandler.answerSuccess('Loading event moules...');
poller.send('cmd|loadevents');
}
};
function shutDown() {
exports.shutDown = function() {
log.print('EN', 'Shutting down Poller and DB Link');
isRunning = false;
if(poller) poller.send('cmd|shutdown');
if(db) db.shutDown();
}
};
exports.init = init;
exports.loadActionModule = loadActionModule;
exports.loadRule = loadRule;
exports.loadEventModule = loadEventModule;
exports.loadEventModules = loadEventModules;
exports.pushEvent = pushEvent;
exports.shutDown = shutDown;
exports.die = function(cb) {
if(typeof cb === 'function') cb();
};

View file

@ -2,11 +2,19 @@
'use strict';
var log, db, ml;
function init() {
log = require('./logging');
if(process.argv.length > 2) log(parseInt(process.argv[2]) || 0);
ml = require('./module_loader').init({ log: log }),
db = require('./db_interface').init({ log: log }, doneInitDB);
if(typeof cb === 'function') cb();
};
var fs = require('fs'),
path = require('path'),
log = require('./logging'),
db = require('./db_interface'),
ml = require('./module_loader'),
listMessageActions = {},
listAdminCommands = {},
listEventModules = {},
@ -16,43 +24,41 @@ var fs = require('fs'),
eId = 0;
//TODO allow different polling intervals (a wrapper together with settimeout per to be polled could be an easy and solution)
function init() {
db.init(function(err) {
if(!err) {
function doneInitDB(err) {
if(!err) {
//TODO eventpoller will not load event modules from filesystem, this will be done by
// the moduel manager and the eventpoller receives messages about new/updated active rules
db.getEventModules(function(err, obj) {
if(err) log.error('EP', 'retrieving Event Modules from DB!');
else {
if(!obj) {
log.print('EP', 'No Event Modules found in DB!');
process.send({ event: 'ep_finished_loading' });
} else {
var m, semaphore = 0;
for(var el in obj) {
semaphore++;
m = ml.requireFromString(obj[el], el);
db.getEventModuleAuth(el, function(mod) {
return function(err, obj) {
if(--semaphore === 0) process.send({ event: 'ep_finished_loading' });
if(obj && mod.loadCredentials) mod.loadCredentials(JSON.parse(obj));
};
}(m));
log.print('EP', 'Loading Event Module: ' + el);
listEventModules[el] = m;
}
db.getEventModules(function(err, obj) {
if(err) log.error('EP', 'retrieving Event Modules from DB!');
else {
if(!obj) {
log.print('EP', 'No Event Modules found in DB!');
process.send({ event: 'ep_finished_loading' });
} else {
var m, semaphore = 0;
for(var el in obj) {
semaphore++;
m = ml.requireFromString(obj[el], el);
db.getEventModuleAuth(el, function(mod) {
return function(err, obj) {
if(--semaphore === 0) process.send({ event: 'ep_finished_loading' });
if(obj && mod.loadCredentials) mod.loadCredentials(JSON.parse(obj));
};
}(m));
log.print('EP', 'Loading Event Module: ' + el);
listEventModules[el] = m;
}
initAdminCommands();
initMessageActions();
}
});
} else {
err.addInfo = 'eventpoller init failed';
log.error('EP', err);
}
});
initAdminCommands();
initMessageActions();
}
});
} else {
err.addInfo = 'eventpoller init failed';
log.error('EP', err);
}
}
function initMessageActions() {
@ -150,5 +156,10 @@ function pollLoop() {
}
}
exports.die = function(cb) {
if(typeof cb === 'function') cb();
};
init();
pollLoop();

View file

@ -1,26 +1,69 @@
// # HTTP Listener
// Isso
'use strict';
var log, config;
exports.init = function(args, cb) {
args = args || {};
if(args.log) log = args.log;
else log = args.log = require('./logging');
config = require('./config').init(args);
if(typeof cb === 'function') cb();
};
var path = require('path'),
express = require('express'),
port = express(),
log = require('./logging'),
app = express(),
RedisStore = require('connect-redis')(express),
qs = require('querystring'),
adminHandler, eventHandler, server;
function init(http_port, funcAdminHandler, funcEvtHandler) {
if(!http_port || !funcEvtHandler) {
exports.addHandlers = function(funcAdminHandler, funcEvtHandler) {
if(!funcEvtHandler) {
log.error('HL', 'ERROR: either port or eventHandler function not defined!');
return;
}
adminHandler = funcAdminHandler;
eventHandler = funcEvtHandler;
port.use('/doc/', express.static(path.resolve(__dirname, '..', 'doc/')));
port.get('/admin', onAdminCommand);
port.post('/pushEvents', onPushEvent);
server = port.listen(http_port); // inbound event channel
log.print('HL', 'Started listening for http requests on port ' + http_port);
}
//FIXME this whole webserver requires clean approach together with session handling all over the engine.
//One entry point, from then collecting response contents and one exit point that sends it!
app.use(express.cookieParser());
app.use('/doc/', express.static(path.resolve(__dirname, '..', 'webpages', 'doc')));
app.use('/mobile/', express.static(path.resolve(__dirname, '..', 'webpages', 'mobile')));
app.use('/rulesforge/', express.static(path.resolve(__dirname, '..', 'webpages', 'rulesforge')));
app.get('/admin', onAdminCommand);
app.post('/pushEvents', onPushEvent);
var db_port = config.getDBPort(),
sess_sec = config.getSessionSecret(),
http_port = config.getHttpPort();
if(db_port) {
app.use(express.session({
store: new RedisStore({
host: 'localhost',
port: db_port,
db: 2
// ,
// pass: 'RedisPASS'
}),
// FIXME use a secret from config
secret: sess_sec
}));
log.print('HL', 'Added redis DB as session backbone');
} else {
if(sess_sec) app.use(express.session({secret: sess_sec}));
else {
app.use(express.session({ secret: '#C[>;j`@".TXm2TA;A2Tg)' }));
log.print('HL', 'no session secret found?!');
}
log.print('HL', 'no session backbone');
}
if(http_port) server = app.listen(http_port); // inbound event channel
else log.error('HL', new Error('No HTTP port found!?'));
};
function answerHandler(r) {
var response = r, hasBeenAnswered = false;
@ -89,8 +132,12 @@ function onPushEvent(request, response) {
});
}
exports.init = init;
exports.shutDown = function() {
log.print('HL', 'Shutting down HTTP listener');
process.exit(); // This is a bit brute force...
};
exports.die = function(cb) {
if(typeof cb === 'function') cb();
};

View file

@ -2,7 +2,32 @@
* Logging
* =======
* Functions to handle logging and errors.
*
* Valid log methods are:
*
* - 0 standard I/O
* - 1 file
* - 2 silent
*/
var logMethods = [ flushToConsole, flushToFile, null],
logMethod = 0, logFile;
exports = module.exports = function(logMeth) {
if(logMeth) logMethod = parseInt(logMeth) || 0;
};
function flush(err, msg) {
if(typeof logMethods[logMethod] === 'function') logMethods[logMethod](err, msg);
}
function flushToConsole(err, msg) {
if(err) console.error(msg);
else console.log(msg);
}
function flushToFile(err, msg) {
fs.appendFile('./server.log', msg, function (err) {});
}
// @function print(module, msg)
@ -12,29 +37,48 @@
* @param {String} msg
*/
exports.print = function(module, msg) {
console.log((new Date()).toISOString() + ' | ' + module + ' | ' + msg);
flush(false, (new Date()).toISOString() + ' | ' + module + ' | ' + msg);
};
// @method error(module, msg)
/*
/**
* Prints a log to stderr.
* @param {String} module
* @param {Error} err
*/
exports.error = function(module, err) {
function printError(module, err, isSevere) {
var ts = (new Date()).toISOString() + ' | ', ai = '';
if(typeof err === 'string') {
var e = new Error();
if(module) console.error(ts + module + ' | ERROR AND BAD HANDLING: ' + err + '\n' + e.stack);
else console.error(ts + '!N/A! | ERROR, BAD HANDLING AND NO MODULE NAME: ' + err + '\n' + e.stack);
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) console.error(ts + module + ' | ERROR'+ai+': ' + err.message + '\n' + err.stack);
else console.error(ts + '!N/A! | ERROR AND NO MODULE NAME'+ai+': ' + err.message + '\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');
console.error(e.message + ': \n' + e.stack);
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);
};

View file

@ -1,8 +1,17 @@
'use strict';
var log;
exports.init = function(args, cb) {
args = args || {};
if(args.log) log = args.log;
else log = args.log = require('./logging');
if(typeof cb === 'function') cb();
};
var fs = require('fs'),
path = require('path'),
log = require('./logging');
path = require('path');
function requireFromString(src, name, dir) {
exports.requireFromString = function(src, name, dir) {
if(!dir) dir = __dirname;
//FIXME load modules only into a safe environment with given modules, no access to whole application
var id = path.resolve(dir, name, name + '.js');
@ -16,16 +25,16 @@ function requireFromString(src, name, dir) {
// log.error('LM', ' during compilation of ' + name + ': ' + err);
}
return m.exports;
}
};
function loadModule(directory, name, callback) {
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 = requireFromString(data, name, directory);
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) {
@ -44,9 +53,9 @@ function loadModule(directory, name, callback) {
} catch(err) {
log.error('LM', 'Failed loading module "' + name + '"');
}
}
};
function loadModules(directory, callback) {
exports.loadModules = function(directory, callback) {
fs.readdir(path.resolve(__dirname, '..', directory), function (err, list) {
if (err) {
log.error('LM', 'loading modules directory: ' + err);
@ -56,14 +65,14 @@ function loadModules(directory, callback) {
list.forEach(function (file) {
fs.stat(path.resolve(__dirname, '..', directory, file), function (err, stat) {
if (stat && stat.isDirectory()) {
loadModule(directory, file, callback);
exports.loadModule(directory, file, callback);
}
});
});
});
}
exports.loadModule = loadModule;
exports.loadModules = loadModules;
exports.requireFromString = requireFromString;
};
exports.die = function(cb) {
if(typeof cb === 'function') cb();
};

View file

@ -6,17 +6,29 @@
> Event and Action modules are loaded as strings and stored in the database,
> then compiled into node modules and and rules
*/
'use strict';
var log, ml;
exports.init = function(args, cb) {
args = args || {};
if(args.log) log = args.log;
else log = args.log = require('./logging');
ml = require('./module_loader').init(args);
if(typeof cb === 'function') cb();
};
var fs = require('fs'),
path = require('path'),
log = require('./logging'),
ml = require('./module_loader'),
db = null, funcLoadAction, funcLoadRule;
function init(db_link, fLoadAction, fLoadRule) {
exports.addHandlers = function(db_link, fLoadAction, fLoadRule) {
db = db_link;
funcLoadAction = fLoadAction;
funcLoadRule = fLoadRule;
}
};
/*
# A First Level Header
@ -43,7 +55,7 @@ This is the function documentation
@param {Object} [args] the optional arguments
@param {String} [args.name] the optional name in the arguments
*/
function loadRulesFile(args, answHandler) {
exports.loadRulesFile = function(args, answHandler) {
if(!args) args = {};
if(!args.name) args.name = 'rules';
if(!funcLoadRule) log.error('ML', 'no rule loader function available');
@ -67,7 +79,7 @@ function loadRulesFile(args, answHandler) {
}
});
}
}
};
/**
*
@ -82,19 +94,19 @@ function loadActionCallback(name, data, mod, auth) {
if(auth) db.storeActionModuleAuth(name, auth);
}
function loadActionModule(args, answHandler) {
exports.loadActionModule = function (args, answHandler) {
if(args && args.name) {
answHandler.answerSuccess('Loading action module ' + args.name + '...');
ml.loadModule('mod_actions', args.name, loadActionCallback);
}
}
};
function loadActionModules(args, answHandler) {
exports.loadActionModules = function(args, answHandler) {
answHandler.answerSuccess('Loading action modules...');
ml.loadModules('mod_actions', loadActionCallback);
}
};
exports.init = init;
exports.loadRulesFile = loadRulesFile;
exports.loadActionModule = loadActionModule;
exports.loadActionModules = loadActionModules;
exports.die = function(cb) {
if(typeof cb === 'function') cb();
};

View file

@ -23,25 +23,86 @@ dog's back.
> ## This is an H2 in a blockquote
*/
var http_listener = require('./http_listener'),
db = require('./db_interface'),
engine = require('./engine'),
mm = require('./module_manager'),
log = require('./logging'),
fs = require('fs'),
//FIXME server should be started via command line arguments http_port and logging level to allow proper testing
var log = require('./logging'), http_port;
log.print('RS', 'STARTING SERVER');
if(process.argv.length > 2) log(process.argv[2]);
else log.print('RS', 'No log method passed, using stdI/O');
if(process.argv.length > 3) http_port = parseInt(process.argv[3]);
else log.print('RS', 'No HTTP port passed, using standard port from config file');
var fs = require('fs'),
path = require('path'),
procCmds = {
'die': function() { shutDown(); }
},
objCmds = {
'loadrules': mm.loadRulesFile,
'loadaction': mm.loadActionModule,
'loadactions': mm.loadActionModules,
'loadevent': engine.loadEventModule,
'loadevents': engine.loadEventModules,
'shutdown': shutDown,
'restart': null //TODO implement
};
function handleModuleLoad(cb, msg) {
return function(err) {
if(!err) {
if(typeof cb === 'function') cb();
log.print('RS', msg + ' initialized successfully');
} else {
err.addInfo = msg + ' init failed';
log.error('RS', err);
}
};
}
function loadHL() {
http_listener = require('./http_listener').init(log,
handleModuleLoad(loadEN, 'http listener')
);
}
function loadEN(cb) {
engine = require('./engine').init(log,
handleModuleLoad(loadMM, 'engine')
);
}
function loadMM(cb) {
mm = require('./module_manager').init(log,
handleModuleLoad(loadDB, 'module manager')
);
}
function loadDB(cb) {
db = require('./db_interface').init(log,
handleModuleLoad(doneInitDB, 'db interface init failed')
);
}
function doneInitDB(err) {
if(!err) {
objCmds = {
'loadrules': mm.loadRulesFile,
'loadaction': mm.loadActionModule,
'loadactions': mm.loadActionModules,
'loadevent': engine.loadEventModule,
'loadevents': engine.loadEventModules,
'shutdown': shutDown
};
//FIXME engine requires db to be finished with init...
engine.addDBLink(db);
log.print('RS', 'Initialzing http listener');
http_listener.addHandlers(handleAdminCommands, engine.pushEvent);
log.print('RS', 'Initialzing module manager');
mm.addHandlers(db, engine.loadActionModule, engine.loadRule);
}
else {
err.addInfo = err.message;
err.message = 'Not Starting engine!';
log.error(err);
}
}
(function() {
loadHL();
// engine = require('./engine').init(log),
// mm = require('./module_manager').init(log),
// db = require('./db_interface').init(log, doneInitDB), //TODO have a close lok at this special case
})();
function handleAdminCommands(args, answHandler) {
if(args && args.cmd) {
@ -74,22 +135,21 @@ process.on('message', function(cmd) {
});
log.print('RS', 'STARTING SERVER');
log.print('RS', 'Initialzing DB');
//FIXME initialization of all modules should depend on one after the other
// in a transaction style manner
db.init(function(err) {
if(!err) {
engine.init(db);
log.print('RS', 'Initialzing http listener');
//FIXME http_port shouldn't be passed here we can load it inside the listener via the new config.js module
http_listener.init(null/*config.http_port*/, handleAdminCommands, engine.pushEvent);
log.print('RS', 'Initialzing module manager');
mm.init(db, engine.loadActionModule, engine.loadRule);
}
else {
err.addInfo = err.message;
err.message = 'Not Starting engine!';
log.error(err);
}
});
/*
* FIXME
* - new consequent error and callback handling starts to manifest in the code,
* still a lot of work required!
* - unit testing seems a bit more complex because of the dependencies, this
* has to be started before solving above point because it will give hints to
* better loose coupling
*/
/*
* FIXME ALL MODULES NEED TO FOLLOW CONVENTION TO ALLOW PROPER MODULE HANDLING:
* - init(args, cb)
* - die()
*/

87
js/users.js Normal file
View file

@ -0,0 +1,87 @@
'use strict';
var log;
exports.init = function(args, cb) {
args = args || {};
if(args.log) log = args.log;
else log = args.log = require('./logging');
if(typeof cb === 'function') cb();
};
var objCmds = {
addUser: addUser,
getUser: getUser,
delUser: delUser,
addRule: addRule,
getRules: getRules,
delRule: delRule
};
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) {
}
exports.die = function(cb) {
if(typeof cb === 'function') cb();
};

View file

@ -9,6 +9,7 @@
"url" : "https://github.com/dominicbosch/webapi-eca.git"
},
"dependencies": {
"connect-redis": "1.4.6",
"express": "3.4.0",
"groc": "0.6.1",
"needle": "0.6.1",

270
testing/mod_config.js Normal file
View file

@ -0,0 +1,270 @@
exports.testUnit_DB = function(test){
test.ok(false, "needs implementation");
test.done();
};
// // # 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;
//
//
// // @function init()
//
// /*
// * Initializes the DB connection. Requires a port where the DB listens to requests
// * and a key that is used for encryptions.
// * @param {int} db_port
// */
// exports.init = function(db_port, key, cbDone){
// if(!db_port || !key) {
// log.error('DB', 'No DB port or cipher key defined!');
// return null;
// }
// crypto_key = key;
// db = redis.createClient(db_port);
// db.on("error", function (err) {
// log.error('DB', ' Message from DB: ' + err);
// });
// if(cbDone) cbDone();
// };
//
// /**
// * ### encrypt
// */
// 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) {
// 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: ' + 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} callback the function to be called on success or error, receives
// * arguments (err, obj)
// */
// function getSetRecords(set, funcSingle, callback) {
// db.smembers(set, function(err, reply) {
// if(err) log.error('DB', 'fetching ' + set + ': ' + err);
// else {
// if(reply.length === 0) {
// callback(null, null);
// } else {
// var semaphore = reply.length, objReplies = {};
// setTimeout(function() {
// if(semaphore > 0) {
// callback('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) callback(null, objReplies);
// }
// };
// }(reply[i]));
// }
// }
// }
// });
// }
//
// // @method shutDown()
//
// // Shuts down the db link.
// exports.shutDown = function() { 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) {
// db.sadd('action_modules', id, replyHandler('storing action module key ' + id));
// db.set('action_module_' + id, data, replyHandler('storing action module ' + id));
// };
//
// /**
// * ### getActionModule(id, callback)
// * Query the DB for an action module.
// * @param {String} id the module id
// * @param {function} callback the callback to receive the answer (err, obj)
// */
// exports.getActionModule = function(id, callback) {
// if(callback) db.get('action_module_' + id, callback);
// };
//
// /**
// * ### getActionModules(callback)
// * Fetch all action modules.
// * @param {function} callback the callback to receive the answer (err, obj)
// */
// exports.getActionModules = function(callback) {
// getSetRecords('action_modules', exports.getActionModule, callback);
// };
//
// /**
// * 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.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, callback)
// * Query the DB for an action module authentication token.
// * @param {String} id the module id
// * @param {function} callback the callback to receive the answer (err, obj)
// */
// exports.getActionModuleAuth = function(id, callback) {
// if(callback) db.get('action_module_' + id + '_auth', function(err, txt) { callback(err, decrypt(txt)); });
// };
//
// // ## 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) {
// db.sadd('event_modules', id, replyHandler('storing event module key ' + id));
// db.set('event_module_' + id, data, replyHandler('storing event module ' + id));
// };
//
// /**
// * ### getEventModule(id, callback)
// * Query the DB for an event module.
// * @param {String} id the module id
// * @param {function} callback the callback to receive the answer (err, obj)
// */
// exports.getEventModule = function(id, callback) {
// if(callback) db.get('event_module_' + id, callback);
// };
//
// /**
// * ### getEventModules(callback)
// * Fetch all event modules.
// * @param {function} callback the callback that receives the arguments (err, obj)
// */
// exports.getEventModules = function(callback) {
// getSetRecords('event_modules', exports.getEventModule, callback);
// };
//
// /**
// * ### 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.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, callback)
//
// // Query the DB for an event module authentication token.
// // @param {String} id the module id
// // @param {function} callback the callback to receive the answer (err, obj)
// exports.getEventModuleAuth = function(id, callback) {
// if(callback) db.get('event_module_' + id +'_auth', function(err, txt) { callback(err, decrypt(txt)); });
// };
//
// // ## 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) {
// db.sadd('rules', id, replyHandler('storing rule key ' + id));
// db.set('rule_' + id, data, replyHandler('storing rule ' + id));
// };
//
// // @method getRule(id, callback)
//
// // Query the DB for a rule.
// // @param {String} id the rule id
// // @param {function} callback the callback to receive the answer (err, obj)
// exports.getRule = function(id, callback) {
// db.get('rule_' + id, callback);
// };
//
// // @method getRules(callback)
//
// // Fetch all rules from the database.
// // @param {function} callback the callback to receive the answer (err, obj)
// exports.getRules = function(callback) {
// getSetRecords('rules', exports.getRule, callback);
// };
//

View file

@ -1,6 +1,6 @@
exports.testUnit_DB = function(test){
test.ok(true, "db");
test.ok(false, "needs implementation");
test.done();
};

View file

@ -24,7 +24,7 @@ exports.group = {
};
exports.testUnit_Engine = function(test){
test.ok(true, "engine");
test.ok(false, "needs implementation");
test.done();
};

View file

@ -1,6 +1,6 @@
exports.testUnit_EventPoller = function(test){
test.ok(true, "ep");
test.ok(false, "needs implementation");
test.done();
};

View file

@ -1,6 +1,6 @@
exports.testUnit_HL = function(test){
test.ok(true, "hl");
test.ok(false, "needs implementation");
test.done();
};

View file

@ -1,6 +1,6 @@
exports.testUnit_LOG = function(test){
test.ok(true, "log");
test.ok(false, "needs implementation");
test.done();
};

View file

@ -1,6 +1,6 @@
exports.testUnit_ML = function(test){
test.ok(true, "ml");
test.ok(false, "needs implementation");
test.done();
};

View file

@ -1,6 +1,6 @@
exports.testUnit_MM = function(test){
test.ok(true, "mm");
test.ok(false, "needs implementation");
test.done();
};

View file

@ -1,11 +1,12 @@
var path = require('path');
//FIXME handle EADDR in use!
exports.setUp = function(cb) {
this.srv = require('child_process').fork(path.resolve(__dirname, '..', 'js', 'server'));
this.srv = require('child_process').fork(path.resolve(__dirname, '..', 'js', 'server'), ['2']);
cb();
};
exports.testUnit_SRV = function(test){
test.ok(false, "needs implementation");
setTimeout(
function() {

9
testing/mod_user.js Normal file
View file

@ -0,0 +1,9 @@
exports.setUp = function() {
this.mod = require('./user');
};
exports.addUser = function() {
test.ok(false, "needs implementation");
};

View file

@ -1,5 +1,7 @@
exports.testUnitIntegration = function(test){
test.ok(true, "unit integration");
test.ok(false, "needs implementation");
test.done();
};
//TODO malicious module has to be loaded only for testing, the error
// it causes need to be verified and in the end the module need to be deleted again

View file

@ -1,5 +1,18 @@
var path = require('path');
//FIXME handle EADDR in use!
exports.setUp = function(cb) {
this.srv = require('child_process').fork(path.resolve(__dirname, '..', 'js', 'server'), ['2']);
cb();
};
exports.testSystem = function(test){
test.ok(true, "system");
test.ok(false, "needs implementation");
test.done();
};
exports.tearDown = function(cb) {
this.srv.send('die');
this.srv = null;
cb();
};

View file

@ -0,0 +1,8 @@
<html>
<head>
<title>Mobile Page</title>
</head>
<body>
<h1>Mobile Page</h1>
</body>
</html>

View file

@ -0,0 +1,8 @@
<html>
<head>
<title>Rules Forge</title>
</head>
<body>
<h1>Rules Forge</h1>
</body>
</html>