1 | var express = require('express');
|
2 | var http = require('http');
|
3 | var https = require('https');
|
4 | var path = require('path');
|
5 | var fs = require('fs');
|
6 | var clone = require('clone');
|
7 | var bytes = require('bytes');
|
8 | var moment = require("moment");
|
9 |
|
10 | var async = require('../util/async');
|
11 |
|
12 | var morgan = require("morgan");
|
13 | var bodyParser = require("body-parser");
|
14 | var multipart = require("connect-multiparty");
|
15 | var session = require('express-session');
|
16 | var cookieParser = require('cookie-parser');
|
17 | var flash = require("connect-flash");
|
18 |
|
19 | // we don't bind a single passport - instead, we get the constructor here by hand
|
20 | var Passport = require("passport").Passport;
|
21 |
|
22 | var util = require("../util/util");
|
23 |
|
24 | var launchPad = require("../launchpad/index");
|
25 | var cluster = require("cluster");
|
26 |
|
27 | var requestParam = require("request-param")();
|
28 |
|
29 | var app = express();
|
30 | app.disable('x-powered-by');
|
31 |
|
32 | // cloudcms app server support
|
33 | var main = require("../index");
|
34 |
|
35 | // duster service
|
36 | var duster = require("../duster/index");
|
37 |
|
38 | var coreHelpers = require("../duster/helpers/core/index");
|
39 |
|
40 | var toobusy = require("toobusy-js");
|
41 | toobusy.maxLag(500); // 500 ms lag in event queue, quite high but usable for now
|
42 | toobusy.interval(250);
|
43 |
|
44 | var responseTime = require("response-time");
|
45 |
|
46 | var requestCounter = 0;
|
47 |
|
48 | // holds configuration settings
|
49 | var 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
|
308 | process.env.PORT = process.env.PORT || 2999;
|
309 |
|
310 | // allows for specification of alternative transports
|
311 | SETTINGS.socketTransports = [
|
312 | 'websocket',
|
313 | 'xhr-polling',
|
314 | 'jsonp-polling',
|
315 | 'polling'
|
316 | ];
|
317 |
|
318 | var exports = module.exports;
|
319 |
|
320 | /**
|
321 | * Sets a configuration key/value.
|
322 | *
|
323 | * @param key
|
324 | * @param value
|
325 | */
|
326 | exports.set = function (key, value) {
|
327 | SETTINGS[key] = value;
|
328 | };
|
329 |
|
330 | /**
|
331 | * Gets a configuration key/value.
|
332 | *
|
333 | * @param key
|
334 | * @return {*}
|
335 | */
|
336 | exports.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 | */
|
346 | exports.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 | */
|
359 | exports.sockets = function (fn) {
|
360 | SETTINGS.socketFunctions.push(fn);
|
361 | };
|
362 |
|
363 | /**
|
364 | * Registers a route configuration function.
|
365 | *
|
366 | * @param fn
|
367 | */
|
368 | exports.routes = function (fn) {
|
369 | SETTINGS.routeFunctions.push(fn);
|
370 | };
|
371 |
|
372 | /**
|
373 | * Registers an error handler function.
|
374 | *
|
375 | * @param fn
|
376 | */
|
377 | exports.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 | */
|
388 | var 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 | */
|
397 | var 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 | */
|
406 | var 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 | */
|
415 | var 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 | */
|
424 | var 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 | */
|
433 | var 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 | */
|
442 | var driver = exports.driver = function(fn) {
|
443 | SETTINGS.driverFunctions.push(fn);
|
444 | };
|
445 |
|
446 | /*******************************************************************************************************/
|
447 | /*******************************************************************************************************/
|
448 | /*******************************************************************************************************/
|
449 |
|
450 | var 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 | */
|
479 | exports.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 |
|
490 | var _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 |
|
548 | var 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 | function cleanup() {
|
1126 | console.log("");
|
1127 | console.log("");
|
1128 |
|
1129 | console.log("Cloud CMS Module shutting down");
|
1130 | // close server connections as cleanly as we can
|
1131 | console.log(" -> Closing server connections");
|
1132 | try
|
1133 | {
|
1134 | server.close();
|
1135 | }
|
1136 | catch (e)
|
1137 | {
|
1138 | console.log("Server.close produced error: " + JSON.stringify(e));
|
1139 | }
|
1140 |
|
1141 | // ask toobusy to shut down as cleanly as we can
|
1142 | console.log(" -> Closing toobusy monitor");
|
1143 | try
|
1144 | {
|
1145 | toobusy.shutdown();
|
1146 | }
|
1147 | catch (e)
|
1148 | {
|
1149 | console.log("toobusy.shutdown produced error: " + JSON.stringify(e));
|
1150 | }
|
1151 |
|
1152 | console.log("");
|
1153 |
|
1154 | // tell the process to exit
|
1155 | process.exit();
|
1156 | }
|
1157 |
|
1158 | // listen for kill or interrupt so that we can shut down cleanly
|
1159 | process.on('SIGINT', cleanup);
|
1160 | process.on('SIGTERM', cleanup);
|
1161 |
|
1162 | // if we are on a worker process, then inform the master that we completed
|
1163 | if (process.send)
|
1164 | {
|
1165 | process.send("server-startup");
|
1166 | }
|
1167 |
|
1168 | afterStartFn(app, server);
|
1169 |
|
1170 | });
|
1171 | });
|
1172 | });
|
1173 | });
|
1174 | });
|
1175 | });
|
1176 | });
|
1177 | });
|
1178 | });
|
1179 | });
|
1180 | });
|
1181 | });
|
1182 | };
|
1183 |
|
1184 |
|
1185 | ////////////////////////////////////////////////////////////////////////////
|
1186 | //
|
1187 | // DEFAULT HANDLERS
|
1188 | //
|
1189 | ////////////////////////////////////////////////////////////////////////////
|
1190 |
|
1191 | // default before function
|
1192 | before(function (app, callback) {
|
1193 | callback();
|
1194 | });
|
1195 |
|
1196 | // default after function
|
1197 | after(function (app, callback) {
|
1198 | callback();
|
1199 | });
|