2016-04-04 12:36:30 +00:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
let config = require('config');
|
|
|
|
let log = require('npmlog');
|
|
|
|
|
2017-03-07 14:30:56 +00:00
|
|
|
let _ = require('./lib/translate')._;
|
2017-03-04 16:15:16 +00:00
|
|
|
let util = require('util');
|
|
|
|
|
2016-04-04 12:36:30 +00:00
|
|
|
let express = require('express');
|
|
|
|
let bodyParser = require('body-parser');
|
|
|
|
let path = require('path');
|
|
|
|
let favicon = require('serve-favicon');
|
|
|
|
let logger = require('morgan');
|
|
|
|
let cookieParser = require('cookie-parser');
|
|
|
|
let session = require('express-session');
|
|
|
|
let RedisStore = require('connect-redis')(session);
|
|
|
|
let flash = require('connect-flash');
|
|
|
|
let hbs = require('hbs');
|
|
|
|
let compression = require('compression');
|
|
|
|
let passport = require('./lib/passport');
|
|
|
|
let tools = require('./lib/tools');
|
|
|
|
|
|
|
|
let routes = require('./routes/index');
|
|
|
|
let users = require('./routes/users');
|
|
|
|
let lists = require('./routes/lists');
|
|
|
|
let settings = require('./routes/settings');
|
2016-05-02 16:50:43 +00:00
|
|
|
let settingsModel = require('./lib/models/settings');
|
2016-04-04 12:36:30 +00:00
|
|
|
let templates = require('./routes/templates');
|
|
|
|
let campaigns = require('./routes/campaigns');
|
|
|
|
let links = require('./routes/links');
|
|
|
|
let fields = require('./routes/fields');
|
|
|
|
let segments = require('./routes/segments');
|
2016-06-03 10:15:33 +00:00
|
|
|
let triggers = require('./routes/triggers');
|
2016-04-04 12:36:30 +00:00
|
|
|
let webhooks = require('./routes/webhooks');
|
|
|
|
let subscription = require('./routes/subscription');
|
|
|
|
let archive = require('./routes/archive');
|
2016-05-07 11:28:24 +00:00
|
|
|
let api = require('./routes/api');
|
2016-04-04 12:36:30 +00:00
|
|
|
|
|
|
|
let app = express();
|
|
|
|
|
|
|
|
// view engine setup
|
|
|
|
app.set('views', path.join(__dirname, 'views'));
|
|
|
|
app.set('view engine', 'hbs');
|
|
|
|
|
|
|
|
// Handle proxies. Needed to resolve client IP
|
|
|
|
if (config.www.proxy) {
|
|
|
|
app.set('trust proxy', config.www.proxy);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do not expose software used
|
|
|
|
app.disable('x-powered-by');
|
|
|
|
|
2017-03-02 17:52:40 +00:00
|
|
|
hbs.registerPartials(__dirname + '/views/partials');
|
|
|
|
|
2016-04-04 12:36:30 +00:00
|
|
|
/**
|
|
|
|
* We need this helper to make sure that we consume flash messages only
|
|
|
|
* when we are able to actually display these. Otherwise we might end up
|
|
|
|
* in a situation where we consume a flash messages but then comes a redirect
|
|
|
|
* and the message is never displayed
|
|
|
|
*/
|
2016-05-02 16:50:43 +00:00
|
|
|
hbs.registerHelper('flash_messages', function () { // eslint-disable-line prefer-arrow-callback
|
2016-04-04 12:36:30 +00:00
|
|
|
if (typeof this.flash !== 'function') { // eslint-disable-line no-invalid-this
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
let messages = this.flash(); // eslint-disable-line no-invalid-this
|
|
|
|
let response = [];
|
|
|
|
|
|
|
|
// group messages by type
|
|
|
|
Object.keys(messages).forEach(key => {
|
|
|
|
let el = '<div class="alert alert-' + key + ' alert-dismissible" role="alert"><button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>';
|
|
|
|
|
|
|
|
if (key === 'danger') {
|
|
|
|
el += '<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> ';
|
|
|
|
}
|
|
|
|
|
|
|
|
let rows = [];
|
|
|
|
|
|
|
|
messages[key].forEach(message => {
|
|
|
|
rows.push(hbs.handlebars.escapeExpression(message));
|
|
|
|
});
|
|
|
|
|
|
|
|
if (rows.length > 1) {
|
|
|
|
el += '<p>' + rows.join('</p>\n<p>') + '</p>';
|
|
|
|
} else {
|
|
|
|
el += rows.join('');
|
|
|
|
}
|
|
|
|
|
|
|
|
el += '</div>';
|
|
|
|
|
|
|
|
response.push(el);
|
|
|
|
});
|
|
|
|
|
|
|
|
return new hbs.handlebars.SafeString(
|
|
|
|
response.join('\n')
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2017-03-07 14:30:56 +00:00
|
|
|
// {{#translate}}abc{{/translate}}
|
2017-03-04 16:15:16 +00:00
|
|
|
hbs.registerHelper('translate', function (context, options) { // eslint-disable-line prefer-arrow-callback
|
|
|
|
if (typeof options === 'undefined' && context) {
|
|
|
|
options = context;
|
|
|
|
context = false;
|
|
|
|
}
|
|
|
|
|
2017-03-07 14:30:56 +00:00
|
|
|
let result = _(options.fn(this)); // eslint-disable-line no-invalid-this
|
2017-03-04 16:15:16 +00:00
|
|
|
|
|
|
|
if (Array.isArray(context)) {
|
|
|
|
result = util.format(result, ...context);
|
|
|
|
}
|
|
|
|
return new hbs.handlebars.SafeString(result);
|
|
|
|
});
|
|
|
|
|
2016-04-04 12:36:30 +00:00
|
|
|
app.use(compression());
|
|
|
|
app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
|
|
|
|
|
|
|
|
app.use(logger(config.www.log, {
|
|
|
|
stream: {
|
|
|
|
write: message => {
|
|
|
|
message = (message || '').toString();
|
|
|
|
if (message) {
|
|
|
|
log.info('HTTP', message.replace('\n', '').trim());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
|
|
|
|
app.use(cookieParser());
|
|
|
|
app.use(express.static(path.join(__dirname, 'public')));
|
|
|
|
|
|
|
|
app.use(session({
|
|
|
|
store: config.redis.enabled ? new RedisStore(config.redis) : false,
|
|
|
|
secret: config.www.secret,
|
|
|
|
saveUninitialized: false,
|
|
|
|
resave: false
|
|
|
|
}));
|
|
|
|
app.use(flash());
|
|
|
|
|
2017-03-04 16:15:16 +00:00
|
|
|
app.use((req, res, next) => {
|
2017-03-07 14:30:56 +00:00
|
|
|
req._ = str => _(str);
|
2017-03-04 16:15:16 +00:00
|
|
|
next();
|
|
|
|
});
|
|
|
|
|
2016-04-04 12:36:30 +00:00
|
|
|
app.use(bodyParser.urlencoded({
|
2016-04-13 05:36:55 +00:00
|
|
|
extended: true,
|
|
|
|
limit: config.www.postsize
|
2016-04-04 12:36:30 +00:00
|
|
|
}));
|
|
|
|
|
2016-04-13 05:36:55 +00:00
|
|
|
app.use(bodyParser.text({
|
|
|
|
limit: config.www.postsize
|
|
|
|
}));
|
|
|
|
|
|
|
|
app.use(bodyParser.json({
|
|
|
|
limit: config.www.postsize
|
|
|
|
}));
|
2016-04-04 12:36:30 +00:00
|
|
|
|
|
|
|
passport.setup(app);
|
|
|
|
|
|
|
|
// make sure flash messages are available
|
|
|
|
app.use((req, res, next) => {
|
|
|
|
res.locals.flash = req.flash.bind(req);
|
|
|
|
res.locals.user = req.user;
|
2016-08-11 12:44:09 +00:00
|
|
|
res.locals.ldap = {
|
|
|
|
enabled: config.ldap.enabled,
|
|
|
|
passwordresetlink: config.ldap.passwordresetlink
|
|
|
|
};
|
2016-04-04 12:36:30 +00:00
|
|
|
|
|
|
|
let menu = [{
|
2017-03-07 14:30:56 +00:00
|
|
|
title: _('Home'),
|
2016-04-04 12:36:30 +00:00
|
|
|
url: '/',
|
|
|
|
selected: true
|
|
|
|
}];
|
|
|
|
|
|
|
|
res.setSelectedMenu = key => {
|
|
|
|
menu.forEach(item => {
|
|
|
|
item.selected = (item.key === key);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
res.locals.menu = menu;
|
|
|
|
tools.updateMenu(res);
|
2016-05-02 16:50:43 +00:00
|
|
|
|
2016-06-03 10:15:33 +00:00
|
|
|
settingsModel.list(['ua_code', 'shoutout'], (err, configItems) => {
|
2016-05-02 16:50:43 +00:00
|
|
|
if (err) {
|
|
|
|
return next(err);
|
|
|
|
}
|
|
|
|
Object.keys(configItems).forEach(key => {
|
|
|
|
res.locals[key] = configItems[key];
|
|
|
|
});
|
|
|
|
next();
|
|
|
|
});
|
2016-04-04 12:36:30 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
app.use('/', routes);
|
|
|
|
app.use('/users', users);
|
|
|
|
app.use('/lists', lists);
|
|
|
|
app.use('/templates', templates);
|
|
|
|
app.use('/campaigns', campaigns);
|
|
|
|
app.use('/settings', settings);
|
|
|
|
app.use('/links', links);
|
|
|
|
app.use('/fields', fields);
|
|
|
|
app.use('/segments', segments);
|
2016-06-03 10:15:33 +00:00
|
|
|
app.use('/triggers', triggers);
|
2016-04-04 12:36:30 +00:00
|
|
|
app.use('/webhooks', webhooks);
|
|
|
|
app.use('/subscription', subscription);
|
|
|
|
app.use('/archive', archive);
|
2016-05-07 11:28:24 +00:00
|
|
|
app.use('/api', api);
|
2016-04-04 12:36:30 +00:00
|
|
|
|
|
|
|
// catch 404 and forward to error handler
|
|
|
|
app.use((req, res, next) => {
|
2017-03-07 14:30:56 +00:00
|
|
|
let err = new Error(_('Not Found'));
|
2016-04-04 12:36:30 +00:00
|
|
|
err.status = 404;
|
|
|
|
next(err);
|
|
|
|
});
|
|
|
|
|
|
|
|
// error handlers
|
|
|
|
|
|
|
|
// development error handler
|
|
|
|
// will print stacktrace
|
|
|
|
if (app.get('env') === 'development') {
|
|
|
|
app.use((err, req, res, next) => {
|
|
|
|
if (!err) {
|
|
|
|
return next();
|
|
|
|
}
|
|
|
|
res.status(err.status || 500);
|
|
|
|
res.render('error', {
|
|
|
|
message: err.message,
|
|
|
|
error: err
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// production error handler
|
|
|
|
// no stacktraces leaked to user
|
|
|
|
app.use((err, req, res, next) => {
|
|
|
|
if (!err) {
|
|
|
|
return next();
|
|
|
|
}
|
|
|
|
res.status(err.status || 500);
|
|
|
|
res.render('error', {
|
|
|
|
message: err.message,
|
|
|
|
error: {}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
module.exports = app;
|