Documentation and private vars update

This commit is contained in:
Dominic Bosch 2013-11-26 23:24:15 +01:00
parent eba1357e9e
commit a778cbcde5
15 changed files with 791 additions and 555 deletions

View file

@ -2,15 +2,16 @@
Config Config
====== ======
> Loads the configuration file and acts as an interface to it.
Loads the configuration file and acts as an interface to it.
### ###
'use strict'
path = require 'path'
log = require './logging'
fs = require 'fs' fs = require 'fs'
config = null path = require 'path'
# Requires:
# - The [Logging](logging.html) module
log = require './logging'
### ###
##Module call ##Module call
@ -19,7 +20,7 @@ Calling the module as a function will make it look for the `relPath` property in
args object and then try to load a config file from that relative path. args object and then try to load a config file from that relative path.
@param {Object} args @param {Object} args
### ###
exports = module.exports = (args) -> exports = module.exports = ( args ) ->
args = args ? {} args = args ? {}
log args log args
if typeof args.relPath is 'string' if typeof args.relPath is 'string'
@ -33,11 +34,11 @@ Reads the config file synchronously from the file system and try to parse it.
@private loadConfigFile @private loadConfigFile
@param {String} relPath @param {String} relPath
### ###
loadConfigFile = (relPath) -> loadConfigFile = ( relPath ) =>
try try
config = JSON.parse fs.readFileSync path.resolve __dirname, '..', relPath @config = JSON.parse fs.readFileSync path.resolve __dirname, '..', relPath
if config and config.http_port and config.db_port and if @config and @config.http_port and @config.db_port and
config.crypto_key and config.session_secret @config.crypto_key and @config.session_secret
log.print 'CF', 'config file loaded successfully' log.print 'CF', 'config file loaded successfully'
else else
log.error 'CF', new Error("""Missing property in config file, requires: log.error 'CF', new Error("""Missing property in config file, requires:
@ -57,14 +58,14 @@ Fetch a property from the configuration
@private fetchProp( *prop* ) @private fetchProp( *prop* )
@param {String} prop @param {String} prop
### ###
fetchProp = (prop) -> config?[prop] fetchProp = ( prop ) => @config?[prop]
### ###
Answer true if the config file is ready, else false Answer true if the config file is ready, else false
@public isReady() @public isReady()
### ###
exports.isReady = -> config? exports.isReady = => @config?
### ###
Returns the HTTP port Returns the HTTP port

View file

@ -2,48 +2,48 @@
DB Interface DB Interface
============ ============
>Handles the connection to the database and provides functionalities for > Handles the connection to the database and provides functionalities for
>event/action modules, rules and the encrypted storing of authentication tokens. > event/action modules, rules and the encrypted storing of authentication tokens.
>General functionality as a wrapper for the module holds initialization, > General functionality as a wrapper for the module holds initialization,
>encryption/decryption, the retrieval of modules and shut down. > encryption/decryption, the retrieval of modules and shut down.
> >
>The general structure for linked data is that the key is stored in a set. > 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 > By fetching all set entries we can then fetch all elements, which is
>automated in this function. > automated in this function.
>For example modules of the same group, e.g. action modules are registered in an > 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 > 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 > 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 > 'action_modules' and then stored in the db with the key 'action\_module\_' + ID
>(e.g. action\_module\_probinder). > (e.g. action\_module\_probinder).
> >
### ###
'use strict'
### Grab all required modules ###
redis = require 'redis' redis = require 'redis'
crypto = require 'crypto' # TODO change to Google's "crypto-js"" crypto = require 'crypto' # TODO change to Google's "crypto-js""
log = require './logging'
crypto_key = null
db = null
# Requires:
# - The [Logging](logging.html) module
log = require './logging'
### ###
##Module call Module call
-----------
Initializes the DB connection. Requires a valid configuration file which contains Initializes the DB connection. Requires a valid configuration file which contains
a db port and a crypto key. a db port and a crypto key.
@param {Object} args @param {Object} args
### ###
exports = module.exports = (args) -> exports = module.exports = ( args ) =>
args = args ? {} args = args ? {}
log args log args
config = require './config' config = require './config'
config args config args
crypto_key = config.getCryptoKey() @crypto_key = config.getCryptoKey()
db = redis.createClient config.getDBPort(), 'localhost', { connect_timeout: 2000 } @db = redis.createClient config.getDBPort(), 'localhost', { connect_timeout: 2000 }
db.on "error", (err) -> @db.on "error", ( err ) ->
err.addInfo = 'message from DB' err.addInfo = 'message from DB'
log.error 'DB', err log.error 'DB', err
@ -55,12 +55,12 @@ ten attempts within five seconds, or nothing on success to the callback(err).
@param {function} cb @param {function} cb
### ###
#}TODO check if timeout works with func in func #}TODO check if timeout works with func in func
exports.isConnected = (cb) -> exports.isConnected = ( cb ) =>
if db.connected then cb() if @db.connected then cb()
else else
numAttempts = 0 numAttempts = 0
fCheckConnection = -> fCheckConnection = =>
if db.connected if @db.connected
log.print 'DB', 'Successfully connected to DB!' log.print 'DB', 'Successfully connected to DB!'
cb() cb()
else if numAttempts++ < 10 else if numAttempts++ < 10
@ -78,10 +78,10 @@ Encrypts a string using the crypto key from the config file, based on aes-256-cb
@private encrypt( *plainText* ) @private encrypt( *plainText* )
@param {String} plainText @param {String} plainText
### ###
encrypt = (plainText) -> encrypt = ( plainText ) =>
if !plainText? then return null if !plainText? then return null
try try
enciph = crypto.createCipher 'aes-256-cbc', crypto_key enciph = crypto.createCipher 'aes-256-cbc', @crypto_key
et = enciph.update plainText, 'utf8', 'base64' et = enciph.update plainText, 'utf8', 'base64'
et + enciph.final 'base64' et + enciph.final 'base64'
catch err catch err
@ -95,10 +95,10 @@ Decrypts an encrypted string and hands it back on success or null.
@private decrypt( *crypticText* ) @private decrypt( *crypticText* )
@param {String} crypticText @param {String} crypticText
### ###
decrypt = (crypticText) -> decrypt = ( crypticText ) =>
if !crypticText? then return null; if !crypticText? then return null;
try try
deciph = crypto.createDecipher 'aes-256-cbc', crypto_key deciph = crypto.createDecipher 'aes-256-cbc', @crypto_key
dt = deciph.update crypticText, 'base64', 'utf8' dt = deciph.update crypticText, 'base64', 'utf8'
dt + deciph.final 'utf8' dt + deciph.final 'utf8'
catch err catch err
@ -112,8 +112,8 @@ Abstracts logging for simple action replies from the DB.
@private replyHandler( *action* ) @private replyHandler( *action* )
@param {String} action @param {String} action
### ###
replyHandler = (action) -> replyHandler = ( action ) ->
(err, reply) -> ( err, reply ) ->
if err if err
err.addInfo = 'during "' + action + '"' err.addInfo = 'during "' + action + '"'
log.error 'DB', err log.error 'DB', err
@ -129,9 +129,9 @@ via the provided function and returns the results to the callback(err, obj) func
@param {function} fSingle a function to retrieve a single data element per set entry @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 @param {function} cb the callback(err, obj) function that receives all the retrieved data or an error
### ###
getSetRecords = (set, fSingle, cb) -> getSetRecords = ( set, fSingle, cb ) =>
log.print 'DB', 'Fetching set records: ' + set log.print 'DB', 'Fetching set records: ' + set
db?.smembers set, (err, arrReply) -> @db.smembers set, ( err, arrReply ) ->
if err if err
err.addInfo = 'fetching ' + set err.addInfo = 'fetching ' + set
log.error 'DB', err log.error 'DB', err
@ -145,8 +145,8 @@ getSetRecords = (set, fSingle, cb) ->
if semaphore > 0 if semaphore > 0
cb new Error('Timeout fetching ' + set) cb new Error('Timeout fetching ' + set)
, 2000 , 2000
fCallback = (prop) -> fCallback = ( prop ) ->
(err, data) -> ( err, data ) ->
--semaphore --semaphore
if err if err
err.addInfo = 'fetching single element: ' + prop err.addInfo = 'fetching single element: ' + prop
@ -171,10 +171,10 @@ Store a string representation of an action module in the DB.
@param {String} data @param {String} data
### ###
# FIXME can the data be an object? # FIXME can the data be an object?
exports.storeActionModule = (id, data) -> exports.storeActionModule = ( id, data ) =>
log.print 'DB', 'storeActionModule: ' + id log.print 'DB', 'storeActionModule: ' + id
db?.sadd 'action-modules', id, replyHandler 'storing action module key ' + id @db.sadd 'action-modules', id, replyHandler 'storing action module key ' + id
db?.set 'action-module:' + id, data, replyHandler 'storing action module ' + id @db.set 'action-module:' + id, data, replyHandler 'storing action module ' + id
### ###
Query the DB for an action module and pass it to the callback(err, obj) function. Query the DB for an action module and pass it to the callback(err, obj) function.
@ -183,9 +183,9 @@ Query the DB for an action module and pass it to the callback(err, obj) function
@param {String} id @param {String} id
@param {function} cb @param {function} cb
### ###
exports.getActionModule = (id, cb) -> exports.getActionModule = ( id, cb ) =>
log.print 'DB', 'getActionModule: ' + id log.print 'DB', 'getActionModule: ' + id
db?.get 'action-module:' + id, cb @db.get 'action-module:' + id, cb
### ###
Fetch all action modules and hand them to the callback(err, obj) function. Fetch all action modules and hand them to the callback(err, obj) function.
@ -193,7 +193,7 @@ Fetch all action modules and hand them to the callback(err, obj) function.
@public getActionModules( *cb* ) @public getActionModules( *cb* )
@param {function} cb @param {function} cb
### ###
exports.getActionModules = (cb) -> exports.getActionModules = ( cb ) ->
getSetRecords 'action-modules', exports.getActionModule, cb getSetRecords 'action-modules', exports.getActionModule, cb
### ###
@ -204,9 +204,9 @@ Store a string representation of the authentication parameters for an action mod
@param {String} moduleId @param {String} moduleId
@param {String} data @param {String} data
### ###
exports.storeActionAuth = (userId, moduleId, data) -> exports.storeActionAuth = ( userId, moduleId, data ) =>
log.print 'DB', 'storeActionAuth: ' + userId + ':' + moduleId log.print 'DB', 'storeActionAuth: ' + userId + ':' + moduleId
db?.set 'action-auth:' + userId + ':' + moduleId, encrypt(data), @db.set 'action-auth:' + userId + ':' + moduleId, encrypt(data),
replyHandler 'storing action auth ' + userId + ':' + moduleId replyHandler 'storing action auth ' + userId + ':' + moduleId
### ###
@ -218,9 +218,9 @@ and pass it to the callback(err, obj) function.
@param {String} moduleId @param {String} moduleId
@param {function} cb @param {function} cb
### ###
exports.getActionAuth = (userId, moduleId, cb) -> exports.getActionAuth = ( userId, moduleId, cb ) =>
log.print 'DB', 'getActionAuth: ' + userId + ':' + moduleId log.print 'DB', 'getActionAuth: ' + userId + ':' + moduleId
db?.get 'action-auth:' + userId + ':' + moduleId, (err, data) -> @db.get 'action-auth:' + userId + ':' + moduleId, ( err, data ) ->
cb err, decrypt data cb err, decrypt data
@ -235,10 +235,10 @@ Store a string representation of an event module in the DB.
@param {String} id @param {String} id
@param {String} data @param {String} data
### ###
exports.storeEventModule = (id, data) -> exports.storeEventModule = ( id, data ) =>
log.print 'DB', 'storeEventModule: ' + id log.print 'DB', 'storeEventModule: ' + id
db?.sadd 'event-modules', id, replyHandler 'storing event module key ' + id @db.sadd 'event-modules', id, replyHandler 'storing event module key ' + id
db?.set 'event-module:' + id, data, replyHandler 'storing event module ' + id @db.set 'event-module:' + id, data, replyHandler 'storing event module ' + id
### ###
Query the DB for an event module and pass it to the callback(err, obj) function. Query the DB for an event module and pass it to the callback(err, obj) function.
@ -247,9 +247,9 @@ Query the DB for an event module and pass it to the callback(err, obj) function.
@param {String} id @param {String} id
@param {function} cb @param {function} cb
### ###
exports.getEventModule = (id, cb) -> exports.getEventModule = ( id, cb ) =>
log.print 'DB', 'getEventModule: ' + id log.print 'DB', 'getEventModule: ' + id
db?.get 'event_module:' + id, cb @db.get 'event_module:' + id, cb
### ###
Fetch all event modules and pass them to the callback(err, obj) function. Fetch all event modules and pass them to the callback(err, obj) function.
@ -257,31 +257,33 @@ Fetch all event modules and pass them to the callback(err, obj) function.
@public getEventModules( *cb* ) @public getEventModules( *cb* )
@param {function} cb @param {function} cb
### ###
exports.getEventModules = (cb) -> exports.getEventModules = ( cb ) ->
getSetRecords 'event_modules', exports.getEventModule, cb getSetRecords 'event_modules', exports.getEventModule, cb
### ###
Store a string representation of he authentication parameters for an event module. Store a string representation of he authentication parameters for an event module.
@public storeEventAuth( *userId, moduleId, data* ) @public storeEventAuth( *userId, moduleId, data* )
@param {String} id @param {String} userId
@param {String} data @param {String} moduleId
@param {Object} data
### ###
exports.storeEventAuth = (userId, moduleId, data) -> exports.storeEventAuth = ( userId, moduleId, data ) =>
log.print 'DB', 'storeEventAuth: ' + userId + ':' + moduleId log.print 'DB', 'storeEventAuth: ' + userId + ':' + moduleId
db?.set 'event-auth:' + userId + ':' + moduleId, encrypt(data), @db.set 'event-auth:' + userId + ':' + moduleId, encrypt(data),
replyHandler 'storing event auth ' + userId + ':' + moduleId replyHandler 'storing event auth ' + userId + ':' + moduleId
### ###
Query the DB for an action module authentication token, associated with a user. Query the DB for an action module authentication token, associated with a user.
@public getEventAuth( *id, cb* ) @public getEventAuth( *userId, moduleId, data* )
@param {String} id @param {String} userId
@param {String} moduleId
@param {function} cb @param {function} cb
### ###
exports.getEventAuth = (userId, moduleId, cb) -> exports.getEventAuth = ( userId, moduleId, cb ) =>
log.print 'DB', 'getEventAuth: ' + userId + ':' + moduleId log.print 'DB', 'getEventAuth: ' + userId + ':' + moduleId
db?.get 'event-auth:' + userId + ':' + moduleId, (err, data) -> @db.get 'event-auth:' + userId + ':' + moduleId, ( err, data ) ->
cb err, decrypt data cb err, decrypt data
@ -296,10 +298,10 @@ Store a string representation of a rule in the DB.
@param {String} id @param {String} id
@param {String} data @param {String} data
### ###
exports.storeRule = (id, data) -> exports.storeRule = ( id, data ) =>
log.print 'DB', 'storeRule: ' + id log.print 'DB', 'storeRule: ' + id
db?.sadd 'rules', id, replyHandler 'storing rule key ' + id @db.sadd 'rules', id, replyHandler 'storing rule key ' + id
db?.set 'rule:' + id, data, replyHandler 'storing rule ' + id @db.set 'rule:' + id, data, replyHandler 'storing rule ' + id
### ###
Query the DB for a rule and pass it to the callback(err, obj) function. Query the DB for a rule and pass it to the callback(err, obj) function.
@ -308,9 +310,9 @@ Query the DB for a rule and pass it to the callback(err, obj) function.
@param {String} id @param {String} id
@param {function} cb @param {function} cb
### ###
exports.getRule = (id, cb) -> exports.getRule = ( id, cb ) =>
log.print 'DB', 'getRule: ' + id log.print 'DB', 'getRule: ' + id
db?.get 'rule:' + id, cb @db.get 'rule:' + id, cb
### ###
Fetch all rules from the database and pass them to the callback function. Fetch all rules from the database and pass them to the callback function.
@ -318,7 +320,7 @@ Fetch all rules from the database and pass them to the callback function.
@public getRules( *cb* ) @public getRules( *cb* )
@param {function} cb @param {function} cb
### ###
exports.getRules = (cb) -> exports.getRules = ( cb ) ->
log.print 'DB', 'Fetching all Rules' log.print 'DB', 'Fetching all Rules'
getSetRecords 'rules', exports.getRule, cb getSetRecords 'rules', exports.getRule, cb
@ -328,12 +330,14 @@ Store a user object (needs to be a flat structure).
@public storeUser( *objUser* ) @public storeUser( *objUser* )
@param {Object} objUser @param {Object} objUser
### ###
exports.storeUser = (objUser) -> exports.storeUser = ( objUser ) =>
# TODO Only store user if not already existing, or at least only then add a private key
# for his encryption. we would want to have one private key per user, right?
log.print 'DB', 'storeUser: ' + objUser.username log.print 'DB', 'storeUser: ' + objUser.username
if objUser and objUser.username and objUser.password if objUser and objUser.username and objUser.password
db?.sadd 'users', objUser.username, replyHandler 'storing user key ' + objUser.username @db.sadd 'users', objUser.username, replyHandler 'storing user key ' + objUser.username
objUser.password = encrypt objUser.password objUser.password = encrypt objUser.password
db?.hmset 'user:' + objUser.username, objUser, replyHandler 'storing user properties ' + objUser.username @db.hmset 'user:' + objUser.username, objUser, replyHandler 'storing user properties ' + objUser.username
else else
log.error 'DB', new Error 'username or password was missing' log.error 'DB', new Error 'username or password was missing'
@ -344,10 +348,10 @@ Associate a role with a user.
@param {String} username @param {String} username
@param {String} role @param {String} role
### ###
exports.storeUserRole = (username, role) -> exports.storeUserRole = ( username, role ) =>
log.print 'DB', 'storeUserRole: ' + username + ':' + role log.print 'DB', 'storeUserRole: ' + username + ':' + role
db?.sadd 'user-roles:' + username, role, replyHandler 'adding role ' + role + ' to user ' + username @db.sadd 'user-roles:' + username, role, replyHandler 'adding role ' + role + ' to user ' + username
db?.sadd 'role-users:' + role, username, replyHandler 'adding user ' + username + ' to role ' + role @db.sadd 'role-users:' + role, username, replyHandler 'adding user ' + username + ' to role ' + role
### ###
Fetch all roles of a user and pass them to the callback(err, obj) Fetch all roles of a user and pass them to the callback(err, obj)
@ -355,9 +359,9 @@ Fetch all roles of a user and pass them to the callback(err, obj)
@public getUserRoles( *username* ) @public getUserRoles( *username* )
@param {String} username @param {String} username
### ###
exports.getUserRoles = (username) -> exports.getUserRoles = ( username ) =>
log.print 'DB', 'getUserRole: ' + username log.print 'DB', 'getUserRole: ' + username
db?.get 'user-roles:' + username, cb @db.get 'user-roles:' + username, cb
### ###
Fetch all users of a role and pass them to the callback(err, obj) Fetch all users of a role and pass them to the callback(err, obj)
@ -365,9 +369,9 @@ Fetch all users of a role and pass them to the callback(err, obj)
@public getUserRoles( *role* ) @public getUserRoles( *role* )
@param {String} role @param {String} role
### ###
exports.getRoleUsers = (role) -> exports.getRoleUsers = ( role ) =>
log.print 'DB', 'getRoleUsers: ' + role log.print 'DB', 'getRoleUsers: ' + role
db?.get 'role-users:' + role, cb @db.get 'role-users:' + role, cb
### ###
Checks the credentials and on success returns the user object to the callback(err, obj) function. Checks the credentials and on success returns the user object to the callback(err, obj) function.
@ -378,10 +382,10 @@ Checks the credentials and on success returns the user object to the callback(er
@param {function} cb @param {function} cb
### ###
# TODO verify and test whole function # TODO verify and test whole function
exports.loginUser = (username, password, cb) -> exports.loginUser = ( username, password, cb ) =>
log.print 'DB', 'User "' + username + '" tries to log in' log.print 'DB', 'User "' + username + '" tries to log in'
fCheck = (pw) -> fCheck = ( pw ) ->
(err, obj) -> ( err, obj ) ->
if err if err
cb err cb err
else if obj and obj.password else if obj and obj.password
@ -392,7 +396,7 @@ exports.loginUser = (username, password, cb) ->
cb new Error 'Wrong credentials!' cb new Error 'Wrong credentials!'
else else
cb new Error 'Empty arguments!' cb new Error 'Empty arguments!'
db?.hgetall 'user:' + username, fCheck password @db.hgetall 'user:' + username, fCheck password
# TODO implement functions required for user sessions and the rule activation # TODO implement functions required for user sessions and the rule activation
@ -401,4 +405,4 @@ Shuts down the db link.
@public shutDown() @public shutDown()
### ###
exports.shutDown = -> db?.quit() exports.shutDown = => @db.quit()

View file

@ -0,0 +1,85 @@
###
HTTP Listener
=============
> Handles the HTTP requests to the server at the port specified by the
> [config](config.html) file.
###
path = require 'path'
express = require 'express'
app = express()
# } RedisStore = require('connect-redis')(express), # TODO use RedisStore for persistent sessions
qs = require 'querystring'
# Requires:
# - The [Logging](logging.html) module
log = require './logging'
# - The [Config](config.html) module
config = require './config'
# - The [User Handler](user_handler.html) module
userHandler = require './user_handler'
sess_sec = '#C[>;j`@".TXm2TA;A2Tg)'
# 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 = ( args ) ->
args = args ? {}
log args
config args
userHandler args
# TODO check whether this really does what it's supposed to do (fetch wrong sess property)
sess_sec = config.getSessionSecret() || sess_sec
module.exports
exports.addHandlers = ( fEvtHandler, fShutDown ) =>
userHandler.addShutdownHandler fShutDown
@eventHandler = fEvtHandler
# Add cookie support for session handling.
app.use express.cookieParser()
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.get '/rulesforge', userHandler.handleRequest
app.get '/admin', userHandler.handleRequest
app.post '/login', userHandler.handleLogin
app.post '/push_event', onPushEvent
try
http_port = config.getHttpPort()
if http_port
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 = 'opening port'
log.error e
#
# If a post request reaches the server, this function handles it and treats the request as a possible event.
#
onPushEvent = ( req, resp ) =>
body = ''
req.on 'data', ( data ) ->
body += data
req.on 'end', =>
obj = qs.parse body
# If required event properties are present we process the event #
if obj and obj.event and obj.eventid
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.shutDown = () ->
log.print 'HL', 'Shutting down HTTP listener'
process.exit() # This is a bit brute force...

View file

@ -1,6 +1,8 @@
### ###
Rules Server Rules Server
============ ============
>This is the main module that is used to run the whole server: >This is the main module that is used to run the whole server:
> >
> node server [log_type http_port] > node server [log_type http_port]
@ -14,46 +16,38 @@ Rules Server
>`http_port` can be set to use another port, than defined in the >`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. >[config](config.html) file, to listen to, e.g. used by the test suite.
> >
>--- >
### ###
'use strict' # Requires:
### Grab all required modules ###
path = require 'path' # - The [Logging](logging.html) module
log = require './logging' log = require './logging'
# - The [Config](config.html) module
conf = require './config' conf = require './config'
# - The [DB Interface](db_interface.html) module
db = require './db_interface' db = require './db_interface'
# - The [Engine](engine.html) module
engine = require './engine' engine = require './engine'
# - The [HTTP Listener](http_listener.html) module
http_listener = require './http_listener' http_listener = require './http_listener'
mm = require './module_manager'
args = {} args = {}
procCmds = {} 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, Error handling of the express port listener requires special attention,
thus we have to catch the process error, which is issued if thus we have to catch the process error, which is issued if
the port is already in use. the port is already in use.
### ###
process.on 'uncaughtException', (err) -> process.on 'uncaughtException', ( err ) ->
switch err.errno switch err.errno
when 'EADDRINUSE' when 'EADDRINUSE'
err.addInfo = 'http_port already in use, shutting down!' err.addInfo = 'http_port already in use, shutting down!'
log.error 'RS', err log.error 'RS', err
shutDown() shutDown()
else log.error err else throw err
null
### ###
This function is invoked right after the module is loaded and starts the server. This function is invoked right after the module is loaded and starts the server.
@ -85,9 +79,10 @@ init = ->
if process.argv.length > 3 then args.http_port = parseInt process.argv[3] if process.argv.length > 3 then args.http_port = parseInt process.argv[3]
else log.print 'RS', 'No HTTP port passed, using standard port from config file' else log.print 'RS', 'No HTTP port passed, using standard port from config file'
log.print 'RS', 'Initialzing DB'
db args db args
### We only proceed with the initialization if the DB is ready ### ### We only proceed with the initialization if the DB is ready ###
db.isConnected (err, result) -> db.isConnected ( err, result ) ->
if !err if !err
### Initialize all required modules with the args object.### ### Initialize all required modules with the args object.###
@ -95,57 +90,23 @@ init = ->
engine args engine args
log.print 'RS', 'Initialzing http listener' log.print 'RS', 'Initialzing http listener'
http_listener args http_listener args
log.print 'RS', 'Initialzing module manager'
mm args
log.print 'RS', 'Initialzing DB'
### Distribute handlers between modules to link the application. ### ### Distribute handlers between modules to link the application. ###
log.print 'RS', 'Passing handlers to engine' log.print 'RS', 'Passing handlers to engine'
engine.addDBLinkAndLoadActionsAndRules db engine.addDBLinkAndLoadActionsAndRules db
log.print 'RS', 'Passing handlers to http listener' log.print 'RS', 'Passing handlers to http listener'
http_listener.addHandlers db, fAdminCommands, engine.pushEvent # TODO engine pushEvent needs to go into redis queue
log.print 'RS', 'Passing handlers to module manager' http_listener.addHandlers db, engine.pushEvent, shutDown
mm.addHandlers db, engine.loadActionModule, engine.addRule # log.print 'RS', 'Passing handlers to module manager'
# TODO loadAction and addRule will be removed
### # 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* )
###
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
### ###
Shuts down the server. Shuts down the server.
@private shutDown( *args, answHandler* ) @private shutDown()
@param {Object} args
@param {Object} answHandler
### ###
shutDown = (args, answHandler) -> shutDown = ->
answHandler?.answerSuccess 'Goodbye!'
log.print 'RS', 'Received shut down command!' log.print 'RS', 'Received shut down command!'
engine?.shutDown() engine?.shutDown()
http_listener?.shutDown() http_listener?.shutDown()
@ -157,7 +118,7 @@ When the server is run as a child process, this function handles messages
from the parent process (e.g. the testing suite) from the parent process (e.g. the testing suite)
### ###
process.on 'message', (cmd) -> procCmds[cmd]?() process.on 'message', ( cmd ) -> procCmds[cmd]?()
### ###
The die command redirects to the shutDown function. The die command redirects to the shutDown function.

View file

@ -1,10 +0,0 @@
efew = require 'fs'
###
root = exports ? this
root.foo = -> 'Hello World'
console.log root.foo()
My comments will show up here
###

138
coffee/user_handler.coffee Normal file
View file

@ -0,0 +1,138 @@
###
User Handler
============
> TODO Add documentation
###
fs = require 'fs'
path = require 'path'
qs = require 'querystring'
# Requires:
# - The [Logging](logging.html) module
log = require './logging'
# - The [DB Interface](db_interface.html) module
db = require './db_interface'
# - The [Module Manager](module_manager.html) module
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 = ( args ) ->
args = args ? {}
log args
db args
mm args
mm.addDBLink db
users = JSON.parse fs.readFileSync path.resolve __dirname, '..', 'config', 'users.json'
db.storeUser user for user in users
module.exports
exports.addShutdownHandler = ( fShutdown ) ->
objAdminCmds.shutdown = fShutdown
exports.handleRequest = ( req, resp ) ->
req.on 'end', -> resp.end()
if req.session and req.session.user
resp.send 'You\'re logged in'
else
resp.sendfile path.resolve __dirname, '..', 'webpages', 'handlers', 'login.html'
req.session.lastPage = req.originalUrl
exports.handleLogin = ( req, resp ) ->
body = ''
req.on 'data', ( data ) -> body += data
req.on 'end', ->
if not req.session or not req.session.user
obj = qs.parse body
db.loginUser obj.username, obj.password, ( err, obj ) ->
if not 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()
else
resp.write 'Welcome ' + req.session.user.name + '!'
resp.end()
answerHandler = ( resp ) ->
hasBeenAnswered = false
postAnswer( msg ) ->
if not hasBeenAnswered
resp.write msg
resp.end()
hasBeenAnswered = true
{
answerSuccess: ( msg ) ->
if not hasBeenAnswered
postAnswer msg,
answerError: ( msg ) ->
if not hasBeenAnswered
resp.writeHead 400, { "Content-Type": "text/plain" }
postAnswer msg,
isAnswered: -> hasBeenAnswered
}
# TODO add loadUsers as directive to admin commands
# exports.loadUsers = ->
# var users = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', 'users.json')));
# for(var name in users) {
# db.storeUser(users[name]);
# }
# };
onAdminCommand = ( req, response ) ->
q = req.query;
log.print 'HL', 'Received admin request: ' + req.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* )
###
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

View file

@ -3,23 +3,20 @@
Config Config
====== ======
> Loads the configuration file and acts as an interface to it.
Loads the configuration file and acts as an interface to it.
*/ */
(function() { (function() {
'use strict'; var exports, fetchProp, fs, loadConfigFile, log, path,
var config, exports, fetchProp, fs, loadConfigFile, log, path; _this = this;
fs = require('fs');
path = require('path'); path = require('path');
log = require('./logging'); log = require('./logging');
fs = require('fs');
config = null;
/* /*
##Module call ##Module call
@ -50,8 +47,8 @@ Loads the configuration file and acts as an interface to it.
loadConfigFile = function(relPath) { loadConfigFile = function(relPath) {
var e; var e;
try { try {
config = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', relPath))); _this.config = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', relPath)));
if (config && config.http_port && config.db_port && config.crypto_key && config.session_secret) { if (_this.config && _this.config.http_port && _this.config.db_port && _this.config.crypto_key && _this.config.session_secret) {
return log.print('CF', 'config file loaded successfully'); return log.print('CF', 'config file loaded successfully');
} else { } else {
return log.error('CF', new Error("Missing property in config file, requires:\n- http_port\n- db_port\n- crypto_key\n- session_secret")); return log.error('CF', new Error("Missing property in config file, requires:\n- http_port\n- db_port\n- crypto_key\n- session_secret"));
@ -74,7 +71,8 @@ Loads the configuration file and acts as an interface to it.
fetchProp = function(prop) { fetchProp = function(prop) {
return config != null ? config[prop] : void 0; var _ref;
return (_ref = _this.config) != null ? _ref[prop] : void 0;
}; };
/* /*
@ -85,7 +83,7 @@ Loads the configuration file and acts as an interface to it.
exports.isReady = function() { exports.isReady = function() {
return config != null; return _this.config != null;
}; };
/* /*

View file

@ -3,28 +3,26 @@
DB Interface DB Interface
============ ============
>Handles the connection to the database and provides functionalities for > Handles the connection to the database and provides functionalities for
>event/action modules, rules and the encrypted storing of authentication tokens. > event/action modules, rules and the encrypted storing of authentication tokens.
>General functionality as a wrapper for the module holds initialization, > General functionality as a wrapper for the module holds initialization,
>encryption/decryption, the retrieval of modules and shut down. > encryption/decryption, the retrieval of modules and shut down.
> >
>The general structure for linked data is that the key is stored in a set. > 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 > By fetching all set entries we can then fetch all elements, which is
>automated in this function. > automated in this function.
>For example modules of the same group, e.g. action modules are registered in an > 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 > 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 > 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 > 'action_modules' and then stored in the db with the key 'action\_module\_' + ID
>(e.g. action\_module\_probinder). > (e.g. action\_module\_probinder).
> >
*/ */
(function() { (function() {
'use strict'; var crypto, decrypt, encrypt, exports, getSetRecords, log, redis, replyHandler,
/* Grab all required modules*/ _this = this;
var crypto, crypto_key, db, decrypt, encrypt, exports, getSetRecords, log, redis, replyHandler;
redis = require('redis'); redis = require('redis');
@ -32,15 +30,12 @@ DB Interface
log = require('./logging'); log = require('./logging');
crypto_key = null;
db = null;
/* /*
##Module call Module call
-----------
Initializes the DB connection. Requires a valid configuration file which contains Initializes the DB connection. Requires a valid configuration file which contains
a db port and a crypto key. a db port and a crypto key.
@param {Object} args @param {Object} args
*/ */
@ -51,11 +46,11 @@ DB Interface
log(args); log(args);
config = require('./config'); config = require('./config');
config(args); config(args);
crypto_key = config.getCryptoKey(); _this.crypto_key = config.getCryptoKey();
db = redis.createClient(config.getDBPort(), 'localhost', { _this.db = redis.createClient(config.getDBPort(), 'localhost', {
connect_timeout: 2000 connect_timeout: 2000
}); });
return db.on("error", function(err) { return _this.db.on("error", function(err) {
err.addInfo = 'message from DB'; err.addInfo = 'message from DB';
return log.error('DB', err); return log.error('DB', err);
}); });
@ -72,13 +67,13 @@ DB Interface
exports.isConnected = function(cb) { exports.isConnected = function(cb) {
var fCheckConnection, numAttempts; var fCheckConnection, numAttempts;
if (db.connected) { if (_this.db.connected) {
return cb(); return cb();
} else { } else {
numAttempts = 0; numAttempts = 0;
fCheckConnection = function() { fCheckConnection = function() {
var e; var e;
if (db.connected) { if (_this.db.connected) {
log.print('DB', 'Successfully connected to DB!'); log.print('DB', 'Successfully connected to DB!');
return cb(); return cb();
} else if (numAttempts++ < 10) { } else if (numAttempts++ < 10) {
@ -107,7 +102,7 @@ DB Interface
return null; return null;
} }
try { try {
enciph = crypto.createCipher('aes-256-cbc', crypto_key); enciph = crypto.createCipher('aes-256-cbc', _this.crypto_key);
et = enciph.update(plainText, 'utf8', 'base64'); et = enciph.update(plainText, 'utf8', 'base64');
return et + enciph.final('base64'); return et + enciph.final('base64');
} catch (_error) { } catch (_error) {
@ -132,7 +127,7 @@ DB Interface
return null; return null;
} }
try { try {
deciph = crypto.createDecipher('aes-256-cbc', crypto_key); deciph = crypto.createDecipher('aes-256-cbc', _this.crypto_key);
dt = deciph.update(crypticText, 'base64', 'utf8'); dt = deciph.update(crypticText, 'base64', 'utf8');
return dt + deciph.final('utf8'); return dt + deciph.final('utf8');
} catch (_error) { } catch (_error) {
@ -175,7 +170,7 @@ DB Interface
getSetRecords = function(set, fSingle, cb) { getSetRecords = function(set, fSingle, cb) {
log.print('DB', 'Fetching set records: ' + set); log.print('DB', 'Fetching set records: ' + set);
return db != null ? db.smembers(set, function(err, arrReply) { return _this.db.smembers(set, function(err, arrReply) {
var fCallback, objReplies, reply, semaphore, _i, _len, _results; var fCallback, objReplies, reply, semaphore, _i, _len, _results;
if (err) { if (err) {
err.addInfo = 'fetching ' + set; err.addInfo = 'fetching ' + set;
@ -213,7 +208,7 @@ DB Interface
} }
return _results; return _results;
} }
}) : void 0; });
}; };
/* /*
@ -232,10 +227,8 @@ DB Interface
exports.storeActionModule = function(id, data) { exports.storeActionModule = function(id, data) {
log.print('DB', 'storeActionModule: ' + id); log.print('DB', 'storeActionModule: ' + id);
if (db != null) { _this.db.sadd('action-modules', id, replyHandler('storing action module key ' + id));
db.sadd('action-modules', id, replyHandler('storing action module key ' + id)); return _this.db.set('action-module:' + id, data, replyHandler('storing action module ' + id));
}
return db != null ? db.set('action-module:' + id, data, replyHandler('storing action module ' + id)) : void 0;
}; };
/* /*
@ -249,7 +242,7 @@ DB Interface
exports.getActionModule = function(id, cb) { exports.getActionModule = function(id, cb) {
log.print('DB', 'getActionModule: ' + id); log.print('DB', 'getActionModule: ' + id);
return db != null ? db.get('action-module:' + id, cb) : void 0; return _this.db.get('action-module:' + id, cb);
}; };
/* /*
@ -276,7 +269,7 @@ DB Interface
exports.storeActionAuth = function(userId, moduleId, data) { exports.storeActionAuth = function(userId, moduleId, data) {
log.print('DB', 'storeActionAuth: ' + userId + ':' + moduleId); log.print('DB', 'storeActionAuth: ' + userId + ':' + moduleId);
return db != null ? db.set('action-auth:' + userId + ':' + moduleId, encrypt(data), replyHandler('storing action auth ' + userId + ':' + moduleId)) : void 0; return _this.db.set('action-auth:' + userId + ':' + moduleId, encrypt(data), replyHandler('storing action auth ' + userId + ':' + moduleId));
}; };
/* /*
@ -292,9 +285,9 @@ DB Interface
exports.getActionAuth = function(userId, moduleId, cb) { exports.getActionAuth = function(userId, moduleId, cb) {
log.print('DB', 'getActionAuth: ' + userId + ':' + moduleId); log.print('DB', 'getActionAuth: ' + userId + ':' + moduleId);
return db != null ? db.get('action-auth:' + userId + ':' + moduleId, function(err, data) { return _this.db.get('action-auth:' + userId + ':' + moduleId, function(err, data) {
return cb(err, decrypt(data)); return cb(err, decrypt(data));
}) : void 0; });
}; };
/* /*
@ -313,10 +306,8 @@ DB Interface
exports.storeEventModule = function(id, data) { exports.storeEventModule = function(id, data) {
log.print('DB', 'storeEventModule: ' + id); log.print('DB', 'storeEventModule: ' + id);
if (db != null) { _this.db.sadd('event-modules', id, replyHandler('storing event module key ' + id));
db.sadd('event-modules', id, replyHandler('storing event module key ' + id)); return _this.db.set('event-module:' + id, data, replyHandler('storing event module ' + id));
}
return db != null ? db.set('event-module:' + id, data, replyHandler('storing event module ' + id)) : void 0;
}; };
/* /*
@ -330,7 +321,7 @@ DB Interface
exports.getEventModule = function(id, cb) { exports.getEventModule = function(id, cb) {
log.print('DB', 'getEventModule: ' + id); log.print('DB', 'getEventModule: ' + id);
return db != null ? db.get('event_module:' + id, cb) : void 0; return _this.db.get('event_module:' + id, cb);
}; };
/* /*
@ -349,30 +340,32 @@ DB Interface
Store a string representation of he authentication parameters for an event module. Store a string representation of he authentication parameters for an event module.
@public storeEventAuth( *userId, moduleId, data* ) @public storeEventAuth( *userId, moduleId, data* )
@param {String} id @param {String} userId
@param {String} data @param {String} moduleId
@param {Object} data
*/ */
exports.storeEventAuth = function(userId, moduleId, data) { exports.storeEventAuth = function(userId, moduleId, data) {
log.print('DB', 'storeEventAuth: ' + userId + ':' + moduleId); log.print('DB', 'storeEventAuth: ' + userId + ':' + moduleId);
return db != null ? db.set('event-auth:' + userId + ':' + moduleId, encrypt(data), replyHandler('storing event auth ' + userId + ':' + moduleId)) : void 0; return _this.db.set('event-auth:' + userId + ':' + moduleId, encrypt(data), replyHandler('storing event auth ' + userId + ':' + moduleId));
}; };
/* /*
Query the DB for an action module authentication token, associated with a user. Query the DB for an action module authentication token, associated with a user.
@public getEventAuth( *id, cb* ) @public getEventAuth( *userId, moduleId, data* )
@param {String} id @param {String} userId
@param {String} moduleId
@param {function} cb @param {function} cb
*/ */
exports.getEventAuth = function(userId, moduleId, cb) { exports.getEventAuth = function(userId, moduleId, cb) {
log.print('DB', 'getEventAuth: ' + userId + ':' + moduleId); log.print('DB', 'getEventAuth: ' + userId + ':' + moduleId);
return db != null ? db.get('event-auth:' + userId + ':' + moduleId, function(err, data) { return _this.db.get('event-auth:' + userId + ':' + moduleId, function(err, data) {
return cb(err, decrypt(data)); return cb(err, decrypt(data));
}) : void 0; });
}; };
/* /*
@ -391,10 +384,8 @@ DB Interface
exports.storeRule = function(id, data) { exports.storeRule = function(id, data) {
log.print('DB', 'storeRule: ' + id); log.print('DB', 'storeRule: ' + id);
if (db != null) { _this.db.sadd('rules', id, replyHandler('storing rule key ' + id));
db.sadd('rules', id, replyHandler('storing rule key ' + id)); return _this.db.set('rule:' + id, data, replyHandler('storing rule ' + id));
}
return db != null ? db.set('rule:' + id, data, replyHandler('storing rule ' + id)) : void 0;
}; };
/* /*
@ -408,7 +399,7 @@ DB Interface
exports.getRule = function(id, cb) { exports.getRule = function(id, cb) {
log.print('DB', 'getRule: ' + id); log.print('DB', 'getRule: ' + id);
return db != null ? db.get('rule:' + id, cb) : void 0; return _this.db.get('rule:' + id, cb);
}; };
/* /*
@ -435,11 +426,9 @@ DB Interface
exports.storeUser = function(objUser) { exports.storeUser = function(objUser) {
log.print('DB', 'storeUser: ' + objUser.username); log.print('DB', 'storeUser: ' + objUser.username);
if (objUser && objUser.username && objUser.password) { if (objUser && objUser.username && objUser.password) {
if (db != null) { _this.db.sadd('users', objUser.username, replyHandler('storing user key ' + objUser.username));
db.sadd('users', objUser.username, replyHandler('storing user key ' + objUser.username));
}
objUser.password = encrypt(objUser.password); objUser.password = encrypt(objUser.password);
return db != null ? db.hmset('user:' + objUser.username, objUser, replyHandler('storing user properties ' + objUser.username)) : void 0; return _this.db.hmset('user:' + objUser.username, objUser, replyHandler('storing user properties ' + objUser.username));
} else { } else {
return log.error('DB', new Error('username or password was missing')); return log.error('DB', new Error('username or password was missing'));
} }
@ -456,10 +445,8 @@ DB Interface
exports.storeUserRole = function(username, role) { exports.storeUserRole = function(username, role) {
log.print('DB', 'storeUserRole: ' + username + ':' + role); log.print('DB', 'storeUserRole: ' + username + ':' + role);
if (db != null) { _this.db.sadd('user-roles:' + username, role, replyHandler('adding role ' + role + ' to user ' + username));
db.sadd('user-roles:' + username, role, replyHandler('adding role ' + role + ' to user ' + username)); return _this.db.sadd('role-users:' + role, username, replyHandler('adding user ' + username + ' to role ' + role));
}
return db != null ? db.sadd('role-users:' + role, username, replyHandler('adding user ' + username + ' to role ' + role)) : void 0;
}; };
/* /*
@ -472,7 +459,7 @@ DB Interface
exports.getUserRoles = function(username) { exports.getUserRoles = function(username) {
log.print('DB', 'getUserRole: ' + username); log.print('DB', 'getUserRole: ' + username);
return db != null ? db.get('user-roles:' + username, cb) : void 0; return _this.db.get('user-roles:' + username, cb);
}; };
/* /*
@ -485,7 +472,7 @@ DB Interface
exports.getRoleUsers = function(role) { exports.getRoleUsers = function(role) {
log.print('DB', 'getRoleUsers: ' + role); log.print('DB', 'getRoleUsers: ' + role);
return db != null ? db.get('role-users:' + role, cb) : void 0; return _this.db.get('role-users:' + role, cb);
}; };
/* /*
@ -517,7 +504,7 @@ DB Interface
} }
}; };
}; };
return db != null ? db.hgetall('user:' + username, fCheck(password)) : void 0; return _this.db.hgetall('user:' + username, fCheck(password));
}; };
/* /*
@ -528,7 +515,7 @@ DB Interface
exports.shutDown = function() { exports.shutDown = function() {
return db != null ? db.quit() : void 0; return _this.db.quit();
}; };
}).call(this); }).call(this);

View file

@ -1,116 +1,95 @@
// HTTP Listener // Generated by CoffeeScript 1.6.3
// =============
//
// 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) { HTTP Listener
if(!funcAdminHandler || !funcEvtHandler) { =============
log.error('HL', 'ERROR: either adminHandler or eventHandler function not defined!'); > Handles the HTTP requests to the server at the port specified by the
return; > [config](config.html) file.
} */
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'});
}
};
/** (function() {
* If a post request reaches the server, this function handles it and treats the request as a possible event. var app, config, exports, express, log, onPushEvent, path, qs, sess_sec, userHandler,
*/ _this = this;
function onPushEvent(req, resp) {
var body = ''; path = require('path');
req.on('data', function (data) { body += data; });
req.on('end', function () { express = require('express');
var obj = qs.parse(body);
/* If required event properties are present we process the event */ app = express();
if(obj && obj.event && obj.eventid){
resp.writeHead(200, { "Content-Type": "text/plain" }); qs = require('querystring');
resp.write('Thank you for the event (' + obj.event + '[' + obj.eventid + '])!');
eventHandler(obj); log = require('./logging');
} else {
resp.writeHead(400, { "Content-Type": "text/plain" }); config = require('./config');
resp.write('Your event was missing important parameters!');
userHandler = require('./user_handler');
sess_sec = '#C[>;j`@".TXm2TA;A2Tg)';
exports = module.exports = function(args) {
args = args != null ? args : {};
log(args);
config(args);
userHandler(args);
sess_sec = config.getSessionSecret() || sess_sec;
return module.exports;
};
exports.addHandlers = function(fEvtHandler, fShutDown) {
var e, http_port;
userHandler.addShutdownHandler(fShutDown);
_this.eventHandler = fEvtHandler;
app.use(express.cookieParser());
app.use(express.session({
secret: sess_sec
}));
log.print('HL', 'no session backbone');
app.use('/', express["static"](path.resolve(__dirname, '..', 'webpages')));
app.get('/rulesforge', userHandler.handleRequest);
app.get('/admin', userHandler.handleRequest);
app.post('/login', userHandler.handleLogin);
app.post('/push_event', onPushEvent);
try {
http_port = config.getHttpPort();
if (http_port) {
return app.listen(http_port);
} else {
return log.error('HL', new Error('No HTTP port found!? Nothing to listen on!...'));
}
} catch (_error) {
e = _error;
e.addInfo = 'opening port';
return log.error(e);
} }
resp.end(); };
});
}
exports.loadUsers = function() { onPushEvent = function(req, resp) {
var users = JSON.parse(require('fs').readFileSync(path.resolve(__dirname, '..', relPath))); var body;
for(var name in users) { body = '';
req.on('data', function(data) {
} return body += data;
}; });
return req.on('end', function() {
var obj;
obj = qs.parse(body);
if (obj && obj.event && obj.eventid) {
resp.write('Thank you for the event (' + obj.event + '[' + obj.eventid + '])!');
_this.eventHandler(obj);
} else {
resp.writeHead(400, {
"Content-Type": "text/plain"
});
resp.write('Your event was missing important parameters!');
}
return resp.end();
});
};
exports.shutDown = function() { exports.shutDown = function() {
log.print('HL', 'Shutting down HTTP listener'); log.print('HL', 'Shutting down HTTP listener');
process.exit(); // This is a bit brute force... return process.exit();
}; };
}).call(this);

View file

@ -21,10 +21,10 @@ exports = module.exports = function(args) {
return module.exports; return module.exports;
}; };
exports.addHandlers = function(db_link, fLoadAction, fLoadRule) { exports.addDBLink = function(db_link) {
db = db_link; db = db_link;
funcLoadAction = fLoadAction; // funcLoadAction = fLoadAction;
funcLoadRule = fLoadRule; // funcLoadRule = fLoadRule;
}; };
/* /*
@ -47,7 +47,7 @@ exports.loadRulesFromFS = function(args, answHandler) {
for(var i = 0; i < arr.length; i++) { for(var i = 0; i < arr.length; i++) {
txt += arr[i].id + ', '; txt += arr[i].id + ', ';
db.storeRule(arr[i].id, JSON.stringify(arr[i])); db.storeRule(arr[i].id, JSON.stringify(arr[i]));
funcLoadRule(arr[i]); // funcLoadRule(arr[i]);
} }
answHandler.answerSuccess('Yep, loaded rules: ' + txt); answHandler.answerSuccess('Yep, loaded rules: ' + txt);
} catch (e) { } catch (e) {
@ -71,7 +71,7 @@ exports.loadRulesFromFS = function(args, answHandler) {
*/ */
function loadActionCallback(name, data, mod, auth) { function loadActionCallback(name, data, mod, auth) {
db.storeActionModule(name, data); // store module in db db.storeActionModule(name, data); // store module in db
funcLoadAction(name, mod); // hand back compiled module // funcLoadAction(name, mod); // hand back compiled module
if(auth) db.storeActionModuleAuth(name, auth); if(auth) db.storeActionModuleAuth(name, auth);
} }

View file

@ -1,7 +1,9 @@
// Generated by CoffeeScript 1.6.3 // Generated by CoffeeScript 1.6.3
/* /*
Rules Server Rules Server
============ ============
>This is the main module that is used to run the whole server: >This is the main module that is used to run the whole server:
> >
> node server [log_type http_port] > node server [log_type http_port]
@ -15,17 +17,12 @@ Rules Server
>`http_port` can be set to use another port, than defined in the >`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. >[config](config.html) file, to listen to, e.g. used by the test suite.
> >
>--- >
*/ */
(function() { (function() {
'use strict'; var args, conf, db, engine, http_listener, init, log, procCmds, shutDown;
/* Grab all required modules*/
var adminCmds, args, conf, db, engine, fAdminCommands, http_listener, init, log, mm, path, procCmds, shutDown;
path = require('path');
log = require('./logging'); log = require('./logging');
@ -37,25 +34,10 @@ Rules Server
http_listener = require('./http_listener'); http_listener = require('./http_listener');
mm = require('./module_manager');
args = {}; args = {};
procCmds = {}; 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, Error handling of the express port listener requires special attention,
thus we have to catch the process error, which is issued if thus we have to catch the process error, which is issued if
@ -68,12 +50,10 @@ Rules Server
case 'EADDRINUSE': case 'EADDRINUSE':
err.addInfo = 'http_port already in use, shutting down!'; err.addInfo = 'http_port already in use, shutting down!';
log.error('RS', err); log.error('RS', err);
shutDown(); return shutDown();
break;
default: default:
log.error(err); throw err;
} }
return null;
}); });
/* /*
@ -119,6 +99,7 @@ Rules Server
} else { } else {
log.print('RS', 'No HTTP port passed, using standard port from config file'); log.print('RS', 'No HTTP port passed, using standard port from config file');
} }
log.print('RS', 'Initialzing DB');
db(args); db(args);
/* We only proceed with the initialization if the DB is ready*/ /* We only proceed with the initialization if the DB is ready*/
@ -130,75 +111,24 @@ Rules Server
engine(args); engine(args);
log.print('RS', 'Initialzing http listener'); log.print('RS', 'Initialzing http listener');
http_listener(args); http_listener(args);
log.print('RS', 'Initialzing module manager');
mm(args);
log.print('RS', 'Initialzing DB');
/* Distribute handlers between modules to link the application.*/ /* Distribute handlers between modules to link the application.*/
log.print('RS', 'Passing handlers to engine'); log.print('RS', 'Passing handlers to engine');
engine.addDBLinkAndLoadActionsAndRules(db); engine.addDBLinkAndLoadActionsAndRules(db);
log.print('RS', 'Passing handlers to http listener'); log.print('RS', 'Passing handlers to http listener');
http_listener.addHandlers(db, fAdminCommands, engine.pushEvent); return http_listener.addHandlers(db, engine.pushEvent, shutDown);
log.print('RS', 'Passing handlers to module manager');
return 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* )
*/
fAdminCommands = function(args, answHandler) {
var fAnsw, _name;
if (args && args.cmd) {
if (typeof adminCmds[_name = args.cmd] === "function") {
adminCmds[_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);
};
/* /*
Shuts down the server. Shuts down the server.
@private shutDown( *args, answHandler* ) @private shutDown()
@param {Object} args
@param {Object} answHandler
*/ */
shutDown = function(args, answHandler) { shutDown = function() {
if (answHandler != null) {
answHandler.answerSuccess('Goodbye!');
}
log.print('RS', 'Received shut down command!'); log.print('RS', 'Received shut down command!');
if (engine != null) { if (engine != null) {
engine.shutDown(); engine.shutDown();

View file

@ -1,16 +0,0 @@
// Generated by CoffeeScript 1.6.3
(function() {
var efew;
efew = require('fs');
/*
root = exports ? this
root.foo = -> 'Hello World'
console.log root.foo()
My comments will show up here
*/
}).call(this);

View file

@ -1,96 +1,180 @@
var path = require('path'), // Generated by CoffeeScript 1.6.3
qs = require('querystring'), /*
log = require('./logging'),
db = require('./db_interface'),
adminHandler;
exports = module.exports = function(args) {
args = args || {};
log(args);
db(args);
var users = JSON.parse(require('fs').readFileSync(path.resolve(__dirname, '..', 'config', 'users.json')));
for(var i = 0; i < users.length; i++) {
log.print('UH', 'Found user ' + users[i].username + ' in user file, storing him in the DB');
db.storeUser(users[i]);
}
return module.exports;
};
exports.addHandler = function(adminHandl) { User Handler
adminHandler = adminHandl; ============
}; > TODO Add documentation
*/
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) { (function() {
var body = ''; var answerHandler, db, exports, fAdminCommands, fs, log, mm, objAdminCmds, onAdminCommand, path, qs;
req.on('data', function (data) { body += data; });
req.on('end', function () { fs = require('fs');
if(!req.session || !req.session.user) {
var obj = qs.parse(body); path = require('path');
db.loginUser(obj.username, obj.password, function(err, obj) {
if(err) { qs = require('querystring');
log.error('UH', err);
resp.writeHead(401, { "Content-Type": "text/plain" }); log = require('./logging');
resp.write('Login failed!');
} db = require('./db_interface');
else {
req.session.user = obj; mm = require('./module_manager');
if(req.session.user) {
/* 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) {
var user, users, _i, _len;
args = args != null ? args : {};
log(args);
db(args);
mm(args);
mm.addDBLink(db);
users = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', 'config', 'users.json')));
for (_i = 0, _len = users.length; _i < _len; _i++) {
user = users[_i];
db.storeUser(user);
}
return module.exports;
};
exports.addShutdownHandler = function(fShutdown) {
return objAdminCmds.shutdown = fShutdown;
};
exports.handleRequest = function(req, resp) {
req.on('end', function() {
return resp.end();
});
if (req.session && req.session.user) {
resp.send('You\'re logged in');
} else {
resp.sendfile(path.resolve(__dirname, '..', 'webpages', 'handlers', 'login.html'));
}
return req.session.lastPage = req.originalUrl;
};
exports.handleLogin = function(req, resp) {
var body;
body = '';
req.on('data', function(data) {
return body += data;
});
return req.on('end', function() {
var obj;
if (!req.session || !req.session.user) {
obj = qs.parse(body);
return 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 + '!'); resp.write('Welcome ' + req.session.user.name + '!');
} else { } else {
resp.writeHead(401, { "Content-Type": "text/plain" }); resp.writeHead(401, {
"Content-Type": "text/plain"
});
resp.write('Login failed!'); resp.write('Login failed!');
} }
} return resp.end();
resp.end(); });
}); } else {
} resp.write('Welcome ' + req.session.user.name + '!');
}); return 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; }
}; };
};
function onAdminCommand(request, response) { answerHandler = function(resp) {
var q = request.query; var hasBeenAnswered;
log.print('HL', 'Received admin request: ' + request.originalUrl); hasBeenAnswered = false;
if(q.cmd) { postAnswer(msg)(function() {
adminHandler(q, answerHandler(response)); if (!hasBeenAnswered) {
// answerSuccess(response, 'Thank you, we try our best!'); resp.write(msg);
} else answerError(response, 'I\'m not sure about what you want from me...'); resp.end();
} return hasBeenAnswered = true;
}
});
return {
answerSuccess: function(msg) {
if (!hasBeenAnswered) {
return postAnswer(msg);
}
},
answerError: function(msg) {
if (!hasBeenAnswered) {
resp.writeHead(400, {
"Content-Type": "text/plain"
});
}
return postAnswer(msg);
},
isAnswered: function() {
return hasBeenAnswered;
}
};
};
onAdminCommand = function(req, response) {
var q;
q = req.query;
log.print('HL', 'Received admin request: ' + req.originalUrl);
if (q.cmd) {
return fAdminCommands(q, answerHandler(response));
} else {
return 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* )
*/
fAdminCommands = function(args, answHandler) {
var fAnsw, _name;
if (args && args.cmd) {
if (typeof adminCmds[_name = args.cmd] === "function") {
adminCmds[_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);
};
}).call(this);

View file

@ -21,10 +21,11 @@ exports = module.exports = function(args) {
return module.exports; return module.exports;
}; };
exports.addHandlers = function(db_link, fLoadAction, fLoadRule) { exports.addDBLink = function(db_link) {
db = db_link; db = db_link;
funcLoadAction = fLoadAction; //TODO Remove fLoadAction and fLoadRule and replace them with user commands
funcLoadRule = fLoadRule; // funcLoadAction = fLoadAction;
// funcLoadRule = fLoadRule;
}; };
/* /*
@ -34,8 +35,8 @@ exports.addHandlers = function(db_link, fLoadAction, fLoadRule) {
exports.loadRulesFromFS = function(args, answHandler) { exports.loadRulesFromFS = function(args, answHandler) {
if(!args) args = {}; if(!args) args = {};
if(!args.name) args.name = 'rules'; if(!args.name) args.name = 'rules';
if(!funcLoadRule) log.error('ML', 'no rule loader function available'); // if(!funcLoadRule) log.error('ML', 'no rule loader function available');
else { // else {
fs.readFile(path.resolve(__dirname, '..', 'rules', args.name + '.json'), 'utf8', function (err, data) { fs.readFile(path.resolve(__dirname, '..', 'rules', args.name + '.json'), 'utf8', function (err, data) {
if (err) { if (err) {
log.error('ML', 'Loading rules file: ' + args.name + '.json'); log.error('ML', 'Loading rules file: ' + args.name + '.json');
@ -47,14 +48,14 @@ exports.loadRulesFromFS = function(args, answHandler) {
for(var i = 0; i < arr.length; i++) { for(var i = 0; i < arr.length; i++) {
txt += arr[i].id + ', '; txt += arr[i].id + ', ';
db.storeRule(arr[i].id, JSON.stringify(arr[i])); db.storeRule(arr[i].id, JSON.stringify(arr[i]));
funcLoadRule(arr[i]); // funcLoadRule(arr[i]);
} }
answHandler.answerSuccess('Yep, loaded rules: ' + txt); answHandler.answerSuccess('Yep, loaded rules: ' + txt);
} catch (e) { } catch (e) {
log.error('ML', 'rules file was corrupt! (' + args.name + '.json)'); log.error('ML', 'rules file was corrupt! (' + args.name + '.json)');
} }
}); });
} // }
}; };
/* /*
@ -71,7 +72,7 @@ exports.loadRulesFromFS = function(args, answHandler) {
*/ */
function loadActionCallback(name, data, mod, auth) { function loadActionCallback(name, data, mod, auth) {
db.storeActionModule(name, data); // store module in db db.storeActionModule(name, data); // store module in db
funcLoadAction(name, mod); // hand back compiled module // funcLoadAction(name, mod); // hand back compiled module
if(auth) db.storeActionModuleAuth(name, auth); if(auth) db.storeActionModuleAuth(name, auth);
} }

View file

@ -2,22 +2,33 @@ var path = require('path'),
qs = require('querystring'), qs = require('querystring'),
log = require('./logging'), log = require('./logging'),
db = require('./db_interface'), db = require('./db_interface'),
adminHandler; 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) { exports = module.exports = function(args) {
args = args || {}; args = args || {};
log(args); log(args);
db(args); db(args);
mm(args);
mm.addDBLink(db);
var users = JSON.parse(require('fs').readFileSync(path.resolve(__dirname, '..', 'config', 'users.json'))); var users = JSON.parse(require('fs').readFileSync(path.resolve(__dirname, '..', 'config', 'users.json')));
for(var name in users) { for(var name in users) {
db.storeUser(users[name]); db.storeUser(users[name]);
} }
log.print('RS', 'Initialzing module manager');
return module.exports; return module.exports;
}; };
exports.addHandler = function(adminHandl) { exports.addHandler = function(fShutdown) {
adminHandler = adminHandl; objAdminCmds.shutdown = fShutdown;
}; };
exports.handleRequest = function(req, resp) { exports.handleRequest = function(req, resp) {
@ -77,12 +88,95 @@ function answerHandler(r) {
}; };
}; };
//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) { function onAdminCommand(request, response) {
var q = request.query; var q = request.query;
log.print('HL', 'Received admin request: ' + request.originalUrl); log.print('HL', 'Received admin request: ' + request.originalUrl);
if(q.cmd) { if(q.cmd) {
adminHandler(q, answerHandler(response)); fAdminCommands(q, answerHandler(response));
// answerSuccess(response, 'Thank you, we try our best!'); // answerSuccess(response, 'Thank you, we try our best!');
} else answerError(response, 'I\'m not sure about what you want from me...'); } 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
*/