UNPKG

actionhero

Version:

actionhero.js is a multi-transport API Server with integrated cluster capabilities and delayed tasks

270 lines (235 loc) 9.23 kB
var uuid = require('node-uuid'); module.exports = { loadPriority: 400, initialize: function(api, next){ api.connections = { middleware: {}, globalMiddleware: [], allowedVerbs: [ 'quit', 'exit', 'documentation', 'paramAdd', 'paramDelete', 'paramView', 'paramsView', 'paramsDelete', 'roomAdd', 'roomLeave', 'roomView', 'detailsView', 'say' ], connections: {}, apply: function(connectionId, method, args, callback){ if(args === undefined && callback === undefined && typeof method === 'function'){ callback = method; args = null; method = null; } api.redis.doCluster('api.connections.applyCatch', [connectionId, method, args], connectionId, callback); }, applyCatch: function(connectionId, method, args, callback){ var connection = api.connections.connections[connectionId]; if(method && args){ connection[method].apply(connection, args); } if(typeof callback === 'function'){ process.nextTick(function(){ callback(cleanConnection(connection)); }); } }, addMiddleware: function(data){ if(!data.name){ throw new Error('middleware.name is required'); } if(!data.priority){ data.priority = api.config.general.defaultMiddlewarePriority; } data.priority = Number(data.priority); api.connections.middleware[data.name] = data; this.globalMiddleware.push(data.name); this.globalMiddleware.sort(function(a,b){ if(api.connections.middleware[a].priority > api.connections.middleware[b].priority){ return 1; }else{ return -1; } }); } }; var cleanConnection = function(connection){ var clean = {}; for(var i in connection){ if(i !== 'rawConnection'){ clean[i] = connection[i]; } } return clean; } // {type: type, remotePort: remotePort, remoteIP: remoteIP, rawConnection: rawConnection} // id is optional and will be generated if missing api.connection = function(data){ var self = this; self.setup(data) api.stats.increment('connections:totalActiveConnections'); api.stats.increment('connections:activeConnections:' + self.type); api.stats.increment('connections:totalConnections'); api.stats.increment('connections:connections:' + self.type); api.connections.connections[self.id] = self; api.connections.globalMiddleware.forEach(function(middlewareName){ if(typeof api.connections.middleware[middlewareName].create === 'function'){ api.connections.middleware[middlewareName].create(self); } }); } api.connection.prototype.setup = function(data){ var self = this; if(data.id){ self.id = data.id; } else { self.id = self.generateID(); } self.connectedAt = new Date().getTime(); ['type', 'remotePort', 'remoteIP', 'rawConnection'].forEach(function(req){ if(data[req] === null || data[req] === undefined){ throw new Error(req + ' is required to create a new connection object') } self[req] = data[req]; }); var connectionDefaults = { error: null, fingerprint: null, rooms: [], params: {}, pendingActions: 0, totalActions: 0, messageCount: 0, canChat: false } for(var i in connectionDefaults){ if(self[i] === undefined && data[i] !== undefined){ self[i] = data[i]; } if(self[i] === undefined){ self[i] = connectionDefaults[i]; } } } api.connection.prototype.generateID = function(){ return uuid.v4(); } api.connection.prototype.sendMessage = function(message){ throw new Error('I should be replaced with a connection-specific method ['+message+']'); } api.connection.prototype.sendFile = function(path){ throw new Error('I should be replaced with a connection-specific method ['+path+']'); } api.connection.prototype.destroy = function(callback){ var self = this; self.destroyed = true; api.connections.globalMiddleware.forEach(function(middlewareName){ if(typeof api.connections.middleware[middlewareName].destroy === 'function'){ api.connections.middleware[middlewareName].destroy(self); } }); api.stats.increment('connections:totalActiveConnections', -1); api.stats.increment('connections:activeConnections:' + self.type, -1); if(self.canChat === true){ self.rooms.forEach(function(room){ api.chatRoom.removeMember(self.id, room); }); } delete api.connections.connections[self.id]; var server = api.servers.servers[self.type]; if(server){ if(server.attributes.logExits === true){ server.log('connection closed', 'info', {to: self.remoteIP}); } if(typeof server.goodbye === 'function'){ server.goodbye(self); } } if(typeof callback === 'function'){ callback() } } api.connection.prototype.set = function(key, value){ var self = this; self[key] = value; } api.connection.prototype.verbs = function(verb, words, callback){ var self = this; var key, value, room; var server = api.servers.servers[self.type]; var allowedVerbs = server.attributes.verbs; if(typeof words === 'function' && !callback){ callback = words; words = []; } if(!(words instanceof Array)){ words = [words]; } if(server && allowedVerbs.indexOf(verb) >= 0){ server.log('verb', 'debug', {verb: verb, to: self.remoteIP, params: JSON.stringify(words)}); if(verb === 'quit' || verb === 'exit'){ server.goodbye(self); } else if(verb === 'paramAdd'){ key = words[0]; value = words[1]; if(words[0].indexOf('=') >= 0){ var parts = words[0].split('='); key = parts[0]; value = parts[1]; } if(api.config.general.disableParamScrubbing || api.params.postVariables.indexOf(key) > 0){ self.params[key] = value; } if(typeof callback === 'function'){ callback(null, null); } } else if(verb === 'paramDelete'){ key = words[0]; delete self.params[key]; if(typeof callback === 'function'){ callback(null, null); } } else if(verb === 'paramView'){ key = words[0]; if(typeof callback === 'function'){ callback(null, self.params[key]); } } else if(verb === 'paramsView'){ if(typeof callback === 'function'){ callback(null, self.params); } } else if(verb === 'paramsDelete'){ for(var i in self.params){ delete self.params[i]; } if(typeof callback === 'function'){ callback(null, null); } } else if(verb === 'roomAdd'){ room = words[0]; api.chatRoom.addMember(self.id, room, function(err, didHappen){ if(typeof callback === 'function'){ callback(err, didHappen); } }); } else if(verb === 'roomLeave'){ room = words[0]; api.chatRoom.removeMember(self.id, room, function(err, didHappen){ if(typeof callback === 'function'){ callback(err, didHappen); } }); } else if(verb === 'roomView'){ room = words[0]; if(self.rooms.indexOf(room) > -1){ api.chatRoom.roomStatus(room, function(err, roomStatus){ if(typeof callback === 'function'){ callback(err, roomStatus); } }); }else{ if(typeof callback === 'function'){ callback('not member of room ' + room); } } } else if(verb === 'detailsView'){ var details = {}; details.id = self.id; details.fingerprint = self.fingerprint; details.remoteIP = self.remoteIP; details.remotePort = self.remotePort; details.params = self.params; details.connectedAt = self.connectedAt; details.rooms = self.rooms; details.totalActions = self.totalActions; details.pendingActions = self.pendingActions; if(typeof callback === 'function'){ callback(null, details); } } else if(verb === 'documentation'){ if(typeof callback === 'function'){ callback(null, api.documentation.documentation); } } else if(verb === 'say'){ room = words.shift(); api.chatRoom.broadcast(self, room, words.join(' '), function(err){ if(typeof callback === 'function'){ callback(err); } }); } else { if(typeof callback === 'function'){ callback(api.config.errors.verbNotFound(verb), null); } } } else { if(typeof callback === 'function'){ callback(api.config.errors.verbNotAllowed(verb), null); } } } next(); } }