PageRenderTime 10ms CodeModel.GetById 4ms app.highlight 69ms RepoModel.GetById 1ms app.codeStats 0ms

/documents/npush-versions/v2/npush-s-v2.io.js

https://bitbucket.org/newicon/npush-server
JavaScript | 1629 lines | 872 code | 398 blank | 359 comment | 172 complexity | ed60d4a5f64a661d224b4c2aed978d49 MD5 | raw file
   1/*! NEWICON NPUSH SERVER build: 2.0.0 , development. Copyright(c) 2012 NEWICON LTD */
   2
   3/*
   4 *	!!! CURRENTLY IN DEVELOPMENT !!!
   5 */
   6
   7
   8/*
   9 * Proper command line to launch n:push
  10 * 
  11 * Example command:
  12 * $ NODE_ENV=production node n-push-s.js 8080 10
  13 * 
  14 * Description:
  15 * $ -environement- -node- -script_name- -port- -instances-
  16 */
  17
  18
  19/*
  20 * TO DO
  21 * ok - optimize users list for app/channel/room - probably best solution is send
  22 *		changes only for user subscribed for path /service/activity/users
  23 * ok - optimize api activity, maybe the best is send to /service/activity/api
  24 * ok - add activity for server, path should be /service/activity/system
  25 * ok - delete user from nicnames object when disconnected,
  26 *		clear also not active api/channel/room from nicknames object
  27 * - add messeage-read event as an option in npush library
  28 * - merge 3 different function for API POST (brodcast, channel, room)
  29 *		to one function
  30 * - api activity - calculate incoming and outgoing traffic
  31 * ok - add active sockets count to message - will be used in receipts area
  32 * - maybe good idea is create array like nicnames but with sockets - it could
  33 *		imprvoe sendind messages to socket in app or in channel or in room
  34 * ok - on connect we should delete event appusers and channelusers. Only room 
  35 *		users should be still. appusers and channelusers should be moved to
  36 *		apiactivty channel stats!
  37 * - add function to calculate number of sockets in api/channel/rooms -> should
  38 *		be updated after each connect and disconnect
  39 * - add activity metter when send messages for public_activity history
  40 * - public_activity - messages history - ability to get messages 
  41 *		with recepit as option. the same functionality to public_activity and
  42 *			public_activity_ms
  43 * - public_activiyt_XXX - optimize this functions, now both are too big
  44 * - new EVENT to get receipt for passed message_id
  45 * - probably good idea is handle event for each socket depends on events handle
  46 *		when channel is created on client side. For this, we have to pass list
  47 *		of events with data to hanfdhake process. if handshaked then we can 
  48 *		create event callback for user list. NOW ALL EVENTS ARE ALWAYS CREATED.
  49 * ok - dont send list of users every second. send only if something was chnaged
  50 * - size of nicknames array
  51 * - size of activity array
  52 * - add console start up arguments: 
  53 * 
  54 *			npush-port
  55 *			number-of-instances to run
  56 *			mongo-adress:port:username:password:db
  57 *			redis-store-address:port:password
  58 * 
  59 * - MULTI INSTANCES PROBLEMS TO SOLVE
  60 * ok - now we have scokets belongs to API on diffrent node processes, so to calculate
  61 *		statistics we have to send stats to MASTER, MERGE data and then send stats
  62 *		to SLAVES; info we need are sockets, dbs
  63 * - important - data should be calculated and send only if any api_key/npush/* 
  64 *		channel exist
  65 * 
  66 * BUGS
  67 * ok - on user connect the new users list is send to all api sockets. Should be
  68 *		send only to sockets in api of joined user
  69 * - when looking for rooms name in socket.manager.rooms using _string.substr
  70 *		need to check also length of string. now /service and service1 
  71 *		is the same for that method and it is really wrong
  72 *		
  73 * SCARY
  74 * - when users receive list of user it generate huge traffic for each
  75 *      user authorisation and disconnection, reconnect etc.
  76 *      IT MUST BE AS OPTION AND ONLY FOR SPECIAL SERVICE CHANNEL
  77 * ok - doconnected user from room dont delete channel/room from history. IT IS THE
  78 *      REASON WHY AFTER MANY CONNECTION AND DISCONNECTION ALL GO SLOWER...
  79 */
  80
  81/**
  82 * Krzysztof Stasiak
  83 * npush-s.js
  84 * Copyright(c) 2012 NEWICON LTD <newicon@newicon.net>
  85 * GPL Licensed
  86 */
  87
  88var cluster = require('cluster');
  89var http = require('http');
  90var startInstancesNumber = (process.argv[3]) || 1;
  91
  92/**
  93*  APIs Activity Manager
  94*  
  95*  Calcualte API activity and send information if any socket 
  96*  handle that event.
  97*  
  98*  @param object socket
  99*/
 100
 101function PusherAPIStatsRecordCreate(socket)
 102{
 103	return {
 104				s:{						// summary
 105					m:0,				// m    -> messages count
 106					pm:0,				// pm	-> messages count 1 second before
 107					mps:0,				// mps  -> messages per second
 108					mpm:0,				// mpm  -> messages per minute		
 109					mph:0,				// mph  -> messages per hour
 110					dbrps:0,			// dbr	-> db reads per second
 111					dbwps:0,			// dbw	-> db writes per second
 112					dbups:0				// dbu	-> db updates per second
 113				},
 114				t:{						// temp history
 115					mh: new Array(),	// last 60 second history
 116					mhp: 0,				// last 60 second history pointer
 117					hh: new Array(),	// last 60 minute history
 118					hhp: 0				// last 60 minute history pointer
 119				},
 120				c:{						// connection
 121					s:false,			// new list is waiting for send
 122					a:true				// allow traffic
 123				},
 124				n:{						// npush
 125					as:0,				// active sockets
 126					n:'n/a'				// client name
 127				}
 128			};			
 129}
 130
 131/**
 132 *	Master Server Instance
 133 *
 134 *	1. Master instance forks workers up to startInstancesNumber number.
 135 *	2. Master process receive worker messages and send own to workers.
 136 *	3. Master process manage load-balancer of TCP traffic in cluster.
 137 */
 138
 139if(cluster.isMaster) {
 140	
 141	// this object should keep all statistics sockets, db, process data for
 142	// all running instaneces
 143	var stats = {
 144                    w:new Array(),				    // worker PID list
 145                    a:new Object(),					// activity for all socket
 146					n:new Object(),					// global nicknames
 147					t:{								// traffic
 148						a:true						// allow traffic
 149					}
 150				};
 151				
 152	// this object contain activity from all workers
 153	var activityGlobal = new Array();
 154	var nicknameGlobal = new Array();
 155					
 156	// do this when worker receive a message
 157	function messageHandler(data) {
 158		
 159		if(data.event == 'worker-activity') {
 160
 161            // get feedback from worker if master send 'send-activity' event
 162            for(s in data.data.a) {
 163				
 164				if(!activityGlobal[s])
 165					activityGlobal[s] = new Array();
 166					
 167				activityGlobal[s][this.id] = data.data.a[s];			
 168			}
 169			
 170			// merge nickanmes for all workers
 171			for(a in data.data.n) {
 172				for(c in data.data.n[a]) {
 173					for(r in data.data.n[a][c]) {
 174						for(u in data.data.n[a][c][r]) {
 175						
 176							if(!stats.n[a]) stats.n[a] = new Object();
 177							if(!stats.n[a][c]) stats.n[a][c] = new Object();
 178							if(!stats.n[a][c][r]) stats.n[a][c][r] = new Object();
 179
 180							stats.n[a][c][r][u] = data.data.n[a][c][r][u];
 181						}
 182					}
 183				}
 184			}
 185        } else if(data.event == 'system-command') {
 186			if(data.data.action) {
 187				if(data.data.action == 'traffic') {
 188					if(data.data.value == '0') {
 189						stats.t.a = false;
 190					} else if(data.data.value == '1') {
 191						stats.t.a = true;
 192					}
 193				}
 194			}
 195		}
 196	}
 197	
 198	// create workers process
 199	for (var i = 0; i < startInstancesNumber; i++) {
 200
 201		cluster.fork();		
 202	}
 203	
 204	// listen for worker messages
 205	Object.keys(cluster.workers).forEach(function(id) {
 206		
 207		stats.w.push(cluster.workers[id].process.pid);
 208		cluster.workers[id].on('message', messageHandler);
 209		
 210	});	
 211
 212	// do this when worker died
 213	cluster.on('exit', function(worker, code, signal) {
 214		
 215		console.log('worker ' + worker.process.pid + ' died');
 216	});
 217	
 218	// calculate stats for all workers
 219	function ModuleMasterCalculateStats() {	
 220	
 221		for(s in activityGlobal) {				
 222			for(w in activityGlobal[s]) {
 223
 224				if(!stats.a[s])
 225					stats.a[s] = PusherAPIStatsRecordCreate();
 226
 227				stats.a[s].s.m += activityGlobal[s][w].s.m;
 228				stats.a[s].s.pm += activityGlobal[s][w].s.pm;
 229				stats.a[s].s.dbrps += activityGlobal[s][w].s.dbrps;
 230				stats.a[s].s.dbwps += activityGlobal[s][w].s.dbwps;
 231				stats.a[s].s.dbups += activityGlobal[s][w].s.dbups;					
 232				stats.a[s].n.as += activityGlobal[s][w].n.as;
 233
 234				if(activityGlobal[s][w].c.s)
 235					stats.a[s].c.s = true;
 236			}
 237		}	
 238					
 239		Object.keys(cluster.workers).forEach(function(id) {
 240			
 241			// send data to worker who send message to master
 242			cluster.workers[id].send({event:'global-stats', data:stats}); 
 243
 244			// ask for current stats from worker
 245			cluster.workers[id].send({event:'send-activity'});		
 246		});
 247		
 248		// reset status before ask workers for new one
 249		stats.a = new Object();
 250		stats.n = new Object();
 251		activityGlobal = new Array();
 252	
 253		// set new timeout
 254		_tseconds = setTimeout(function(){ModuleMasterCalculateStats(); },1000);
 255	}
 256
 257	// start stats for all workers
 258	var _tseconds = setTimeout(function(){ModuleMasterCalculateStats(); },1000);	
 259  
 260} else {
 261	
 262	/**
 263	 *	THIS CODE IS LAUNCH ONLY FOR SLAVE/WORKERS
 264	 *	
 265	 *	Private Server / Cluster Keys for management
 266	 */
 267
 268	var NPUSH_SERVER_STATUS_APIKEY	= '12345678987654321';
 269	var NPUSH_SERVER_CONTROL_APIKEY = '12345678987654321';
 270
 271	/**
 272	* Module dependencies.
 273	*/
 274
 275	var express = require('express')
 276	, stylus = require('stylus')
 277	, nib = require('nib')
 278	, sio = require('socket.io')
 279	, RedisStore = sio.RedisStore
 280	, databaseUrl = "ni_sockets" //'username:password@example.com:port/mydb'
 281	, collections = ["messages_public", "messages_private", "accounts"]
 282	, db = require("mongojs").connect(databaseUrl, collections)
 283	, port = process.argv[2]
 284	, qs = require('querystring')
 285	, util = require('util')
 286	, os  = require('os-utils');	
 287	
 288	/*******************************************************************************
 289	* App.
 290	******************************************************************************/
 291
 292	var app = express.createServer();
 293
 294	/*******************************************************************************
 295	* App configuration.
 296	******************************************************************************/
 297
 298	app.configure(function () {
 299		app.use(stylus.middleware({
 300			src: __dirname + '/public', 
 301			compile: compile
 302		}));
 303		app.use(express.static(__dirname + '/public'));
 304		app.set('views', __dirname);
 305		app.set('view engine', 'jade');
 306
 307		function compile (str, path) {
 308			return stylus(str)
 309			.set('filename', path)
 310			.use(nib());
 311		};
 312	});
 313
 314	/*******************************************************************************
 315	* App routes.
 316	* 
 317	* This function redirect standard http traffic to index.html file
 318	******************************************************************************/
 319
 320	app.get('/', function (req, res) {
 321		res.render('public/index', {
 322			layout: false
 323		});
 324	});
 325
 326	/**
 327	* App listen.
 328	*/
 329
 330	app.listen(port, function () {
 331		var addr = app.address();
 332		console.log('   app listening on http://' + addr.address + ':' + addr.port);
 333	});
 334
 335	/*******************************************************************************
 336	* Socket.IO server (single process only)
 337	******************************************************************************/
 338
 339	var io = sio.listen(app)
 340	, nicknames = new Object()
 341	, activity = new Object()
 342    , activityGlobal = new Object()
 343	, nicknameGlobal = new Object()
 344	, system = os.platform()
 345	, workersInstances = new Array()
 346	, allowGlobalTraffic = true;
 347
 348	// detect system
 349
 350	if(system == 'linux') {
 351		system = 1;
 352	} else {
 353		system = 2;
 354	}
 355
 356	// list of active users
 357	nicknames = {};
 358
 359	// activity
 360	activity = {};
 361
 362	// socket.io SETTINGS
 363
 364	io.configure('production', function(){
 365		io.enable('browser client minification');  // send minified client
 366		io.enable('browser client etag');          // apply etag caching logic based on version number
 367		io.enable('browser client gzip');          // gzip the file
 368		io.set('log level', 1);                    // reduce logging
 369		io.set('transports', [                     // enable all transports (optional if you want flashsocket)
 370			'websocket'
 371		, 'flashsocket'
 372		, 'htmlfile'
 373		, 'xhr-polling'
 374		, 'jsonp-polling'
 375		]);
 376		
 377		io.set('store', new RedisStore());	
 378	});
 379
 380	io.configure('development', function(){
 381		
 382		io.set('transports', ['websocket']);
 383		io.set('store', new RedisStore({ id: function () { cluster.worker.id } }));				
 384	});
 385
 386	/**
 387	*	Socket.io configuration for:
 388	*	
 389	*	Authorisation process. This function looking for API key and on success
 390	*	return object with proper socket data.
 391	*	
 392	*	@param object handshakeData
 393	*	@param function callback
 394	*/
 395
 396	io.configure(function (){
 397		io.set('authorization', function (handshakeData, callback) {
 398			
 399			if(!allowGlobalTraffic && NPUSH_SERVER_STATUS_APIKEY != handshakeData.query.k
 400					&& NPUSH_SERVER_CONTROL_APIKEY != handshakeData.query.k) 
 401						return;
 402
 403			db.collection('apis')
 404			.find(JSON.parse('{ "key" : "' + handshakeData.query.k + '", "enabled" : "true" }'))
 405			.forEach(function(err, doc) {
 406
 407				if(!doc && NPUSH_SERVER_STATUS_APIKEY != handshakeData.query.k
 408						&& NPUSH_SERVER_CONTROL_APIKEY != handshakeData.query.k) {
 409
 410					callback(null, false);
 411				} else {
 412
 413					handshakeData.client_room = handshakeData.query.k;
 414					handshakeData.client_wellcome_msg = 'Welcome in Newicon Pusher System V.2';
 415					handshakeData.address = handshakeData.address.address;
 416					handshakeData.port = handshakeData.address.port;
 417					handshakeData.api_key = handshakeData.query.k;
 418					handshakeData.channel = handshakeData.query.c;
 419					handshakeData.user_id = handshakeData.query.id;
 420					handshakeData.user_name = handshakeData.query.name;
 421					handshakeData.user_room = handshakeData.query.room;
 422				   
 423					PusherAPIsActivity({client:handshakeData.query.k}, 0, 1, 0, 0);
 424
 425					callback(null, true);
 426				}
 427			});		
 428		});
 429	});
 430
 431	/**
 432	*	Create proper msg object using data sent form socket to server.
 433	*	Return object ready to send from server to sockets.
 434	*	
 435	*	@param object msg - message from socket which must be broadcasted
 436	*	@param object socket - message sender socket owner
 437	*	
 438	*	@return object
 439	*/
 440
 441	function PusherMakeMessage(msg, socket) {
 442
 443		var _time = new Date();
 444
 445		return {
 446			dataSystem : {
 447				user_id:    socket.user_id,
 448				user_name:  socket.nickname, 
 449				room:       socket.room, 
 450				channel:    socket.channel,
 451				time:       parseInt(_time.getTime()),
 452				client:     socket.client,
 453				type:       0,						// 0 - room, 1 - channel, 2 - app
 454				archive:    0,						// 0 - from socket, 1 - from db
 455				msg_id:		0,						// msg id in db
 456				source:		0						// 0 - socket, 1 - POST
 457			},
 458			dataUser :      msg.data,
 459			event :         msg.event
 460		};
 461	}
 462
 463	/**
 464	*	Create proper msg object using mongo document. Return object
 465	*	ready to send from server to socket.
 466	*	
 467	*	@param object doc - mongoDB document
 468	*	@return object
 469	*/
 470
 471	function PusherMakeMessageFromDB(doc) {
 472
 473		return {
 474			dataSystem : {
 475				user_id:    doc['user']['id'],
 476				user_name:  doc['user']['name'], 
 477				room:       doc['room'], 
 478				channel:    '',
 479				time:       doc['date'],
 480				client:     doc['api_key'],
 481				type:       0,						// 0 - room, 1 - channel, 2 - app
 482				archive:    1,						// 0 - from socket, 1 - from db
 483				msg_id:		0,						// msg id in db
 484				source:		doc['source']			// 0 - socket, 1 - POST
 485			},
 486			dataUser :      doc['message']['data'],
 487			event :         doc['message']['event']
 488		};
 489	}
 490
 491	/**
 492	*	Create proper msg object using POST Request. Return object
 493	*	ready to send from server to socket.
 494	*	
 495	*	@param object request - POST data
 496	*	@return object
 497	*/
 498
 499	function PusherMakeMessageFromPOST(req) {
 500
 501		// must be implemented
 502	}
 503
 504	/**
 505	*	Updating users activity. Activity is updated for userId
 506	*	
 507	*	@param object socket
 508	*/
 509
 510	function PusherUserActivityUpdate(socket) {
 511
 512		var _time = new Date();	
 513
 514		var _search_json = '{ "user_id" : "' + socket.user_id + '" }';
 515		var _update_json = '{ "user_id" : "' + socket.user_id + '", "date" : ' + _time.getTime() + ', "date_string" : "' + _time.getTime() + '", \n\
 516				"rooms./' + socket.client + '/' + socket.channel + '/' + socket.room + '/.date" : "' + _time.getTime() + '",\n\
 517				"rooms./' + socket.client + '/' + socket.channel + '/' + socket.room + '/.name" : "/' + socket.client + '/' + socket.channel + '/' + socket.room + '/" } ';
 518
 519		_search = JSON.parse(_search_json);
 520		_update = JSON.parse(_update_json);
 521
 522		db.collection('act_' + socket.client).update(_search, {
 523			$set : _update
 524		} , {
 525			upsert : true
 526		} );
 527
 528		PusherAPIsActivity(socket, 0, 0, 0, 1);
 529	}
 530
 531	/**
 532	*	Saving message sent by socket to server
 533	*	
 534	*	@param object msg
 535	*	@param string path
 536	*	@param object socket
 537	*	@param function callback
 538	*/
 539
 540	function PusherUserMessageSave(msg, path, socket, callback)
 541	{
 542		var _data = PusherMakeMessage(msg, socket);
 543
 544		db.collection('msg_' + socket.handshake.client_room).save({
 545			"api_key": socket.api_key, 
 546			"room" : path, 
 547			"user" : {
 548				"id" : socket.user_id, 
 549				"name" : socket.user_name
 550			} , 
 551			"date": _data.dataSystem.time, 
 552			"date_string": _data.dataSystem.time.toString(),
 553			"message" : msg,
 554			"client" : socket.handshake.address + ':' + socket.handshake.port,
 555			"source" : 0,									// 0 - socket, 1 - POST
 556			"as" : 0										// active sockets
 557		}, function(err, obj){
 558
 559			_data.dataSystem.msg_id = obj._id;
 560			callback.call(this, _data);
 561
 562			// send activity stats for debuging
 563			PusherAPIsActivity(socket, 1, 0, 1, 0);
 564		});
 565	}
 566
 567	/**
 568	*	Saving message sent by POST to server
 569	*	
 570	*	@param object msg
 571	*	@param string path
 572	*	@param object socket
 573	*	@param function callback
 574	*/
 575
 576	function PusherUserMessageSaveFromPOST(msg, path, req, callback)
 577	{
 578		// waiting for implementing. Needed for POST API.
 579	}
 580
 581	/**
 582	*	Updating Receipt Collection for each delivery confirmation
 583	*	
 584	*	@param object msg - object with message id as string
 585	*	@param object socket
 586	*/
 587
 588	function PusherUserMessageRead(msg, socket)
 589	{
 590		var _time = new Date();	
 591
 592		var _search_json = '{ "msg_id" : "' + msg.msg_id + '" }';
 593		var _update_json = '{ "msg_id" : "' + msg.msg_id + '", "users.' + socket.user_id + '.date" : "' + _time.getTime() + '"} ';
 594
 595		_search = JSON.parse(_search_json);
 596		_update = JSON.parse(_update_json);
 597
 598		db.collection('msg_read_' + socket.client).update(_search, {
 599			$set : _update
 600		} , {
 601			upsert : true
 602		} );
 603
 604		PusherAPIsActivity(socket, 1, 0, 0, 1);
 605	}
 606
 607	/**
 608	*	Increase counters of traffic in sockets and database
 609	*	
 610	*	@param int m	- number of messages send in socket
 611	*	@param int dbr	- number of db read operations
 612	*	@param int dbw	- nubmer of db write operations
 613	*	@param int dbu	- number of db update operations
 614	*/
 615
 616	function PusherAPIsActivity(socket, m, dbr, dbw, dbu) {
 617
 618		if(!activity[socket.client])		    
 619			activity[socket.client] = PusherAPIStatsRecordCreate(socket);
 620
 621		// sockets sounters   
 622		activity[socket.client].s.m += m;
 623
 624		// db counters
 625		activity[socket.client].s.dbrps += dbr;	
 626		activity[socket.client].s.dbwps += dbw;
 627		activity[socket.client].s.dbups += dbu;
 628	}
 629
 630	/**
 631	*  Send API Activity for socket on path npush/stats and npush/users
 632	*  with subscribed event for "apiactivity"
 633	*/
 634
 635	function PusherAPIsActivityStats()
 636	{  		            
 637        // finish here if worker is not first one
 638		if(cluster.worker.id > 1) return;
 639				
 640		// get list of rooms in socket.io
 641		var rooms = io.sockets.manager.rooms;	
 642
 643		// calculate statistics
 644
 645		var pmem = process.memoryUsage();
 646		var ppid = process.pid;
 647
 648		// temp object with important info for Administrator
 649
 650		var sstats = {};										// all sockets stats
 651		var tstats = {mps:0,mpm:0,mph:0,dbr:0,dbw:0,dbu:0};		// global traffic stats
 652		var aconn = 0;											// all active connections
 653
 654		for(socket in activityGlobal) {		
 655			
 656			if(!activity[socket])
 657				activity[socket] = PusherAPIStatsRecordCreate();
 658
 659			// calculate how many messages was sent last second
 660			activityGlobal[socket].s.mps = activityGlobal[socket].s.m - activityGlobal[socket].s.pm;
 661
 662			// calculate how many messages in last minute
 663			activity[socket].t.mh[activity[socket].t.mhp] = activityGlobal[socket].s.mps;
 664
 665			if(activity[socket].t.mhp <  60) {
 666				activity[socket].t.mhp++
 667			} else {
 668				activity[socket].t.mhp = 0;
 669				activity[socket].t.hhp++				
 670			}
 671
 672			var mpm = 0;
 673
 674			for(i=0; i<activity[socket].t.mh.length;i++)
 675				mpm += activity[socket].t.mh[i];
 676
 677			activity[socket].s.mpm = mpm;			
 678
 679			// calculate how many messages in last hour
 680			activity[socket].t.hh[activity[socket].t.hhp] = activity[socket].s.mpm;
 681
 682			if(activity[socket].t.hhp >  59) {
 683
 684				activity[socket].t.hhp = 0;
 685			}
 686
 687			var mph = 0;
 688
 689			for(i=0; i<activity[socket].t.hh.length;i++)
 690				mph += activity[socket].t.hh[i];			
 691
 692			activity[socket].s.mph = mph;
 693
 694			// send stats to subscribed sockets
 695
 696			var _path = '/' + socket + '/npush/api/stats';
 697
 698			activity[socket].s.proc = {mem:pmem.rss, pid:ppid};
 699
 700			activityGlobal[socket].s.mpm = activity[socket].s.mpm;
 701			activityGlobal[socket].s.mph = activity[socket].s.mph;
 702
 703			for(room in rooms)
 704				if(room.search(_path) == 0) {
 705					io.sockets.in(room.substr(1, room.length)).emit('apiactivity', activityGlobal[socket].s);
 706					PusherAPIsActivity({client:socket}, 1, 0, 0, 0);
 707				}
 708
 709			// send users sockets list to subscribed sockets
 710			if(activityGlobal[socket].c.s) {
 711
 712				var _path = '/' + socket + '/npush/api/users';
 713
 714				for(room in rooms)
 715					if(room.search(_path) == 0) {
 716
 717						io.sockets.in(room.substr(1, room.length)).emit('usersapp', nicknameGlobal[socket]);
 718						PusherAPIsActivity({client:socket}, 1, 0, 0, 0);
 719					}
 720
 721				activityGlobal[socket].c.s = false;
 722			}
 723
 724			// set admin-stats for this API
 725			sstats[socket] = {mps:activityGlobal[socket].s.mps,mpm:activityGlobal[socket].s.mpm,s:activityGlobal[socket].n.as,n:activityGlobal[socket].n.n};
 726
 727			// gobal traffic info
 728			tstats.mps += activityGlobal[socket].s.mps;
 729			tstats.mpm += activityGlobal[socket].s.mpm;
 730			tstats.mph += activityGlobal[socket].s.mph;
 731			tstats.dbr += activityGlobal[socket].s.dbrps;
 732			tstats.dbw += activityGlobal[socket].s.dbwps;
 733			tstats.dbu += activityGlobal[socket].s.dbups;
 734			
 735			// summ all connections
 736			aconn += activityGlobal[socket].n.as;
 737		}  	
 738
 739		// prepare system stats
 740		os.cpuUsage(function(v) {		
 741
 742			var _path = '/' + NPUSH_SERVER_STATUS_APIKEY + '/npush/system/stats';	
 743			var _data = {
 744						node: new Array(),
 745						api:{
 746							act:sstats
 747						},
 748						os:{
 749							cpu:v,
 750							tm:os.totalmem(),
 751							fm:os.freemem(),
 752							node:{
 753								i: workersInstances.length,
 754								s: startInstancesNumber
 755							}
 756						},
 757						mongo: new Array(),
 758						sockets:{
 759							active:aconn,
 760							traffic:allowGlobalTraffic
 761						},
 762						traffic:tstats
 763					};
 764
 765			if(system == 1) { // linux
 766
 767				var command = 'top -n1 -b';
 768
 769				require('child_process').exec(command, function(error, stdout, stderr) {
 770
 771					var lines = stdout.split("\n");
 772
 773					lines.forEach(function(_item,_i) {
 774
 775						var _p = _item.replace( /[\s\n\r]+/g,' ').split(' ');
 776
 777						if(String(_p[12]).indexOf('mongod') > -1) {
 778
 779							_data.mongo.push({pid:_p[1],cpu:_p[9],pmem:_p[6],rss:_p[10],name:_p[12]});
 780
 781						} else if(_p[0] == ppid) {
 782
 783							_data.node = {pid:_p[0],cpu:_p[8],pmem:_p[5],rss:_p[9],name:_p[11]};
 784						} 					
 785					});
 786
 787					for(room in rooms)
 788						if(room.search(_path) == 0) {
 789							io.sockets.in(room.substr(1, room.length)).emit('announcement', _data);
 790							PusherAPIsActivity({client:NPUSH_SERVER_STATUS_APIKEY}, 1, 0, 0, 0);
 791						}
 792				});		
 793
 794			} else { // mac
 795
 796				var command = 'ps -eo pid,pcpu,pmem,rss,comm';
 797				//var command = 'top';
 798				 
 799				require('child_process').exec(command, function(error, stdout, stderr) {				
 800
 801					var lines = stdout.split("\n");
 802
 803					lines.forEach(function(_item,_i) {
 804
 805						var _p = _item.replace( /[\s+\n\r]+/g,' ').split(' ');
 806						
 807						if(String(_p[4]).indexOf('mongod') > -1) {
 808
 809							_data.mongo.push({pid:_p[0],cpu:_p[1],pmem:_p[2],rss:_p[3],name:_p[4]});
 810
 811						} else if((parseInt(_p[0]) > 0 && PusherFindInArray(workersInstances ,_p[0])) 
 812							|| (_p[0] == '' && parseInt(_p[1]) > 0 && PusherFindInArray(workersInstances ,_p[1]))) {
 813							
 814							// we need get list of PIDs in cluster and get data for them
 815
 816							if(_p[0] == '') {
 817								_data.node.push({pid:_p[1],cpu:_p[2],pmem:_p[3],rss:_p[4],name:_p[5]});
 818							} else {
 819								_data.node.push({pid:_p[0],cpu:_p[1],pmem:_p[2],rss:_p[3],name:_p[4]});
 820							}
 821						}                    
 822					});
 823
 824					for(room in rooms)
 825						if(room.search(_path) == 0) {
 826							io.sockets.in(room.substr(1, room.length)).emit('announcement', _data);
 827							PusherAPIsActivity({client:NPUSH_SERVER_STATUS_APIKEY}, 1, 0, 0, 0);
 828						}
 829				});
 830			}
 831		});
 832	}
 833	
 834	/**
 835	 * Listen for Master Cluster Messages
 836	 * Send Respond for Master requests.
 837	 * 
 838	 * @param object data as message from master process
 839	 */
 840	
 841	function ModulePusherMessageHandler(data) {        
 842        
 843		if(data.event == 'global-stats') {
 844			
 845            // all worker PIDs
 846			workersInstances = data.data.w;            
 847            // data activity from master (all workers)
 848            activityGlobal = data.data.a;
 849			// data nicknames from master (all workers)
 850			nicknameGlobal = data.data.n;
 851			// global traffic
 852			allowGlobalTraffic = data.data.t.a;
 853			// send stats to scokets
 854			PusherAPIsActivityStats();
 855            
 856		} else if(data.event == 'send-activity') {
 857            
 858            // data actovity from worker to master
 859            process.send({
 860				event:'worker-activity', data:{a:activity,n:nicknames} });
 861			
 862			// some action after send stats to master
 863			for(socket in activity) {
 864				
 865				activity[socket].s.pm = activity[socket].s.m;
 866				activity[socket].s.dbrps = activity[socket].s.dbwps = activity[socket].s.dbups = 0;		
 867			}
 868        }
 869	}
 870	
 871	/**
 872	 *	Cluster messages handler
 873	 */
 874	
 875	process.on('message', ModulePusherMessageHandler);
 876
 877	/*
 878	* Handle event for each socket. This 'connection' event function is called 
 879	* when handshake proccess return true.
 880	* 
 881	* Events:
 882	* - message
 883	* - message-read
 884	* - message boradcast rooms
 885	* - message broadcast app
 886	* - message broadcast channel
 887	* - public_activity_ms
 888	* - public_activity
 889	* - disconnect
 890	* 
 891	*/
 892
 893	io.sockets.on('connection', function (socket) {
 894
 895		// USER LOGIN AND ENVIRONMENT SETTINGS
 896
 897		// SEND WELCOME MESSAGE
 898		io.sockets.socket(socket.id).emit('announcement', socket.handshake.client_wellcome_msg);
 899
 900		// SET USER SOCKET PARAMETERS
 901		socket.api_key = socket.handshake.api_key;		// THIS IS API KEY
 902		socket.channel = socket.handshake.channel;
 903		socket.client = socket.handshake.client_room;	// THIS IS API KEY
 904		socket.path = socket.handshake.client_room + '/' + socket.handshake.channel + '/' + socket.handshake.user_room;
 905		socket.room = socket.handshake.user_room;
 906		socket.user_id = socket.handshake.user_id;
 907		socket.user_name = socket.handshake.user_name;
 908				
 909		// ADD USER TO LIST
 910		
 911		if(!nicknames[socket.handshake.client_room])		    
 912			nicknames[socket.handshake.client_room] = {};
 913		
 914		if(!activity[socket.handshake.client_room])		    
 915			activity[socket.handshake.client_room] = PusherAPIStatsRecordCreate(socket);
 916
 917		if(!nicknames[socket.handshake.client_room][socket.handshake.channel])		    
 918			nicknames[socket.handshake.client_room][socket.handshake.channel] = {};
 919
 920		if(!nicknames[socket.handshake.client_room][socket.handshake.channel][socket.handshake.user_room])
 921			nicknames[socket.handshake.client_room][socket.handshake.channel][socket.handshake.user_room] = {};		
 922		
 923		nicknames[socket.handshake.client_room][socket.handshake.channel][socket.handshake.user_room][socket.handshake.user_id] = socket.nickname = socket.handshake.user_name;
 924		
 925		// JOIN ROOM
 926		socket.join(socket.path);
 927
 928		// set info in activity object - list will be send to watchers	
 929		activity[socket.client].c.s = true;
 930
 931		// update connected user in socket -> as is active-sockets
 932
 933		activity[socket.client].n.as++;
 934
 935		// USER MESSAGES
 936
 937		socket.on('message', function (_msg) {
 938
 939			if(!allowGlobalTraffic) return;
 940
 941			// set path to room
 942			var _path = '/' + socket.handshake.client_room + '/' + socket.channel + '/' + socket.room + '/';        
 943
 944			// save message in db
 945			PusherUserMessageSave(_msg, _path, socket, function(_data) { 
 946
 947				// send message
 948				io.sockets.in(socket.path).emit('message', _data);
 949			});
 950		});
 951
 952		socket.on('message-read', function (_msg) {
 953
 954			PusherUserActivityUpdate(socket);		
 955			PusherUserMessageRead(_msg, socket);
 956		});
 957
 958		// BROADCAST MESSAGES
 959
 960		socket.on('message boradcast rooms', function (_msg, fn) {
 961			
 962			if(!allowGlobalTraffic) return;
 963
 964			var _path = '/' + socket.client + '/' + socket.channel + '/';
 965
 966			// save message in db
 967			PusherUserMessageSave(_msg, _path, socket, function(_data) { 
 968
 969				// broadcast to app
 970				_data.dataSystem.type = 1;      
 971
 972				// get all rooms
 973				var rooms = io.sockets.manager.rooms;        
 974
 975				for(room in rooms)
 976					if(room.search(_path) == 0)
 977						io.sockets.in(room.substr(1, room.length)).emit('message', _data);
 978			});
 979		});
 980
 981		socket.on('message broadcast app', function (_msg, fn) {
 982			
 983			if(!allowGlobalTraffic) return;
 984
 985			var _path = '/' + socket.client + '/';
 986
 987			// save message in db
 988			PusherUserMessageSave(_msg, _path, socket, function(_data) { 
 989
 990				// broadcast to app
 991				_data.dataSystem.type = 3;
 992
 993				// get all rooms
 994				var rooms = io.sockets.manager.rooms;
 995
 996				for(room in rooms)
 997					if(room.search(_path) == 0)
 998						io.sockets.in(room.substr(1, room.length)).emit('message', _data);	
 999			});
1000		});  
1001
1002		socket.on('message broadcast channel', function (_channel, _msg, fn) {
1003			
1004			if(!allowGlobalTraffic) return;
1005
1006			var _path = '/' + socket.client + '/' + _channel + '/';
1007
1008			// save message in db
1009			PusherUserMessageSave(_msg, _path, socket, function(_data) { 
1010
1011				// broadcast to app
1012				_data.dataSystem.type = 2;
1013
1014				// get all rooms
1015				var rooms = io.sockets.manager.rooms; 
1016
1017				for(room in rooms)
1018					if(room.search(_path) == 0)
1019						io.sockets.in(room.substr(1, room.length)).emit('message', _data);
1020			});
1021		});  
1022
1023		// READ TIME OF LAST ACTIVITY IN MS FROM DB AND SEND UNREAD MESSAGES
1024
1025		socket.on('public_activity_ms', function (_period, fn) {
1026			
1027			if(!allowGlobalTraffic) return;
1028
1029			var _search_time = ' { "user_id" : "' + socket.user_id + '", "rooms./' + socket.client + '/' + socket.channel + '/' + socket.room + '/.date" : {"$exists": true} } ';
1030
1031			_search = JSON.parse(_search_time);
1032
1033			db.collection('act_' + socket.handshake.client_room).find(_search).forEach(function(err, doc) {
1034
1035				if (!doc) return;
1036
1037				var _time = new Date();
1038				socket.user_last_activity = _time.getTime() - _period;
1039
1040				var _time_info = new Date(socket.user_last_activity*1);
1041
1042				// send info
1043				io.sockets.socket(socket.id).emit('announcement', 'activity since ' + _time_info + ' on ' 
1044					+ '/' + socket.client + '/' + socket.channel + '/' + socket.room + '/');
1045
1046				// SEARCH FOR MESSAGES FOR ROOM
1047				var _search_msg = ' { "date" : { "$gt" : ' + socket.user_last_activity + ' }, "room" : "/' + socket.client + '/' + socket.channel + '/' + socket.room + '/" } ';				
1048				_search = JSON.parse(_search_msg);
1049
1050				db.collection('msg_' + socket.handshake.client_room).find(_search).forEach(function(err, doc) {
1051
1052					if (!doc) return;
1053
1054					io.sockets.socket(socket.id).emit('message', PusherMakeMessageFromDB(doc));
1055
1056					PusherAPIsActivity(socket, 1, 1, 0, 0);
1057				});
1058
1059				// SEARCH FOR MESSAGES FOR CHANNEL
1060				var _search_msg = ' { "date" : { "$gt" : ' + socket.user_last_activity + ' }, "room" : "/' + socket.client + '/' + socket.channel + '/" } ';				
1061				_search = JSON.parse(_search_msg);
1062
1063				db.collection('msg_' + socket.handshake.client_room).find(_search).forEach(function(err, doc) {
1064
1065					if (!doc) return;
1066
1067					io.sockets.socket(socket.id).emit('message', PusherMakeMessageFromDB(doc));
1068
1069					PusherAPIsActivity(socket, 1, 1, 0, 0);
1070				});
1071
1072				// SEARCH FOR MESSAGES FOR APP
1073				var _search_msg = ' { "date" : { "$gt" : ' + socket.user_last_activity + ' }, "room" : "/' + socket.client + '/" } ';				
1074				_search = JSON.parse(_search_msg);
1075
1076				db.collection('msg_' + socket.handshake.client_room).find(_search).forEach(function(err, doc) {
1077
1078					if (!doc) return;
1079
1080					io.sockets.socket(socket.id).emit('message', PusherMakeMessageFromDB(doc));
1081
1082					PusherAPIsActivity(socket, 1, 1, 0, 0);
1083				});		
1084			});
1085		});  	
1086
1087		// READ TIME OF LAST ACTIVITY FROM DB AND SEND UNREAD MESSAGES
1088
1089		socket.on('public_activity', function (nick, fn) {
1090			
1091			if(!allowGlobalTraffic) return;
1092
1093			var _search_time = ' { "user_id" : "' + socket.user_id + '", "rooms./' + socket.client + '/' + socket.channel + '/' + socket.room + '/.date" : {"$exists": true} } ';
1094
1095			search = JSON.parse(_search_time);
1096
1097			db.collection('act_' + socket.handshake.client_room).find(search).forEach(function(err, doc) {
1098
1099				if (!doc) return;
1100
1101				socket.user_last_activity = doc['rooms']['/' + socket.client + '/' + socket.channel + '/' + socket.room + '/']['date'];
1102				var _time = new Date(socket.user_last_activity*1);
1103
1104				// send info
1105				io.sockets.socket(socket.id).emit('announcement', 'last activity ' + _time + ' on ' 
1106					+ '/' + socket.client + '/' + socket.channel + '/' + socket.room + '/');
1107
1108				// SEARCH FOR MESSAGES FOR ROOM
1109				var _search_msg = ' { "date" : { "$gt" : ' + socket.user_last_activity + ' }, "room" : "/' + socket.client + '/' + socket.channel + '/' + socket.room + '/" } ';				
1110				_search = JSON.parse(_search_msg);
1111
1112				db.collection('msg_' + socket.handshake.client_room).find(_search).forEach(function(err, doc) {
1113
1114					if (!doc) return;
1115
1116					io.sockets.socket(socket.id).emit('message', PusherMakeMessageFromDB(doc));
1117
1118					PusherAPIsActivity(socket, 1, 1, 0, 0);
1119				});
1120
1121				// SEARCH FOR MESSAGES FOR CHANNEL
1122				var _search_msg = ' { "date" : { "$gt" : ' + socket.user_last_activity + ' }, "room" : "/' + socket.client + '/' + socket.channel + '/" } ';				
1123				_search = JSON.parse(_search_msg);
1124
1125				db.collection('msg_' + socket.handshake.client_room).find(_search).forEach(function(err, doc) {
1126
1127					if (!doc) return;
1128
1129					io.sockets.socket(socket.id).emit('message', PusherMakeMessageFromDB(doc));
1130
1131					PusherAPIsActivity(socket, 1, 1, 0, 0);
1132				});
1133
1134				// SEARCH FOR MESSAGES FOR APP
1135				var _search_msg = ' { "date" : { "$gt" : ' + socket.user_last_activity + ' }, "room" : "/' + socket.client + '/" } ';				
1136				_search = JSON.parse(_search_msg);
1137
1138				db.collection('msg_' + socket.handshake.client_room).find(_search).forEach(function(err, doc) {
1139
1140					if (!doc) return;
1141
1142					io.sockets.socket(socket.id).emit('message', PusherMakeMessageFromDB(doc));
1143
1144					PusherAPIsActivity(socket, 1, 1, 0, 0);
1145				});
1146			});
1147		});
1148		
1149		// SERVER CONTROLL COMMAND
1150		
1151		socket.on('system', function(data) {
1152			
1153            process.send({
1154				event:'system-command', data:data });
1155		});
1156
1157		// DISCONNECT AND SAVE TIME OF EVENT IN DB
1158
1159		socket.on('disconnect', function () {
1160
1161			// delete user from list
1162
1163			if (!socket.nickname) return;
1164
1165			PusherUserActivityUpdate(socket);
1166
1167			try {
1168
1169				// delete user_id from array    
1170				delete nicknames[socket.handshake.client_room][socket.handshake.channel][socket.handshake.user_room][socket.user_id];
1171
1172				// delete room space in array if no user exist
1173				if(Object.keys(nicknames[socket.handshake.client_room][socket.handshake.channel][socket.handshake.user_room]).length == 0)
1174					delete nicknames[socket.handshake.client_room][socket.handshake.channel][socket.handshake.user_room];
1175
1176				// delete channe; space in array if no channel exist
1177				if(Object.keys(nicknames[socket.handshake.client_room][socket.handshake.channel]).length == 0)
1178					delete nicknames[socket.handshake.client_room][socket.handshake.channel];
1179
1180				// delete channe; space in array if no channel exist
1181				if(Object.keys(nicknames[socket.handshake.client_room]).length == 0) {
1182
1183					delete nicknames[socket.handshake.client_room];
1184					delete activity[socket.handshake.client_room];
1185
1186				} else {
1187
1188					// set info in activity object - list will be send to watchers
1189					activity[socket.client].c.s = true;		
1190
1191					// update connected user in socket -> as is active-sockets
1192
1193					activity[socket.client].n.as--;						
1194				}
1195
1196			} catch(e) {
1197
1198				console.log('error on disconnect');
1199			}
1200		});
1201	});
1202
1203
1204	/*******************************************************************************
1205	* POST REQUESTS
1206	* NEED OPTIMIZATION IN VERSION 3.0
1207	* 
1208	* TO DO !!!!!!!!!!!!!!!!!!!!!!
1209	* - implement universal function to save/update for POST API INTERFACE
1210	* 
1211	******************************************************************************/
1212
1213	function PusherSendMessageFromPOST(req, res, type, callback) {
1214
1215		if(req.method == 'POST' && allowGlobalTraffic) {
1216
1217			// DATA
1218
1219			var body ='';
1220			var _time_server = new Date();
1221
1222			req.on('data', function (data) {
1223				body += data;
1224			});		
1225
1226			req.on('end', function () {
1227
1228				var _search_msg = ' { "key" : "' + req.params.k + '", "enabled" : "true" } ';
1229				_search = JSON.parse(_search_msg); 
1230
1231				db.collection('apis').find(_search).forEach(function(err, doc) {			
1232
1233					if (!doc) {
1234
1235						res.send('AUTH_ERROR');
1236						return;
1237					} else {
1238
1239						var _path = '';
1240
1241						try {
1242							_msg_object = JSON.parse(body);
1243						} 
1244						catch (e) {
1245							_msg_object = { data : body, event : 'message' }
1246						}
1247
1248						try {
1249
1250							_time_stamp = parseInt(req.params.t);
1251						} catch(e) {
1252
1253							_time_stamp = req.params.t;
1254						}					
1255
1256						var data = {
1257							dataSystem : {
1258								client:req.params.k,
1259								user_id:req.params.suid,
1260								user_name:req.params.sun, 
1261								room:req.params.r,					// becarefull here
1262								channel:req.params.c,				// becarefull here
1263								time:Math.floor(_time_server.getTime()),
1264								type:type,							// becarefull here
1265								archive:0,
1266								msg_id:0,
1267								source:1
1268							},
1269							dataUser : _msg_object.data,
1270							event : _msg_object.event
1271						}
1272
1273						if(req.params.k != '')
1274							_path = req.params.k;
1275						if(req.params.c != '')
1276							_path = _path + '/' + req.params.c;		// becarefull here
1277						if(req.params.r != '')
1278							_path = _path + '/' + req.params.r		// becarefull here
1279
1280						db.collection('msg_'+req.params.k).save({
1281							"api_key": req.params.k, 
1282							"room" : '/' + _path + '/', 
1283							"user" : {
1284								"id" : req.params.suid, 
1285								"name" : req.params.sun
1286							} , 
1287							"date": _time_server.getTime(), 
1288							"date_string": _time_server.getTime().toString(), 
1289							"message" : _msg_object,
1290							"client" : req.connection.remoteAddress + ':' + req.connection.remotePort,
1291							"source" : 1,
1292							"as" : 0
1293
1294						}, function(err, obj) {
1295
1296							data.dataSystem.msg_id = obj._id;									
1297
1298							PusherUserActivityUpdate(data.dataSystem);
1299
1300							// update API Activity Stats
1301							PusherAPIsActivity({client:req.params.k}, 1, 1, 1, 0);                        
1302
1303							callback.call(this, true, data, _path);
1304						});
1305					}
1306				});				
1307			});
1308		} else {
1309			
1310			res.send('TRAFFIC_STOPPED');
1311			return;			
1312		}
1313	}
1314
1315	// POST FOR APP/CHANNEL/ROOM
1316
1317	app.post('/api/channel/:c/room/:r/api/:k/version/:v/t/:t/suid/:suid/suname/:sun', function (req, res) {
1318
1319		// :c - destination channel
1320		// :r - destination room
1321		// :k - api key
1322		// :v - api version
1323		// :t - time stamp
1324		// :suid - sender user id
1325		// :sun - sender user name
1326
1327		// EXAMPLE:
1328		// curl -H "Content-Type: application/post" 
1329		//		-d '{"object":"helloworld"}' 
1330		//		"http://push.vm.newicon.net:80/api/channel/ch1/room/room1/api/ab3245cbeaf456244abcdfa/version/2.0/t/234324/suid/1/suname/me"
1331
1332
1333		PusherSendMessageFromPOST(req, res, 0, function(status, data, path) {
1334
1335			if(status) {
1336
1337				if(req.params.r != '') {
1338
1339					io.sockets.in(path).emit('message', data);						
1340				}
1341
1342				res.send('OK');
1343
1344			} else {
1345
1346				res.send('AUTH_ERROR');
1347			}
1348		});
1349	});
1350
1351	// POST FOR /APP/CHANNEL
1352
1353	app.post('/api/channel/:c/api/:k/version/:v/t/:t/suid/:suid/suname/:sun', function (req, res) {
1354
1355		// :c - destination channel
1356		// :r - destination room
1357		// :k - api key
1358		// :v - api version
1359		// :t - time stamp
1360		// :suid - sender user id
1361		// :sun - sender user name
1362
1363		// EXAMPLE:
1364		// curl -H "Content-Type: application/post" 
1365		//		-d '{"object":"helloworld"}' 
1366		//		"http://push.vm.newicon.net:80/api/channel/ch1/api/ab3245cbeaf456244abcdfa/version/2.0/t/234324/suid/1/suname/me"
1367
1368		if(req.method == 'POST' && allowGlobalTraffic) {
1369
1370			// DATA
1371
1372			var body ='';
1373			var _time_server = new Date();
1374
1375			req.on('data', function (data) {
1376				body += data;
1377			});		
1378
1379			req.on('end', function () {
1380
1381				var _search_msg = ' { "key" : "' + req.params.k + '", "enabled" : "true" } ';
1382				_search = JSON.parse(_search_msg);			
1383
1384				db.collection('apis').find(_search).forEach(function(err, doc) {			
1385
1386					if (!doc) {
1387
1388						res.send('AUTH_ERROR');
1389						return;
1390					} else {
1391
1392						var rooms = io.sockets.manager.rooms;
1393						var _path = '';
1394
1395						try {
1396							_msg_object = JSON.parse(body);
1397						} 
1398						catch (e) {
1399							_msg_object = { data : body, event : 'message' }
1400						}
1401
1402						try {
1403
1404							_time_stamp = parseInt(req.params.t);
1405						} catch(e) {
1406
1407							_time_stamp = req.params.t;
1408						}					
1409
1410						var data = {
1411							dataSystem : {
1412								client:req.params.k,
1413								user_id:req.params.suid,
1414								user_name:req.params.sun, 
1415								room:'', 
1416								channel:req.params.c,
1417								time:Math.floor(_time_server.getTime()),
1418								type:2,
1419								archive:0,
1420								msg_id:0,
1421								source:1
1422							},
1423							dataUser : _msg_object.data,
1424							event : _msg_object.event
1425						}
1426
1427						if(req.params.k != '')
1428							_path = req.params.k;
1429						if(req.params.c != '')
1430							_path = _path + '/' + req.params.c;
1431
1432						db.collection('msg_'+req.params.k).save({
1433
1434							"api_key": req.params.k, 
1435							"room" : '/' + _path + '/', 
1436							"user" : {
1437								"id" : req.params.suid, 
1438								"name" : req.params.sun
1439							} , 
1440							"date": _time_server.getTime(), 
1441							"date_string": _time_server.getTime().toString(), 
1442							"message" : _msg_object,
1443							"client" : req.connection.remoteAddress + ':' + req.connection.remotePort,
1444							"source" : 1,
1445							"as" : 0
1446
1447						}, function(err, obj) {
1448
1449							data.dataSystem.msg_id = obj._id;
1450
1451							PusherUserActivityUpdate(data.dataSystem);
1452
1453							if(req.params.c != '') {
1454
1455								for(room in rooms) {
1456
1457									if(room.search('/'+_path + '/') == 0) {
1458										io.sockets.in(room.substr(1, room.length)).emit('message', data);		
1459									}
1460								}
1461							}
1462
1463							// update API Activity Stats
1464							PusherAPIsActivity({client:req.params.k}, 1, 1, 1, 0);                        
1465
1466							res.send('OK');
1467							return;						
1468						});
1469					}
1470				});				
1471			});
1472		} else {
1473			
1474			res.send('TRAFFIC_STOPPED');
1475			return;			
1476		}
1477	});
1478
1479	// POST FOR APP/
1480
1481	app.post('/api/api/:k/version/:v/t/:t/suid/:suid/suname/:sun', function (req, res) {
1482
1483		// :c - destination channel
1484		// :r - destination room
1485		// :k - api key
1486		// :v - api version
1487		// :t - time stamp
1488		// :suid - sender user id
1489		// :sun - sender user name
1490
1491		// EXAMPLE:
1492		// curl -H "Content-Type: application/post" 
1493		//		-d '{"object":"helloworld"}' 
1494		//		"http://push.vm.newicon.net:80/api/api/ab3245cbeaf456244abcdfa/version/2.0/t/234324/suid/1/suname/me"
1495
1496		if(req.method == 'POST' && allowGlobalTraffic) {
1497
1498			// DATA
1499
1500			var body ='';
1501			var _time_server = new Date();
1502
1503			req.on('data', function (data) {
1504				body += data;
1505			});		
1506
1507			req.on('end', function () {
1508
1509				var _search_msg = ' { "key" : "' + req.params.k + '", "enabled" : "true" } ';
1510				_search = JSON.parse(_search_msg);			
1511
1512				db.collection('apis').find(_search).forEach(function(err, doc) {			
1513
1514					if (!doc) {
1515
1516						res.send('AUTH_ERROR');
1517						return;
1518					} else {
1519
1520						var rooms = io.sockets.manager.rooms;
1521						var _path = '';
1522
1523						try {
1524							_msg_object = JSON.parse(body);
1525						} 
1526						catch (e) {
1527							_msg_object = { data : body, event : 'message' }
1528						}
1529
1530						try {
1531
1532							_time_stamp = parseInt(req.params.t);
1533						} catch(e) {
1534
1535							_time_stamp = req.params.t;
1536						}					
1537
1538						var data = {
1539							dataSystem : {
1540								client:req.params.k,
1541								user_id:req.params.suid,
1542								user_name:req.params.sun, 
1543								room:'', 
1544								channel:'',
1545								time:Math.floor(_time_server.getTime()),
1546								type:3,
1547								archive:0,
1548								msg_id:0,
1549								source:1
1550							},
1551							dataUser : _msg_object.data,
1552							event : _msg_object.event
1553						}
1554
1555						if(req.params.k != '')
1556							_path = req.params.k;
1557
1558						db.collection('msg_'+req.params.k).save({
1559							"api_key": req.params.k, 
1560							"room" : '/' + _path + '/', 
1561							"user" : {
1562								"id" : req.params.suid, 
1563								"name" : req.params.sun
1564							} , 
1565							"date": _time_server.getTime(), 
1566							"date_string": _time_server.getTime().toString(), 
1567							"message" : _msg_object,
1568							"client" : req.connection.remoteAddress + ':' + req.connection.remotePort,
1569							"source" : 1,
1570							"as" : 0
1571
1572						}, function(err, obj) {
1573
1574							data.dataSystem.msg_id = obj._id;	
1575
1576							PusherUserActivityUpdate(data.dataSystem);
1577
1578							if(req.params.k != '') {
1579
1580								for(room in rooms) {
1581
1582									if(room.search('/'+_path + '/') == 0)
1583									{
1584										io.sockets.in(room.substr(1, room.length)).emit('message', data);
1585									}
1586								}					
1587							}
1588
1589							// update API Activity Stats
1590							PusherAPIsActivity({client:req.params.k}, 1, 1, 1, 0);
1591
1592							res.send('OK');
1593							return;
1594						});
1595					}
1596				});				
1597			});
1598		} else {
1599			
1600			res.send('TRAFFIC_STOPPED');
1601			return;			
1602		}
1603	});
1604
1605	/**
1606	*	Helpers function 
1607	* 
1608	*	PusherRoundNumber
1609	*	@param float num
1610	*	@param int decimal
1611	*/
1612
1613	function PusherRoundNumber(num, dec) {
1614
1615		return Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
1616	}
1617	
1618	function PusherFindInArray(_array, _value) {
1619		
1620		for(_x in _array) {
1621			
1622			if(_value == _array[_x]) return true;
1623		}
1624		
1625		return false;
1626	}
1627	
1628// end of cluster worker
1629}