1 | /*! edge-express v1.0.0-alpha.3 by Sebastian Software <s.werner@sebastian-software.de> */
|
2 | import compression from 'compression';
|
3 | import createLocaleMiddleware from 'express-locale';
|
4 | import cookieParser from 'cookie-parser';
|
5 | import bodyParser from 'body-parser';
|
6 | import PrettyError from 'pretty-error';
|
7 | import helmet from 'helmet';
|
8 | import parameterProtection from 'hpp';
|
9 | import uuid from 'uuid';
|
10 | import express from 'express';
|
11 | import dotenv from 'dotenv';
|
12 |
|
13 | function 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 |
|
38 | var pretty = new PrettyError();
|
39 |
|
40 | // this will skip events.js and http.js and similar core node files
|
41 | pretty.skipNodeFiles();
|
42 |
|
43 | // this will skip all the trace lines about express` core and sub-modules
|
44 | pretty.skipPackage("express");
|
45 |
|
46 | function 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 */
|
56 | function 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 |
|
80 | function 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 |
|
188 | var defaultLocale = {
|
189 | "default": "en-US",
|
190 | supported: ["en-US"]
|
191 | };
|
192 |
|
193 | var defaultStatic = {
|
194 | "public": "/static/",
|
195 | path: "build/client"
|
196 | };
|
197 |
|
198 | function 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
|
254 | dotenv.config();
|
255 |
|
256 | export { addCoreMiddleware, addErrorMiddleware, addFallbackHandler, addSecurityMiddleware, createExpressServer };
|
257 | //# sourceMappingURL=index.esm.js.map
|