diff --git a/coffee/http-listener.coffee b/coffee/http-listener.coffee index 271f5e1..1b53b6b 100644 --- a/coffee/http-listener.coffee +++ b/coffee/http-listener.coffee @@ -61,14 +61,10 @@ initRouting = ( port ) => # - **`GET` to _"/"_:** Static redirect to the _"webpages/public"_ directory app.use '/', express.static path.resolve __dirname, '..', 'webpages', 'public' - # - **`GET` to _"/admin"_:** Only admins can issue requests to this handler + # - **`GET` to _"/admin"_:** Displays the admin console if user is admin app.get '/admin', requestHandler.handleAdmin - # - **`GET` to _"/forge\_modules"_:** Webpage that lets the user create modules - app.get '/forge_modules', requestHandler.handleForgeModules - # - **`GET` to _"/forge\_rules"_:** Webpage that lets the user create rules - app.get '/forge_rules', requestHandler.handleForgeRules - # - **`GET` to _"/invoke\_event"_:** Webpage that lets the user invoke events - app.get '/invoke_event', requestHandler.handleInvokeEvent + # - **`GET` to _"/forge"_:** Displays different forge pages + app.get '/forge', requestHandler.handleForge # POST Requests @@ -78,8 +74,10 @@ initRouting = ( port ) => app.post '/login', requestHandler.handleLogin # - **`POST` to _"/logout"_:** User will be logged out app.post '/logout', requestHandler.handleLogout - # - **`POST` to _"/user"_:** User requests are possible for all users with an account + # - **`POST` to _"/usercommand"_:** User requests are possible for all users with an account app.post '/usercommand', requestHandler.handleUserCommand + # - **`POST` to _"/admincommand"_:** Admin requests are only possible for admins + app.post '/admincommand', requestHandler.handleAdminCommand server = app.listen parseInt( port ) || 8111 # inbound event channel diff --git a/coffee/persistence.coffee b/coffee/persistence.coffee index e3c8c43..c05c125 100644 --- a/coffee/persistence.coffee +++ b/coffee/persistence.coffee @@ -756,7 +756,6 @@ because we only store hashes of passwords for security6 reasons. @param {String} password @param {function} cb ### -#TODO verify and test whole function exports.loginUser = ( userId, password, cb ) => @log.info "DB | User '#{ userId }' tries to log in" fCheck = ( pw ) => diff --git a/coffee/request-handler.coffee b/coffee/request-handler.coffee index bb8c58e..f5379af 100644 --- a/coffee/request-handler.coffee +++ b/coffee/request-handler.coffee @@ -27,15 +27,21 @@ mustache = require 'mustache' crypto = require 'crypto-js' # Prepare the user command handlers which are invoked via HTTP requests. - +dirHandlers = path.resolve __dirname, '..', 'webpages', 'handlers' exports = module.exports = ( args ) => - log = args.logger + @log = args.logger + + # Register the request service @userRequestHandler = args[ 'request-service' ] + + # Register the shutdown handler to the admin command. @objAdminCmds = shutdown: ( args, answerHandler ) -> answerHandler.answerSuccess 'Shutting down... BYE!' setTimeout args[ 'shutdown-function' ], 500 db args + + # Load the standard users from the user config file users = JSON.parse fs.readFileSync path.resolve __dirname, '..', 'config', 'users.json' db.storeUser user for user in users module.exports @@ -52,11 +58,11 @@ objects.* @public handleEvent( *req, resp* ) ### -exports.handleEvent = ( req, resp ) => +exports.handleEvent = ( req, resp ) -> body = '' req.on 'data', ( data ) -> body += data - req.on 'end', => + 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 @@ -67,6 +73,7 @@ exports.handleEvent = ( req, resp ) => ### +Associates the user object with the session if login is successful. *Requires the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest) @@ -75,25 +82,22 @@ objects.* @public handleLogin( *req, resp* ) ### -exports.handleLogin = ( req, resp ) -> +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, usr ) -> - if(err) - # Tapping on fingers, at least in log... - log.warn err, "RH | AUTH-UH-OH (#{obj.username})" - else - # no error, so we can associate the user object from the DB to the session - req.session.user = usr - if req.session.user - resp.send 'OK!' - else - resp.send 401, 'NO!' - else - resp.send 'Welcome ' + req.session.user.name + '!' + req.on 'end', => + obj = qs.parse body + db.loginUser obj.username, obj.password, ( err, usr ) => + if err + # Tapping on fingers, at least in log... + @log.warn "RH | AUTH-UH-OH ( #{ obj.username } ): #{ err.message }" + else + # no error, so we can associate the user object from the DB to the session + req.session.user = usr + if req.session.user + resp.send 'OK!' + else + resp.send 401, 'NO!' ### A post request retrieved on this handler causes the user object to be @@ -119,106 +123,101 @@ Resolves the path to a handler webpage. @param {String} name ### getHandlerPath = ( name ) -> - path.resolve __dirname, '..', 'webpages', 'handlers', name + '.html' - + path.join dirHandlers, name + '.html' ### -Resolves the path to a handler webpage and returns it as a string. +Fetches a template. -@private getHandlerFileAsString( *name* ) +@private getTemplate( *name* ) @param {String} name ### -getHandlerFileAsString = ( name ) -> - fs.readFileSync getHandlerPath( name ), 'utf8' - -### -Fetches an include file. - -@private getIncludeFileAsString( *name* ) -@param {String} name -### -getIncludeFileAsString = ( name ) -> - pth = path.resolve __dirname, '..', 'webpages', 'handlers', 'includes', name + '.html' +getTemplate = ( name ) -> + pth = path.join dirHandlers, 'templates', name + '.html' fs.readFileSync pth, 'utf8' ### -Renders a page depending on the user session and returns it. +Fetches a script. -@private renderPage( *name, sess* ) +@private getScript( *name* ) +@param {String} name +### +getScript = ( name ) -> + pth = path.join dirHandlers, 'js', name + '.js' + fs.readFileSync pth, 'utf8' + +### +Fetches remote scripts snippets. + +@private getRemoteScripts( *name* ) +@param {String} name +### +getRemoteScripts = ( name ) -> + pth = path.join dirHandlers, 'remote-scripts', name + '.html' + fs.readFileSync pth, 'utf8' + +### +Renders a page, with helps of mustache, depending on the user session and returns it. + +@private renderPage( *name, sess, msg* ) @param {String} name @param {Object} sess +@param {Object} msg ### -renderPage = ( name, sess, msg ) -> - template = getHandlerFileAsString name - menubar = getIncludeFileAsString 'menubar' - requires = getIncludeFileAsString 'requires' - view = - user: sess.user, - head_requires: requires, - div_menubar: menubar, +renderPage = ( name, req, resp, msg ) -> + # Grab the skeleton + pathSkel = path.join dirHandlers, 'skeleton.html' + skeleton = fs.readFileSync pathSkel, 'utf8' + code = 200 + data = message: msg - mustache.render template, view + + # Try to grab the script belonging to this page. But don't bother if it's not existing + try + script = getScript name + # Try to grab the remote scripts belonging to this page. But don't bother if it's not existing + try + remote_scripts = getRemoteScripts name + + # Now try to find the page the user requested. + try + content = getTemplate name + catch err + # If the page doesn't exist we return the error page, load the error script into it + # and render the error page with some additional data + content = getTemplate 'error' + script = getScript 'error' + code = 404 + data = + message: 'Invalid Page!' + + content = mustache.render content, data + + if req.session.user + menubar = getTemplate 'menubar' + + view = + user: req.session.user, + content: content, + script: script, + remote_scripts: remote_scripts, + menubar: menubar + resp.send code, mustache.render skeleton, view ### -Sends the desired page or the login to the user. +Present the desired forge page to the user. *Requires the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest) and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse) objects.* -@public renderPageOrLogin( *req, resp, pagename* ) -@param {String} pagename +@public handleForge( *req, resp* ) ### -sendLoginOrPage = ( pagename, req, resp ) -> - if !req.session - req.session = {} - if !req.session.user - pagename = 'login' - # resp.send renderPage pagename, req.session - # else - # resp.sendfile getHandlerPath 'login' - resp.send renderPage pagename, req.session - -### -Present the module forge to the user. - -*Requires -the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest) -and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse) -objects.* - -@public handleForgeModules( *req, resp* ) -### -exports.handleForgeModules = ( req, resp ) -> - sendLoginOrPage 'forge_modules', req, resp - -### -Present the rules forge to the user. - -*Requires -the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest) -and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse) -objects.* - -@public handleForgeRules( *req, resp* ) -### -exports.handleForgeRules = ( req, resp ) -> - sendLoginOrPage 'forge_rules', req, resp - -### -Present the event invoke page to the user. - -*Requires -the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest) -and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse) -objects.* - -@public handleInvokeEvent( *req, resp* ) -### -exports.handleInvokeEvent = ( req, resp ) -> - sendLoginOrPage 'push_event', req, resp - +exports.handleForge = ( req, resp ) -> + page = req.query.page + if not req.session.user + page = 'login' + renderPage page, req, resp ### Handles the user command requests. @@ -230,23 +229,41 @@ objects.* @public handleUser( *req, resp* ) ### -exports.handleUserCommand = ( req, resp ) -> +exports.handleUserCommand = ( req, resp ) => if not req.session or not req.session.user resp.send 401, 'Login first!' else body = '' req.on 'data', ( data ) -> body += data - req.on 'end', -> + req.on 'end', => obj = qs.parse body - console.log obj - @userRequestHandler req.session.user, obj, ( err, obj) -> - console.log 'user request handler sent answer!' - console.log obj - if !err - resp.send 'yay!' + @userRequestHandler req.session.user, obj, ( err, obj ) -> + if err + resp.send 404, 'Rethink your request!' else - resp.send 404, 'Command unknown!' + resp.send obj + +### +Present the admin console to the user if he's allowed to see it. + +*Requires +the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest) +and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse) +objects.* + +@public handleForge( *req, resp* ) +### +exports.handleAdmin = ( req, resp ) -> + if not req.session.user + page = 'login' + # TODO isAdmin should come from the db role + else if req.session.user.isAdmin isnt "true" + page = 'login' + msg = 'You need to be admin!' + else + page = 'admin' + renderPage page, req, resp, msg ### Handles the admin command requests. @@ -256,13 +273,13 @@ the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest) and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse) objects.* -@public handleAdmin( *req, resp* ) +@public handleAdminCommand( *req, resp* ) ### -exports.handleAdmin = ( req, resp ) => +exports.handleAdminCommand = ( req, resp ) => if req.session and req.session.user if req.session.user.isAdmin is "true" q = req.query - log.info 'RH | Received admin request: ' + req.originalUrl + @log.info 'RH | Received admin request: ' + req.originalUrl if q.cmd @objAdminCmds[q.cmd]? q, answerHandler req, resp, true else @@ -273,7 +290,7 @@ exports.handleAdmin = ( req, resp ) => resp.sendfile getHandlerPath 'login' - +#TODO remove this ugly legacy blob answerHandler = (req, resp, ntbr) -> request = req response = resp diff --git a/coffee/webapi-eca.coffee b/coffee/webapi-eca.coffee index 8fdb88e..cfefab2 100644 --- a/coffee/webapi-eca.coffee +++ b/coffee/webapi-eca.coffee @@ -178,7 +178,7 @@ Shuts down the server. ### shutDown = () => @log.warn 'RS | Received shut down command!' - # db?.shutDown() + db?.shutDown() engine?.shutDown() # We need to call process.exit() since the express server in the http-listener # can't be stopped gracefully. Why would you stop this system anyways!?? diff --git a/compile_pagescripts.sh b/compile_pagescripts.sh new file mode 100755 index 0000000..fd6683b --- /dev/null +++ b/compile_pagescripts.sh @@ -0,0 +1,3 @@ +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +echo "Started listening on file changes to compile them!..." +coffee -wc -o $DIR/webpages/handlers/js $DIR/webpages/handlers/coffee diff --git a/js-coffee/components-manager.js b/js-coffee/components-manager.js index b6548c5..d1025ab 100644 --- a/js-coffee/components-manager.js +++ b/js-coffee/components-manager.js @@ -35,6 +35,11 @@ exports.addListener = function( evt, eh ) { exports.processRequest = function( user, obj, cb ) { console.log('module manager needs to process request: '); console.log(obj.command); + var answ = { + test: 'object', + should: 'work' + } + cb(null, answ); } exports.requireFromString = function(src, name, dir) { diff --git a/js-coffee/http-listener.js b/js-coffee/http-listener.js index 2a72556..ba133cc 100644 --- a/js-coffee/http-listener.js +++ b/js-coffee/http-listener.js @@ -58,13 +58,12 @@ HTTP Listener _this.log.info('HL | no session backbone'); app.use('/', express["static"](path.resolve(__dirname, '..', 'webpages', 'public'))); app.get('/admin', requestHandler.handleAdmin); - app.get('/forge_modules', requestHandler.handleForgeModules); - app.get('/forge_rules', requestHandler.handleForgeRules); - app.get('/invoke_event', requestHandler.handleInvokeEvent); + app.get('/forge', requestHandler.handleForge); app.post('/event', requestHandler.handleEvent); app.post('/login', requestHandler.handleLogin); app.post('/logout', requestHandler.handleLogout); app.post('/usercommand', requestHandler.handleUserCommand); + app.post('/admincommand', requestHandler.handleAdminCommand); server = app.listen(parseInt(port) || 8111); server.on('listening', function() { var addr; diff --git a/js-coffee/request-handler.js b/js-coffee/request-handler.js index 8047c41..3c2e7be 100644 --- a/js-coffee/request-handler.js +++ b/js-coffee/request-handler.js @@ -11,7 +11,7 @@ Request Handler (function() { - var answerHandler, crypto, db, exports, fs, getHandlerFileAsString, getHandlerPath, getIncludeFileAsString, mustache, path, qs, renderPage, sendLoginOrPage, + var answerHandler, crypto, db, dirHandlers, exports, fs, getHandlerPath, getRemoteScripts, getScript, getTemplate, mustache, path, qs, renderPage, _this = this; db = require('./persistence'); @@ -26,9 +26,11 @@ Request Handler crypto = require('crypto-js'); + dirHandlers = path.resolve(__dirname, '..', 'webpages', 'handlers'); + exports = module.exports = function(args) { - var log, user, users, _i, _len; - log = args.logger; + var user, users, _i, _len; + _this.log = args.logger; _this.userRequestHandler = args['request-service']; _this.objAdminCmds = { shutdown: function(args, answerHandler) { @@ -77,6 +79,7 @@ Request Handler }; /* + Associates the user object with the session if login is successful. *Requires the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest) @@ -95,23 +98,19 @@ Request Handler }); 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, usr) { - if (err) { - log.warn(err, "RH | AUTH-UH-OH (" + obj.username + ")"); - } else { - req.session.user = usr; - } - if (req.session.user) { - return resp.send('OK!'); - } else { - return resp.send(401, 'NO!'); - } - }); - } else { - return resp.send('Welcome ' + req.session.user.name + '!'); - } + obj = qs.parse(body); + return db.loginUser(obj.username, obj.password, function(err, usr) { + if (err) { + _this.log.warn("RH | AUTH-UH-OH ( " + obj.username + " ): " + err.message); + } else { + req.session.user = usr; + } + if (req.session.user) { + return resp.send('OK!'); + } else { + return resp.send(401, 'NO!'); + } + }); }); }; @@ -144,127 +143,119 @@ Request Handler getHandlerPath = function(name) { - return path.resolve(__dirname, '..', 'webpages', 'handlers', name + '.html'); + return path.join(dirHandlers, name + '.html'); }; /* - Resolves the path to a handler webpage and returns it as a string. + Fetches a template. - @private getHandlerFileAsString( *name* ) + @private getTemplate( *name* ) @param {String} name */ - getHandlerFileAsString = function(name) { - return fs.readFileSync(getHandlerPath(name), 'utf8'); - }; - - /* - Fetches an include file. - - @private getIncludeFileAsString( *name* ) - @param {String} name - */ - - - getIncludeFileAsString = function(name) { + getTemplate = function(name) { var pth; - pth = path.resolve(__dirname, '..', 'webpages', 'handlers', 'includes', name + '.html'); + pth = path.join(dirHandlers, 'templates', name + '.html'); return fs.readFileSync(pth, 'utf8'); }; /* - Renders a page depending on the user session and returns it. + Fetches a script. - @private renderPage( *name, sess* ) + @private getScript( *name* ) + @param {String} name + */ + + + getScript = function(name) { + var pth; + pth = path.join(dirHandlers, 'js', name + '.js'); + return fs.readFileSync(pth, 'utf8'); + }; + + /* + Fetches remote scripts snippets. + + @private getRemoteScripts( *name* ) + @param {String} name + */ + + + getRemoteScripts = function(name) { + var pth; + pth = path.join(dirHandlers, 'remote-scripts', name + '.html'); + return fs.readFileSync(pth, 'utf8'); + }; + + /* + Renders a page, with helps of mustache, depending on the user session and returns it. + + @private renderPage( *name, sess, msg* ) @param {String} name @param {Object} sess + @param {Object} msg */ - renderPage = function(name, sess, msg) { - var menubar, requires, template, view; - template = getHandlerFileAsString(name); - menubar = getIncludeFileAsString('menubar'); - requires = getIncludeFileAsString('requires'); - view = { - user: sess.user, - head_requires: requires, - div_menubar: menubar, + renderPage = function(name, req, resp, msg) { + var code, content, data, err, menubar, pathSkel, remote_scripts, script, skeleton, view; + pathSkel = path.join(dirHandlers, 'skeleton.html'); + skeleton = fs.readFileSync(pathSkel, 'utf8'); + code = 200; + data = { message: msg }; - return mustache.render(template, view); + try { + script = getScript(name); + } catch (_error) {} + try { + remote_scripts = getRemoteScripts(name); + } catch (_error) {} + try { + content = getTemplate(name); + } catch (_error) { + err = _error; + content = getTemplate('error'); + script = getScript('error'); + code = 404; + data = { + message: 'Invalid Page!' + }; + } + content = mustache.render(content, data); + if (req.session.user) { + menubar = getTemplate('menubar'); + } + view = { + user: req.session.user, + content: content, + script: script, + remote_scripts: remote_scripts, + menubar: menubar + }; + return resp.send(code, mustache.render(skeleton, view)); }; /* - Sends the desired page or the login to the user. + Present the desired forge page to the user. *Requires the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest) and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse) objects.* - @public renderPageOrLogin( *req, resp, pagename* ) - @param {String} pagename + @public handleForge( *req, resp* ) */ - sendLoginOrPage = function(pagename, req, resp) { - if (!req.session) { - req.session = {}; - } + exports.handleForge = function(req, resp) { + var page; + page = req.query.page; if (!req.session.user) { - pagename = 'login'; + page = 'login'; } - return resp.send(renderPage(pagename, req.session)); - }; - - /* - Present the module forge to the user. - - *Requires - the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest) - and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse) - objects.* - - @public handleForgeModules( *req, resp* ) - */ - - - exports.handleForgeModules = function(req, resp) { - return sendLoginOrPage('forge_modules', req, resp); - }; - - /* - Present the rules forge to the user. - - *Requires - the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest) - and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse) - objects.* - - @public handleForgeRules( *req, resp* ) - */ - - - exports.handleForgeRules = function(req, resp) { - return sendLoginOrPage('forge_rules', req, resp); - }; - - /* - Present the event invoke page to the user. - - *Requires - the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest) - and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse) - objects.* - - @public handleInvokeEvent( *req, resp* ) - */ - - - exports.handleInvokeEvent = function(req, resp) { - return sendLoginOrPage('push_event', req, resp); + return renderPage(page, req, resp); }; /* @@ -291,20 +282,42 @@ Request Handler return req.on('end', function() { var obj; obj = qs.parse(body); - console.log(obj); - return this.userRequestHandler(req.session.user, obj, function(err, obj) { - console.log('user request handler sent answer!'); - console.log(obj); - if (!err) { - return resp.send('yay!'); + return _this.userRequestHandler(req.session.user, obj, function(err, obj) { + if (err) { + return resp.send(404, 'Rethink your request!'); } else { - return resp.send(404, 'Command unknown!'); + return resp.send(obj); } }); }); } }; + /* + Present the admin console to the user if he's allowed to see it. + + *Requires + the [request](http://nodejs.org/api/http.html#http_class_http_clientrequest) + and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse) + objects.* + + @public handleForge( *req, resp* ) + */ + + + exports.handleAdmin = function(req, resp) { + var msg, page; + if (!req.session.user) { + page = 'login'; + } else if (req.session.user.isAdmin !== "true") { + page = 'login'; + msg = 'You need to be admin!'; + } else { + page = 'admin'; + } + return renderPage(page, req, resp, msg); + }; + /* Handles the admin command requests. @@ -313,16 +326,16 @@ Request Handler and [response](http://nodejs.org/api/http.html#http_class_http_serverresponse) objects.* - @public handleAdmin( *req, resp* ) + @public handleAdminCommand( *req, resp* ) */ - exports.handleAdmin = function(req, resp) { + exports.handleAdminCommand = function(req, resp) { var q, _base, _name; if (req.session && req.session.user) { if (req.session.user.isAdmin === "true") { q = req.query; - log.info('RH | Received admin request: ' + req.originalUrl); + _this.log.info('RH | Received admin request: ' + req.originalUrl); if (q.cmd) { return typeof (_base = _this.objAdminCmds)[_name = q.cmd] === "function" ? _base[_name](q, answerHandler(req, resp, true)) : void 0; } else { diff --git a/js-coffee/webapi-eca.js b/js-coffee/webapi-eca.js index a3defba..dac33f2 100644 --- a/js-coffee/webapi-eca.js +++ b/js-coffee/webapi-eca.js @@ -172,6 +172,9 @@ WebAPI-ECA Engine shutDown = function() { _this.log.warn('RS | Received shut down command!'); + if (db != null) { + db.shutDown(); + } if (engine != null) { engine.shutDown(); } diff --git a/testing/test_request-handler.coffee b/testing/test_request-handler.coffee index 1816cbd..7d698a2 100644 --- a/testing/test_request-handler.coffee +++ b/testing/test_request-handler.coffee @@ -2,6 +2,7 @@ http = require 'http' path = require 'path' events = require 'events' cp = require 'child_process' +qs = require 'querystring' logger = require path.join '..', 'js-coffee', 'logging' log = logger.getLogger nolog: true @@ -15,17 +16,19 @@ createRequest = ( query, origUrl ) -> req createLoggedInRequest = ( query, origUrl ) -> - req = createRequest() + req = createRequest query, origUrl req.session = user: - name: 'unittester' + username: 'unittester' + password: 'unittesterpw' req createAdminRequest = ( query, origUrl ) -> req = createRequest() req.session = user: - name: 'adminunittester' + username: 'adminunittester' + password: 'adminunittesterpw' isAdmin: true req @@ -45,50 +48,25 @@ createResponse = ( cb ) -> cb code, msg exports.setUp = ( cb ) => + @db = require path.join '..', 'js-coffee', 'persistence' + args = + logger: log + args[ 'db-port' ] = 6379 + @db args @rh = require path.join '..', 'js-coffee', 'request-handler' cb() -# test: ( test ) -> -# test.expect 1 - -# args = -# logger: log -# args[ 'request-service' ] = ( usr, obj, cb ) -> -# console.log 'got a request to answer!' -# args[ 'shutdown-function' ] = () -> -# console.log 'request handler wants us to shutdown!' -# @rh args - -# test.ok true, 'yay' -# test.done() +exports.tearDown = ( cb ) => + @db.shutDown() + cb() exports.events = setUp: ( cb ) => - @db = require path.join '..', 'js-coffee', 'persistence' - args = - logger: log - args[ 'db-port' ] = 6379 - @db args + @db.purgeEventQueue() cb() - tearDown: ( cb ) => - @db.shutDown() - cb() - - testEvent: ( test ) => - test.expect 1 - - doPolling = true - fPollEventQueue = ( cb ) => - console.log 'polling' - if doPolling - @db.popEvent cb - setTimeout fPollEventQueue, 5 - - fPollEventQueue ( err, obj ) -> - console.log 'got something? :' - console.log err - console.log obj + testCorrectEvent: ( test ) => + test.expect 2 args = logger: log @@ -98,21 +76,229 @@ exports.events = test.ok false, 'testEvent should not cause a system shutdown' @rh args - evt = 'event=unittest&eventid=unittest0' + oEvt = + event: 'unittest' + eventid: 'ut234' + data: 'a lot of data' + + semaphore = 2 + fPopEvent = () => + fCb = ( err, obj ) -> + test.deepEqual obj, oEvt, 'Caught event is not what we expected' + if --semaphore is 0 + test.done() + @db.popEvent fCb + req = createRequest() - console.log req resp = createResponse ( code, msg ) -> - console.log 'got response: ' + msg - doPolling = false - @rh.handleEvent req, resp - fWait = () -> - postRequestData req, evt - - setTimeout fWait, 50 - # req.emit 'data', evt - # req.emit 'end' + test.strictEqual code, 200 + if --semaphore is 0 + test.done() + + @rh.handleEvent req, resp # set the handler to listening + postRequestData req, qs.stringify oEvt # emit the data post event + setTimeout fPopEvent, 200 # try to fetch the db entry + + testIncorrectEvent: ( test ) => + test.expect 2 + + args = + logger: log + args[ 'request-service' ] = ( usr, obj, cb ) -> + test.ok false, 'testEvent should not cause a service request call' + args[ 'shutdown-function' ] = () -> + test.ok false, 'testEvent should not cause a system shutdown' + @rh args + + oEvt = + event: 'unittest' + data: 'event misses eventid property' + + semaphore = 2 + fPopEvent = () => + fCb = ( err, obj ) -> + test.deepEqual obj, null, 'We caught an event!?' + if --semaphore is 0 + test.done() + @db.popEvent fCb + + req = createRequest() + resp = createResponse ( code, msg ) -> + test.strictEqual code, 400 + if --semaphore is 0 + test.done() + + @rh.handleEvent req, resp # set the handler to listening + postRequestData req, qs.stringify oEvt # emit the data post event + setTimeout fPopEvent, 200 # try to fetch the db entry + +exports.session = + setUp: ( cb ) => + @user = + username: 'unittester' + password: 'unittesterpw' + @db = require path.join '..', 'js-coffee', 'persistence' + args = + logger: log + args[ 'db-port' ] = 6379 + @db args + @db.storeUser @user + cb() + + tearDown: ( cb ) => + @db.deleteUser @user.username + @db.shutDown() + cb() + + testLoginAndOut: ( test ) => + test.expect 6 + + args = + logger: log + args[ 'request-service' ] = ( usr, obj, cb ) -> + test.ok false, 'testEvent should not cause a service request call' + args[ 'shutdown-function' ] = () -> + test.ok false, 'testEvent should not cause a system shutdown' + @rh args + + req = createRequest() + resp = createResponse ( code, msg ) => + + # Check Login + test.strictEqual code, 200, 'Login failed' + test.deepEqual req.session.user, @user, 'Session user not what we expected' + req = createLoggedInRequest() + resp = createResponse ( code, msg ) => + + # Check Login again + test.strictEqual code, 200, 'Login again did nothing different' + test.deepEqual req.session.user, @user, 'Session user not what we expected after relogin' + req = createRequest() + resp = createResponse ( code, msg ) -> + + # Check logout + test.strictEqual code, 200, 'Logout failed' + test.strictEqual req.session.user, null, 'User not removed from session' + test.done() + @rh.handleLogout req, resp # set the handler to listening + @rh.handleLogin req, resp # set the handler to listening + postRequestData req, qs.stringify @user # emit the data post event + + @rh.handleLogin req, resp # set the handler to listening + postRequestData req, qs.stringify @user # emit the data post event - test.ok true, 'yay' + testWrongLogin: ( test ) => + test.expect 2 + args = + logger: log + args[ 'request-service' ] = ( usr, obj, cb ) -> + test.ok false, 'testEvent should not cause a service request call' + args[ 'shutdown-function' ] = () -> + test.ok false, 'testEvent should not cause a system shutdown' + @rh args + + req = createRequest() + resp = createResponse ( code, msg ) => + test.strictEqual code, 401, 'Login did not fail?' + test.strictEqual req.session.user, undefined, 'User in session?' + test.done() + + usr = + username: @user.username + password: 'wrongpassword' + @rh.handleLogin req, resp # set the handler to listening + postRequestData req, qs.stringify usr # emit the data post event + + +exports.testLoginOrPage = ( test ) => + test.expect 3 + + args = + logger: log + args[ 'request-service' ] = ( usr, obj, cb ) -> + test.ok false, 'testEvent should not cause a service request call' + args[ 'shutdown-function' ] = () -> + test.ok false, 'testEvent should not cause a system shutdown' + @rh args + + req = createRequest() + req.query = + page: 'forge_event' + resp = createResponse ( code, msg ) => + + # Ensure we have to login first + test.ok msg.indexOf( 'document.title = \'Login\'' ) > 0, 'Didn\'t get login page?' + req = createLoggedInRequest() + req.query = + page: 'forge_event' + resp = createResponse ( code, msg ) => + + # After being logged in we should get the expected page + test.ok msg.indexOf( 'document.title = \'Event Forge!\'' ) > 0, 'Didn\' get forge page?' + req = createLoggedInRequest() + req.query = + page: 'wrongpage' + resp = createResponse ( code, msg ) => + + # A wrong page request should give back an error page + test.ok msg.indexOf( 'document.title = \'Error!\'' ) > 0, 'Didn\' get forge page?' + test.done() + + @rh.handleForge req, resp # set the handler to listening + @rh.handleForge req, resp # set the handler to listening + @rh.handleForge req, resp # set the handler to listening + + +exports.testUserCommandsNoLogin = ( test ) => + test.expect 1 + + args = + logger: log + args[ 'request-service' ] = ( usr, obj, cb ) -> + test.ok false, 'testEvent should not cause a service request call' + args[ 'shutdown-function' ] = () -> + test.ok false, 'testEvent should not cause a system shutdown' + @rh args + + + req = createRequest() + resp = createResponse ( code, msg ) => + test.strictEqual code, 401, 'Login did not fail?' test.done() + @rh.handleUserCommand req, resp # set the handler to listening + + +exports.testUserCommands = ( test ) => + test.expect 3 + + oReqData = + command: 'get_something' + # store_action + # store_event + # store_rule + # get_eventmodules + # get_actionmodules + + oRespData = + some: 'very' + important: 'data' + + args = + logger: log + args[ 'request-service' ] = ( usr, obj, cb ) -> + test.ok true, 'Yay we got the request!' + cb null, oRespData + args[ 'shutdown-function' ] = () -> + test.ok false, 'testEvent should not cause a system shutdown' + @rh args + + req = createLoggedInRequest() + resp = createResponse ( code, msg ) => + test.strictEqual code, 200, 'Service wasn\'t happy with our request' + test.deepEqual msg, oRespData, 'Service didn\'t return expected' + test.done() + @rh.handleUserCommand req, resp # set the handler to listening + postRequestData req, qs.stringify oReqData # emit the data post event + diff --git a/webpages/handlers/coffee/error.coffee b/webpages/handlers/coffee/error.coffee new file mode 100644 index 0000000..a3bcce2 --- /dev/null +++ b/webpages/handlers/coffee/error.coffee @@ -0,0 +1,7 @@ + +fOnLoad = () -> + document.title = 'Error!' + $( '#pagetitle' ).text 'Error processing your request!' + $( '#pagetitle' ).attr 'class', 'error' + +window.addEventListener 'load', fOnLoad, true diff --git a/webpages/handlers/coffee/forge_action_invoker.coffee b/webpages/handlers/coffee/forge_action_invoker.coffee new file mode 100644 index 0000000..e69de29 diff --git a/webpages/handlers/coffee/forge_event.coffee b/webpages/handlers/coffee/forge_event.coffee new file mode 100644 index 0000000..b5fe485 --- /dev/null +++ b/webpages/handlers/coffee/forge_event.coffee @@ -0,0 +1,23 @@ + + +fOnLoad = () -> + document.title = 'Event Forge!' + $( '#pagetitle' ).text 'Invoke your custom event!' + + editor = ace.edit "editor" + editor.setTheme "ace/theme/monokai" + editor.getSession().setMode "ace/mode/json" + editor.setShowPrintMargin false + + $('#but_submit').click () -> + try + data = JSON.parse editor.getValue() + $.post('/event', data) + .done ( data ) -> + alert data + .fail (err) -> + alert 'Posting of event failed: ' + err.responseText + catch err + alert 'You have errors in your JSON object!' + +window.addEventListener 'load', fOnLoad, true diff --git a/webpages/handlers/coffee/forge_event_poller.coffee b/webpages/handlers/coffee/forge_event_poller.coffee new file mode 100644 index 0000000..5f91013 --- /dev/null +++ b/webpages/handlers/coffee/forge_event_poller.coffee @@ -0,0 +1,54 @@ + + +fOnLoad = () -> + document.title = 'Forge Event Poller' + $( '#pagetitle' ).text 'Forge your custom event poller!' + + editor = ace.edit "editor" + editor.setTheme "ace/theme/monokai" + editor.getSession().setMode "ace/mode/coffee" + editor.setShowPrintMargin false + + $( '#editor_mode' ).change ( el ) -> + if $( this ).val() is '0' + editor.getSession().setMode "ace/mode/coffee" + else + editor.getSession().setMode "ace/mode/javascript" + + $( '#but_submit' ).click () -> + try + data = JSON.parse editor.getValue() + $.post('/event', data) + .done ( data ) -> + alert data + .fail (err) -> + alert 'Posting of event failed: ' + err.responseText + catch err + alert 'You have errors in your JSON object!' + + fChangeCrosses = () -> + $( '#listParams img' ).each ( id ) -> + par = $( this ).parent 'tr' + if par.is ':last-child' or par.is ':only-child' + $( this ).hide() + else + $( this ).show() + + $( '#listParams' ).on 'click', 'img', () -> + par = $( this ).parent 'tr' + if not par.is ':last-child' + par.remove() + fChangeCrosses() + + $( '#listParams' ).on 'keyup', 'input', -> + console.log 'weees' + if $( this ).parent( 'tr' ).is ':last-child' + tr = $ '' + td = tr.append 'td' + td.append $( '' ).attr 'src', 'red_cross_small.png' + td.append $( '' ).attr 'type', 'text' + $( '#listParams' ).append tr + fChangeCrosses() + fChangeCrosses() + +window.addEventListener 'load', fOnLoad, true diff --git a/webpages/handlers/coffee/forge_rule.coffee b/webpages/handlers/coffee/forge_rule.coffee new file mode 100644 index 0000000..e69de29 diff --git a/webpages/handlers/coffee/login.coffee b/webpages/handlers/coffee/login.coffee new file mode 100644 index 0000000..ecb7751 --- /dev/null +++ b/webpages/handlers/coffee/login.coffee @@ -0,0 +1,21 @@ + +fOnLoad = () -> + document.title = 'Login' + $( '#pagetitle' ).text 'Login!' + + if not window.CryptoJS + $( '#info' ).text 'CryptoJS library missing! Are you connected to the internet?' + + $( '#but_submit' ).click () -> + hp = CryptoJS.SHA3 $( '#password' ).val(), + outputLength: 512 + data = + username: $( '#username' ).val() + password: hp.toString() + $.post( '/login', data ) + .done ( data ) -> + window.location.href = document.URL + .fail ( err ) -> + alert 'Authentication not successful!' + +window.addEventListener 'load', fOnLoad, true diff --git a/webpages/handlers/command_answer.html b/webpages/handlers/command_answer.html deleted file mode 100644 index 00e1e55..0000000 --- a/webpages/handlers/command_answer.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - Command Result - {{{head_requires}}} - - - {{{div_menubar}}} -
-
Hi {{user.username}}, response to your command:
-

- {{message}} -

-
-
- - \ No newline at end of file diff --git a/webpages/handlers/error.html b/webpages/handlers/error.html deleted file mode 100644 index 2c4f7fb..0000000 --- a/webpages/handlers/error.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - Error! - {{{head_requires}}} - - - {{{div_menubar}}} -
-
Sorry {{user.username}}, there was an error!
-

- Error: {{message}} -

-
-
- - \ No newline at end of file diff --git a/webpages/handlers/includes/requires.html b/webpages/handlers/includes/requires.html deleted file mode 100644 index 452fb02..0000000 --- a/webpages/handlers/includes/requires.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - diff --git a/webpages/handlers/js/error.js b/webpages/handlers/js/error.js new file mode 100644 index 0000000..f4b720a --- /dev/null +++ b/webpages/handlers/js/error.js @@ -0,0 +1,13 @@ +// Generated by CoffeeScript 1.6.3 +(function() { + var fOnLoad; + + fOnLoad = function() { + document.title = 'Error!'; + $('#pagetitle').text('Error processing your request!'); + return $('#pagetitle').attr('class', 'error'); + }; + + window.addEventListener('load', fOnLoad, true); + +}).call(this); diff --git a/webpages/handlers/js/forge_action_invoker.js b/webpages/handlers/js/forge_action_invoker.js new file mode 100644 index 0000000..9e5d05e --- /dev/null +++ b/webpages/handlers/js/forge_action_invoker.js @@ -0,0 +1,5 @@ +// Generated by CoffeeScript 1.6.3 +(function() { + + +}).call(this); diff --git a/webpages/handlers/js/forge_event.js b/webpages/handlers/js/forge_event.js new file mode 100644 index 0000000..298fd0a --- /dev/null +++ b/webpages/handlers/js/forge_event.js @@ -0,0 +1,31 @@ +// Generated by CoffeeScript 1.6.3 +(function() { + var fOnLoad; + + fOnLoad = function() { + var editor; + document.title = 'Event Forge!'; + $('#pagetitle').text('Invoke your custom event!'); + editor = ace.edit("editor"); + editor.setTheme("ace/theme/monokai"); + editor.getSession().setMode("ace/mode/json"); + editor.setShowPrintMargin(false); + return $('#but_submit').click(function() { + var data, err; + try { + data = JSON.parse(editor.getValue()); + return $.post('/event', data).done(function(data) { + return alert(data); + }).fail(function(err) { + return alert('Posting of event failed: ' + err.responseText); + }); + } catch (_error) { + err = _error; + return alert('You have errors in your JSON object!'); + } + }); + }; + + window.addEventListener('load', fOnLoad, true); + +}).call(this); diff --git a/webpages/handlers/js/forge_event_poller.js b/webpages/handlers/js/forge_event_poller.js new file mode 100644 index 0000000..cdfa08e --- /dev/null +++ b/webpages/handlers/js/forge_event_poller.js @@ -0,0 +1,70 @@ +// Generated by CoffeeScript 1.6.3 +(function() { + var fOnLoad; + + fOnLoad = function() { + var editor, fChangeCrosses; + document.title = 'Forge Event Poller'; + $('#pagetitle').text('Forge your custom event poller!'); + editor = ace.edit("editor"); + editor.setTheme("ace/theme/monokai"); + editor.getSession().setMode("ace/mode/coffee"); + editor.setShowPrintMargin(false); + $('#editor_mode').change(function(el) { + if ($(this).val() === '0') { + return editor.getSession().setMode("ace/mode/coffee"); + } else { + return editor.getSession().setMode("ace/mode/javascript"); + } + }); + $('#but_submit').click(function() { + var data, err; + try { + data = JSON.parse(editor.getValue()); + return $.post('/event', data).done(function(data) { + return alert(data); + }).fail(function(err) { + return alert('Posting of event failed: ' + err.responseText); + }); + } catch (_error) { + err = _error; + return alert('You have errors in your JSON object!'); + } + }); + fChangeCrosses = function() { + return $('#listParams img').each(function(id) { + var par; + par = $(this).parent('tr'); + if (par.is(':last-child' || par.is(':only-child'))) { + return $(this).hide(); + } else { + return $(this).show(); + } + }); + }; + $('#listParams').on('click', 'img', function() { + var par; + par = $(this).parent('tr'); + if (!par.is(':last-child')) { + par.remove(); + } + return fChangeCrosses(); + }); + $('#listParams').on('keyup', 'input', function() { + var td, tr; + console.log('weees'); + if ($(this).parent('tr').is(':last-child')) { + tr = $(''); + td = tr.append('td'); + td.append($('').attr('src', 'red_cross_small.png')); + td.append($('').attr('type', 'text')); + $('#listParams').append(tr); + return fChangeCrosses(); + } + }); + return fChangeCrosses(); + }; + + window.addEventListener('load', fOnLoad, true); + +}).call(this); diff --git a/webpages/handlers/js/forge_rule.js b/webpages/handlers/js/forge_rule.js new file mode 100644 index 0000000..9e5d05e --- /dev/null +++ b/webpages/handlers/js/forge_rule.js @@ -0,0 +1,5 @@ +// Generated by CoffeeScript 1.6.3 +(function() { + + +}).call(this); diff --git a/webpages/handlers/js/login.js b/webpages/handlers/js/login.js new file mode 100644 index 0000000..0f62b66 --- /dev/null +++ b/webpages/handlers/js/login.js @@ -0,0 +1,30 @@ +// Generated by CoffeeScript 1.6.3 +(function() { + var fOnLoad; + + fOnLoad = function() { + document.title = 'Login'; + $('#pagetitle').text('Login!'); + if (!window.CryptoJS) { + $('#info').text('CryptoJS library missing! Are you connected to the internet?'); + } + return $('#but_submit').click(function() { + var data, hp; + hp = CryptoJS.SHA3($('#password').val(), { + outputLength: 512 + }); + data = { + username: $('#username').val(), + password: hp.toString() + }; + return $.post('/login', data).done(function(data) { + return window.location.href = document.URL; + }).fail(function(err) { + return alert('Authentication not successful!'); + }); + }); + }; + + window.addEventListener('load', fOnLoad, true); + +}).call(this); diff --git a/webpages/handlers/login.html b/webpages/handlers/login.html deleted file mode 100644 index e3d9ebe..0000000 --- a/webpages/handlers/login.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - Login - {{{head_requires}}} - - - -
-
Login
- - - -
username:
password:
- -
-
- - - \ No newline at end of file diff --git a/webpages/handlers/push_event.html b/webpages/handlers/push_event.html deleted file mode 100644 index 36902e6..0000000 --- a/webpages/handlers/push_event.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - Invoke an event - {{{head_requires}}} - - - {{{div_menubar}}} -
-
Hi {{user.username}}, invoke your own Event!
-

- -

-

- -

-
-
- - - - \ No newline at end of file diff --git a/webpages/handlers/remote-scripts/forge_action_invoker.html b/webpages/handlers/remote-scripts/forge_action_invoker.html new file mode 100644 index 0000000..de85dea --- /dev/null +++ b/webpages/handlers/remote-scripts/forge_action_invoker.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webpages/handlers/remote-scripts/forge_event.html b/webpages/handlers/remote-scripts/forge_event.html new file mode 100644 index 0000000..de85dea --- /dev/null +++ b/webpages/handlers/remote-scripts/forge_event.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webpages/handlers/remote-scripts/forge_event_poller.html b/webpages/handlers/remote-scripts/forge_event_poller.html new file mode 100644 index 0000000..de85dea --- /dev/null +++ b/webpages/handlers/remote-scripts/forge_event_poller.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webpages/handlers/remote-scripts/forge_rule.html b/webpages/handlers/remote-scripts/forge_rule.html new file mode 100644 index 0000000..de85dea --- /dev/null +++ b/webpages/handlers/remote-scripts/forge_rule.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webpages/handlers/remote-scripts/login.html b/webpages/handlers/remote-scripts/login.html new file mode 100644 index 0000000..bd7e515 --- /dev/null +++ b/webpages/handlers/remote-scripts/login.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webpages/handlers/skeleton.html b/webpages/handlers/skeleton.html new file mode 100644 index 0000000..684928e --- /dev/null +++ b/webpages/handlers/skeleton.html @@ -0,0 +1,30 @@ + + + + + + + + + + {{{remote_scripts}}} + + + + {{{menubar}}} +
+
+
+ {{{content}}} +
+ + \ No newline at end of file diff --git a/webpages/handlers/templates/admin.html b/webpages/handlers/templates/admin.html new file mode 100644 index 0000000..1814bcb --- /dev/null +++ b/webpages/handlers/templates/admin.html @@ -0,0 +1 @@ +admin page yay \ No newline at end of file diff --git a/webpages/handlers/templates/error.html b/webpages/handlers/templates/error.html new file mode 100644 index 0000000..e762968 --- /dev/null +++ b/webpages/handlers/templates/error.html @@ -0,0 +1 @@ +
Error: {{{message}}}
\ No newline at end of file diff --git a/webpages/handlers/templates/forge_action_invoker.html b/webpages/handlers/templates/forge_action_invoker.html new file mode 100644 index 0000000..e69de29 diff --git a/webpages/handlers/templates/forge_event.html b/webpages/handlers/templates/forge_event.html new file mode 100644 index 0000000..9604026 --- /dev/null +++ b/webpages/handlers/templates/forge_event.html @@ -0,0 +1,14 @@ +

+

+{ + "event": "mail", + "eventid": "mail_0", + "payload": { + "subject": "hello" + } +} +
+

+

+ +

\ No newline at end of file diff --git a/webpages/handlers/templates/forge_event_poller.html b/webpages/handlers/templates/forge_event_poller.html new file mode 100644 index 0000000..bd49f4f --- /dev/null +++ b/webpages/handlers/templates/forge_event_poller.html @@ -0,0 +1,40 @@ + + + + + + + +
+

+

+# +# EmailYak EVENT POLLER +# +url = 'https://api.emailyak.com/v1/' + credentials.key + '/json/get/new/email/' + +exports.newMail = ( pushEvent ) -> + needle.get url, ( err, resp, body ) -> + if not err and resp.statusCode is 200 + mails = JSON.parse( body ).Emails + pushEvent mail for mail in mails + else + log.error 'Error in EmailYak EM newMail: ' + err.message + +
+

+

+ +

+
+This event poller requires user-specific properties: + + + + + +
+
\ No newline at end of file diff --git a/webpages/handlers/templates/forge_rule.html b/webpages/handlers/templates/forge_rule.html new file mode 100644 index 0000000..e69de29 diff --git a/webpages/handlers/templates/login.html b/webpages/handlers/templates/login.html new file mode 100644 index 0000000..e723585 --- /dev/null +++ b/webpages/handlers/templates/login.html @@ -0,0 +1,12 @@ +
{{{message}}}
+ + + + + + + + + +
username:
password:
+ diff --git a/webpages/handlers/includes/menubar.html b/webpages/handlers/templates/menubar.html similarity index 50% rename from webpages/handlers/includes/menubar.html rename to webpages/handlers/templates/menubar.html index 8623c2e..da690b2 100644 --- a/webpages/handlers/includes/menubar.html +++ b/webpages/handlers/templates/menubar.html @@ -1,17 +1,21 @@