345 lines
9.2 KiB
JavaScript
Executable file
345 lines
9.2 KiB
JavaScript
Executable file
'use strict';
|
|
|
|
/**
|
|
* Module dependencies.
|
|
*/
|
|
var fs = require('fs'),
|
|
https = require('https'),
|
|
express = require('express'),
|
|
morgan = require('morgan'),
|
|
logger = require('./logger'),
|
|
bodyParser = require('body-parser'),
|
|
session = require('express-session'),
|
|
compression = require('compression'),
|
|
methodOverride = require('method-override'),
|
|
cookieParser = require('cookie-parser'),
|
|
helmet = require('helmet'),
|
|
passport = require('passport'),
|
|
raven = require('raven'),
|
|
MongoStore = require('connect-mongo')(session),
|
|
config = require('./config'),
|
|
consolidate = require('consolidate'),
|
|
path = require('path'),
|
|
client = new raven.Client(config.DSN),
|
|
i18n = require('i18n');
|
|
|
|
var mongoose = require('mongoose');
|
|
|
|
/**
|
|
* Configure Socket.io
|
|
*/
|
|
var configureSocketIO = function (app, db) {
|
|
// Load the Socket.io configuration
|
|
var server = require('./socket.io')(app, db);
|
|
|
|
// Return server object
|
|
return server;
|
|
};
|
|
|
|
var supportedLanguages = ['en', 'de', 'fr', 'it', 'es'];
|
|
|
|
function containsAnySupportedLanguages(preferredLanguages){
|
|
var i, currIndex;
|
|
for (i = 0; i < preferredLanguages.length; i++) {
|
|
currIndex = supportedLanguages.indexOf(preferredLanguages[i]);
|
|
if (currIndex > -1) {
|
|
return supportedLanguages[currIndex];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
module.exports = function(db) {
|
|
// Initialize express app
|
|
var app = express();
|
|
var url = require('url');
|
|
|
|
// Globbing model files
|
|
config.getGlobbedFiles('./app/models/**/*.js').forEach(function(modelPath) {
|
|
require(path.resolve(modelPath));
|
|
});
|
|
|
|
// Setting application local variables
|
|
app.locals.google_analytics_id = config.app.google_analytics_id;
|
|
app.locals.title = config.app.title;
|
|
app.locals.signupDisabled = config.signupDisabled;
|
|
app.locals.description = config.app.description;
|
|
app.locals.keywords = config.app.keywords;
|
|
|
|
app.locals.subdomainsDisabled = config.subdomainsDisabled;
|
|
|
|
if(config.socketPortExternallyVisible){
|
|
app.locals.socketPort = config.socketPort;
|
|
} else {
|
|
app.locals.socketPort = '';
|
|
}
|
|
|
|
if(config.socketUrl){
|
|
app.locals.socketUrl = config.socketUrl;
|
|
}
|
|
|
|
app.locals.bowerFormJSFiles = config.getBowerFormJSAssets();
|
|
app.locals.bowerJSFiles = config.getBowerJSAssets();
|
|
app.locals.bowerCssFiles = config.getBowerCSSAssets();
|
|
app.locals.bowerOtherFiles = config.getBowerOtherAssets();
|
|
|
|
app.locals.jsFiles = config.getJavaScriptAssets();
|
|
app.locals.formJSFiles = config.getFormJavaScriptAssets();
|
|
app.locals.cssFiles = config.getCSSAssets();
|
|
|
|
app.use(function (req, res, next) {
|
|
var urlPath;
|
|
if(!config.subdomainsDisabled) {
|
|
var User = mongoose.model('User');
|
|
var subdomainPath = '/subdomain/';
|
|
var subdomains = req.subdomains;
|
|
|
|
if (subdomains.slice(0, 4).join('.') + '' === '1.0.0.127') {
|
|
subdomains = subdomains.slice(4);
|
|
}
|
|
|
|
// continue if no subdomains
|
|
if (!subdomains.length) {
|
|
return next();
|
|
}
|
|
|
|
urlPath = url.parse(req.url).path.split('/');
|
|
if (urlPath.indexOf('static') > -1) {
|
|
urlPath.splice(1, 1);
|
|
req.root = req.protocol + '://' + config.baseUrl + urlPath.join('/');
|
|
return next();
|
|
}
|
|
|
|
if (urlPath.indexOf('users') > -1 && urlPath.indexOf('me') > -1) {
|
|
return next();
|
|
}
|
|
|
|
if (subdomains.indexOf('stage') > -1 || subdomains.indexOf('admin') > -1) {
|
|
return next();
|
|
}
|
|
|
|
if (subdomains.indexOf('api') > -1) {
|
|
// rebuild url
|
|
subdomainPath += 'api' + req.url;
|
|
// TODO: check path and query strings are preserved
|
|
// reassign url
|
|
req.url = subdomainPath;
|
|
return next();
|
|
}
|
|
|
|
User.findOne({username: req.subdomains.reverse()[0]}).exec(function (err, user) {
|
|
|
|
if (err) {
|
|
req.subdomains = null;
|
|
// Error page
|
|
return res.status(404).render('404', {
|
|
error: 'Page Does Not Exist'
|
|
});
|
|
}
|
|
if (user === null) {
|
|
// Error page
|
|
return res.status(404).render('404', {
|
|
error: 'Page Does Not Exist'
|
|
});
|
|
}
|
|
|
|
// rebuild url
|
|
subdomainPath += subdomains.join('/') + req.url;
|
|
|
|
// TODO: check path and query strings are preserved
|
|
// reassign url
|
|
req.url = subdomainPath;
|
|
|
|
// Q.E.D.
|
|
return next();
|
|
});
|
|
} else {
|
|
|
|
urlPath = url.parse(req.url).path.split('/');
|
|
if (urlPath.indexOf('static') > -1 && urlPath.indexOf('view') === urlPath.indexOf('static')-1) {
|
|
urlPath.splice(1, 1);
|
|
req.url = urlPath.join('/');
|
|
}
|
|
|
|
return next();
|
|
}
|
|
});
|
|
|
|
//Setup Prerender.io
|
|
app.use(require('prerender-node').set('prerenderToken', process.env.PRERENDER_TOKEN));
|
|
|
|
|
|
// Passing the request url to environment locals
|
|
app.use(function(req, res, next) {
|
|
if(config.baseUrl === ''){
|
|
config.baseUrl = req.protocol + '://' + req.headers.host;
|
|
}
|
|
res.locals.url = req.protocol + '://' + req.headers.host + req.url;
|
|
next();
|
|
});
|
|
|
|
// Should be placed before express.static
|
|
app.use(compression({
|
|
// only compress files for the following content types
|
|
filter: function(req, res) {
|
|
return (/json|text|javascript|css/).test(res.getHeader('Content-Type'));
|
|
},
|
|
// zlib option for compression level
|
|
level: 9
|
|
}));
|
|
|
|
|
|
// Set template engine as defined in the config files
|
|
app.engine('server.view.pug', consolidate.pug);
|
|
|
|
// Set views path and view engine
|
|
app.set('view engine', 'server.view.pug');
|
|
app.set('views', './app/views');
|
|
|
|
// Enable logger (morgan)
|
|
app.use(morgan(logger.getLogFormat(), logger.getMorganOptions()));
|
|
|
|
// Environment dependent middleware
|
|
if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
|
|
// Disable views cache
|
|
app.set('view cache', false);
|
|
} else if (process.env.NODE_ENV === 'production') {
|
|
app.locals.cache = 'memory';
|
|
app.set('view cache', true);
|
|
}
|
|
|
|
// Request body parsing middleware should be above methodOverride
|
|
app.use(bodyParser.urlencoded({
|
|
extended: true,
|
|
limit: '100mb'
|
|
}));
|
|
|
|
app.use(bodyParser.json({ limit: '100mb' }));
|
|
app.use(methodOverride());
|
|
|
|
// Use helmet to secure Express headers
|
|
app.use(helmet.frameguard());
|
|
app.use(helmet.xssFilter());
|
|
app.use(helmet.noSniff());
|
|
app.use(helmet.ieNoOpen());
|
|
app.use(helmet.dnsPrefetchControl());
|
|
app.use(helmet.hidePoweredBy());
|
|
|
|
|
|
// Setting the app router and static folder
|
|
app.use('/static', express.static(path.resolve('./public')));
|
|
|
|
// CookieParser should be above session
|
|
app.use(cookieParser());
|
|
|
|
// Express MongoDB session storage
|
|
app.use(session({
|
|
saveUninitialized: true,
|
|
resave: true,
|
|
secret: config.sessionSecret,
|
|
store: new MongoStore({
|
|
mongooseConnection: db.connection,
|
|
collection: config.sessionCollection
|
|
}),
|
|
cookie: config.sessionCookie,
|
|
name: config.sessionName
|
|
}));
|
|
|
|
// use passport session
|
|
app.use(passport.initialize());
|
|
app.use(passport.session());
|
|
|
|
//Setup i18n
|
|
i18n.configure({
|
|
locales: supportedLanguages,
|
|
directory: __dirname + '/locales',
|
|
defaultLocale: 'en',
|
|
cookie: 'userLang'
|
|
});
|
|
|
|
app.use(i18n.init);
|
|
|
|
//Visitor Language Detection
|
|
app.use(function(req, res, next) {
|
|
var acceptLanguage = req.headers['accept-language'];
|
|
|
|
var languages, supportedLanguage;
|
|
|
|
if(acceptLanguage){
|
|
languages = acceptLanguage.match(/[a-z]{2}(?!-)/g) || [];
|
|
supportedLanguage = containsAnySupportedLanguages(languages);
|
|
}
|
|
|
|
if(!req.user && supportedLanguage !== null){
|
|
var currLanguage = res.cookie('userLang');
|
|
if(currLanguage && currLanguage !== supportedLanguage || !currLanguage){
|
|
res.clearCookie('userLang');
|
|
res.cookie('userLang', supportedLanguage, { maxAge: 90000, httpOnly: true });
|
|
} else if(req.user && (!req.cookies.hasOwnProperty('userLang') || req.cookies.userLang !== req.user.language) ){
|
|
res.cookie('userLang', req.user.language, { maxAge: 90000, httpOnly: true });
|
|
}
|
|
}
|
|
next();
|
|
});
|
|
|
|
// Globbing routing files
|
|
config.getGlobbedFiles('./app/routes/**/*.js').forEach(function(routePath) {
|
|
require(path.resolve(routePath))(app);
|
|
});
|
|
|
|
// Add headers for Sentry
|
|
app.use(function (req, res, next) {
|
|
|
|
// Website you wish to allow to connect
|
|
res.setHeader('Access-Control-Allow-Origin', 'https://sentry.io');
|
|
|
|
// Request methods you wish to allow
|
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
|
|
|
|
// Request headers you wish to allow
|
|
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
|
|
|
|
// Set to true if you need the website to include cookies in the requests sent
|
|
// to the API (e.g. in case you use sessions)
|
|
res.setHeader('Access-Control-Allow-Credentials', true);
|
|
|
|
// Pass to next layer of middleware
|
|
next();
|
|
});
|
|
|
|
// Sentry (Raven) middleware
|
|
app.use(raven.middleware.express.requestHandler(config.DSN));
|
|
|
|
// Should come before any other error middleware
|
|
app.use(raven.middleware.express.errorHandler(config.DSN));
|
|
|
|
// Assume 'not found' in the error msgs is a 404. this is somewhat silly, but valid, you can do whatever you like, set properties, use instanceof etc.
|
|
app.use(function(err, req, res, next) {
|
|
// If the error object doesn't exists
|
|
if (!err) {
|
|
return next();
|
|
}
|
|
|
|
// Log it
|
|
client.captureError(err);
|
|
|
|
// Error page
|
|
res.status(500).render('500', {
|
|
error: err.stack
|
|
});
|
|
});
|
|
|
|
// Assume 404 since no middleware responded
|
|
app.use(function(req, res) {
|
|
client.captureError(new Error('Page Not Found'));
|
|
res.status(404).render('404', {
|
|
url: req.originalUrl,
|
|
error: 'Not Found'
|
|
});
|
|
});
|
|
|
|
app = configureSocketIO(app, db);
|
|
|
|
// Return Express server instance
|
|
return app;
|
|
};
|