config/express.js
import express from 'express';
import logger from 'morgan';
import bodyParser from 'body-parser';
import cookieParser from 'cookie-parser';
import compress from 'compression';
import methodOverride from 'method-override';
import cors from 'cors';
import httpStatus from 'http-status';
import expressFlash from 'express-flash';
import expressSession from 'express-session';
import expressWinston from 'express-winston';
import expressValidation from 'express-validation';
import passport from 'passport';
import helmet from 'helmet';
import path from 'path';
import moment from 'moment';
import winstonInstance from './winston';
import routes from '../server/routes/index.route';
import adminRoutes from '../server/routes/admin.route';
import config from './config';
import APIError from '../server/helpers/APIError';
import ExtendableError from '../server/helpers/ExtendableError';
const app = express();
if (config.env === 'development') {
app.use(logger('dev'));
}
// parse body params and attache them to req.body
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressFlash());
app.use(cookieParser());
app.use(compress());
app.use(methodOverride());
app.use(expressSession({
secret: config.sessionSecret,
resave: false,
saveUninitialized: true,
}));
app.use(passport.initialize());
app.use(passport.session());
app.use((req, res, next) => {
res.setHeader('Sylow-ServerID', app.sylowServer);
next();
});
// secure apps by setting various HTTP headers
app.use(helmet());
// enable CORS - Cross Origin Resource Sharing
const corsOptions = {
origin: '*',
allowedHeaders: [
'Content-Type', 'Count', 'ETag', 'Link', 'Server-Authorization', 'WWW-Authenticate'
],
maxAge: 2592000
};
app.use(cors(corsOptions));
// set up view templates
app.set('views', path.join(__dirname, '../admin/views'));
app.set('view engine', 'pug');
// enable detailed API logging in dev env
if (config.env === 'development') {
expressWinston.requestWhitelist.push('body');
expressWinston.responseWhitelist.push('body');
app.use(expressWinston.logger({
winstonInstance,
meta: true, // optional: log meta data about request (defaults to true)
msg: 'HTTP {{req.method}} {{req.url}} {{res.statusCode}} {{res.responseTime}}ms',
colorStatus: true, // Color the status code (default green, 3XX cyan, 4XX yellow, 5XX red).
ignoreRoutes: ['/assets']
}));
}
// mount api routes on /api path
app.use('/api', routes);
// mount admin routes on the root path
app.use('/', adminRoutes);
// mount admin static assets
app.use('/assets', express.static(path.join(__dirname, '../admin/assets')));
// if error is not an instanceOf one of our errors, convert it.
app.use((err, req, res, next) => {
if (err instanceof expressValidation.ValidationError) {
// validation error contains errors which is an array of error each containing message[]
const unifiedErrorMessage = err.errors.map(error => error.messages.join('. ')).join(' and ');
const error = new APIError(unifiedErrorMessage, err.status, true);
return next(error);
} else if (!['APIError', 'AdminError'].includes(err.name) && !(err instanceof ExtendableError)) {
const apiError = new APIError(err.message, err.status, err.isPublic);
return next(apiError);
}
return next(err);
});
// catch 404 and forward to error handler
app.use((req, res, next) => {
const err = new APIError('API not found', httpStatus.NOT_FOUND);
return next(err);
});
// log error in winston transports except when executing test suite
if (config.env !== 'test') {
app.use(expressWinston.errorLogger({
winstonInstance
}));
}
// error handler, send stacktrace only during development
app.use((err, req, res, next) => { // eslint-disable-line no-unused-vars
if (err.returnType === 'json') {
return res.status(err.status).json({
message: err.isPublic ? err.message : httpStatus[err.status],
stack: config.env === 'development' ? err.stack : {}
});
}
return res.status(err.status).render('error', {
message: err.isPublic ? err.message : httpStatus[err.status]
});
});
app.locals.moment = moment;
export { app as default };