diff --git a/coffee/config.coffee b/coffee/config.coffee index c6e9166..76422ab 100644 --- a/coffee/config.coffee +++ b/coffee/config.coffee @@ -1,3 +1,10 @@ +### + +Config +====== + +Loads the configuration file and acts as an interface to it. +### 'use strict' path = require 'path' @@ -5,8 +12,9 @@ log = require './logging' fs = require 'fs' config = null - ### +##Module call + Calling the module as a function will make it look for the `relPath` property in the args object and then try to load a config file from that relative path. @param {Object} args @@ -19,14 +27,14 @@ exports = module.exports = (args) -> module.exports ### -@Function loadConfigFile - 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} relPath ### loadConfigFile = (relPath) -> try - ### We read the config file synchronously from the file system and try to parse it ### config = JSON.parse fs.readFileSync path.resolve __dirname, '..', relPath if config and config.http_port and config.db_port and config.crypto_key and config.session_secret @@ -43,31 +51,45 @@ loadConfigFile = (relPath) -> loadConfigFile path.join('config', 'config.json') -### Answer true if the config file is ready, else false ### -exports.isReady = -> config? - ### Fetch a property from the configuration + +@private fetchProp( *prop* ) @param {String} prop ### fetchProp = (prop) -> config?[prop] ### -Get the HTTP port +Answer true if the config file is ready, else false + +@public isReady() +### +exports.isReady = -> config? + +### +Returns the HTTP port + +@public getHttpPort() ### exports.getHttpPort = -> fetchProp 'http_port' ### -Get the DB port +Returns the DB port + +@public getDBPort() ### exports.getDBPort = -> fetchProp 'db_port' ### -Get the crypto key +Returns the crypto key + +@public getCryptoKey() ### exports.getCryptoKey = -> fetchProp 'crypto_key' ### -Get the session secret +Returns the session secret + +@public getSessionSecret() ### exports.getSessionSecret = -> fetchProp 'session_secret' diff --git a/coffee/db_interface.coffee b/coffee/db_interface.coffee new file mode 100644 index 0000000..8f49c9f --- /dev/null +++ b/coffee/db_interface.coffee @@ -0,0 +1,352 @@ +### + +DB Interface +============ +>Handles the connection to the database and provides functionalities for +>event/action modules, rules and the encrypted storing of authentication tokens. +>General functionality as a wrapper for the module holds initialization, +>encryption/decryption, 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 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' +### Grab all required modules ### +redis = require 'redis' +crypto = require 'crypto' +log = require './logging' +crypto_key = null +db = null + + + +### +##Module call + +Initializes the DB connection. Requires a valid configuration file which contains +a db port and a crypto key. +@param {Object} args +### +exports = module.exports = (args) -> + args = args ? {} + log args + config = require './config' + config args + crypto_key = config.getCryptoKey() + db = redis.createClient config.getDBPort(), 'localhost', { connect_timeout: 2000 } + db.on "error", (err) -> + err.addInfo = 'message from DB' + log.error 'DB', err + +### +Checks whether the db is connected and calls the callback function if successful, +or an error after ten attempts within five seconds. + +@public isConnected( *cb* ) +@param {function} cb +### +#}TODO check if timeout works with func in func +exports.isConnected = (cb) -> + if db.connected then cb() + else + numAttempts = 0 + fCheckConnection = -> + if db.connected + log.print 'DB', 'Successfully connected to DB!' + cb() + else if numAttempts++ < 10 + setTimeout fCheckConnection, 500 + else + e = new Error 'Connection to DB failed!' + log.error 'DB', e + cb e + setTimeout fCheckConnection, 500 + + +### +Encrypts a string using the crypto key from the config file, based on aes-256-cbc. + +@private encrypt( *plainText* ) +@param {String} plainText +### +encrypt = (plainText) -> + if !plainText? then return null + try + enciph = crypto.createCipher 'aes-256-cbc', crypto_key + et = enciph.update plainText, 'utf8', 'base64' + et + enciph.final 'base64' + catch err + err.addInfo = 'during encryption' + log.error 'DB', err + null + +### +Decrypts an encrypted string and hands it back on success or null. + +@private decrypt( *crypticText* ) +@param {String} crypticText +### +decrypt = (crypticText) -> + if !crypticText? then return null; + try + deciph = crypto.createDecipher 'aes-256-cbc', crypto_key + dt = deciph.update crypticText, 'base64', 'utf8' + dt + deciph.final 'utf8' + catch err + err.addInfo = 'during decryption' + log.error 'DB', err + null + +### +Abstracts logging for simple action replies from the DB. + +@private replyHandler( *action* ) +@param {String} action +### +replyHandler = (action) -> + (err, reply) -> + if err + err.addInfo = 'during "' + action + '"' + log.error 'DB', err + else + log.print 'DB', action + ': ' + reply + +### +Fetches all linked data set keys from a linking set, fetches the single data objects +via the provided function and returns the results to the callback function. + +@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 function that receives all the retrieved data or an error +### +getSetRecords = (set, funcSingle, cb) -> + db?.smembers set, (err, arrReply) -> + if err + err.addInfo = 'fetching ' + set + log.error 'DB', err + else if arrReply.length == 0 + cb() + else + semaphore = arrReply.length + objReplies = {} + # } TODO What if the DB needs longer than two seconds to respond?... + setTimeout -> + if semaphore > 0 + cb new Error('Timeout fetching ' + set) + , 2000 + fCallback = (prop) -> + (err, data) -> + if err + err.addInfo = 'fetching single element: ' + prop + log.error 'DB', err + else + objReplies[prop] = data + if --semaphore == 0 + cb null, objReplies + fSingle reply, fCallback(reply) for reply in arrReply + +### +@Function shutDown() + +Shuts down the db link. +### +###exports.shutDown = function() { if(db) db.quit(); }; +### +### +## Action Modules + +@Function 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)); + } +}; +### +### +@Function 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); +}; +### +### +@Function 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); +}; +### +### +@Function 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)); + } +}; +### +### +@Function 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 + +@Function 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)); + } +}; +### +### +@Function 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); +}; +### +### +@Function 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); +}; +### +### +@Function 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)); + } +}; +### +### +@Function 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 + +@Function 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)); + } +}; +### +### +@Function 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); +}; +### +### +@Function getRules(cb) + +Fetch all rules from the database. +@param {function} cb +### +###exports.getRules = function(cb) { + getSetRecords('rules', exports.getRule, cb); +}; +### +### +@Function storeUser + +@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!')); +}; + +### \ No newline at end of file diff --git a/coffee/server.coffee b/coffee/server.coffee index 282f575..2b96121 100644 --- a/coffee/server.coffee +++ b/coffee/server.coffee @@ -1,4 +1,3 @@ - ### Rules Server ============ @@ -57,8 +56,9 @@ process.on 'uncaughtException', (err) -> null ### -## Initialize the Server This function is invoked right after the module is loaded and starts the server. + +@private init() ### init = -> @@ -103,15 +103,17 @@ init = -> 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 + http_listener.addHandlers db, fAdminCommands, 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. + +@private fAdminCommands( *args, answHandler* ) ### -handleAdminCommands = (args, answHandler) -> +fAdminCommands = (args, answHandler) -> if args and args.cmd adminCmds[args.cmd]? args, answHandler else @@ -137,6 +139,8 @@ handleAdminCommands = (args, answHandler) -> ### Shuts down the server. + +@private shutDown( *args, answHandler* ) @param {Object} args @param {Object} answHandler ### diff --git a/create_doc.js b/create_doc.js index c6df08f..1835898 100644 --- a/create_doc.js +++ b/create_doc.js @@ -1,3 +1,23 @@ +/* + * # groc Documentation + * Create the documentation to be displayed through the webserver. + */ +// +require('groc').CLI( + [ + "README.md", + "LICENSE.md", + "coffee/*.coffee", + "mod_actions/**/*.js", + "mod_events/**/*.js", + "-o./webpages/doc" + ], + function(err) { + if (err) console.error(err); + else console.log('Done!'); + } +); + /* * # docco Documentation @@ -28,23 +48,3 @@ // } // }); // } - -/* - * # groc Documentation - * Create the documentation to be displayed through the webserver. - */ -// -require('groc').CLI( - [ - "README.md", - "LICENSE.md", - "coffee/*.coffee", - "mod_actions/**/*.js", - "mod_events/**/*.js", - "-o./webpages/doc" - ], - function(err) { - if (err) console.error(err); - else console.log('Done!'); - } -); diff --git a/js-coffee/config.js b/js-coffee/config.js index 1957edb..70dc870 100644 --- a/js-coffee/config.js +++ b/js-coffee/config.js @@ -1,4 +1,13 @@ // Generated by CoffeeScript 1.6.3 +/* + +Config +====== + +Loads the configuration file and acts as an interface to it. +*/ + + (function() { 'use strict'; var config, exports, fetchProp, fs, loadConfigFile, log, path; @@ -12,6 +21,8 @@ config = null; /* + ##Module call + Calling the module as a function will make it look for the `relPath` property in the args object and then try to load a config file from that relative path. @param {Object} args @@ -28,9 +39,10 @@ }; /* - @Function loadConfigFile - 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} relPath */ @@ -38,8 +50,6 @@ loadConfigFile = function(relPath) { var e; try { - /* We read the config file synchronously from the file system and try to parse it*/ - config = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', relPath))); if (config && config.http_port && config.db_port && config.crypto_key && config.session_secret) { return log.print('CF', 'config file loaded successfully'); @@ -55,15 +65,10 @@ loadConfigFile(path.join('config', 'config.json')); - /* Answer true if the config file is ready, else false*/ - - - exports.isReady = function() { - return config != null; - }; - /* Fetch a property from the configuration + + @private fetchProp( *prop* ) @param {String} prop */ @@ -73,7 +78,20 @@ }; /* - Get the HTTP port + Answer true if the config file is ready, else false + + @public isReady() + */ + + + exports.isReady = function() { + return config != null; + }; + + /* + Returns the HTTP port + + @public getHttpPort() */ @@ -82,7 +100,9 @@ }; /* - Get the DB port + Returns the DB port + + @public getDBPort() */ @@ -91,7 +111,9 @@ }; /* - Get the crypto key + Returns the crypto key + + @public getCryptoKey() */ @@ -100,7 +122,9 @@ }; /* - Get the session secret + Returns the session secret + + @public getSessionSecret() */ diff --git a/js-coffee/db_interface.js b/js-coffee/db_interface.js index b31cfaf..7304956 100644 --- a/js-coffee/db_interface.js +++ b/js-coffee/db_interface.js @@ -1,322 +1,473 @@ -/** - * # 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'; +// Generated by CoffeeScript 1.6.3 +/* -var redis = require('redis'), - crypto = require('crypto'), - log = require('./logging'), - crypto_key, db; - +DB Interface +============ +>Handles the connection to the database and provides functionalities for +>event/action modules, rules and the encrypted storing of authentication tokens. +>General functionality as a wrapper for the module holds initialization, +>encryption/decryption, 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 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). +> +*/ -/** - * 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); + +(function() { + 'use strict'; + /* Grab all required modules*/ + + var crypto, crypto_key, db, decrypt, encrypt, exports, getSetRecords, log, redis, replyHandler; + + redis = require('redis'); + + crypto = require('crypto'); + + log = require('./logging'); + + crypto_key = null; + + db = null; + + /* + ##Module call - 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; -}; + Initializes the DB connection. Requires a valid configuration file which contains + a db port and a crypto key. + @param {Object} args + */ -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); + exports = module.exports = function(args) { + var config; + args = args != null ? args : {}; + log(args); + config = require('./config'); + config(args); + crypto_key = config.getCryptoKey(); + db = redis.createClient(config.getDBPort(), 'localhost', { + connect_timeout: 2000 + }); + return db.on("error", function(err) { + err.addInfo = 'message from DB'; + return log.error('DB', err); + }); }; -} -/** - * ### 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])); + /* + Checks whether the db is connected and calls the callback function if successful, + or an error after ten attempts within five seconds. + + @public isConnected( *cb* ) + @param {function} cb + */ + + + exports.isConnected = function(cb) { + var fCheckConnection, numAttempts; + if (db.connected) { + return cb(); + } else { + numAttempts = 0; + fCheckConnection = function() { + var e; + if (db.connected) { + log.print('DB', 'Successfully connected to DB!'); + return cb(); + } else if (numAttempts++ < 10) { + return setTimeout(fCheckConnection, 500); + } else { + e = new Error('Connection to DB failed!'); + log.error('DB', e); + return cb(e); } - } + }; + return setTimeout(fCheckConnection, 500); } - }); -} + }; -// @method shutDown() - -// Shuts down the db link. -exports.shutDown = function() { if(db) db.quit(); }; + /* + Encrypts a string using the crypto key from the config file, based on aes-256-cbc. + + @private encrypt( *plainText* ) + @param {String} plainText + */ -// ## Action Modules + encrypt = function(plainText) { + var enciph, err, et; + if (plainText == null) { + return null; + } + try { + enciph = crypto.createCipher('aes-256-cbc', crypto_key); + et = enciph.update(plainText, 'utf8', 'base64'); + return et + enciph.final('base64'); + } catch (_error) { + err = _error; + err.addInfo = 'during encryption'; + log.error('DB', err); + return null; + } + }; -/** - * ### 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)); - } -}; + /* + Decrypts an encrypted string and hands it back on success or null. + + @private decrypt( *crypticText* ) + @param {String} crypticText + */ -/** - * ### 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); -}; + decrypt = function(crypticText) { + var deciph, dt, err; + if (crypticText == null) { + return null; + } + try { + deciph = crypto.createDecipher('aes-256-cbc', crypto_key); + dt = deciph.update(crypticText, 'base64', 'utf8'); + return dt + deciph.final('utf8'); + } catch (_error) { + err = _error; + err.addInfo = 'during decryption'; + log.error('DB', err); + return null; + } + }; -/** - * 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)); - } -}; + /* + Abstracts logging for simple action replies from the DB. + + @private replyHandler( *action* ) + @param {String} action + */ -/** - * ### 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!')); + replyHandler = function(action) { + return function(err, reply) { + if (err) { + err.addInfo = 'during "' + action + '"'; + return log.error('DB', err); + } else { + return log.print('DB', action + ': ' + reply); + } }; - }(password)); - else cb(new Error('No database link available!')); -}; + }; + + /* + Fetches all linked data set keys from a linking set, fetches the single data objects + via the provided function and returns the results to the callback function. + + @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 function that receives all the retrieved data or an error + */ + + + getSetRecords = function(set, funcSingle, cb) { + return db != null ? db.smembers(set, function(err, arrReply) { + var fCallback, objReplies, reply, semaphore, _i, _len, _results; + if (err) { + err.addInfo = 'fetching ' + set; + return log.error('DB', 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) { + if (err) { + err.addInfo = 'fetching single element: ' + prop; + return log.error('DB', err); + } 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; + } + }) : void 0; + }; + + /* + @Function shutDown() + + Shuts down the db link. + */ + + + /*exports.shutDown = function() { if(db) db.quit(); }; + */ + + + /* + ## Action Modules + + @Function 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)); + } + }; + */ + + + /* + @Function 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); + }; + */ + + + /* + @Function 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); + }; + */ + + + /* + @Function 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)); + } + }; + */ + + + /* + @Function 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 + + @Function 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)); + } + }; + */ + + + /* + @Function 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); + }; + */ + + + /* + @Function 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); + }; + */ + + + /* + @Function 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)); + } + }; + */ + + + /* + @Function 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 + + @Function 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)); + } + }; + */ + + + /* + @Function 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); + }; + */ + + + /* + @Function getRules(cb) + + Fetch all rules from the database. + @param {function} cb + */ + + + /*exports.getRules = function(cb) { + getSetRecords('rules', exports.getRule, cb); + }; + */ + + + /* + @Function storeUser + + @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!')); + }; + */ + + +}).call(this); diff --git a/js-coffee/server.js b/js-coffee/server.js index ea31f4e..777b0be 100644 --- a/js-coffee/server.js +++ b/js-coffee/server.js @@ -23,7 +23,7 @@ Rules Server 'use strict'; /* Grab all required modules*/ - var adminCmds, args, conf, db, engine, handleAdminCommands, http_listener, init, log, mm, path, procCmds, shutDown; + var adminCmds, args, conf, db, engine, fAdminCommands, http_listener, init, log, mm, path, procCmds, shutDown; path = require('path'); @@ -77,8 +77,9 @@ Rules Server }); /* - ## Initialize the Server This function is invoked right after the module is loaded and starts the server. + + @private init() */ @@ -137,7 +138,7 @@ Rules Server 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); + http_listener.addHandlers(db, fAdminCommands, engine.pushEvent); log.print('RS', 'Passing handlers to module manager'); return mm.addHandlers(db, engine.loadActionModule, engine.addRule); } @@ -147,10 +148,12 @@ Rules Server /* admin commands handler receives all command arguments and an answerHandler object that eases response handling to the HTTP request issuer. + + @private fAdminCommands( *args, answHandler* ) */ - handleAdminCommands = function(args, answHandler) { + fAdminCommands = function(args, answHandler) { var fAnsw, _name; if (args && args.cmd) { if (typeof adminCmds[_name = args.cmd] === "function") { @@ -185,6 +188,8 @@ Rules Server /* Shuts down the server. + + @private shutDown( *args, answHandler* ) @param {Object} args @param {Object} answHandler */ diff --git a/package.json b/package.json index cef5541..5c1a0d2 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,7 @@ }, "dependencies": { "connect-redis": "1.4.6", - "docco": "0.6.2", "express": "3.4.0", - "glob" :"3.2.7", "groc": "0.6.1", "needle": "0.6.1", "nodeunit": "0.8.2", @@ -21,6 +19,8 @@ }, "__comment": { "dependencies": { + "glob" :"3.2.7", + "docco": "0.6.2", "groc": "0.6.1", "diff": "1.0.5", "socket.io": "0.9.16",