168 lines
4.2 KiB
JavaScript
168 lines
4.2 KiB
JavaScript
// @flow
|
|
import compress from 'koa-compress';
|
|
import { compact } from 'lodash';
|
|
import helmet, {
|
|
contentSecurityPolicy,
|
|
dnsPrefetchControl,
|
|
referrerPolicy,
|
|
} from 'koa-helmet';
|
|
import logger from 'koa-logger';
|
|
import mount from 'koa-mount';
|
|
import enforceHttps from 'koa-sslify';
|
|
import Koa from 'koa';
|
|
import onerror from 'koa-onerror';
|
|
import * as Sentry from '@sentry/node';
|
|
import updates from './utils/updates';
|
|
|
|
import auth from './auth';
|
|
import api from './api';
|
|
import emails from './emails';
|
|
import routes from './routes';
|
|
|
|
const app = new Koa();
|
|
|
|
app.use(compress());
|
|
|
|
if (process.env.NODE_ENV === 'development') {
|
|
/* eslint-disable global-require */
|
|
const convert = require('koa-convert');
|
|
const webpack = require('webpack');
|
|
const devMiddleware = require('koa-webpack-dev-middleware');
|
|
const hotMiddleware = require('koa-webpack-hot-middleware');
|
|
const config = require('../webpack.config.dev');
|
|
const compile = webpack(config);
|
|
/* eslint-enable global-require */
|
|
|
|
app.use(
|
|
convert(
|
|
devMiddleware(compile, {
|
|
// display no info to console (only warnings and errors)
|
|
noInfo: true,
|
|
|
|
// display nothing to the console
|
|
quiet: false,
|
|
|
|
// switch into lazy mode
|
|
// that means no watching, but recompilation on every request
|
|
lazy: false,
|
|
|
|
// public path to bind the middleware to
|
|
// use the same as in webpack
|
|
publicPath: config.output.publicPath,
|
|
|
|
// options for formatting the statistics
|
|
stats: {
|
|
colors: true,
|
|
},
|
|
})
|
|
)
|
|
);
|
|
app.use(
|
|
convert(
|
|
hotMiddleware(compile, {
|
|
log: console.log, // eslint-disable-line
|
|
path: '/__webpack_hmr',
|
|
heartbeat: 10 * 1000,
|
|
})
|
|
)
|
|
);
|
|
app.use(logger());
|
|
|
|
app.use(mount('/emails', emails));
|
|
} else if (process.env.NODE_ENV === 'production') {
|
|
// Force redirect to HTTPS protocol unless explicitly disabled
|
|
if (process.env.FORCE_HTTPS !== 'false') {
|
|
app.use(
|
|
enforceHttps({
|
|
trustProtoHeader: true,
|
|
})
|
|
);
|
|
} else {
|
|
console.warn('Enforced https was disabled with FORCE_HTTPS env variable');
|
|
}
|
|
|
|
// trust header fields set by our proxy. eg X-Forwarded-For
|
|
app.proxy = true;
|
|
}
|
|
|
|
// catch errors in one place, automatically set status and response headers
|
|
onerror(app);
|
|
|
|
if (process.env.SENTRY_DSN) {
|
|
Sentry.init({
|
|
dsn: process.env.SENTRY_DSN,
|
|
environment: process.env.NODE_ENV,
|
|
maxBreadcrumbs: 0,
|
|
});
|
|
}
|
|
|
|
app.on('error', (error, ctx) => {
|
|
// we don't need to report every time a request stops to the bug tracker
|
|
if (error.code === 'EPIPE' || error.code === 'ECONNRESET') {
|
|
console.warn('Connection error', { error });
|
|
return;
|
|
}
|
|
|
|
if (process.env.SENTRY_DSN) {
|
|
Sentry.withScope(function(scope) {
|
|
const requestId = ctx.headers['x-request-id'];
|
|
if (requestId) {
|
|
scope.setTag('request_id', requestId);
|
|
}
|
|
scope.addEventProcessor(function(event) {
|
|
return Sentry.Handlers.parseRequest(event, ctx.request);
|
|
});
|
|
Sentry.captureException(error);
|
|
});
|
|
} else {
|
|
console.error(error);
|
|
}
|
|
});
|
|
|
|
app.use(mount('/auth', auth));
|
|
app.use(mount('/api', api));
|
|
|
|
app.use(helmet());
|
|
app.use(
|
|
contentSecurityPolicy({
|
|
directives: {
|
|
defaultSrc: ["'self'"],
|
|
scriptSrc: [
|
|
"'self'",
|
|
"'unsafe-inline'",
|
|
"'unsafe-eval'",
|
|
'gist.github.com',
|
|
'www.google-analytics.com',
|
|
'browser.sentry-cdn.com',
|
|
],
|
|
styleSrc: ["'self'", "'unsafe-inline'", 'github.githubassets.com'],
|
|
imgSrc: ['*', 'data:', 'blob:'],
|
|
frameSrc: ['*'],
|
|
connectSrc: compact([
|
|
"'self'",
|
|
process.env.AWS_S3_UPLOAD_BUCKET_URL.replace('s3:', 'localhost:'),
|
|
'www.google-analytics.com',
|
|
'sentry.io',
|
|
]),
|
|
},
|
|
})
|
|
);
|
|
app.use(dnsPrefetchControl({ allow: true }));
|
|
app.use(referrerPolicy({ policy: 'no-referrer' }));
|
|
app.use(mount(routes));
|
|
|
|
/**
|
|
* Production updates and anonymous analytics.
|
|
*
|
|
* Set ENABLE_UPDATES=false to disable them for your installation
|
|
*/
|
|
if (
|
|
process.env.ENABLE_UPDATES !== 'false' &&
|
|
process.env.NODE_ENV === 'production'
|
|
) {
|
|
updates();
|
|
setInterval(updates, 24 * 3600 * 1000);
|
|
}
|
|
|
|
export default app;
|