@boomerang-io/webapp-spa-server 0.0.11-beta.9 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,49 +1,49 @@
1
- # Change Log
2
-
3
- All notable changes to this project will be documented in this file.
4
- See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
-
6
- ## [1.7.5](https://github.ibm.com/Boomerang-Lib/boomerang.carbon.utilities/compare/@boomerang.carbon.utilities/boomerang-webapp-server@1.7.4...@boomerang.carbon.utilities/boomerang-webapp-server@1.7.5) (2020-05-26)
7
-
8
-
9
- ### Bug Fixes
10
-
11
- * config ([c8c57f8](https://github.ibm.com/Boomerang-Lib/boomerang.carbon.utilities/commit/c8c57f81ab3d70ce5d456d53b70c889cfcac8969))
12
-
13
-
14
-
15
-
16
-
17
- ## [1.7.4](https://github.ibm.com/Boomerang-Lib/boomerang.carbon.utilities/compare/@boomerang.carbon.utilities/boomerang-webapp-server@1.7.3...@boomerang.carbon.utilities/boomerang-webapp-server@1.7.4) (2020-05-26)
18
-
19
-
20
- ### Bug Fixes
21
-
22
- * some tests ([253a0ae](https://github.ibm.com/Boomerang-Lib/boomerang.carbon.utilities/commit/253a0aee7e12d68544a66e3192c01fe65099f22a))
23
-
24
-
25
-
26
-
27
-
28
- ## [1.7.3](https://github.ibm.com/Boomerang-Lib/boomerang.carbon.utilities/compare/@boomerang.carbon.utilities/boomerang-webapp-server@1.7.2...@boomerang.carbon.utilities/boomerang-webapp-server@1.7.3) (2020-05-26)
29
-
30
-
31
- ### Bug Fixes
32
-
33
- * some tests ([167ea7b](https://github.ibm.com/Boomerang-Lib/boomerang.carbon.utilities/commit/167ea7b951f151cba28ca547b4ad8d5aecb50a04))
34
-
35
-
36
-
37
-
38
-
39
- ## 1.7.2 (2020-05-26)
40
-
41
- **Note:** Version bump only for package @boomerang.carbon.utilities/boomerang-webapp-server
42
-
43
-
44
-
45
-
46
-
47
- ## 1.7.1 (2020-05-26)
48
-
49
- **Note:** Version bump only for package @boomerang.carbon.utilities/boomerang-webapp-server
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
+
6
+ ## [1.7.5](https://github.ibm.com/Boomerang-Lib/boomerang.carbon.utilities/compare/@boomerang.carbon.utilities/boomerang-webapp-server@1.7.4...@boomerang.carbon.utilities/boomerang-webapp-server@1.7.5) (2020-05-26)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * config ([c8c57f8](https://github.ibm.com/Boomerang-Lib/boomerang.carbon.utilities/commit/c8c57f81ab3d70ce5d456d53b70c889cfcac8969))
12
+
13
+
14
+
15
+
16
+
17
+ ## [1.7.4](https://github.ibm.com/Boomerang-Lib/boomerang.carbon.utilities/compare/@boomerang.carbon.utilities/boomerang-webapp-server@1.7.3...@boomerang.carbon.utilities/boomerang-webapp-server@1.7.4) (2020-05-26)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * some tests ([253a0ae](https://github.ibm.com/Boomerang-Lib/boomerang.carbon.utilities/commit/253a0aee7e12d68544a66e3192c01fe65099f22a))
23
+
24
+
25
+
26
+
27
+
28
+ ## [1.7.3](https://github.ibm.com/Boomerang-Lib/boomerang.carbon.utilities/compare/@boomerang.carbon.utilities/boomerang-webapp-server@1.7.2...@boomerang.carbon.utilities/boomerang-webapp-server@1.7.3) (2020-05-26)
29
+
30
+
31
+ ### Bug Fixes
32
+
33
+ * some tests ([167ea7b](https://github.ibm.com/Boomerang-Lib/boomerang.carbon.utilities/commit/167ea7b951f151cba28ca547b4ad8d5aecb50a04))
34
+
35
+
36
+
37
+
38
+
39
+ ## 1.7.2 (2020-05-26)
40
+
41
+ **Note:** Version bump only for package @boomerang.carbon.utilities/boomerang-webapp-server
42
+
43
+
44
+
45
+
46
+
47
+ ## 1.7.1 (2020-05-26)
48
+
49
+ **Note:** Version bump only for package @boomerang.carbon.utilities/boomerang-webapp-server
package/README.md CHANGED
@@ -1,107 +1,107 @@
1
- # Webapp SPA Server
2
-
3
- Provide a consistent way to deploy Boomerang React SPAs with client-side routing into an IBM Cloud Private environment.
4
-
5
- ## Features
6
-
7
- - Serve static assets
8
- - Client-side routing support for SPAs
9
- - Dynamic data and script injection into HTML document at run-time
10
- - Logging with [@boomerang-io/logger-middleware](https://github.com/boomerang-io/webapp-packages/src/packages/logger-middleware)
11
- - Cloud native health checking with [Cloud Native Health Connect](@cloudnative/health-connect)
12
- - New Relic monitoring
13
-
14
- ## Design
15
-
16
- The server can be invoked via a command line or imported as a configurable function to be executed.
17
-
18
- - CLI - for easy stand alone use that can be invoked via a script e.g. in `npm scripts`
19
- - Function - create server and run it within a `node.js` file
20
-
21
- ## CLI
22
-
23
- The server can be run via the CLI interface with configuration pass as options.
24
-
25
- Enter the following to see the manual
26
-
27
- ```shell
28
- boomerang-webapp-server --help
29
- ```
30
-
31
- Command
32
-
33
- ```shell
34
- boomerang-webapp-server serve
35
- ```
36
-
37
- Options
38
-
39
- | **Option** | **Alias** | **Description** |
40
- | :-------------------------- | :-------: | :----------------------------------------------------------------------------------------------- |
41
- | --cors | -c | CORS configuration using [cors](https://www.npmjs.com/package/cors) package. Accepts JSON string |
42
- | --disableInjectHTMLHeadData | -d | Toggle whether the app needs to inject data into the header. Defaults to `false` |
43
- | --dotenvFilePath | -p | Path to local .env file to read in. Useful for local testing |
44
-
45
- ## Use as a function
46
-
47
- ```javascript
48
- const server = require("@boomerang-io/webapp-spa-server");
49
- server({
50
- cors: {},
51
- disableInjectHTMLHeadData: true,
52
- });
53
- ```
54
-
55
- ## Environment Variables
56
-
57
- The following env variables are assumed to exist either from a local `.env` file or passed in to the container at runtime. If nothing is passed it, it will default to the following:
58
-
59
- | **Variable** | **Purpose** | **Type** |
60
- | :--------------------------: | :-----------------------------------------------------------------------: | :------------------: |
61
- | APP_ROOT | Root context of the application | string |
62
- | BUILD_DIR | directory relative to the exeuction where app files are located | string |
63
- | HTML_HEAD_INJECTED_DATA_KEYS | Environment variables to inject into the HTML document | comma delimited list |
64
- | HTML_HEAD_INJECTED_SCRIPTS | Scripts to inject into HTML document. Files need to be in the `BUILD_DIR` | comma delimited list |
65
- | NEW_RELIC_APP_NAME | App name for monitoring | string |
66
- | NEW_RELIC_LICENSE_KEY | License key for monitoring | string |
67
- | PORT | Port for server to run on | number |
68
-
69
- ## Defaults
70
-
71
- Some of the values, both config and environment variables have defaults in the server that make deploying to the IBM Cloud Private work out-of-the-box.
72
-
73
- APP_ROOT
74
-
75
- - "/"
76
-
77
- BUILD_DIR
78
-
79
- - "build"
80
-
81
- CORS
82
-
83
- ```json
84
- {
85
- "origin": "*",
86
- "allowedHeaders": "Content-Type, Authorization, Content-Length, X-Requested-With",
87
- "methods": "DELETE,GET,OPTIONS,PATCH,POST,PUT"
88
- }
89
- ```
90
-
91
- HTML_HEAD_INJECTED_DATA_KEYS
92
-
93
- - APP_ROOT
94
- - BASE_APPS_ENV_URL
95
- - BASE_LAUNCH_ENV_URL
96
- - BASE_SERVICE_ENV_URL
97
- - BASE_WWW_ENV_URL
98
- - CORE_APPS_ENV_URL
99
- - CORE_ENV_URL
100
- - CORE_SERVICE_ENV_URL
101
- - PRODUCT_APPS_ENV_URL
102
- - PRODUCT_ENV_URL
103
- - PRODUCT_SERVICE_ENV_URL
104
-
105
- PORT
106
-
107
- - 3000
1
+ # Webapp SPA Server
2
+
3
+ Provide a consistent way to deploy Boomerang React SPAs with client-side routing into an IBM Cloud Private environment.
4
+
5
+ ## Features
6
+
7
+ - Serve static assets
8
+ - Client-side routing support for SPAs
9
+ - Dynamic data and script injection into HTML document at run-time
10
+ - Logging with [@boomerang-io/logger-middleware](https://github.com/boomerang-io/webapp-packages/src/packages/logger-middleware)
11
+ - Cloud native health checking with [Cloud Native Health Connect](@cloudnative/health-connect)
12
+ - New Relic monitoring
13
+
14
+ ## Design
15
+
16
+ The server can be invoked via a command line or imported as a configurable function to be executed.
17
+
18
+ - CLI - for easy stand alone use that can be invoked via a script e.g. in `npm scripts`
19
+ - Function - create server and run it within a `node.js` file
20
+
21
+ ## CLI
22
+
23
+ The server can be run via the CLI interface with configuration pass as options.
24
+
25
+ Enter the following to see the manual
26
+
27
+ ```shell
28
+ boomerang-webapp-server --help
29
+ ```
30
+
31
+ Command
32
+
33
+ ```shell
34
+ boomerang-webapp-server serve
35
+ ```
36
+
37
+ Options
38
+
39
+ | **Option** | **Alias** | **Description** |
40
+ | :-------------------------- | :-------: | :----------------------------------------------------------------------------------------------- |
41
+ | --cors | -c | CORS configuration using [cors](https://www.npmjs.com/package/cors) package. Accepts JSON string |
42
+ | --disableInjectHTMLHeadData | -d | Toggle whether the app needs to inject data into the header. Defaults to `false` |
43
+ | --dotenvFilePath | -p | Path to local .env file to read in. Useful for local testing |
44
+
45
+ ## Use as a function
46
+
47
+ ```javascript
48
+ const server = require("@boomerang-io/webapp-spa-server");
49
+ server({
50
+ cors: {},
51
+ disableInjectHTMLHeadData: true,
52
+ });
53
+ ```
54
+
55
+ ## Environment Variables
56
+
57
+ The following env variables are assumed to exist either from a local `.env` file or passed in to the container at runtime. If nothing is passed it, it will default to the following:
58
+
59
+ | **Variable** | **Purpose** | **Type** |
60
+ | :--------------------------: | :-----------------------------------------------------------------------: | :------------------: |
61
+ | APP_ROOT | Root context of the application | string |
62
+ | BUILD_DIR | directory relative to the exeuction where app files are located | string |
63
+ | HTML_HEAD_INJECTED_DATA_KEYS | Environment variables to inject into the HTML document | comma delimited list |
64
+ | HTML_HEAD_INJECTED_SCRIPTS | Scripts to inject into HTML document. Files need to be in the `BUILD_DIR` | comma delimited list |
65
+ | NEW_RELIC_APP_NAME | App name for monitoring | string |
66
+ | NEW_RELIC_LICENSE_KEY | License key for monitoring | string |
67
+ | PORT | Port for server to run on | number |
68
+
69
+ ## Defaults
70
+
71
+ Some of the values, both config and environment variables have defaults in the server that make deploying to the IBM Cloud Private work out-of-the-box.
72
+
73
+ APP_ROOT
74
+
75
+ - "/"
76
+
77
+ BUILD_DIR
78
+
79
+ - "build"
80
+
81
+ CORS
82
+
83
+ ```json
84
+ {
85
+ "origin": "*",
86
+ "allowedHeaders": "Content-Type, Authorization, Content-Length, X-Requested-With",
87
+ "methods": "DELETE,GET,OPTIONS,PATCH,POST,PUT"
88
+ }
89
+ ```
90
+
91
+ HTML_HEAD_INJECTED_DATA_KEYS
92
+
93
+ - APP_ROOT
94
+ - BASE_APPS_ENV_URL
95
+ - BASE_LAUNCH_ENV_URL
96
+ - BASE_SERVICE_ENV_URL
97
+ - BASE_WWW_ENV_URL
98
+ - CORE_APPS_ENV_URL
99
+ - CORE_ENV_URL
100
+ - CORE_SERVICE_ENV_URL
101
+ - PRODUCT_APPS_ENV_URL
102
+ - PRODUCT_ENV_URL
103
+ - PRODUCT_SERVICE_ENV_URL
104
+
105
+ PORT
106
+
107
+ - 3000
package/cli.js CHANGED
@@ -1,40 +1,40 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Import and execute app
5
- */
6
- const app = require("./index");
7
-
8
- require("yargs") // eslint-disable-line
9
- .command("serve", "start the webapp server", (yargs) => {
10
- const { cors, dotenvFilePath, disableInjectHTMLHeadData } = yargs.argv;
11
- // Import .env file
12
- if (dotenvFilePath) {
13
- require("dotenv").config({
14
- path: require("path").join(process.cwd(), dotenvFilePath),
15
- });
16
- }
17
- // Invoke server
18
- app({ cors, disableInjectHTMLHeadData });
19
- })
20
- .option("cors", {
21
- alias: "c",
22
- describe: "CORS configuration using cors package. Accepts JSON string.",
23
- type: "string",
24
- })
25
-
26
- .option("disableInjectHTMLHeadData", {
27
- alias: "d",
28
- describe: "Enable injection of data and scripts into the head of the HTML file.",
29
- default: false,
30
- type: "boolean",
31
- })
32
- .option("dotenvFilePath", {
33
- alias: "p",
34
- default: false,
35
- describe: "Path to local .env file to read in. Useful for local testing.",
36
- type: "string",
37
- })
38
- .coerce({
39
- cors: JSON.parse,
40
- }).argv;
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Import and execute app
5
+ */
6
+ const app = require("./index");
7
+
8
+ require("yargs") // eslint-disable-line
9
+ .command("serve", "start the webapp server", (yargs) => {
10
+ const { cors, dotenvFilePath, disableInjectHTMLHeadData } = yargs.argv;
11
+ // Import .env file
12
+ if (dotenvFilePath) {
13
+ require("dotenv").config({
14
+ path: require("path").join(process.cwd(), dotenvFilePath),
15
+ });
16
+ }
17
+ // Invoke server
18
+ app({ cors, disableInjectHTMLHeadData });
19
+ })
20
+ .option("cors", {
21
+ alias: "c",
22
+ describe: "CORS configuration using cors package. Accepts JSON string.",
23
+ type: "string",
24
+ })
25
+
26
+ .option("disableInjectHTMLHeadData", {
27
+ alias: "d",
28
+ describe: "Enable injection of data and scripts into the head of the HTML file.",
29
+ default: false,
30
+ type: "boolean",
31
+ })
32
+ .option("dotenvFilePath", {
33
+ alias: "p",
34
+ default: false,
35
+ describe: "Path to local .env file to read in. Useful for local testing.",
36
+ type: "string",
37
+ })
38
+ .coerce({
39
+ cors: JSON.parse,
40
+ }).argv;
package/config.js CHANGED
@@ -1,20 +1,20 @@
1
- exports.defaultHtmlHeadInjectDataKeys = [
2
- "APP_ROOT",
3
- "BASE_APPS_ENV_URL",
4
- "BASE_LAUNCH_ENV_URL",
5
- "BASE_SERVICE_ENV_URL",
6
- "BASE_WWW_ENV_URL",
7
- "BOSUN_PRODUCT_APP_ENV_URL",
8
- "BOSUN_PRODUCT_SERVICE_ENV_URL",
9
- "CICD_PRODUCT_APP_ENV_URL",
10
- "CICD_PRODUCT_SERVICE_ENV_URL",
11
- "CORE_APPS_ENV_URL",
12
- "CORE_ENV_URL",
13
- "CORE_SERVICE_ENV_URL",
14
- "NEW_RELIC_BROWSER_ID",
15
- "NEW_RELIC_BROWSER_KEY",
16
- "PRODUCT_STANDALONE",
17
- "PRODUCT_SERVICE_ENV_URL",
18
- "PRODUCT_APPS_ENV_URL",
19
- "PRODUCT_ENV_URL",
20
- ];
1
+ exports.defaultHtmlHeadInjectDataKeys = [
2
+ "APP_ROOT",
3
+ "BASE_APPS_ENV_URL",
4
+ "BASE_LAUNCH_ENV_URL",
5
+ "BASE_SERVICE_ENV_URL",
6
+ "BASE_WWW_ENV_URL",
7
+ "BOSUN_PRODUCT_APP_ENV_URL",
8
+ "BOSUN_PRODUCT_SERVICE_ENV_URL",
9
+ "CICD_PRODUCT_APP_ENV_URL",
10
+ "CICD_PRODUCT_SERVICE_ENV_URL",
11
+ "CORE_APPS_ENV_URL",
12
+ "CORE_ENV_URL",
13
+ "CORE_SERVICE_ENV_URL",
14
+ "NEW_RELIC_BROWSER_ID",
15
+ "NEW_RELIC_BROWSER_KEY",
16
+ "PRODUCT_STANDALONE",
17
+ "PRODUCT_SERVICE_ENV_URL",
18
+ "PRODUCT_APPS_ENV_URL",
19
+ "PRODUCT_ENV_URL",
20
+ ];
package/index.js CHANGED
@@ -1,204 +1,243 @@
1
- "use strict";
2
- /*eslint-env node*/
3
-
4
- const path = require("path");
5
- const fs = require("fs");
6
- const cors = require("cors");
7
- const serialize = require("serialize-javascript");
8
- const boomerangLogger = require("@boomerang-io/logger-middleware")("webapp-spa-server/index.js");
9
- const health = require("@cloudnative/health-connect");
10
- const defaultHtmlHeadInjectDataKeys = require("./config").defaultHtmlHeadInjectDataKeys;
11
-
12
- // Get logger function
13
- const logger = boomerangLogger.logger;
14
-
15
- /**
16
- * Begin exported module
17
- */
18
-
19
- function createBoomerangServer({
20
- corsConfig = {
21
- origin: "*",
22
- allowedHeaders: "Content-Type, Authorization, Content-Length, X-Requested-With",
23
- methods: "DELETE,GET,OPTIONS,PATCH,POST,PUT",
24
- },
25
- disableInjectHTMLHeadData,
26
- }) {
27
- /**
28
- * Read in values from process.env object
29
- * Set defaults for the platform for unprovided values
30
- */
31
- const {
32
- APP_ROOT = "/",
33
- PORT = 3000,
34
- HTML_HEAD_INJECTED_DATA_KEYS = defaultHtmlHeadInjectDataKeys.join(),
35
- NEW_RELIC_APP_NAME,
36
- NEW_RELIC_LICENSE_KEY,
37
- HTML_HEAD_INJECTED_SCRIPTS,
38
- BUILD_DIR = "build",
39
- BASE_LAUNCH_ENV_URL,
40
- GA_SITE_ID,
41
- } = process.env;
42
-
43
- // Monitoring
44
- if (NEW_RELIC_APP_NAME && NEW_RELIC_LICENSE_KEY) {
45
- require("newrelic");
46
- }
47
-
48
- /**
49
- * Start Express app
50
- */
51
- const express = require("express");
52
- const app = express();
53
-
54
- // Compression
55
- const compression = require("compression");
56
- app.use(compression());
57
-
58
- // Logging
59
- app.use(boomerangLogger.middleware);
60
-
61
- // Security
62
- const helmet = require("helmet");
63
- app.use(helmet());
64
- app.disable("x-powered-by");
65
- app.use(cors(corsConfig));
66
-
67
- // Parsing
68
- const bodyParser = require("body-parser");
69
- app.use(bodyParser.urlencoded({ extended: true }));
70
-
71
- // Initialize healthchecker and add routes
72
- const healthchecker = new health.HealthChecker();
73
- app.use("/health", health.LivenessEndpoint(healthchecker));
74
- app.use("/ready", health.ReadinessEndpoint(healthchecker));
75
-
76
- // Create endpoint for the app serve static assets
77
- const appRouter = express.Router();
78
-
79
- /**
80
- * Next two routes are needed for serving apps with client-side routing
81
- * Do NOT return index.html file by default if `disableInjectHTMLHeadData = true`. We need append data to it.
82
- * It will be returned on the second route
83
- * https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#serving-apps-with-client-side-routing
84
- */
85
- if (!disableInjectHTMLHeadData) {
86
- appRouter.use(
87
- "/",
88
- express.static(path.join(process.cwd(), BUILD_DIR), {
89
- index: false,
90
- })
91
- );
92
- appRouter.get("/*", (req, res) =>
93
- injectEnvDataAndScriptsIntoHTML(
94
- res,
95
- BUILD_DIR,
96
- HTML_HEAD_INJECTED_DATA_KEYS,
97
- HTML_HEAD_INJECTED_SCRIPTS,
98
- APP_ROOT,
99
- GA_SITE_ID,
100
- BASE_LAUNCH_ENV_URL,
101
- )
102
- );
103
- } else {
104
- appRouter.use("/", express.static(path.join(process.cwd(), BUILD_DIR)));
105
- }
106
-
107
- app.use(APP_ROOT, appRouter);
108
-
109
- // Start server on the specified port and binding host
110
- app.listen(PORT, "0.0.0.0", function () {
111
- logger.debug("server starting on", PORT);
112
- logger.debug(`serving on root context: ${APP_ROOT}`);
113
- logger.info(`View app: http://localhost:${PORT}${APP_ROOT}`);
114
- });
115
-
116
- // Return server if needed to be used in an app
117
- return app;
118
- }
119
-
120
- /**
121
- * Start utility functions
122
- */
123
-
124
- /**
125
- * Add JSON data and scripts to the html file based on environment. Enables same docker image to be used in any environment
126
- * https://medium.com/@housecor/12-rules-for-professional-javascript-in-2015-f158e7d3f0fc
127
- * https://stackoverflow.com/questions/33027089/res-sendfile-in-node-express-with-passing-data-along
128
- * @param {function} res - Express response function
129
- * @param {string} buildDir - build directory for building up path to index.html file
130
- * @param {string} injectedDataKeys - string of comma delimited values
131
- * @param {string} injectedScripts - string of comma delimited values
132
- * @param {string} appRoot - root context off app. Used for script injection
133
- * @param {string} gaSiteId - siteID to be injected on scripts to support GA
134
- * @param {string} baseLaunchUrl - base url to determine GA primaryCategory
135
- */
136
- function injectEnvDataAndScriptsIntoHTML(res, buildDir, injectedDataKeys, injectedScripts, appRoot, gaSiteId, baseLaunchUrl) {
137
- /**
138
- * Create objects to be injected into application via the HEAD tag
139
- */
140
- // Build script for GA integration
141
- const headScripstGA = Boolean(gaSiteId) ?
142
- `<script type="text/javascript">
143
- window.idaPageIsSPA = true;
144
- digitalData = {
145
- page: {
146
- pageInfo: {
147
- ibm: {
148
- siteID: 'IBMTESTWWW',
149
- }
150
- },
151
- category: {
152
- primaryCategory: 'PC100'
153
- }
154
- }
155
- };
156
- </script>
157
- <script src="//1.www.s81c.com/common/stats/ibm-common.js" type="text/javascript"></script>`
158
- : "";
159
- // Build up object of external data to append
160
- const headInjectedData = injectedDataKeys.split(",").reduce((acc, key) => {
161
- acc[key] = process.env[key];
162
- return acc;
163
- }, {});
164
-
165
- // Build up string of scripts to append, absolute path
166
- const headScriptsTags = injectedScripts
167
- ? injectedScripts
168
- .split(",")
169
- .reduce((acc, currentValue) => `${acc}<script src="${appRoot}/${currentValue}"></script>`, "")
170
- : "";
171
- // Set the response type so browser interprets it as an html file
172
- res.type(".html");
173
-
174
- // Read in HTML file and add callback functions for EventEmitter events produced by ReadStream
175
- fs.createReadStream(path.join(process.cwd(), buildDir, "index.html"))
176
- .on("end", () => {
177
- res.end();
178
- })
179
- .on("error", (e) => logger.error(e))
180
- .on("data", (chunk) => res.write(addHeadData(chunk)));
181
-
182
- /**
183
- * Convert buffer to string and replace closing head tag with env-specific data and additional scripts
184
- * Serialize data for security
185
- * https://medium.com/node-security/the-most-common-xss-vulnerability-in-react-js-applications-2bdffbcc1fa0
186
- * @param {Buffer} chunk
187
- * @return {string} replaced string with data interopolated
188
- */
189
- function addHeadData(chunk) {
190
- return chunk.toString().replace(
191
- "</head>",
192
- `<script>
193
- window._SERVER_DATA = ${serialize(headInjectedData, {
194
- isJSON: true,
195
- })};
196
- </script>
197
- ${headScripstGA}
198
- ${headScriptsTags}
199
- </head>`
200
- );
201
- }
202
- }
203
-
204
- module.exports = createBoomerangServer;
1
+ "use strict";
2
+ /*eslint-env node*/
3
+
4
+ const path = require("path");
5
+ const fs = require("fs");
6
+ const cors = require("cors");
7
+ const serialize = require("serialize-javascript");
8
+ const boomerangLogger = require("@boomerang-io/logger-middleware")("webapp-spa-server/index.js");
9
+ const health = require("@cloudnative/health-connect");
10
+ const defaultHtmlHeadInjectDataKeys = require("./config").defaultHtmlHeadInjectDataKeys;
11
+
12
+ // Get logger function
13
+ const logger = boomerangLogger.logger;
14
+
15
+ /**
16
+ * Begin exported module
17
+ */
18
+
19
+ function createBoomerangServer({
20
+ corsConfig = {
21
+ origin: "*",
22
+ allowedHeaders: "Content-Type, Authorization, Content-Length, X-Requested-With",
23
+ methods: "DELETE,GET,OPTIONS,PATCH,POST,PUT",
24
+ },
25
+ disableInjectHTMLHeadData,
26
+ }) {
27
+ /**
28
+ * Read in values from process.env object
29
+ * Set defaults for the platform for unprovided values
30
+ */
31
+ const {
32
+ APP_ROOT = "/",
33
+ PORT = 3000,
34
+ HTML_HEAD_INJECTED_DATA_KEYS = defaultHtmlHeadInjectDataKeys.join(),
35
+ NEW_RELIC_APP_NAME,
36
+ NEW_RELIC_LICENSE_KEY,
37
+ HTML_HEAD_INJECTED_SCRIPTS,
38
+ BUILD_DIR = "build",
39
+ BASE_LAUNCH_ENV_URL,
40
+ GA_SITE_ID,
41
+ ENABLE_BEEHEARD_SURVEY,
42
+ } = process.env;
43
+ logger.debug("PROCESS ENV: ", process.env);
44
+
45
+ // Monitoring
46
+ if (NEW_RELIC_APP_NAME && NEW_RELIC_LICENSE_KEY) {
47
+ require("newrelic");
48
+ }
49
+
50
+ /**
51
+ * Start Express app
52
+ */
53
+ const express = require("express");
54
+ const app = express();
55
+
56
+ // Compression
57
+ const compression = require("compression");
58
+ app.use(compression());
59
+
60
+ // Logging
61
+ app.use(boomerangLogger.middleware);
62
+
63
+ // Security
64
+ const helmet = require("helmet");
65
+ app.use(
66
+ helmet({
67
+ contentSecurityPolicy: false,
68
+ })
69
+ );
70
+ app.disable("x-powered-by");
71
+ app.use(cors(corsConfig));
72
+
73
+ // Parsing
74
+ const bodyParser = require("body-parser");
75
+ app.use(bodyParser.urlencoded({ extended: true }));
76
+
77
+ // Initialize healthchecker and add routes
78
+ const healthchecker = new health.HealthChecker();
79
+ app.use("/health", health.LivenessEndpoint(healthchecker));
80
+ app.use("/ready", health.ReadinessEndpoint(healthchecker));
81
+
82
+ // Create endpoint for the app serve static assets
83
+ const appRouter = express.Router();
84
+
85
+ /**
86
+ * Next two routes are needed for serving apps with client-side routing
87
+ * Do NOT return index.html file by default if `disableInjectHTMLHeadData = true`. We need append data to it.
88
+ * It will be returned on the second route
89
+ * https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#serving-apps-with-client-side-routing
90
+ */
91
+ logger.debug("0 - disableInjectHTMLHeadData: ", disableInjectHTMLHeadData);
92
+ if (!disableInjectHTMLHeadData) {
93
+ logger.debug("1 - URL and ID: ", GA_SITE_ID, BASE_LAUNCH_ENV_URL);
94
+ appRouter.use(
95
+ "/",
96
+ express.static(path.join(process.cwd(), BUILD_DIR), {
97
+ index: false,
98
+ })
99
+ );
100
+ appRouter.get("/*", (req, res) =>
101
+ injectEnvDataAndScriptsIntoHTML(
102
+ res,
103
+ BUILD_DIR,
104
+ HTML_HEAD_INJECTED_DATA_KEYS,
105
+ HTML_HEAD_INJECTED_SCRIPTS,
106
+ APP_ROOT,
107
+ GA_SITE_ID,
108
+ BASE_LAUNCH_ENV_URL,
109
+ ENABLE_BEEHEARD_SURVEY
110
+ )
111
+ );
112
+ } else {
113
+ logger.debug("1 - disableInjectHTMLHeadData: ", disableInjectHTMLHeadData);
114
+ appRouter.use("/", express.static(path.join(process.cwd(), BUILD_DIR)));
115
+ }
116
+
117
+ app.use(APP_ROOT, appRouter);
118
+
119
+ // Start server on the specified port and binding host
120
+ app.listen(PORT, "0.0.0.0", function () {
121
+ logger.debug("server starting on", PORT);
122
+ logger.debug(`serving on root context: ${APP_ROOT}`);
123
+ logger.info(`View app: http://localhost:${PORT}${APP_ROOT}`);
124
+ });
125
+
126
+ // Return server if needed to be used in an app
127
+ return app;
128
+ }
129
+
130
+ /**
131
+ * Start utility functions
132
+ */
133
+
134
+ /**
135
+ * Add JSON data and scripts to the html file based on environment. Enables same docker image to be used in any environment
136
+ * https://medium.com/@housecor/12-rules-for-professional-javascript-in-2015-f158e7d3f0fc
137
+ * https://stackoverflow.com/questions/33027089/res-sendfile-in-node-express-with-passing-data-along
138
+ * @param {function} res - Express response function
139
+ * @param {string} buildDir - build directory for building up path to index.html file
140
+ * @param {string} injectedDataKeys - string of comma delimited values
141
+ * @param {string} injectedScripts - string of comma delimited values
142
+ * @param {string} appRoot - root context off app. Used for script injection
143
+ * @param {string} gaSiteId - siteID to be injected on scripts to support GA
144
+ * @param {string} baseLaunchUrl - base url to determine GA primaryCategory
145
+ * @param {boolean} enableBeeheardSurvey - true/false value configured at helm to decide to insert survey script
146
+ */
147
+ function injectEnvDataAndScriptsIntoHTML(
148
+ res,
149
+ buildDir,
150
+ injectedDataKeys,
151
+ injectedScripts,
152
+ appRoot,
153
+ gaSiteId,
154
+ baseLaunchUrl,
155
+ enableBeeheardSurvey
156
+ ) {
157
+ /**
158
+ * Create objects to be injected into application via the HEAD tag
159
+ */
160
+ // Build script for GA integration
161
+ logger.debug("2 - GA Site ID: ", gaSiteId);
162
+ const headScripstGA = Boolean(gaSiteId)
163
+ ? `<script type="text/javascript">
164
+ window.idaPageIsSPA = true;
165
+ window._ibmAnalytics = {
166
+ settings: {
167
+ name: "IBM_Services_Essentials",
168
+ isSpa: true,
169
+ tealiumProfileName: "ibm-web-app",
170
+ },
171
+ trustarc: {
172
+ isCookiePreferencesButtonAlwaysOn: false,
173
+ },
174
+ };
175
+ digitalData = {
176
+ page: {
177
+ pageInfo: {
178
+ ibm: {
179
+ siteID: '${gaSiteId}',
180
+ }
181
+ },
182
+ category: {
183
+ primaryCategory: 'PC100'
184
+ }
185
+ }
186
+ };
187
+ </script>
188
+ <script src="//1.www.s81c.com/common/stats/ibm-common.js" type="text/javascript"></script>
189
+ `
190
+ : "";
191
+
192
+ const headScriptBeeheardSurvey = Boolean(enableBeeheardSurvey)
193
+ ? '<script async src="https://beeheard.dal1a.cirrus.ibm.com/survey/preconfig/HHPxpQgN.js"></script>'
194
+ : "";
195
+
196
+ // Build up object of external data to append
197
+ const headInjectedData = injectedDataKeys.split(",").reduce((acc, key) => {
198
+ acc[key] = process.env[key];
199
+ return acc;
200
+ }, {});
201
+
202
+ // Build up string of scripts to append, absolute path
203
+ const headScriptsTags = injectedScripts
204
+ ? injectedScripts
205
+ .split(",")
206
+ .reduce((acc, currentValue) => `${acc}<script src="${appRoot}/${currentValue}"></script>`, "")
207
+ : "";
208
+ // Set the response type so browser interprets it as an html file
209
+ res.type(".html");
210
+
211
+ // Read in HTML file and add callback functions for EventEmitter events produced by ReadStream
212
+ fs.createReadStream(path.join(process.cwd(), buildDir, "index.html"))
213
+ .on("end", () => {
214
+ res.end();
215
+ })
216
+ .on("error", (e) => logger.error(e))
217
+ .on("data", (chunk) => res.write(addHeadData(chunk)));
218
+
219
+ /**
220
+ * Convert buffer to string and replace closing head tag with env-specific data and additional scripts
221
+ * Serialize data for security
222
+ * https://medium.com/node-security/the-most-common-xss-vulnerability-in-react-js-applications-2bdffbcc1fa0
223
+ * @param {Buffer} chunk
224
+ * @return {string} replaced string with data interopolated
225
+ */
226
+ logger.debug("3 - GA script: ", headScripstGA);
227
+ function addHeadData(chunk) {
228
+ return chunk.toString().replace(
229
+ "</head>",
230
+ `<script>
231
+ window._SERVER_DATA = ${serialize(headInjectedData, {
232
+ isJSON: true,
233
+ })};
234
+ </script>
235
+ ${headScriptBeeheardSurvey}
236
+ ${headScripstGA}
237
+ ${headScriptsTags}
238
+ </head>`
239
+ );
240
+ }
241
+ }
242
+
243
+ module.exports = createBoomerangServer;
package/newrelic.js CHANGED
@@ -1,28 +1,28 @@
1
- "use strict";
2
-
3
- /**
4
- * New Relic agent configuration.
5
- *
6
- * See lib/config.default.js in the agent distribution for a more complete
7
- * description of configuration variables and their potential values.
8
- */
9
- exports.config = {
10
- /**
11
- * Array of application names.
12
- */
13
- app_name: process.env.NEW_RELIC_APP_NAME,
14
- /**
15
- * Your New Relic license key.
16
- */
17
- license_key: process.env.NEW_RELIC_LICENSE_KEY,
18
- proxy_host: process.env.NEW_RELIC_PROXY_HOST,
19
- proxy_port: process.env.NEW_RELIC_PROXY_PORT,
20
- logging: {
21
- /**
22
- * Level at which to log. 'trace' is most useful to New Relic when diagnosing
23
- * issues with the agent, 'info' and higher will impose the least overhead on
24
- * production applications.
25
- */
26
- level: "info",
27
- },
28
- };
1
+ "use strict";
2
+
3
+ /**
4
+ * New Relic agent configuration.
5
+ *
6
+ * See lib/config.default.js in the agent distribution for a more complete
7
+ * description of configuration variables and their potential values.
8
+ */
9
+ exports.config = {
10
+ /**
11
+ * Array of application names.
12
+ */
13
+ app_name: process.env.NEW_RELIC_APP_NAME,
14
+ /**
15
+ * Your New Relic license key.
16
+ */
17
+ license_key: process.env.NEW_RELIC_LICENSE_KEY,
18
+ proxy_host: process.env.NEW_RELIC_PROXY_HOST,
19
+ proxy_port: process.env.NEW_RELIC_PROXY_PORT,
20
+ logging: {
21
+ /**
22
+ * Level at which to log. 'trace' is most useful to New Relic when diagnosing
23
+ * issues with the agent, 'info' and higher will impose the least overhead on
24
+ * production applications.
25
+ */
26
+ level: "info",
27
+ },
28
+ };
package/package.json CHANGED
@@ -1,49 +1,49 @@
1
- {
2
- "name": "@boomerang-io/webapp-spa-server",
3
- "description": "Webapp Server for React-based SPA w/ client-side routing",
4
- "version": "0.0.11-beta.9",
5
- "author": {
6
- "name": "Tim Bula",
7
- "email": "timrbula@gmail.com"
8
- },
9
- "license": "Apache-2.0",
10
- "repository": {
11
- "type": "git",
12
- "url": "git@github.com:boomerang-io/webapp-packages",
13
- "directory": "packages/webapp-spa-server"
14
- },
15
- "homepage": "https://github.com/boomerang-io/webapp-packages",
16
- "bugs": {
17
- "url": "https://github.com/boomerang-io/webapp-packages/issues"
18
- },
19
- "bin": {
20
- "boomerang-webapp-server": "./cli.js"
21
- },
22
- "main": "index.js",
23
- "scripts": {
24
- "dev": "nodemon --exec npm run-script start",
25
- "start": "node tester.js"
26
- },
27
- "dependencies": {
28
- "@boomerang-io/logger-middleware": "0.0.1",
29
- "@cloudnative/health-connect": "1.0.2",
30
- "body-parser": "1.18.3",
31
- "compression": "1.7.3",
32
- "cors": "2.8.5",
33
- "dotenv": "6.2.0",
34
- "express": "4.16.4",
35
- "helmet": "^3.23.1",
36
- "newrelic": "^7.3.1",
37
- "serialize-javascript": "4.0.0",
38
- "yargs": "^16.2.0"
39
- },
40
- "devDependencies": {
41
- "nodemon": "1.18.10"
42
- },
43
- "keywords": [
44
- "express",
45
- "node",
46
- "server",
47
- "spa"
48
- ]
49
- }
1
+ {
2
+ "name": "@boomerang-io/webapp-spa-server",
3
+ "description": "Webapp Server for React-based SPA w/ client-side routing",
4
+ "version": "1.0.0",
5
+ "author": {
6
+ "name": "Tim Bula",
7
+ "email": "timrbula@gmail.com"
8
+ },
9
+ "license": "Apache-2.0",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git@github.com:boomerang-io/webapp-packages",
13
+ "directory": "packages/webapp-spa-server"
14
+ },
15
+ "homepage": "https://github.com/boomerang-io/webapp-packages",
16
+ "bugs": {
17
+ "url": "https://github.com/boomerang-io/webapp-packages/issues"
18
+ },
19
+ "bin": {
20
+ "boomerang-webapp-server": "./cli.js"
21
+ },
22
+ "main": "index.js",
23
+ "scripts": {
24
+ "dev": "nodemon --exec npm run-script start",
25
+ "start": "node tester.js"
26
+ },
27
+ "dependencies": {
28
+ "@boomerang-io/logger-middleware": "1.0.0",
29
+ "@cloudnative/health-connect": "^2.1.0",
30
+ "body-parser": "^1.19.1",
31
+ "compression": "^1.7.4",
32
+ "cors": "^2.8.5",
33
+ "dotenv": "^14.2.0",
34
+ "express": "^4.17.2",
35
+ "helmet": "^5.0.1",
36
+ "newrelic": "^8.7.1",
37
+ "serialize-javascript": "^6.0.0",
38
+ "yargs": "^17.3.1"
39
+ },
40
+ "devDependencies": {
41
+ "nodemon": "^2.0.15"
42
+ },
43
+ "keywords": [
44
+ "express",
45
+ "node",
46
+ "server",
47
+ "spa"
48
+ ]
49
+ }
package/tester.js CHANGED
@@ -1,5 +1,5 @@
1
- // Used for development
2
- require("dotenv").config({
3
- path: "./.env.local",
4
- });
5
- require("./index")({});
1
+ // Used for development
2
+ require("dotenv").config({
3
+ path: "./.env.local",
4
+ });
5
+ require("./index")({});