UNPKG

8.98 kBJavaScriptView Raw
1/*! edge-express v1.0.0-alpha.2 by Sebastian Software <s.werner@sebastian-software.de> */
2import compression from 'compression';
3import createLocaleMiddleware from 'express-locale';
4import cookieParser from 'cookie-parser';
5import bodyParser from 'body-parser';
6import PrettyError from 'pretty-error';
7import helmet from 'helmet';
8import parameterProtection from 'hpp';
9import uuid from 'uuid';
10import express from 'express';
11import dotenv from 'dotenv';
12
13function addCoreMiddleware(server, _ref) {
14 var locale = _ref.locale;
15
16 // Parse cookies via standard express tooling
17 server.use(cookieParser());
18
19 // Detect client locale and match it with configuration
20 server.use(createLocaleMiddleware({
21 priority: ["query", "cookie", "accept-language", "default"],
22 "default": locale["default"].replace(/-/, "_"),
23 allowed: locale.supported.map(function (entry) {
24 return entry.replace(/-/, "_");
25 })
26 }));
27
28 // Parse application/x-www-form-urlencoded
29 server.use(bodyParser.urlencoded({ extended: false }));
30
31 // Parse application/json
32 server.use(bodyParser.json());
33
34 // Compress output stream
35 server.use(compression());
36}
37
38var pretty = new PrettyError();
39
40// this will skip events.js and http.js and similar core node files
41pretty.skipNodeFiles();
42
43// this will skip all the trace lines about express` core and sub-modules
44pretty.skipPackage("express");
45
46function addErrorMiddleware(server) {
47 // and use it for our app's error handler:
48 server.use(function (error, request, response, next) {
49 // eslint-disable-line max-params
50 console.log(pretty.render(error));
51 next();
52 });
53}
54
55/* eslint-disable no-magic-numbers, max-params */
56function addFallbackHandler(server) {
57 // Handle 404 errors.
58 // Note: the react application middleware hands 404 paths, but it is good to
59 // have this backup for paths not handled by the universal middleware. For
60 // example you may bind a /api path to express.
61 server.use(function (request, response) {
62 // eslint-disable-line no-unused-vars,max-len
63 response.status(404).send("Sorry, that resource was not found.");
64 });
65
66 // Handle all other errors (i.e. 500).
67 // Note: You must provide specify all 4 parameters on this callback function
68 // even if they aren't used, otherwise it won't be used.
69 server.use(function (error, request, response) {
70 if (error) {
71 /* eslint-disable no-console */
72 console.log(error);
73 console.log(error.stack);
74 }
75
76 response.status(500).send("Sorry, an unexpected error occurred.");
77 });
78}
79
80function addSecurityMiddleware(server, _ref) {
81 var _ref$enableNonce = _ref.enableNonce,
82 enableNonce = _ref$enableNonce === undefined ? true : _ref$enableNonce,
83 _ref$enableCSP = _ref.enableCSP,
84 enableCSP = _ref$enableCSP === undefined ? true : _ref$enableCSP;
85
86 if (enableNonce) {
87 /* eslint-disable max-params */
88
89 // Attach a unique "nonce" to every response. This allows use to declare
90 // inline scripts as being safe for execution against our content security policy.
91 // @see https://helmetjs.github.io/docs/csp/
92 server.use(function (request, response, next) {
93 response.locals.nonce = uuid();
94 next();
95 });
96 }
97
98 // Don't expose any software information to hackers.
99 server.disable("x-powered-by");
100
101 // Prevent HTTP Parameter pollution.
102 server.use(parameterProtection());
103
104 // Content Security Policy (CSP)
105 //
106 // If you are unfamiliar with CSPs then I highly recommend that you do some
107 // reading on the subject:
108 // - https://content-security-policy.com/
109 // - https://developers.google.com/web/fundamentals/security/csp/
110 // - https://developer.mozilla.org/en/docs/Web/Security/CSP
111 // - https://helmetjs.github.io/docs/csp/
112 //
113 // If you are relying on scripts/styles/assets from other servers (internal or
114 // external to your company) then you will need to explicitly configure the
115 // CSP below to allow for this. For example you can see I have had to add
116 // the polyfill.io CDN in order to allow us to use the polyfill script.
117 // It can be a pain to manage these, but it's a really great habit to get in
118 // to.
119 //
120 // You may find CSPs annoying at first, but it is a great habit to build.
121 // The CSP configuration is an optional item for helmet, however you should
122 // not remove it without making a serious consideration that you do not require
123 // the added security.
124 var cspConfig = enableCSP ? {
125 directives: {
126 defaultSrc: ["'self'"],
127
128 scriptSrc: [
129 // Allow scripts hosted from our application.
130 "'self'",
131
132 // Note: We will execution of any inline scripts that have the following
133 // nonce identifier attached to them.
134 // This is useful for guarding your application whilst allowing an inline
135 // script to do data store rehydration (redux/mobx/apollo) for example.
136 // @see https://helmetjs.github.io/docs/csp/
137 function (request, response) {
138 return "'nonce-" + response.locals.nonce + "'";
139 },
140
141 // Required for eval-source-maps (devtool in webpack)
142 process.env.NODE_ENV === "development" ? "'unsafe-eval'" : ""].filter(function (value) {
143 return value !== "";
144 }),
145
146 styleSrc: ["'self'", "'unsafe-inline'", "blob:"],
147 imgSrc: ["'self'", "data:"],
148 fontSrc: ["'self'", "data:"],
149
150 // Note: Setting this to stricter than * breaks the service worker. :(
151 // I can't figure out how to get around this, so if you know of a safer
152 // implementation that is kinder to service workers please let me know.
153 // ["'self'", 'ws:'],
154 connectSrc: ["*"],
155
156 // objectSrc: [ "'none'" ],
157 // mediaSrc: [ "'none'" ],
158
159 childSrc: ["'self'"]
160 }
161 } : null;
162
163 if (enableCSP) {
164 server.use(helmet.contentSecurityPolicy(cspConfig));
165 }
166
167 // The xssFilter middleware sets the X-XSS-Protection header to prevent
168 // reflected XSS attacks.
169 // @see https://helmetjs.github.io/docs/xss-filter/
170 server.use(helmet.xssFilter());
171
172 // Frameguard mitigates clickjacking attacks by setting the X-Frame-Options header.
173 // @see https://helmetjs.github.io/docs/frameguard/
174 server.use(helmet.frameguard("deny"));
175
176 // Sets the X-Download-Options to prevent Internet Explorer from executing
177 // downloads in your site’s context.
178 // @see https://helmetjs.github.io/docs/ienoopen/
179 server.use(helmet.ieNoOpen());
180
181 // Don’t Sniff Mimetype middleware, noSniff, helps prevent browsers from trying
182 // to guess (“sniff”) the MIME type, which can have security implications. It
183 // does this by setting the X-Content-Type-Options header to nosniff.
184 // @see https://helmetjs.github.io/docs/dont-sniff-mimetype/
185 server.use(helmet.noSniff());
186}
187
188var defaultLocale = {
189 "default": "en-US",
190 supported: ["en-US"]
191};
192
193var defaultStatic = {
194 "public": "/static/",
195 path: "build/client"
196};
197
198function createExpressServer(_ref) {
199 var _ref$localeConfig = _ref.localeConfig,
200 localeConfig = _ref$localeConfig === undefined ? defaultLocale : _ref$localeConfig,
201 _ref$staticConfig = _ref.staticConfig,
202 staticConfig = _ref$staticConfig === undefined ? defaultStatic : _ref$staticConfig,
203 _ref$afterSecurity = _ref.afterSecurity,
204 afterSecurity = _ref$afterSecurity === undefined ? [] : _ref$afterSecurity,
205 _ref$beforeFallback = _ref.beforeFallback,
206 beforeFallback = _ref$beforeFallback === undefined ? [] : _ref$beforeFallback,
207 _ref$enableCSP = _ref.enableCSP,
208 enableCSP = _ref$enableCSP === undefined ? false : _ref$enableCSP,
209 _ref$enableNonce = _ref.enableNonce,
210 enableNonce = _ref$enableNonce === undefined ? false : _ref$enableNonce;
211
212 // Create our express based server.
213 var server = express();
214
215 addErrorMiddleware(server);
216 addSecurityMiddleware(server, { enableCSP: enableCSP, enableNonce: enableNonce });
217
218 // Allow for some early additions for middleware
219 if (afterSecurity.length > 0) {
220 afterSecurity.forEach(function (middleware) {
221 if (middleware instanceof Array) {
222 server.use.apply(server, middleware);
223 } else {
224 server.use(middleware);
225 }
226 });
227 }
228
229 addCoreMiddleware(server, { locale: localeConfig });
230
231 // Configure static serving of our webpack bundled client files.
232 if (staticConfig) {
233 server.use(staticConfig["public"], express["static"](staticConfig.path));
234 }
235
236 // Allow for some late additions for middleware
237 if (beforeFallback.length > 0) {
238 beforeFallback.forEach(function (middleware) {
239 if (middleware instanceof Array) {
240 server.use.apply(server, middleware);
241 } else {
242 server.use(middleware);
243 }
244 });
245 }
246
247 // For all things which did not went well.
248 addFallbackHandler(server);
249
250 return server;
251}
252
253// Initialize environment configuration
254dotenv.config();
255
256export { addCoreMiddleware, addErrorMiddleware, addFallbackHandler, addSecurityMiddleware, createExpressServer };
257//# sourceMappingURL=index.esm.js.map