changed event data to payload, worked on user sessions, mobile location event pusher webpage added

This commit is contained in:
Dominic Bosch 2013-11-18 16:54:40 +01:00
parent b3de695b07
commit 89fc5d26a1
9 changed files with 207 additions and 103 deletions

3
config/users.json Normal file
View file

@ -0,0 +1,3 @@
{
"dominic": "test"
}

View file

@ -33,6 +33,14 @@ function loadConfigFile(relPath) {
}
}
/**
* Answer true if the config file is ready, else false
*/
exports.isReady = function() {
if(config) return true;
else return false;
};
/**
* Fetch a property from the configuration
* @param {String} prop

View file

@ -39,6 +39,8 @@ exports = module.exports = function(args) {
/**
* ### encrypt
* this is used to decrypt
* @param {String} plainText
*/
function encrypt(plainText) {
if(!plainText) return null;
@ -56,13 +58,13 @@ function encrypt(plainText) {
/**
* ### decrypt
*/
function decrypt(crypticText) {
function decrypt(crypticText, id) {
if(!crypticText) return null;
try {
var deciph = crypto.createDecipher('aes-256-cbc', crypto_key);
return deciph.update(crypticText, 'base64', 'utf8') + deciph.final('utf8');
} catch (err) {
log.error('DB', 'in decrypting: ' + err);
log.error('DB', 'in decrypting "' + id + '": ' + err);
return null;
}
}
@ -179,7 +181,9 @@ exports.storeActionModuleAuth = function(id, data) {
* @param {function} callback the callback to receive the answer (err, obj)
*/
exports.getActionModuleAuth = function(id, callback) {
if(callback && db) db.get('action_module_' + id + '_auth', function(err, txt) { callback(err, decrypt(txt)); });
if(callback && db) db.get('action_module_' + id + '_auth', function(id) {
return function(err, txt) { callback(err, decrypt(txt, 'action_module_' + id + '_auth')); };
}(id));
};
// ## Event Modules
@ -235,7 +239,9 @@ exports.storeEventModuleAuth = function(id, data) {
// @param {String} id the module id
// @param {function} callback the callback to receive the answer (err, obj)
exports.getEventModuleAuth = function(id, callback) {
if(callback) db.get('event_module_' + id +'_auth', function(err, txt) { callback(err, decrypt(txt)); });
if(callback) db.get('event_module_' + id +'_auth', function(id) {
return function(err, txt) { callback(err, decrypt(txt, 'event_module_' + id + '_auth')); };
}(id));
};
// ## Rules

View file

@ -132,7 +132,7 @@ function checkEvent(evt) {
for(var rn in listRules) {
//TODO this needs to get depth safe, not only data but eventually also
// on one level above (eventid and other meta)
if(listRules[rn].event === evt.event && validConditions(evt.data, listRules[rn])) {
if(listRules[rn].event === evt.event && validConditions(evt.payload, listRules[rn])) {
log.print('EN', 'Rule "' + rn + '" fired');
actions = actions.concat(listRules[rn].actions);
}
@ -167,7 +167,7 @@ function invokeAction(evt, action) {
var srvc = listActionModules[arrModule[0]];
if(srvc && srvc[arrModule[1]]) {
//FIXME preprocessing not only on data
preprocessActionArguments(evt.data, action.arguments, actionargs);
preprocessActionArguments(evt.payload, action.arguments, actionargs);
try {
if(srvc[arrModule[1]]) srvc[arrModule[1]](actionargs);
} catch(err) {

View file

@ -126,7 +126,7 @@ function checkRemotes() {
process.send({
event: p,
eventid: 'polled_' + eId++,
data: obj
payload: obj
});
}
};

View file

@ -1,5 +1,8 @@
// # HTTP Listener
// Isso
// HTTP Listener
// =============
//
// Handles the HTTP requests to the server at the port specified by the [config](config.html) file.
'use strict';
var path = require('path'),
@ -10,12 +13,18 @@ var path = require('path'),
log = require('./logging'),
sess_sec = '#C[>;j`@".TXm2TA;A2Tg)',
db_port, http_port, server,
adminHandler, eventHandler;
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();
@ -23,110 +32,78 @@ exports = module.exports = function(args) {
};
exports.addHandlers = function(funcAdminHandler, funcEvtHandler) {
if(!funcEvtHandler) {
log.error('HL', 'ERROR: either port or eventHandler function not defined!');
if(!funcAdminHandler || !funcEvtHandler) {
log.error('HL', 'ERROR: either adminHandler or eventHandler function not defined!');
return;
}
adminHandler = funcAdminHandler;
userHandler.addHandler(funcAdminHandler);
eventHandler = funcEvtHandler;
//FIXME this whole webserver requires clean approach together with session handling all over the engine.
//One entry point, from then collecting response contents and one exit point that sends it!
// Add cookie support for session handling.
app.use(express.cookieParser());
app.use('/doc/', express.static(path.resolve(__dirname, '..', 'webpages', 'doc')));
app.use('/mobile/', express.static(path.resolve(__dirname, '..', 'webpages', 'mobile')));
app.use('/rulesforge/', express.static(path.resolve(__dirname, '..', 'webpages', 'rulesforge')));
app.get('/admin', onAdminCommand);
app.post('/pushEvents', onPushEvent);
if(db_port) {
app.use(express.session({
store: new RedisStore({
host: 'localhost',
port: db_port,
db: 2
// ,
// pass: 'RedisPASS'
}),
// FIXME use a secret from config
secret: sess_sec
}));
log.print('HL', 'Added redis DB as session backbone');
} else {
app.use(express.session({secret: sess_sec}));
log.print('HL', 'no session backbone');
}
if(http_port) server = app.listen(http_port); // inbound event channel
else log.error('HL', new Error('No HTTP port found!?'));
};
function answerHandler(r) {
var response = r, hasBeenAnswered = false;
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; }
};
};
/**
* Handles correct event posts, replies thank you.
*/
function answerSuccess(resp, msg){
resp.writeHead(200, { "Content-Type": "text/plain" });
resp.write(msg);
resp.end();
}
/**
* Handles erroneous requests.
* @param {Object} msg the error message to be returned
*/
function answerError(resp, msg) {
resp.writeHead(400, { "Content-Type": "text/plain" });
resp.write(msg);
resp.end();
}
//FIXME this answer handling is a very ugly hack, improve!
function onAdminCommand(request, response) {
var q = request.query;
log.print('HL', 'Received admin request: ' + request.originalUrl);
if(q.cmd) {
adminHandler(q, answerHandler(response));
// answerSuccess(response, 'Thank you, we try our best!');
} else answerError(response, 'I\'m not sure about what you want from me...');
}
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('/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('/push_event', onPushEvent);
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!...'));
};
/**
* If a request is made to the server, this function is used to handle it.
* If a post request reaches the server, this function handles it and treats the request as a possible event.
*/
function onPushEvent(request, response) {
function onPushEvent(req, resp) {
var body = '';
request.on('data', function (data) { body += data; });
request.on('end', function () {
req.on('data', function (data) { body += data; });
req.on('end', function () {
var obj = qs.parse(body);
/* If required event properties are present we process the event */
if(obj && obj.event && obj.eventid){
answerSuccess(response, 'Thank you for the event (' + obj.event + '[' + obj.eventid + '])!');
resp.writeHead(200, { "Content-Type": "text/plain" });
resp.write('Thank you for the event (' + obj.event + '[' + obj.eventid + '])!');
resp.end();
eventHandler(obj);
} else answerError(response, 'Your event was missing important parameters!');
} else {
resp.writeHead(400, { "Content-Type": "text/plain" });
resp.write('Your event was missing important parameters!');
resp.end();
}
resp.end();
});
}
exports.loadUsers = function() {
var users = JSON.parse(require('fs').readFileSync(path.resolve(__dirname, '..', relPath)));
for(var name in users) {
}
};
exports.shutDown = function() {
log.print('HL', 'Shutting down HTTP listener');
process.exit(); // This is a bit brute force...

View file

@ -24,8 +24,7 @@ dog's back.
*/
//FIXME server should be started via command line arguments http_port and logging level to allow proper testing
var fs = require('fs'),
path = require('path'),
var path = require('path'),
log = require('./logging'),
procCmds = {
'die': function() { shutDown(); }
@ -36,7 +35,10 @@ var fs = require('fs'),
function init() {
log.print('RS', 'STARTING SERVER');
if(!require('./config').isReady()) {
log.error('RS', 'Config file not ready!');
return;
}
if(process.argv.length > 2) {
args.logType = parseInt(process.argv[2]) || 0 ;
log(args);
@ -56,12 +58,13 @@ function init() {
'loadactions': mm.loadActionModulesFromFS,
'loadevent': mm.loadEventModuleFromFS,
'loadevents': mm.loadEventModulesFromFS,
'loadusers': http_listener.loadUsers,
'shutdown': shutDown
};
log.print('RS', 'Initialzing engine');
engine.addDBLinkAndLoadActionsAndRules(db);
log.print('RS', 'Initialzing http listener');
http_listener.addHandlers(handleAdminCommands, engine.pushEvent);
http_listener.addHandlers(db, handleAdminCommands, engine.pushEvent);
log.print('RS', 'Initialzing module manager');
mm.addHandlers(db, engine.loadActionModule, engine.addRule);
//FIXME load actions and events, then rules, do this here, visible for everybody on the first glance
@ -82,6 +85,7 @@ function handleAdminCommands(args, answHandler) {
}, 2000);
}
function shutDown(args, answHandler) {
if(answHandler) answHandler.answerSuccess('Goodbye!');
log.print('RS', 'Received shut down command!');

61
js/user_handler.js Normal file
View file

@ -0,0 +1,61 @@
var log = require('./logging'),
db = require('./db_interface'), adminHandler;
exports = module.exports = function(args) {
args = args || {};
log(args);
db(args);
return module.exports;
};
exports.addHandler = function(adminHandl) {
adminHandler = adminHandl;
};
exports.handleRequest = function(req, resp) {
req.on('end', function () {
resp.end();
});
if(req.session && req.session.lastPage) resp.send('You visited last: ' + req.session.lastPage);
else resp.send('You are new!');
// 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);
};
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; }
};
};
//FIXME this answer handling is a very ugly hack, improve!
function onAdminCommand(request, response) {
var q = request.query;
log.print('HL', 'Received admin request: ' + request.originalUrl);
if(q.cmd) {
adminHandler(q, answerHandler(response));
// answerSuccess(response, 'Thank you, we try our best!');
} else answerError(response, 'I\'m not sure about what you want from me...');
}

View file

@ -1,8 +1,53 @@
<html>
<head>
<title>Mobile Page</title>
<script type="text/javascript">
document.write("\<script src='//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js' type='text/javascript'>\<\/script>");
</script>
<style>
body {
font-family: sans-serif, "Times New Roman", Georgia, Serif;
}
</style>
</head>
<body>
<h1>Mobile Page</h1>
<script>
function displayPosition(position) {
var lat = position.coords.latitude,
lon = position.coords.longitude;
$('#print').html("<table border='1'><tr><th>Timestamp</th><td>" + position.timestamp +
"<tr><th>Latitude</th><td>" + lat + " deg</td></tr>" +
"<tr><th>Longitude</th><td>" + lon + " deg</td></tr></table>");
var img_url="http://maps.googleapis.com/maps/api/staticmap?center="
+lat+","+lon+"&zoom=15&size=400x300&sensor=false&maptype=roadmap&markers=color:orange|label:1|"+lat+","+lon;
document.getElementById("mapholder").innerHTML="<img src='"+img_url+"'>";
$.post('push_event', { event: 'geoposition', eventid: 'geoposition_' + position.timestamp })
.done(function(data) {
$('#info').text("Sent event to engine: " + data);
})
.fail(function(err) {
$('#info').text("Error: " + err);
});
}
function displayError(positionError) {
$('#info').text('Error: ' + positionError);
}
var gl = navigator.geolocation;
if (gl) {
gl.getCurrentPosition(displayPosition, displayError);
} else {
$('#info').text('Geolocation services are not supported by your web browser.');
}
</script>
<div id="print"></div>
<div id="mapholder"></div>
<div id="info"></div>
</body>
</html>