UNPKG

32.2 kBJavaScriptView Raw
1#!/usr/bin/env node
2/*
3
4Fast Loop-Server reader - runs an http server which accepts requests like the PHP software, and quickly reads the results ready for display on-screen.
5To be used in the most common cases only (i.e. it doesn't handle bulk message downloads etc.)
6
7The loop-server config.json contains the settings for this server.
8Usage: node loop-server-fast.js config/path/config.json [-production]
9
10
11Testing https connection:
12openssl s_client -CApath /etc/ssl/certs -connect yourdomain.com:5566
13
14
15Possible future project:
16Implement a mysql connection pool ala:
17https://codeforgeek.com/2015/01/nodejs-mysql-tutorial/
18
19However, this may not be necessary - we already have multi-core handled by pm2, and we are one connection per
20database, but the requests are async.
21
22*/
23
24
25
26var multiparty = require('multiparty');
27var http = require('http');
28var https = require('https');
29var util = require('util');
30var path = require("path");
31require("date-format-lite");
32var mv = require('mv');
33var fs = require('fs');
34var exec = require('child_process').exec;
35var fsExtra = require('fs-extra');
36var request = require("request");
37var needle = require('needle');
38var readChunk = require('read-chunk'); // npm install read-chunk
39var async = require('async');
40var mysql = require('mysql');
41var os = require('os');
42const querystring = require('querystring');
43var crypto = require('crypto');
44var get_ip = require('ipware')().get_ip;
45
46
47var httpsFlag = false; //whether we are serving up https (= true) or http (= false)
48var serverOptions = {}; //default https server options (see nodejs https module)
49var listenPort = 3277; //default listen port. Will get from the config readPort if it is set there
50var msg = {};
51var lang;
52var timeUnits; //time units
53var agoDefault; //Usually in English set to 'ago' - the word displayed after the time units
54var verbose = false;
55var currentDbServer = [];
56currentDbServer[0] = 0;
57var usage = "Usage: node loop-server-fast.js config/path/config.json config/path/messages.json [-production]\n\nOr:\n\nnpm config set loop-server-fast:configFile /path/to/your/loop/server/config.json\nnpm config set loop-server-fast:messagesFile /path/to/your/loop/server/messages.json\n[npm config set loop-server-fast:production true]\nnpm run start\n\n";
58var defaultPHPScript = "search-chat.php?";
59var defaultPHPScriptLen = defaultPHPScript.length;
60
61
62process.on('SIGINT', function() {
63 //Cleanly handle a process kill
64 console.log("Requesting a shutdown.");
65 closeAllConnections();
66 setTimeout(function() {
67 // 300ms later the process kill it self to allow a restart
68 console.log("Clean exit.");
69 process.exit(0);
70 }, 300);
71});
72
73
74
75if((process.argv)&&(process.argv[2])){
76 var loopServerConfig = process.argv[2];
77} else {
78 if(process.env.npm_package_config_configFile) {
79 var loopServerConfig = process.env.npm_package_config_configFile;
80 } else {
81
82 console.log(usage);
83 process.exit(0);
84 }
85}
86
87
88
89
90var config = JSON.parse(fs.readFileSync(loopServerConfig));
91if(!config) {
92 console.log("Couldn't find config file " + loopServerConfig);
93 process.exit(0);
94}
95
96
97if(((process.argv)&&(process.argv[3]))||(process.env.npm_package_config_messagesFile)){
98 //Get the messages and ago constants
99 if(process.argv[3]) {
100 var loopServerMessages = process.argv[3];
101 } else {
102 //Get from the npm config
103 var loopServerMessages = process.env.npm_package_config_messagesFile;
104 }
105 msg = JSON.parse(fs.readFileSync(loopServerMessages));
106 if(!msg) {
107 console.log("Couldn't find messages file " + loopServerMessages);
108 process.exit(0);
109 }
110 lang = msg.defaultLanguage;
111
112
113 var time = msg.msgs[lang].time;
114 timeUnits = [
115 { name: time.second, plural: time.seconds, limit: 60, in_seconds: 1 },
116 { name: time.minute, plural: time.minutes, limit: 3600, in_seconds: 60 },
117 { name: time.hour, plural: time.hours, limit: 86400, in_seconds: 3600 },
118 { name: time.day, plural: time.days, limit: 604800, in_seconds: 86400 },
119 { name: time.week, plural: time.weeks, limit: 2629743, in_seconds: 604800 },
120 { name: time.month, plural: time.months, limit: 31556926, in_seconds: 2629743 },
121 { name: time.year, plural: time.years, limit: null, in_seconds: 31556926 }
122 ];
123
124 agoDefault = time.ago;
125
126
127} else {
128 console.log(usage);
129 process.exit(0);
130}
131
132
133if((process.argv[4]) && (process.argv[4] == '-production')){
134 var cnf = config.production;
135} else {
136 if(process.env.npm_package_config_production) {
137 if(process.env.npm_package_config_production == 'true') {
138 var cnf = config.production;
139 } else {
140 var cnf = config.staging;
141 }
142
143 } else {
144 var cnf = config.staging;
145 }
146}
147
148//Configurable verbose variable
149if(process.env.npm_package_config_verbose) {
150 if(process.env.npm_package_config_verbose == 'true') {
151 verbose = true;
152 } else {
153 verbose = false;
154 }
155
156}
157
158//Use the port specified in the config
159if(cnf.readPort) {
160 listenPort = cnf.readPort;
161}
162
163//Create an https server if we specify a key and cert file
164if(cnf.httpsKey) {
165 //httpsKey should point to the key .pem file
166 httpsFlag = true;
167 if(!serverOptions.key) {
168 serverOptions.key = fs.readFileSync(cnf.httpsKey);
169 console.log("https key loaded");
170 }
171 }
172
173 if(cnf.httpsCert) {
174 //httpsCert should point to the cert .pem file
175 httpsFlag = true;
176 if(!serverOptions.cert) {
177 serverOptions.cert = fs.readFileSync(cnf.httpsCert);
178 console.log("https cert loaded");
179 }
180
181 }
182
183 var connections = [];
184
185 var closing = false; //This is a global shutting down from mysql flag
186 function closeAllConnections() {
187 if(closing == false) { //Only do this once
188 for(var ccnt = 0; ccnt< cnf.db.hosts.length; ccnt++) {
189 if(connections[0][ccnt]) {
190 connections[0][ccnt].end();
191 }
192 }
193
194 if(cnf.db.scaleUp) {
195 for(var scaleCnt = 0; scaleCnt< cnf.db.scaleUp.length; scaleCnt++) {
196 for(var ccnt = 0; ccnt< cnf.db.scaleUp[scaleCnt].hosts.length; ccnt++) {
197 if(connections[scaleCnt+1][ccnt]) {
198 connections[scaleCnt+1][ccnt].end();
199 }
200 }
201
202 }
203 }
204 }
205
206 }
207
208
209 function handleDisconnect() {
210
211 //Check for a different database
212 /*
213 if((isset($db_cnf['scaleUp']))&&(isset($layer_name))) {
214 //We are scaling up
215 for($cnt = 0; $cnt< count($db_cnf['scaleUp']); $cnt ++) {
216 if(preg_match($db_cnf['scaleUp'][$cnt]['labelRegExp'],$layer_name, $matches) == true) {
217 //Override with this database
218 $db_cnf = $db_cnf['scaleUp'][$cnt];
219 return;
220 }
221
222 }
223 }
224 */
225
226
227
228
229 //Reconnect to all db hosts
230 console.log("Connecting to the database.");
231 connections[0] = {};
232 for(var cnt = 0; cnt< cnf.db.hosts.length; cnt++) {
233
234 if(cnf.db.ssl && cnf.db.ssl.use === true) {
235 var ssl = {
236 ca : fs.readFileSync(cnf.db.ssl.capath)
237 };
238 } else {
239 var ssl = null;
240 }
241
242 connections[0][cnt] = mysql.createConnection({
243 host : cnf.db.hosts[cnt],
244 user : cnf.db.user,
245 password : cnf.db.pass,
246 database : cnf.db.name,
247 port : cnf.db.port,
248 ssl : ssl
249 });
250
251 //connections[cnt].connect();
252 connections[0][cnt].connect(function(err) { // The server is either down
253 if(err) { // or restarting (takes a while sometimes).
254 //Error on trying to connect - try again in 2 seconds
255 console.log('error when connecting to db:', err);
256 closeAllConnections();
257
258 if(closing == false) {
259 closing = true;
260 setTimeout(handleDisconnect, 2000); // We introduce a delay before attempting to reconnect,
261 }
262
263 } // to avoid a hot loop, and to allow our node script to
264 }); // process asynchronous requests in the meantime.
265 // If you're also serving http, display a 503 error.
266 connections[0][cnt].on('error', function(err) {
267 console.log('db error: ', err);
268
269 closeAllConnections();
270
271
272 if(err.code === 'PROTOCOL_CONNECTION_LOST') { // Connection to the MySQL server is usually
273 //Close and restart all the connections
274
275
276 if(closing == false) {
277 closing = true;
278 setTimeout(handleDisconnect, 2000); // lost due to either server restart, or a
279 }
280 } else { // connnection idle timeout (the wait_timeout
281 //throw err; // server variable configures this)
282 //closeAllConnections();
283 if(closing == false) {
284 closing = true;
285 setTimeout(handleDisconnect, 2000);
286 }
287
288 }
289 });
290
291
292 }
293
294 if(cnf.db.scaleUp) {
295 //Create more connections
296 for(var scaleCnt = 0; scaleCnt< cnf.db.scaleUp.length; scaleCnt++) {
297
298 connections[scaleCnt+1] = [];
299 currentDbServer[scaleCnt+1] = 0;
300 dbCnf = cnf.db.scaleUp[scaleCnt];
301
302 if(dbCnf.ssl && dbCnf.ssl.use === true) {
303 var ssl = {
304 ca : fs.readFileSync(dbCnf.ssl.capath)
305 };
306 } else {
307 var ssl = null;
308 }
309
310
311 for(var cnt = 0; cnt< dbCnf.hosts.length; cnt++) {
312
313 connections[scaleCnt+1][cnt] = mysql.createConnection({
314 host : dbCnf.hosts[cnt],
315 user : dbCnf.user,
316 password : dbCnf.pass,
317 database : dbCnf.name,
318 port : dbCnf.port,
319 ssl : ssl
320 });
321
322 //connections[cnt].connect();
323 connections[scaleCnt+1][cnt].connect(function(err) { // The server is either down
324 if(err) { // or restarting (takes a while sometimes).
325 console.log('error when connecting to db:', err);
326 closeAllConnections();
327
328 if(closing == false) {
329 closing = true;
330 setTimeout(handleDisconnect, 2000); // We introduce a delay before attempting to reconnect,
331 }
332 } // to avoid a hot loop, and to allow our node script to
333 }); // process asynchronous requests in the meantime.
334 // If you're also serving http, display a 503 error.
335 connections[scaleCnt+1][cnt].on('error', function(err) {
336 console.log('db error: ', err);
337
338 closeAllConnections();
339
340
341 if(err.code === 'PROTOCOL_CONNECTION_LOST') { // Connection to the MySQL server is usually
342 //Close and restart all the connections
343 //closeAllConnections();
344
345 if(closing == false) {
346 closing = true;
347 setTimeout(handleDisconnect, 2000); // lost due to either server restart, or a
348 }
349 } else { // connnection idle timeout (the wait_timeout
350 //throw err; // server variable configures this)
351
352 if(closing == false) {
353 closing = true;
354 setTimeout(handleDisconnect, 2000);
355 }
356 }
357
358
359 });
360
361
362 }
363 }
364
365 }
366
367 if(closing == true) {
368 //Have finished getting db connections
369 console.log("Finished connecting to the database.");
370 closing = false;
371 }
372
373
374}
375
376handleDisconnect();
377
378function escapeRegExp(str) {
379 return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
380}
381
382
383function cleanData(str)
384{
385 //Clean for database requests
386 return escapeRegExp(str);
387}
388
389
390function trimChar(string, charToRemove) {
391 if(string) {
392 while(string.substring(0,1) == charToRemove) {
393 string = string.substring(1);
394 }
395
396 while(string.slice(-1) == charToRemove) {
397 string = string.slice(0, -1);
398 }
399
400 return string;
401 } else {
402 return null;
403 }
404}
405
406
407
408
409function readSession(params, cb)
410{
411
412 /*
413
414 Sample session record
415 | sgo3vosp1ej150sln9cvdslqm0 | 736 | 2016-06-09 16:04:03 | 2016-06-26 16:40:54 | view-count|i:1;logged-user|i:736;user-ip|s:15:"128.199.221.111";layer-group-user|s:0:"";authenticated-layer|s:3:"181";temp-user-name|s:7:"Anon 11";lat|i:51;lon|i:0;
416 */
417 var keyValues = {};
418
419 if(verbose == true) console.log("SessionID" + cleanData(params.sessionId));
420
421 if(!params.connection) {
422 //No connection
423 cb(null);
424 return;
425
426 }
427
428 params.connection.query("SELECT * FROM php_session WHERE session_id='" + cleanData(params.sessionId) + "'", function(err, rows, fields) {
429
430 if (err) {
431 console.log("Database error:" + err);
432 cb(null);
433 } else {
434
435
436 if((rows[0])&&(rows[0].session_data)) {
437
438 if(verbose == true) console.log("Session data:" + rows[0].session_data);
439 var params = rows[0].session_data.split(";");
440 for(var cnt=0; cnt< params.length; cnt++) {
441
442 var paramData = params[cnt].split("|");
443 if(paramData[1]) {
444 //There is some data about this param
445 var paramValues = paramData[1].split(":");
446 if(paramValues[0] == 'i') {
447 //An integer - value proceeds
448 var paramValue = paramValues[1];
449 } else {
450 //A string, [1] is the string length, [2] is the string itself
451 var paramValue = trimChar(paramValues[2], '"');
452 }
453
454 keyValues[paramData[0]] = paramValue;
455 if(verbose == true) console.log("Key:" + paramData[0] + " = " + paramValue);
456 }
457 }
458 }
459
460 cb(keyValues);
461 }
462 });
463
464}
465
466
467
468function httpHttpsCreateServer(options) {
469 if(httpsFlag == true) {
470 console.log("Starting https server.");
471 https.createServer(options, handleServer).listen(listenPort);
472
473
474 } else {
475 console.log("Starting http server.");
476 http.createServer(handleServer).listen(listenPort);
477 }
478
479
480
481}
482
483
484function determineSubdomain(req) {
485
486 if(req) {
487 //Handle any subdomains in our
488 if(verbose == true) console.log("Requesting: " + req.headers.host);
489
490 //req.headers.host = eg. "outer.fast.atomjump.com"
491
492 //"readURL" : "https://[subdomain]fast.atomjump.com",
493 var myReadURL = cnf.readURL.replace("https://", "");
494 myReadURL = myReadURL.replace("http://","");
495
496 //Now myReadURL = e.g. "[subdomain]fast.atomjump.com"
497 //req.headers.host = eg. "outer.fast.atomjump.com"
498 //We want to find "outer".
499 var noSubReadURL = myReadURL.replace("[subdomain]",""); //becomes e.g. .fast.atomjump.com
500
501 var subdomain = req.headers.host.replace(noSubReadURL,""); //Strip off the non-subdomain url. Becomes outer. If we are at the same host e.g. atomjump.com, then the subdomain will be a blank string.
502
503 if(verbose == true) console.log("Subdomain: " + subdomain);
504
505
506 return subdomain;
507 } else {
508 return "";
509 }
510
511}
512
513
514
515
516function handleServer(_req, _res) {
517
518 var req = _req;
519 var res = _res;
520 var body = [];
521
522 var subdomain = determineSubdomain(req);
523
524 //Start ordinary error handling
525 req.on('error', function(err) {
526 // This prints the error message and stack trace to `stderr`.
527 console.error(err.stack);
528
529 res.statusCode = 400; //Error during transmission - tell the app about it
530 res.end();
531 });
532
533 req.on('data', function(chunk) {
534 body.push(chunk);
535 });
536
537 req.on('end', function() {
538
539
540 //A get request to pull from the server
541
542
543
544 // show a file upload form
545 if(verbose == true) console.log("Requesting: " + req.url);
546
547 //It must include search-chat.php or it is ignored.
548 if(req.url.indexOf(defaultPHPScript) < 0) {
549 res.writeHead(200, {'content-type': 'text/html'});
550 res.end("Not available");
551 return;
552 }
553
554 var url = req.url.substring(req.url.indexOf(defaultPHPScript) + defaultPHPScriptLen); //16 is length of 'search-chat.php'. We want the url as all the params after this. search-chat.php
555 //is the current standard request entry point
556
557 if(verbose == true) console.log("Parsed to query string:" + url);
558 var params = querystring.parse(url);
559 if(verbose == true) console.log("Query params = " + JSON.stringify(params));
560
561 var cookies = parseCookies(req);
562 if(!params.sessionId) { //Only if not passed in on the command line
563 params.sessionId = cookies.PHPSESSID; //This is our custom cookie.
564 }
565 if(!params.sessionId) {
566 res.statusCode = 400; //Error - tell the app about it
567 res.end();
568 console.log("Error: You must include a PHP Session ID");
569 return;
570
571 }
572
573 if(params.subdomain) { //Override the subdomain
574 subdomain = params.subdomain;
575 }
576
577 var mylang = null;
578 if(cookies.lang) {
579 //If we are set in the cookies
580 mylang = cookies.lang;
581 }
582 if(params.lang) {
583 //Or set by a passed in parameter
584 mylang = params.lang;
585 }
586 if(mylang) {
587 //A language modifier exists
588 var time = msg.msgs[mylang].time;
589 params.timeUnits = [
590 { name: time.second, plural: time.seconds, limit: 60, in_seconds: 1 },
591 { name: time.minute, plural: time.minutes, limit: 3600, in_seconds: 60 },
592 { name: time.hour, plural: time.hours, limit: 86400, in_seconds: 3600 },
593 { name: time.day, plural: time.days, limit: 604800, in_seconds: 86400 },
594 { name: time.week, plural: time.weeks, limit: 2629743, in_seconds: 604800 },
595 { name: time.month, plural: time.months, limit: 31556926, in_seconds: 2629743 },
596 { name: time.year, plural: time.years, limit: null, in_seconds: 31556926 }
597 ];
598
599 params.ago = time.ago;
600
601 if(msg.msgs[mylang].numbers) {
602 params.numbersTranslation = msg.msgs[mylang].numbers; //Get the number translation table for this language
603 }
604
605 }
606
607 params.ip = getFakeIpAddress(params.sessionId);
608
609 //Choose a random db connection
610 params.connection = connections[0][currentDbServer[0]];
611
612 //Round robin the connection
613 currentDbServer[0] ++;
614 if(currentDbServer[0] >= cnf.db.hosts.length) currentDbServer[0] = 0;
615
616 //Double up on this
617 var myres = res;
618
619 var jsonData = searchProcess(params, function(err, data) {
620 if(err) {
621 if(err == 'PHP') {
622 //Call the PHP version of this script
623
624 //Replace any 'fast' subdomains for the PHP request
625 if(subdomain) {
626 var replaceWith = subdomain + ".";
627 } else {
628 var replaceWith = "";
629 }
630 var webRoot = cnf.webRoot.replace("[subdomain]", replaceWith); //strip out any subdomain codes from the url
631
632 var fullUrl = webRoot + '/' + defaultPHPScript + url;
633 if(verbose == true) console.log("Webroot:" + cnf.webRoot + " Default PHP script:" + defaultPHPScript + " Url:" + url + " fullUrl:" + fullUrl);
634 callPHP(fullUrl, myres);
635 return;
636 }
637
638 console.log(err);
639 res.statusCode = 400;
640 res.end();
641 }
642
643 //Prepare the result set for the jsonp result
644 var strData = params.callback + "(" + JSON.stringify( data ) + ")";
645
646 res.on('error', function(err){
647 //Handle the errors here
648 res.statusCode = 400;
649 res.end();
650 })
651
652 res.writeHead(200, {'content-type': 'text/html'});
653
654
655 res.end(strData, function(err) {
656 //Wait until finished sending, then delete locally
657 if(err) {
658 console.log(err);
659 } else {
660 //success, do nothing
661 process.stdout.write("."); //Show successful pings
662 }
663 });
664 }); //End of process
665
666 }); //End of req end
667
668}
669
670
671function parseCookies (request) {
672 var list = {},
673 rc = request.headers.cookie;
674
675 rc && rc.split(';').forEach(function( cookie ) {
676 var parts = cookie.split('=');
677 list[parts.shift().trim()] = decodeURI(parts.join('='));
678 });
679
680 return list;
681}
682
683
684function showTranslatedNumber(number, translationTable) {
685 /* PHP Function this is based on
686 function show_translated_number($number, $lang)
687 {
688 //Input, ideally an integer between 0 - 60 (60 seconds in a minute means this
689 //is usually the largest number we need for most things, i.e. 59 seconds ago).
690 //However, any number can be used and the default is to return the English number
691 //if there is no matching number in the messages array "number" conversion for the
692 //input language.
693 global $msg;
694
695 if($msg['msgs'][$lang]['numbers']) {
696 //Yes the numbers array exists for this language
697 //Check definitely an integer
698 if(is_int($number)) {
699 if($msg['msgs'][$lang]['numbers'][$number]) {
700 //Return the string if this is in the 'number' array as the indexed version of that number
701 return $msg['msgs'][$lang]['numbers'][$number];
702 } else {
703 return $number;
704 }
705 } else {
706 return $number;
707 }
708 } else {
709 return $number;
710 }
711
712
713 }
714 */
715
716 if(translationTable) {
717 if(Number.isInteger(number)) {
718 if(translationTable[number]) {
719 return translationTable[number];
720 } else {
721 return number;
722 }
723 } else {
724 return number;
725 }
726 } else {
727 return number;
728 }
729}
730
731
732
733function ago(timeStr, thisTimeUnits, agoWord, numbersTranslation) {
734
735
736
737
738 var recTime = new Date(timeStr);
739 var recTimeSecs = recTime.getTime();
740 //Get diff in seconds
741
742 var nowSecs = new Date().getTime();
743 var diff = (nowSecs - recTimeSecs) / 1000;
744
745 var i = 0;
746 var unit = {};
747
748 while (unit = thisTimeUnits[i]) {
749 if ((diff < unit.limit) || (!unit.limit)){
750 var diff = Math.round(diff / unit.in_seconds); //was floor
751 diff = showTranslatedNumber(diff, numbersTranslation);
752 var timeOut = diff + " " + (diff>1 ? unit.plural : unit.name) + " " + agoWord;
753 return timeOut;
754 }
755 i++;
756 }
757
758 return "Unknown";
759
760
761
762}
763
764function md5(data) {
765
766 return crypto.createHash('md5').update(data).digest("hex");
767}
768
769
770function getFakeIpAddress(sessionId) {
771 //This is a copy of the PHP version - it creates an ip with 192.a.b.c where
772 //a = ASCII value of sessionId's first character
773 //b = ASCII value of sessionId's second character
774 //c = ASCII value of sessionId's third character
775
776 if(sessionId) {
777 var ip = "192." + parseInt(sessionId.charCodeAt(0)) + "." +
778 parseInt(sessionId.charCodeAt(1)) + "." +
779 parseInt(sessionId.charCodeAt(2));
780 return ip;
781 } else {
782 //OK, so we don't have a session id - use a fixed random ip
783 return "192.168.10.10";
784 }
785}
786
787function getRealIpAddress(req) {
788
789 var ip_info = get_ip(req);
790
791 return ip_info.clientIp.replace(/^[0-9.,]+$/,"");
792}
793
794
795
796
797
798function callPHP(url, res) {
799 //Reads in from the PHP script url for a .jsonp response (plain text)
800 //and write it out to the requester
801 if(verbose == true) console.log("Redirecting to " + url);
802
803 res.writeHead(302, {
804 'Location': url
805 //add other headers here...
806 });
807 res.end();
808
809
810}
811
812
813
814function foundLayer(params,
815 session,
816 layer,
817 ip,
818 userCheck,
819 initialRecords,
820 outputJSON,
821 debug,
822 cb) {
823
824 if((params.units) && (params.units != '')) {
825 units = params.units;
826 }
827
828 if((params.dbg) && (params.dbg == 'true')) {
829 debug = true;
830 } else {
831 debug = false;
832 }
833
834 if(session['logged-user']) {
835 userCheck = " OR int_author_id = " + session['logged-user'] + " OR int_whisper_to_id = " + session['logged-user'];
836
837 }
838
839 if(session['logged-group-user']) {
840 userCheck = userCheck + " OR int_author_id = " + session['logged-group-user'] + " OR int_whisper_to_id = " + session['logged-group-user'];
841
842 }
843
844 if((params.records) && (params.records < 100)) {
845 initialRecords = 100; //min this can be - needs to be about 4 to 1 of private to public to start reducing the number of public messages visible
846 } else {
847 if(params.records) {
848 initialRecords = params.records;
849 }
850 }
851
852
853
854
855
856
857
858
859 var sql = "SELECT * FROM tbl_ssshout WHERE int_layer_id = " + layer + " AND enm_active = 'true' AND (var_whisper_to = '' OR ISNULL(var_whisper_to) OR var_whisper_to ='" + ip + "' OR var_ip = '" + ip + "' " + userCheck + ") ORDER BY date_when_shouted DESC, int_ssshout_id DESC LIMIT " + initialRecords;
860 if(verbose == true) console.log("Query: " + sql);
861 if(!params.connection) { //check the connection is still valid
862 //No connection
863 cb(err, null);
864 return;
865 }
866
867 params.connection.query(sql, function(err, rows, fields) {
868
869
870 if (err) {
871
872 console.log("Database query error:" + err);
873 cb(err, null);
874 return;
875 }
876
877
878 outputJSON.res = [];
879 outputJSON.ses = params.sessionId;
880
881
882 var mymaxResults = rows.length;
883 if(mymaxResults > params.records) {
884 mymaxResults = params.records; //limit number to records requested
885 var more = true; //show more flag for
886 }
887 var actualCnt = 0;
888
889 //Exit early with no results on no layer access
890 if((session['access-layer-granted'])&&(session['access-layer-granted'] !== "true")
891 &&(session['access-layer-granted'] != true)) {
892 if((session['access-layer-granted'] == 'false') || (session['access-layer-granted'] != layer)) {
893
894 //See if we are in the array of layers, ignore this
895 if((session['access-layers-granted'])&&(Array.isArray(session['access-layers-granted']))) {
896 //Check if the layer is in, if not, then exit
897 var granted = session['access-layers-granted'];
898 if(granted.includes(layer)) {
899 //No action needed
900 } else {
901
902 outputJSON.res = []; //No results
903 cb(null, outputJSON); //No errors
904 return;
905 }
906
907 } else {
908
909 outputJSON.res = []; //No results
910 cb(null, outputJSON); //No errors
911 return;
912 }
913 }
914 }
915
916 //Time unit override
917 var thisTimeUnits = timeUnits; //The default language
918 var thisAgo = agoDefault;
919 if(params.timeUnits) {
920 //An override, which was set by the cookie 'lang'
921 thisTimeUnits = params.timeUnits;
922 thisAgo = params.ago;
923 }
924 if(params.numbersTranslation) {
925 var numbersTranslation = params.numbersTranslation;
926
927 } else {
928 numbersTranslation = null;
929 }
930
931
932 for(var cnt = 0; cnt< rows.length; cnt++) {
933
934 var whisper = true; //default
935 var authorIP = rows[cnt].var_ip;
936 var authorUserID = rows[cnt].int_author_id;
937 var combinedAuthor = authorIP;
938 if(authorUserID) {
939 combinedAuthor = combinedAuthor + ":" + authorUserID;
940 }
941
942 var whisperToIP = rows[cnt].var_whisper_to;
943 var whisperToUserID = rows[cnt].int_whisper_to_id;
944
945
946
947 //If no whispering, or are whispering but to the viewer's ip address, or from the viewer's own ip
948 if((whisperToIP == '')|| //ie. public
949 ((whisperToIP == ip)&&(whisperToUserID == null))|| //private but no user id known
950 (whisperToUserID == session['logged-user'])|| //talk direct to owner
951 ((authorIP == ip)&&(authorUserID == null))|| //authored by this ip no user known of author
952 (authorUserID == session['logged-user'])|| //authored by this viewer
953 ((session['logged-group-user'] != "")&&(whisperToUserID != "") && (whisperToUserID == session['logged-group-user']))) { //private message to group
954
955 //Right actually going to include message - now decide if whispering or public
956
957 if(((whisperToIP == ip) && (whisperToUserID == null))|| //if it was a whisper intended for our ip but unknown user
958 (whisperToUserID == session['logged-user'])|| //or a whisper specifically for us
959 ((authorIP == ip) && ((whisperToIP != '')||(whisperToUserID)))|| //or def a whisper by viewer
960 (authorUserID == session['logged-user'] && ((whisperToIP != '')|| (whisperToUserID)))) { //or a whisper by viewer logged in
961 //This is a whisper to you or from you, use 1/3 font size
962 whisper = true;
963 } else {
964 //A shout
965 whisper = false;
966 }
967
968 if(session['logged-group-user']) {
969 if(whisperToUserID == session['logged-group-user']) {
970 whisper = true;
971
972 }
973 }
974
975 if(!session['logged-user']) {
976 //Force a blank user to see only public requests, until he has actually commented.
977 whisper = false;
978
979 }
980
981
982
983 var shade = rows[cnt].int_ssshout_id %2;
984 if(layer == 0) {
985 //Public layer
986 if(shade == 0) {
987 bgcolor = "public-light";
988 } else {
989 bgcolor = "public-dark";
990 }
991 } else {
992 //Private layer - different colours
993 if(shade == 0) {
994 bgcolor = "private-light";
995 } else {
996 bgcolor = "private-dark";
997 }
998
999 }
1000
1001 //In PHP here? date_default_timezone_set($server_timezone); //E.g. "UTC" GMT"
1002
1003
1004 if(actualCnt <= mymaxResults) {
1005
1006
1007
1008
1009 var newEntry = {
1010 'id': rows[cnt].int_ssshout_id,
1011 'text': rows[cnt].var_shouted_processed,
1012 'lat': rows[cnt].latitude,
1013 'lon': rows[cnt].longtiude,
1014 'dist': rows[cnt].dist,
1015 'ago': ago(rows[cnt].date_when_shouted, thisTimeUnits, thisAgo, numbersTranslation),
1016 'whisper': whisper
1017
1018 }
1019
1020 outputJSON.res.push(newEntry);
1021
1022 actualCnt ++; //Increment the actual result count
1023 }
1024
1025
1026 } //End of valid message
1027
1028 } //End of for loop
1029
1030 cb(null, outputJSON); //No errors
1031
1032
1033 }); //End of query
1034
1035
1036}
1037
1038
1039function checkScaleupHorizontally(layerName, params) {
1040
1041 if(cnf.db.scaleUp) {
1042 //Create more connections
1043 for(var scaleCnt = 0; scaleCnt< cnf.db.scaleUp.length; scaleCnt++) {
1044
1045 var regExp = new RegExp(cnf.db.scaleUp[scaleCnt].labelRegExp);
1046 if(layerName.search(regExp) >= 0) {
1047 //OK switch over to this db connection
1048 //Choose a random db connection
1049 params.connection = connections[scaleCnt+1][currentDbServer[scaleCnt+1]];
1050
1051 //Round robin the connection
1052 currentDbServer[scaleCnt+1] ++;
1053 if(currentDbServer[scaleCnt+1] >= cnf.db.scaleUp[scaleCnt].hosts.length) currentDbServer[scaleCnt+1] = 0;
1054 return;
1055 }
1056 }
1057 }
1058
1059 return;
1060}
1061
1062
1063
1064function searchProcess(params, cb) {
1065
1066 //Get the session data
1067 checkScaleupHorizontally(params.passcode, params); //This should be before the 'readSession' because the session comes from this horiontal database also.
1068
1069
1070 readSession(params, function(session) { //eg. 'sgo3vosp1ej150sln9cvdslqm0'
1071 if(verbose == true) console.log("Finished getting session data. Logged user:" + session['logged-user']);
1072
1073
1074
1075
1076 if((session)&&(session['logged-user'])&&(session['logged-user'] != '')) {
1077 //Already logged in, but check if we know the ip address
1078 if((!session['user-ip'])||(session['user-ip'] == '')) {
1079 //No ip. Will have to revert back to the PHP version
1080 console.log('No ip. Going to PHP');
1081 cb("PHP", null);
1082 return;
1083 } else {
1084
1085 //We're good to make a db request
1086
1087 //If this is the first request this session, we need to use the PHP
1088 //version to ensure we have registered the count
1089 if(session['view-count'] == 0) {
1090 console.log("view-count = " + session['view-count'] + " Going to PHP");
1091 cb("PHP", null);
1092 return;
1093 }
1094
1095 var layer = 1;
1096 var ip = params.ip;
1097 var userCheck = "";
1098 var initialRecords = 100;
1099 var outputJSON = {};
1100 var debug = false;
1101
1102
1103 if((params.passcode) && (params.passcode != '')||((params.reading) && (params.reading != ''))) {
1104
1105 //See if we need to switch to a different db connection based off the layer name
1106 var sql = "SELECT int_layer_id FROM tbl_layer WHERE passcode = '" + md5(params.passcode) + "'";
1107
1108 if(params.connection) { //check the connection is still valid
1109 params.connection.query(sql, function(err, rows, fields) {
1110
1111 if(err) {
1112 console.log("Database error: " + err);
1113 return;
1114 } else {
1115 if((rows)&&(rows[0])) {
1116 layer = rows[0].int_layer_id;
1117
1118 foundLayer(params, session, layer, ip, userCheck, initialRecords, outputJSON, debug, cb);
1119 } else {
1120 console.log("No layer " + md5(params.passcode) + " - likely new. Going to PHP");
1121 cb("PHP", null);
1122 return;
1123 //Unknown or new layer - head back to PHP
1124
1125
1126 }
1127 }
1128 });
1129 }
1130
1131
1132
1133
1134
1135
1136 } else { //End of passcode not = ''
1137
1138 if(session['authenticated-layer']) {
1139 layer = session['authenticated-layer'];
1140 } else {
1141 layer = 1; //Default to about layer
1142 }
1143
1144 foundLayer(params, session, layer, ip, userCheck, initialRecords, outputJSON, debug, cb);
1145 }
1146
1147
1148 } //End of do have an ip
1149
1150
1151 } else {
1152 //Not logged in - revert back to the PHP version
1153 console.log("Not logged in - back to PHP version");
1154 cb("PHP", null);
1155 return;
1156
1157 }
1158
1159
1160 }); //End of readSession
1161
1162
1163}
1164
1165
1166
1167
1168
1169
1170
1171//Run at server startup
1172httpHttpsCreateServer(serverOptions);
1173
1174