UNPKG

41.9 kBJavaScriptView Raw
1var express = require('express');
2var http = require('http');
3var https = require('https');
4var path = require('path');
5var fs = require('fs');
6var clone = require('clone');
7var bytes = require('bytes');
8var moment = require("moment");
9
10var async = require('../util/async');
11
12var morgan = require("morgan");
13var bodyParser = require("body-parser");
14var multipart = require("connect-multiparty");
15var session = require('express-session');
16var cookieParser = require('cookie-parser');
17var flash = require("connect-flash");
18
19// we don't bind a single passport - instead, we get the constructor here by hand
20var Passport = require("passport").Passport;
21
22var util = require("../util/util");
23
24var launchPad = require("../launchpad/index");
25var cluster = require("cluster");
26
27var requestParam = require("request-param")();
28
29var app = express();
30app.disable('x-powered-by');
31
32// cloudcms app server support
33var main = require("../index");
34
35// duster service
36var duster = require("../duster/index");
37
38var coreHelpers = require("../duster/helpers/core/index");
39
40var toobusy = require("toobusy-js");
41toobusy.maxLag(500); // 500 ms lag in event queue, quite high but usable for now
42toobusy.interval(250);
43
44var responseTime = require("response-time");
45
46var requestCounter = 0;
47
48// holds configuration settings
49var SETTINGS = {
50 "setup": "single", // single, cluster, sticky-cluster
51 "name": "Cloud CMS Application Server",
52 "socketFunctions": [],
53 "routeFunctions": [],
54 "errorFunctions": [],
55 "configureFunctions": {},
56 "beforeFunctions": [],
57 "afterFunctions": [],
58 "reportFunctions": [],
59 "dustFunctions": [],
60 "initFunctions": [],
61 "filterFunctions": [],
62 "driverFunctions": [],
63 "viewEngine": "handlebars",
64 "storeEngines": {
65 "app": {
66 "type": "fs",
67 "config": {
68 "basePath": "{appBasePath}"
69 }
70 },
71 "tmp": {
72 "type": "fs",
73 "config": {
74 "basePath": "{tmpdirPath}/hosts/{host}",
75 "hostsPath": "{tmpdirPath}/hosts"
76 }
77 },
78 "hosts_fs": {
79 "type": "fs",
80 "config": {
81 "basePath": "/hosts/{host}",
82 "hostsPath": "/hosts"
83 }
84 },
85 "hosts_s3": {
86 "type": "s3",
87 "config": {
88 "accessKey": "",
89 "secretKey": "",
90 "bucket": "",
91 "basePath": "/hosts/{host}",
92 "hostsPath": "/hosts"
93 }
94 },
95 "hosts_s3fs": {
96 "type": "s3fs",
97 "config": {
98 "accessKey": "",
99 "secretKey": "",
100 "bucket": "",
101 "basePath": "/hosts/{host}",
102 "hostsPath": "/hosts"
103 }
104 }
105 },
106 "storeConfigurations": {
107 "default": {
108 "root": "app",
109 "config": "app",
110 "web": "app",
111 "content": "tmp",
112 "templates": "app",
113 "modules": "app"
114 },
115 "virtual": {
116 "root": "tmp",
117 "config": "tmp",
118 "web": "tmp",
119 "content": "tmp",
120 "templates": "tmp",
121 "modules": "tmp"
122 },
123 "oneteam": {
124 "root": "hosts_fs",
125 "config": "app",
126 "web": "app",
127 "content": "hosts_fs",
128 "templates": "app",
129 "modules": "hosts_fs"
130 },
131 "net-development": {
132 "root": "hosts_fs",
133 "config": "hosts_fs",
134 "web": "hosts_fs",
135 "content": "hosts_fs",
136 "templates": "hosts_fs",
137 "modules": "hosts_fs"
138 },
139 "net-production": {
140 "root": "hosts_s3fs",
141 "config": "hosts_s3fs",
142 "web": "hosts_s3fs",
143 "content": "hosts_s3fs",
144 "templates": "hosts_s3fs",
145 "modules": "hosts_s3fs"
146 },
147 "net-development-s3": {
148 "root": "hosts_s3",
149 "config": "hosts_s3",
150 "web": "hosts_s3",
151 "content": "hosts_s3",
152 "templates": "hosts_s3"
153 },
154 "net-development-s3fs": {
155 "root": "hosts_s3fs",
156 "config": "hosts_s3fs",
157 "web": "hosts_s3fs",
158 "content": "hosts_s3fs",
159 "templates": "hosts_s3fs",
160 "modules": "hosts_s3fs"
161 }
162 },
163 "duster": {
164 "fragments": {
165 "cache": true
166 }
167 },
168 "virtualHost": {
169 "enabled": false
170 },
171 "wcm": {
172 "enabled": false,
173 "cache": false,
174 "matchCase": true,
175 "cacheKey": {
176 "params": {
177 "includes": [],
178 "excludes": [],
179 "excludeAll": false
180 }
181 },
182 "pageCacheTTL": undefined,
183 "pageCacheRetryTimeout": undefined
184 },
185 "serverTags": {
186 "enabled": false
187 },
188 "insight": {
189 "enabled": false
190 },
191 "perf": {
192 "enabled": true
193 },
194 "driverConfig": {
195 "enabled": true
196 },
197 "virtualDriver": {
198 "enabled": false
199 },
200 "virtualContent": {
201 "enabled": true
202 },
203 "flow": {
204 "enabled": false
205 },
206 "form": {
207 "enabled": true
208 },
209 "auth": {
210 "enabled": true,
211 "providers": {
212 "facebook": {
213 "enabled": false
214 },
215 "twitter": {
216 "enabled": false
217 },
218 "linkedin": {
219 "enabled": false
220 }
221 }
222 },
223 "notifications": {
224 "enabled": false,
225 "type": null,
226 "configuration": {
227 }
228 },
229 "broadcast": {
230 "enabled": true
231 },
232 "local": {
233 "enabled": true
234 },
235 "welcome": {
236 "enabled": true,
237 "file": "index.html"
238 },
239 "config": {
240 "enabled": true
241 },
242 "cache": {
243 "enabled": true
244 },
245 "templates": {
246 "enabled": true
247 },
248 "modules": {
249 "enabled": true
250 },
251 "debug": {
252 "enabled": false,
253 "logGlobalTimings": false
254 },
255 "cors": {
256 "enabled": true,
257 "origin": null,
258 "methods": "GET, POST, PUT, DELETE, OPTIONS",
259 "headers": "X-Forwarded-Host, X-Requested-With, Content-Type, Authorization, Origin, X-Requested-With, X-Prototype-Version, Cache-Control, Pragma, X-CSRF-TOKEN, X-XSRF-TOKEN",
260 "credentials": true
261 },
262 "admin": {
263 "enabled": true,
264 "username": "admin",
265 "password": "admin"
266 },
267 "bodyParsers": {
268 "multipart": {
269 },
270 "json": {
271 "limit": "100kb"
272 },
273 "urlencoded": {
274 "extended": true
275 }
276 },
277 "renditions": {
278 "enabled": true
279 },
280 "gitana": {
281 "httpWorkQueueSize": 5
282 },
283 "proxy": {
284 "enabled": true,
285 "cache": []
286 },
287 "session": {
288 "enabled": false//,
289 //"secret": null,
290 //"type": "file",
291 //"ttl": -1,
292 //"reapInterval": -1
293 },
294 "awareness": {
295 "enabled": false,
296 "type": "memory",
297 "config": {}
298 },
299 "graphql": {
300 "enabled": true,
301 "config": {
302 "anonymous": true
303 }
304 }
305};
306
307// runs on 2999 by default
308process.env.PORT = process.env.PORT || 2999;
309
310// allows for specification of alternative transports
311SETTINGS.socketTransports = [
312 'websocket',
313 'xhr-polling',
314 'jsonp-polling',
315 'polling'
316];
317
318var exports = module.exports;
319
320/**
321 * Sets a configuration key/value.
322 *
323 * @param key
324 * @param value
325 */
326exports.set = function (key, value) {
327 SETTINGS[key] = value;
328};
329
330/**
331 * Gets a configuration key/value.
332 *
333 * @param key
334 * @return {*}
335 */
336exports.get = function (key) {
337 return SETTINGS[key];
338};
339
340/**
341 * Registers an express configuration function for a specific environment.
342 *
343 * @param env
344 * @param fn
345 */
346exports.configure = function (env, fn) {
347 if (!SETTINGS.configureFunctions[env]) {
348 SETTINGS.configureFunctions[env] = [];
349 }
350
351 SETTINGS.configureFunctions[env].push(fn);
352};
353
354/**
355 * Registers a socket configuration function.
356 *
357 * @param fn
358 */
359exports.sockets = function (fn) {
360 SETTINGS.socketFunctions.push(fn);
361};
362
363/**
364 * Registers a route configuration function.
365 *
366 * @param fn
367 */
368exports.routes = function (fn) {
369 SETTINGS.routeFunctions.push(fn);
370};
371
372/**
373 * Registers an error handler function.
374 *
375 * @param fn
376 */
377exports.error = function (fn) {
378 SETTINGS.errorFunctions.push(fn);
379};
380
381/**
382 * Adds an initialization function to set up dust.
383 *
384 * The function must have signature fn(app, dust)
385 *
386 * @param helperFn
387 */
388var dust = exports.dust = function(fn) {
389 SETTINGS.dustFunctions.push(fn);
390};
391
392/**
393 * Registers a function to run before the server starts.
394 *
395 * @param fn
396 */
397var before = exports.before = function (fn) {
398 SETTINGS.beforeFunctions.push(fn);
399};
400
401/**
402 * Registers a function to run after the server starts.
403 *
404 * @param fn
405 */
406var after = exports.after = function (fn) {
407 SETTINGS.afterFunctions.push(fn);
408};
409
410/**
411 * Registers a function to run after all server instances have started
412 *
413 * @param fn
414 */
415var report = exports.report = function (fn) {
416 SETTINGS.reportFunctions.push(fn);
417};
418
419/**
420 * Registers a function to run at init.
421 *
422 * @param fn
423 */
424var init = exports.init = function (fn) {
425 SETTINGS.initFunctions.push(fn);
426};
427
428/**
429 * Registers a function to run in filters phase.
430 *
431 * @param fn
432 */
433var filters = exports.filters = exports.filter = function (fn) {
434 SETTINGS.filterFunctions.push(fn);
435};
436
437/**
438 * Registers a function to run in the "driver" phase.
439 *
440 * @type {Function}
441 */
442var driver = exports.driver = function(fn) {
443 SETTINGS.driverFunctions.push(fn);
444};
445
446/*******************************************************************************************************/
447/*******************************************************************************************************/
448/*******************************************************************************************************/
449
450var runFunctions = function (functions, args, callback) {
451
452 // skip out early if nothing to do
453 if (!functions || functions.length === 0) {
454 return callback();
455 }
456
457 async.series(functions, args, function (err) {
458
459 if (err) {
460 console.log(err);
461 throw new Error(err);
462 }
463
464 callback(err);
465 });
466};
467
468
469/*******************************************************************************************************/
470/*******************************************************************************************************/
471/*******************************************************************************************************/
472
473/**
474 * Starts the Cloud CMS server.
475 *
476 * @param overrides optional config overrides
477 * @param callback optional callback function
478 */
479exports.start = function(overrides, callback) {
480
481 setTimeout(function() {
482 _start(overrides, function(err) {
483 if (callback) {
484 callback(err);
485 }
486 });
487 }, 10);
488};
489
490var _start = function(overrides, callback) {
491
492 if (typeof(overrides) === "function") {
493 callback = overrides;
494 overrides = null;
495 }
496
497 if (!callback) {
498 callback = function() {};
499 }
500
501 // always push core tag helpers to the front
502 SETTINGS.dustFunctions.unshift(coreHelpers);
503
504 // if SETTINGS.errorFunctions is empty, plug in a default error handler
505 if (SETTINGS.errorFunctions.length === 0)
506 {
507 SETTINGS.errorFunctions.push(main.defaultErrorHandler);
508 }
509 else
510 {
511 // otherwise, if they plugged in a custom error handler, make sure we at least have a console logger ahead of it
512 // so that things are sure to get logged out to console
513 SETTINGS.errorFunctions.unshift(main.consoleErrorLogger);
514 }
515
516 // insert an error handler to handle refresh token failures
517 SETTINGS.errorFunctions.unshift(main.refreshTokenErrorHandler);
518
519 // create our master config
520 var config = clone(SETTINGS);
521 if (overrides) {
522 util.merge(overrides, config);
523 }
524
525 // assume for launchpad
526 if (!config.setup) {
527 config.setup = "single";
528 }
529
530 launchPad({
531 "setup": config.setup,
532 "factory": function(done) {
533 startSlave(config, function(app, server) {
534 done(server);
535 });
536 },
537 "report": function() {
538 runFunctions(config.reportFunctions, [], function(err) {
539 // todo
540 });
541 },
542 "complete": function() {
543 callback();
544 }
545 });
546};
547
548var startSlave = function(config, afterStartFn)
549{
550 // set up modes
551 process.env.CLOUDCMS_APPSERVER_MODE = "development";
552
553 if (process.env.NODE_ENV == "production") {
554 process.env.CLOUDCMS_APPSERVER_MODE = "production";
555 }
556
557 /*
558 // set up domain hosting
559 // if not otherwise specified, we assume hosting at *.cloudcms.net
560 if (!process.env.CLOUDCMS_VIRTUAL_HOST) {
561
562 if (!process.env.CLOUDCMS_VIRTUAL_HOST_DOMAIN) {
563 process.env.CLOUDCMS_VIRTUAL_HOST_DOMAIN = "cloudcms.net";
564 }
565 }
566 */
567
568 // store config on process instance
569 process.configuration = config;
570
571 // some config overrides can come in through process.configuration
572 if (process.configuration) {
573 if (process.configuration.virtualHost && process.configuration.virtualHost.domain) {
574 if (!process.env.CLOUDCMS_VIRTUAL_HOST) {
575 if (!process.env.CLOUDCMS_VIRTUAL_HOST_DOMAIN) {
576 process.env.CLOUDCMS_VIRTUAL_HOST_DOMAIN = process.configuration.virtualHost.domain;
577 }
578 }
579 }
580 }
581 if (process.env.CLOUDCMS_VIRTUAL_HOST_DOMAIN) {
582 process.env.CLOUDCMS_VIRTUAL_HOST_DOMAIN = process.env.CLOUDCMS_VIRTUAL_HOST_DOMAIN.toLowerCase();
583 }
584
585 if (!process.env.CLOUDCMS_STANDALONE_HOST) {
586 process.env.CLOUDCMS_STANDALONE_HOST = "local";
587 }
588
589 // session store
590 var initializedSession = null;
591 if (process.configuration.session)
592 {
593 if (process.configuration.session.enabled)
594 {
595 var sessionSecret = process.configuration.session.secret;
596 if (!sessionSecret) {
597 sessionSecret = "secret";
598 }
599
600 var sessionConfig = {
601 secret: sessionSecret,
602 resave: false,
603 saveUninitialized: false
604 };
605
606 if (process.configuration.session.type === "file")
607 {
608 var options = {};
609 if (process.configuration.session.ttl)
610 {
611 options.ttl = process.configuration.session.ttl;
612 }
613 if (process.configuration.session.reapInterval)
614 {
615 options.reapInterval = process.configuration.session.reapInterval;
616 }
617 // session file store
618 var SessionFileStore = require('session-file-store')(session);
619 sessionConfig.store = new SessionFileStore(options);
620 }
621 else if (process.configuration.session.type === "memory" || !process.configuration.session.type)
622 {
623 var options = {};
624 options.checkPeriod = 86400000; // prune expired entries every 24h
625
626 // session memory store
627 var MemoryStore = require('memorystore')(session);
628 sessionConfig.store = new MemoryStore(options);
629 }
630
631 initializedSession = session(sessionConfig);
632 }
633 }
634
635 // global temp directory
636 util.createTempDirectory(function(err, tempDirectory) {
637 process.env.CLOUDCMS_TEMPDIR_PATH = tempDirectory;
638
639 // determine the max files
640 util.maxFiles(function(err, maxFiles) {
641
642 process.env.CLOUDCMS_MAX_FILES = maxFiles;
643
644 // global service starts
645 main.init(app, function (err) {
646
647 //console.log("");
648 //console.log("Starting " + config.name);
649 //console.log("Settings: " + JSON.stringify(config, null, " "));
650
651 app.enable('strict routing');
652
653 ////////////////////////////////////////////////////////////////////////////
654 //
655 // BASE CONFIGURATION
656 //
657 // Configures NodeJS app server using dustjs templating engine
658 // Runs on port 2999 by default
659 //
660 ////////////////////////////////////////////////////////////////////////////
661
662 // all environments
663 app.set('port', process.env.PORT);
664 app.set('views', process.env.CLOUDCMS_APPSERVER_BASE_PATH + "/views");
665
666 if (config.viewEngine === "dust")
667 {
668 var cons = require('consolidate');
669
670 app.set('view engine', 'html');
671 app.set('view engine', 'dust');
672 app.engine('html', cons.dust);
673 app.engine('dust', cons.dust);
674 }
675 else if (config.viewEngine === "handlebars" || config.viewEngine === "hbs")
676 {
677 var hbs = require('hbs');
678
679 app.set('view engine', 'html');
680 app.set('view engine', 'hbs');
681 app.engine('html', hbs.__express);
682 app.engine('hbs', hbs.__express);
683 }
684
685 ////////////////////////////////////////////////////////////////////////////
686 //
687 // VIRTUAL SUPPORT
688 //
689 // Configure NodeJS to load virtual driver and configure for virtual descriptors
690 // ahead of anything else running.
691 //
692 ////////////////////////////////////////////////////////////////////////////
693
694 // custom morgan logger
695 morgan(function (tokens, req, res) {
696
697 var status = res.statusCode;
698 var len = parseInt(res.getHeader('Content-Length'), 10);
699 var host = req.domainHost;
700 if (req.virtualHost) {
701 host = req.virtualHost;
702 }
703
704 len = isNaN(len) ? '0b' : len = bytes(len);
705
706 var d = new Date();
707 var dateString = d.toDateString();
708 var timeString = d.toTimeString();
709
710 // gray color
711 var grayColor = "\x1b[90m";
712
713 // status color
714 var color = 32;
715 if (status >= 500) {
716 color = 31;
717 }
718 else if (status >= 400) {
719 color = 33;
720 }
721 else if (status >= 300) {
722 color = 36;
723 }
724 var statusColor = "\x1b[" + color + "m";
725
726 // final color
727 var finalColor = "\x1b[0m";
728
729 if (process.env.CLOUDCMS_APPSERVER_MODE == "production")
730 {
731 grayColor = "";
732 statusColor = "";
733 finalColor = "";
734 }
735
736 var message = '';
737 message += grayColor + '<' + req.id + '> ';
738 message += grayColor + '[' + dateString + ' ' + timeString + '] ';
739 message += grayColor + host + ' ';
740 //message += grayColor + '(' + req.ip + ') ';
741 message += statusColor + res.statusCode + ' ';
742 message += statusColor + (new Date - req._startTime) + ' ms ';
743 message += grayColor + '"' + req.method + ' ';
744 message += grayColor + req.originalUrl + '" ';
745 message += grayColor + len + ' ';
746 message += finalColor;
747
748 return message;
749 });
750
751 /*
752 // debug headers being set
753 app.use(function(req, res, next) {
754 var setHeader = res.setHeader;
755 res.setHeader = function(a,b) {
756 console.trace("Writing header: " + a + " = " + b);
757 setHeader.call(this, a,b);
758 };
759 next();
760 });
761 */
762
763 // middleware which blocks requests when we're too busy
764 app.use(function(req, res, next) {
765 if (toobusy()) {
766 res.status(503).send("The web application is too busy to serve this request. Please try again.");
767 } else {
768 next();
769 }
770 });
771
772 // add req.id re
773 app.use(function (req, res, next) {
774 requestCounter++;
775 req.id = requestCounter;
776 next();
777 });
778
779 // APPLY CUSTOM INIT FUNCTIONS
780 runFunctions(config.initFunctions, [app], function (err) {
781
782 // retain originalUrl and originalPath since these can get modified along the way
783 app.use(function (req, res, next) {
784 req.originalUrl = req.url;
785 req.originalPath = req.path;
786 next();
787 });
788
789 // req.param method
790 app.use(requestParam);
791
792 // add req.log function
793 app.use(function (req, res, next) {
794
795 req._log = req.log = function (text/*, warn*/) {
796
797 var host = req.domainHost;
798 if (req.virtualHost)
799 {
800 host = req.virtualHost;
801 }
802
803 var timestamp = moment(new Date()).format("MM/DD/YYYY HH:mm:ss Z");
804 var grayColor = "\x1b[90m";
805 var finalColor = "\x1b[0m";
806
807 // in production, don't use colors
808 if (process.env.CLOUDCMS_APPSERVER_MODE === "production")
809 {
810 grayColor = "";
811 finalColor = "";
812 }
813
814 var message = '';
815 message += grayColor + '<' + req.id + '> ';
816 if (cluster.worker && cluster.worker.id)
817 {
818 message += grayColor + '(' + cluster.worker.id + ') ';
819 }
820 message += grayColor + '[' + timestamp + '] ';
821 message += grayColor + host + ' ';
822 message += grayColor + text + '';
823 message += finalColor;
824
825 /*
826 if (warn)
827 {
828 message = "\r\n**** SLOW RESPONSE ****\r\n" + message + "\r\n";
829 }
830 */
831
832 console.log(message);
833 };
834
835 next();
836 });
837
838 // common interceptors and config
839 main.common1(app);
840
841 // general logging of requests
842 // gather statistics on response time
843 app.use(responseTime(function (req, res, time) {
844
845 var warn = false;
846 if (time > 1000)
847 {
848 warn = true;
849 }
850
851 var requestPath = req.originalPath;
852 if (requestPath)
853 {
854 var filter = false;
855 if (requestPath.indexOf("/login") > -1)
856 {
857 filter = true;
858 }
859 if (requestPath.indexOf("/token") > -1)
860 {
861 filter = true;
862 }
863 if (filter)
864 {
865 requestPath = util.stripQueryStringFromUrl(requestPath);
866 }
867 }
868
869 req.log(req.method + " " + requestPath + " [" + res.statusCode + "] (" + time.toFixed(2) + " ms)", warn);
870 }));
871
872 // set up CORS allowances
873 // this lets CORS requests float through the proxy
874 app.use(main.ensureCORS());
875
876 // set up default security headers
877 app.use(main.ensureHeaders());
878
879 // common interceptors and config
880 main.common2(app);
881
882 // APPLY CUSTOM DRIVER FUNCTIONS
883 runFunctions(config.driverFunctions, [app], function(err) {
884
885 // binds gitana driver into place
886 main.common3(app);
887
888 // parse cookies
889 app.use(cookieParser());
890
891 // cloudcms things need to run here
892 main.common4(app, true);
893
894 // APPLY CUSTOM FILTER FUNCTIONS
895 runFunctions(config.filterFunctions, [app], function (err) {
896
897 // PATH BASED PERFORMANCE CACHING
898 main.perf1(app);
899
900 // proxy - anything that goes to /proxy is handled here early and nothing processes afterwards
901 main.proxy(app);
902
903 // MIMETYPE BASED PERFORMANCE CACHING
904 main.perf2(app);
905
906 // DEVELOPMENT BASED PERFORMANCE CACHING
907 main.perf3(app);
908
909 // standard body parsing + a special cloud cms body parser that makes a last ditch effort for anything
910 // that might be JSON (regardless of content type)
911 app.use(function (req, res, next) {
912
913 multipart(process.configuration.bodyParsers.multipart || {})(req, res, function (err) {
914 bodyParser.json(process.configuration.bodyParsers.json || {})(req, res, function (err) {
915 bodyParser.urlencoded(process.configuration.bodyParsers.urlencoded || {})(req, res, function (err) {
916 main.bodyParser()(req, res, function (err) {
917 next(err);
918 });
919 });
920 });
921 });
922
923 });
924
925 if (initializedSession)
926 {
927 app.use(initializedSession);
928 app.use(flash());
929 }
930
931 // this is the same as calling
932 // app.use(passport.initialize());
933 // except we create a new passport each time and store on request to support multitenancy
934 app.use(function(req, res, next) {
935
936 var passport = new Passport();
937 passport._key = "passport-" + req.virtualHost;
938
939 req._passport = {};
940 req._passport.instance = passport;
941
942 if (req.session && req.session[passport._key])
943 {
944 // load data from existing session
945 req._passport.session = req.session[passport._key];
946 }
947
948 // add this in
949 req.passport = req._passport.instance;
950
951 // passport - serialize and deserialize
952 req.passport.serializeUser(function(user, done) {
953 done(null, user);
954 });
955 req.passport.deserializeUser(function(user, done) {
956 done(null, user);
957 });
958
959 next();
960 });
961
962 // passport session
963 if (initializedSession)
964 {
965 app.use(function(req, res, next) {
966 req.passport.session()(req, res, next);
967 });
968 }
969
970 // welcome files
971 main.welcome(app);
972
973 // configure cloudcms app server command handing
974 main.interceptors(app, true);
975
976 //app.use(app.router);
977
978 // healthcheck middleware
979 main.healthcheck(app);
980
981 // APPLY CUSTOM ROUTES
982 runFunctions(config.routeFunctions, [app], function (err) {
983
984 // configure cloudcms app server handlers
985 main.handlers(app, true);
986
987 // register error functions
988 runFunctions(config.errorFunctions, [app], function (err) {
989
990 // APPLY CUSTOM CONFIGURE FUNCTIONS
991 var allConfigureFunctions = [];
992 for (var env in config.configureFunctions)
993 {
994 var functions = config.configureFunctions[env];
995 if (functions)
996 {
997 for (var i = 0; i < functions.length; i++)
998 {
999 allConfigureFunctions.push(functions[i]);
1000 }
1001 }
1002 }
1003 runFunctions(allConfigureFunctions, [app], function (err) {
1004
1005 ////////////////////////////////////////////////////////////////////////////
1006 //
1007 // INITIALIZE THE SERVER
1008 //
1009 ////////////////////////////////////////////////////////////////////////////
1010
1011
1012 // CORE OBJECTS
1013 var server = http.Server(app);
1014
1015 // request timeout
1016 var requestTimeout = 30000; // 30 seconds
1017 if (process.configuration && process.configuration.timeout)
1018 {
1019 requestTimeout = process.configuration.timeout;
1020 }
1021 server.setTimeout(requestTimeout);
1022
1023 // socket
1024 server.on("connection", function (socket) {
1025 socket.setNoDelay(true);
1026 });
1027 var io = process.IO = require("socket.io")(server);
1028 io.set('transports', config.socketTransports);
1029 io.use(function (socket, next) {
1030
1031 // console.log("New socket being initialized");
1032
1033 // attach _log function
1034 socket._log = function (text) {
1035
1036 var host = socket.handshake.headers.host;
1037 if (socket.handshake.headers["x-forwarded-host"])
1038 {
1039 host = socket.handshake.headers["x-forwarded-host"];
1040 }
1041
1042 var d = new Date();
1043 var dateString = d.toDateString();
1044 var timeString = d.toTimeString();
1045
1046 // gray color
1047 var grayColor = "\x1b[90m";
1048
1049 // final color
1050 var finalColor = "\x1b[0m";
1051
1052 if (process.env.CLOUDCMS_APPSERVER_MODE === "production")
1053 {
1054 grayColor = "";
1055 finalColor = "";
1056 }
1057
1058 var message = '';
1059 message += grayColor + '<socket> ';
1060 message += grayColor + '[' + dateString + ' ' + timeString + '] ';
1061 message += grayColor + host + ' ';
1062 message += grayColor + text + '';
1063 message += finalColor;
1064
1065 console.log(message);
1066 };
1067 /*
1068 socket.on("connect", function () {
1069 console.log("Socket connect()");
1070 });
1071 */
1072 /*
1073 socket.on("disconnect", function () {
1074 var message = "Socket disconnected";
1075 if (socket && socket.host)
1076 {
1077 message += ", host=" + socket.host;
1078 }
1079 if (socket && socket.gitana && socket.gitana.application && socket.gitana.application())
1080 {
1081 message += ", application=" + socket.gitana.application().title;
1082 }
1083 console.log(message);
1084 });
1085 */
1086
1087 // APPLY CUSTOM SOCKET.IO CONFIG
1088 runFunctions(config.socketFunctions, [socket], function (err) {
1089
1090 require("../middleware/awareness/awareness").initSocketIO(function() {
1091 next();
1092 });
1093
1094 // INSIGHT SERVER
1095 // if (config.insight && config.insight.enabled)
1096 // {
1097 // console.log("Init Insight to Socket");
1098
1099 // require("../insight/insight").init(socket, function () {
1100 // next();
1101 // });
1102 // }
1103 // else
1104 // {
1105 // next();
1106 // }
1107 });
1108
1109 });
1110
1111 // SET INITIAL VALUE FOR SERVER TIMESTAMP
1112 process.env.CLOUDCMS_APPSERVER_TIMESTAMP = new Date().getTime();
1113
1114 // DUST
1115 runFunctions(config.dustFunctions, [app, duster.getDust()], function (err) {
1116
1117 // APPLY SERVER BEFORE START FUNCTIONS
1118 runFunctions(config.beforeFunctions, [app], function (err) {
1119
1120 server._listenPort = app.get("port");
1121
1122 // AFTER SERVER START
1123 runFunctions(config.afterFunctions, [app], function (err) {
1124
1125 // listen for kill or interrupt so that we can shut down cleanly
1126 process.on('SIGINT', function () {
1127
1128 console.log("");
1129 console.log("");
1130
1131 console.log("Cloud CMS Module shutting down");
1132 // close server connections as cleanly as we can
1133 console.log(" -> Closing server connections");
1134 try
1135 {
1136 server.close();
1137 }
1138 catch (e)
1139 {
1140 console.log("Server.close produced error: " + JSON.stringify(e));
1141 }
1142
1143 // ask toobusy to shut down as cleanly as we can
1144 console.log(" -> Closing toobusy monitor");
1145 try
1146 {
1147 toobusy.shutdown();
1148 }
1149 catch (e)
1150 {
1151 console.log("toobusy.shutdown produced error: " + JSON.stringify(e));
1152 }
1153
1154 console.log("");
1155
1156 // tell the process to exit
1157 process.exit();
1158 });
1159
1160 // if we are on a worker process, then inform the master that we completed
1161 if (process.send)
1162 {
1163 process.send("server-startup");
1164 }
1165
1166 afterStartFn(app, server);
1167
1168 });
1169 });
1170 });
1171 });
1172 });
1173 });
1174 });
1175 });
1176 });
1177 });
1178 });
1179 });
1180};
1181
1182
1183////////////////////////////////////////////////////////////////////////////
1184//
1185// DEFAULT HANDLERS
1186//
1187////////////////////////////////////////////////////////////////////////////
1188
1189// default before function
1190before(function (app, callback) {
1191 callback();
1192});
1193
1194// default after function
1195after(function (app, callback) {
1196 callback();
1197});