// This module is only used to attach middlewares to the express app import bodyParser from 'body-parser'; import nconf from 'nconf'; import morgan from 'morgan'; import cookieSession from 'cookie-session'; import mongoose from 'mongoose'; import compression from 'compression'; import methodOverride from 'method-override'; import passport from 'passport'; import basicAuth from 'express-basic-auth'; import helmet from 'helmet'; import setupExpress from '../libs/setupExpress'; import errorHandler from './errorHandler'; import notFoundHandler from './notFound'; import cors from './cors'; import staticMiddleware from './static'; import domainMiddleware from './domain'; // import favicon from 'serve-favicon'; // import path from 'path'; import maintenanceMode from './maintenanceMode'; import { ENABLE_CLUSTER } from '../libs/config'; import { forceSSL, forceHabitica, } from './redirects'; import blocker from './blocker'; import v1 from './v1'; import v2 from './v2'; import appRoutes from './appRoutes'; import responseHandler from './response'; import { attachTranslateFunction, } from './language'; import { logRequestData, logSlowRequests, } from './requestLogHandler'; const IS_PROD = nconf.get('IS_PROD'); const DISABLE_LOGGING = nconf.get('DISABLE_REQUEST_LOGGING') === 'true'; const ENABLE_HTTP_AUTH = nconf.get('SITE_HTTP_AUTH_ENABLED') === 'true'; const LOG_REQUESTS_EXCESSIVE_MODE = nconf.get('LOG_REQUESTS_EXCESSIVE_MODE') === 'true'; const SLOW_REQUEST_THRESHOLD = nconf.get('SLOW_REQUEST_THRESHOLD'); // const PUBLIC_DIR = path.join(__dirname, '/../../client'); const SESSION_SECRET = nconf.get('SESSION_SECRET'); const TEN_YEARS = 1000 * 60 * 60 * 24 * 365 * 10; export default function attachMiddlewares (app, server) { setupExpress(app); if (LOG_REQUESTS_EXCESSIVE_MODE) { app.use(logRequestData); } if (SLOW_REQUEST_THRESHOLD > 0) { app.use(logSlowRequests); } if (ENABLE_CLUSTER) { app.use(domainMiddleware(server, mongoose)); } if (!IS_PROD && !DISABLE_LOGGING) app.use(morgan('dev')); // See https://helmetjs.github.io/ for the list of headers enabled by default app.use(helmet({ // New middlewares added by default in Helmet 4 are disabled contentSecurityPolicy: { directives: { defaultSrc: [ '*.amazonaws.com', '*.habitica.com', 'cloudfront.loggly.com', 'js.stripe.com', 'm.stripe.network', 'static-na.payments-amazon.com', ], imgSrc: '*', scriptSrc: [ '\'unsafe-eval\'', ], upgradeInsecureRequests: IS_PROD ? [] : null, }, }, expectCt: false, permittedCrossDomainPolicies: false, referrerPolicy: false, })); // add res.respond and res.t app.use(responseHandler); app.use(attachTranslateFunction); app.use(compression()); // app.use(favicon(`${PUBLIC_DIR}/favicon.ico`)); app.use(maintenanceMode); app.use(blocker); app.use(cors); app.use(forceSSL); app.use(forceHabitica); app.use(bodyParser.urlencoded({ extended: true, // Uses 'qs' library as old connect middleware limit: '10mb', })); app.use(function bodyMiddleware (req, res, next) { // eslint-disable-line prefer-arrow-callback if (req.path === '/stripe/webhooks') { // Do not parse the body for `/stripe/webhooks` // See https://stripe.com/docs/webhooks/signatures#verify-official-libraries bodyParser.raw({ type: 'application/json' })(req, res, next); } else { bodyParser.json({ limit: '10mb' })(req, res, next); } }); app.use(methodOverride()); app.use(cookieSession({ name: 'connect:sess', // Used to keep backward compatibility with Express 3 cookies secret: SESSION_SECRET, httpOnly: true, // so cookies are not accessible with browser JS // TODO what about https only (secure) ? maxAge: TEN_YEARS, })); // Initialize Passport! Also use passport.session() middleware, to support // persistent login sessions (recommended). app.use(passport.initialize()); app.use(passport.session()); // The site can require basic HTTP authentication to be accessed if (ENABLE_HTTP_AUTH) { const httpBasicAuthUsers = {}; const usernames = nconf.get('SITE_HTTP_AUTH_USERNAMES').split(','); const passwords = nconf.get('SITE_HTTP_AUTH_PASSWORDS').split(','); usernames.forEach((user, index) => { httpBasicAuthUsers[user] = passwords[index]; }); app.use(basicAuth({ users: httpBasicAuthUsers, challenge: true, realm: 'Habitica', })); } app.use('/api/v2', v2); app.use('/api/v1', v1); app.use(appRoutes); // the main app, also setup top-level routes staticMiddleware(app); app.use(notFoundHandler); // Error handler middleware, define as the last one. app.use(errorHandler); }