Harmonize client & server env handling

* Remove defaults from multiple places in code. Put them to .env-template instead
* Do not use imported config.js in server. Access process.env directly instead
* Instruct using .env for local development, not .env.development.local
This commit is contained in:
Olli Vanhapiha 2018-03-15 13:15:14 +02:00
parent 5ae25e2e62
commit 6380d7569f
14 changed files with 128 additions and 49 deletions

33
.env-template Normal file
View file

@ -0,0 +1,33 @@
# Mandatory configuration
#
REACT_APP_SHARETRIBE_SDK_CLIENT_ID=
REACT_APP_GOOGLE_MAPS_API_KEY=
REACT_APP_STRIPE_PUBLISHABLE_KEY=
# Defaults
#
REACT_APP_SHARETRIBE_SDK_BASE_URL=http://localhost:8088
REACT_APP_SHARETRIBE_MARKETPLACE_CURRENCY=USD
REACT_APP_CANONICAL_ROOT_URL=http://localhost:3000
PORT=4000
# This is overwritten by configuration in .env.development and
# .env.test. In production deployments use env variable and set it to
# 'production'
REACT_APP_ENV=production
# Options. Uncomment and set to test.
#
# REACT_APP_SHARETRIBE_USING_SSL=true
# SERVER_SHARETRIBE_TRUST_PROXY=true
# REACT_APP_PUBLIC_SENTRY_DSN=change-me
# SERVER_SENTRY_DSN=change-me
# REACT_APP_CSP=report
# BASIC_AUTH_USERNAME=sharetribe
# BASIC_AUTH_PASSWORD=secret
# REACT_APP_GOOGLE_ANALYTICS_ID=change-me

View file

@ -1,2 +1 @@
NODE_ENV=development
REACT_APP_ENV=development REACT_APP_ENV=development

View file

@ -1,3 +1,3 @@
NODE_ENV=test
REACT_APP_ENV=test REACT_APP_ENV=test
REACT_APP_SHARETRIBE_MARKETPLACE_CURRENCY=USD REACT_APP_SHARETRIBE_MARKETPLACE_CURRENCY=USD
REACT_APP_CANONICAL_ROOT_URL=http://localhost:3000

2
.gitignore vendored
View file

@ -13,4 +13,4 @@ build
.DS_Store .DS_Store
.env .env
npm-debug.log npm-debug.log
/.env.development.local yarn-error.log

View file

@ -24,13 +24,16 @@ Clone this repository and install dependencies:
## Development ## Development
**First, you need to configure Client Id, Google Maps API key and
Stripe publishable API key via [environment
variables](./docs/env.md).** The quickest way to do this is to copy
.env-template -> .env, and edit the file to add the three mandatory
configuration values.
To develop the application and to see changes live, start the frontend development server: To develop the application and to see changes live, start the frontend development server:
yarn run dev yarn run dev
**You need to configure Client Id, Google Maps API key and Stripe
publishable API key via [environment variables](./docs/env.md).**
**Known issues:** **Known issues:**
* Adding/changing `import`s may not be synced properly with ESLint. You may see an error * Adding/changing `import`s may not be synced properly with ESLint. You may see an error

View file

@ -3,27 +3,32 @@
The following configuration variables can be set to control the Starter App behaviour. Most of them The following configuration variables can be set to control the Starter App behaviour. Most of them
have defaults that work for development environment. For production deploys most should be set. have defaults that work for development environment. For production deploys most should be set.
| Variable | Must be set? | Default | Description | | Variable | Must be set? | Default | Description |
| ----------------------------------------- | ------------ | ----------------------- | ------------------------------------------------------------------------------------------------------- | | ----------------------------------------- | ------------ | --------------------- | ----------------------------------------------------------------------------------------------------------------- |
| REACT_APP_GOOGLE_MAPS_API_KEY | X | - | See: [Google Maps API key](./google-maps.md) | | REACT_APP_GOOGLE_MAPS_API_KEY | X | - | See: [Google Maps API key](./google-maps.md) |
| REACT_APP_SHARETRIBE_SDK_CLIENT_ID | X | - | Client ID (API key). You will get this from the Sharetribe team. | | REACT_APP_SHARETRIBE_SDK_CLIENT_ID | X | - | Client ID (API key). You will get this from the Sharetribe team. |
| REACT_APP_STRIPE_PUBLISHABLE_KEY | X | - | Stripe publishable API key for generating tokens with Stripe API. | | REACT_APP_STRIPE_PUBLISHABLE_KEY | X | - | Stripe publishable API key for generating tokens with Stripe API. Use test key (prefix pk*test*) for development. |
| REACT_APP_SHARETRIBE_SDK_BASE_URL | | `http://localhost:8088` | The base url to access the Marketplace API. | | REACT_APP_SHARETRIBE_SDK_BASE_URL | | http://localhost:8088 | The base url to access the Sharetribe Flex Marketplace API. |
| REACT_APP_SHARETRIBE_MARKETPLACE_CURRENCY | | USD | The currency used in the Marketplace as ISO 4217 currency code. For example: USD, EUR, CAD, AUD, etc. | | REACT_APP_SHARETRIBE_MARKETPLACE_CURRENCY | | USD | The currency used in the Marketplace as ISO 4217 currency code. For example: USD, EUR, CAD, AUD, etc. |
| REACT_APP_CANONICAL_ROOT_URL | | `http://localhost:3000` | Canonical root url of the marketplace is running. Needed for social media sharing and SEO optimization. | | REACT_APP_CANONICAL_ROOT_URL | | http://localhost:3000 | Canonical root url of the marketplace. Needed for social media sharing and SEO optimization. |
| NODE_ENV | | ? | Node env. Use 'dev' for development and 'production' for production. | | NODE_ENV | | ? | Node env. Use 'development' for development and 'production' for production. |
| REACT_APP_ENV | | production | TODO Didn't understand NODE_ENV vs this? | | PORT | | 4000 | Port for server to accept connections. |
| REACT_APP_SHARETRIBE_USING_SSL | | false | Redirect HTTP to HTTPS. | | REACT_APP_ENV | | production | Env for client side. Use the same value as with NODE_ENV. |
| SERVER_SHARETRIBE_TRUST_PROXY | | false | Set when running the app behind a reverse proxy. | | REACT_APP_SHARETRIBE_USING_SSL | | false | Redirect HTTP to HTTPS? |
| REACT_APP_PUBLIC_SENTRY_DSN | | - | See: [Error logging with Sentry](./sentry.md) | | SERVER_SHARETRIBE_TRUST_PROXY | | false | Set when running the app behind a reverse proxy, e.g. in Heroku. |
| SERVER_SENTRY_DSN | | - | See: [Error logging with Sentry](./sentry.md) | | REACT_APP_PUBLIC_SENTRY_DSN | | - | See: [Error logging with Sentry](./sentry.md) |
| REACT_APP_CSP | | - | See: [Content Security Policy (CSP)](./content-security-policy.md) | | SERVER_SENTRY_DSN | | - | See: [Error logging with Sentry](./sentry.md) |
| BASIC_AUTH_USERNAME | | - | Set to enable HTTP Basic Auth | | REACT_APP_CSP | | - | See: [Content Security Policy (CSP)](./content-security-policy.md) |
| BASIC_AUTH_PASSWORD | | - | Set to enable HTTP Basic Auth | | BASIC_AUTH_USERNAME | | - | Set to enable HTTP Basic Auth |
| REACT_APP_GOOGLE_ANALYTICS_ID | | - | See: [Google Analytics](./analytics.md) | | BASIC_AUTH_PASSWORD | | - | Set to enable HTTP Basic Auth |
| REACT_APP_GOOGLE_ANALYTICS_ID | | - | See: [Google Analytics](./analytics.md) |
## Defining the env variables ## Defining the env variables
When the Starter App is started locally with `yarn run dev` you can set environment variables by When the Starter App is started locally with `yarn run dev` or `yarn run dev-server`, you can set
using the file .env.development.local. However, the server process doesn't currently pick up values environment variables by using the file .env file. The repository contains a template file
from this file. In production it is recommended to set the environment variables on OS level. .env-template. Just copy that as .env and edit as necessary.
In production, it's recommended that you set the configuration via env variables and do not deploy
an .env file. The client application will only be packaged with env variables that start with
REACT_APP. This way server secrets don't end up in client bundles.

View file

@ -20,8 +20,7 @@ section of the Places library documentation to enable using the Google Places AP
## Setup the application to use the API key ## Setup the application to use the API key
The application uses the `REACT_APP_GOOGLE_MAPS_API_KEY` environment variable for the key value. For The application uses the `REACT_APP_GOOGLE_MAPS_API_KEY` environment variable for the key value. For
local development, you can add the variable in the Gitignored `.env.development.local` file in the local development, you can add the variable in the Gitignored `.env` file in the project root:
project root:
``` ```
REACT_APP_GOOGLE_MAPS_API_KEY=my-key-here REACT_APP_GOOGLE_MAPS_API_KEY=my-key-here

View file

@ -66,7 +66,7 @@
"test-ci": "sharetribe-scripts test --env=jsdom --runInBand", "test-ci": "sharetribe-scripts test --env=jsdom --runInBand",
"eject": "sharetribe-scripts eject", "eject": "sharetribe-scripts eject",
"start": "node server/index.js", "start": "node server/index.js",
"dev-server": "export REACT_APP_ENV=development REACT_APP_CANONICAL_ROOT_URL=http://localhost:4000&&yarn run build&&nodemon --watch server server/index.js", "dev-server": "export NODE_ENV=development REACT_APP_CANONICAL_ROOT_URL=http://localhost:4000&&yarn run build&&nodemon --watch server server/index.js",
"heroku-prebuild": "./heroku-copy-ssh-key-from-env.sh", "heroku-prebuild": "./heroku-copy-ssh-key-from-env.sh",
"heroku-postbuild": "yarn run build", "heroku-postbuild": "yarn run build",
"audit": "nsp check --preprocessor yarn" "audit": "nsp check --preprocessor yarn"

View file

@ -1,5 +1,4 @@
const helmet = require('helmet'); const helmet = require('helmet');
const { config } = require('./importer');
const dev = process.env.REACT_APP_ENV === 'development'; const dev = process.env.REACT_APP_ENV === 'development';
const googleAnalyticsEnabled = !!process.env.REACT_APP_GOOGLE_ANALYTICS_ID; const googleAnalyticsEnabled = !!process.env.REACT_APP_GOOGLE_ANALYTICS_ID;
@ -21,7 +20,7 @@ module.exports = (reportUri, enforceSsl, reportOnly) => {
const self = "'self'"; const self = "'self'";
const inline = "'unsafe-inline'"; const inline = "'unsafe-inline'";
const data = 'data:'; const data = 'data:';
const sharetribeApi = config.sdk.baseUrl; const sharetribeApi = process.env.REACT_APP_SHARETRIBE_SDK_BASE_URL;
const sharetribeAssets = 'https://assets-sharetribecom.sharetribe.com/'; const sharetribeAssets = 'https://assets-sharetribecom.sharetribe.com/';
const imgix = '*.imgix.net/'; const imgix = '*.imgix.net/';
const loremPixel = 'https://lorempixel.com/'; const loremPixel = 'https://lorempixel.com/';

41
server/env.js Normal file
View file

@ -0,0 +1,41 @@
/**
Implements .env file loading that mimicks the way create-react-app
does it. We want this to get consistent configuration handling
between client and node server.
*/
const fs = require('fs');
const NODE_ENV = process.env.NODE_ENV;
if (!NODE_ENV) {
throw new Error('The NODE_ENV environment variable is required but was not specified.');
}
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
var dotenvFiles = [
`.env.${NODE_ENV}.local`,
`.env.${NODE_ENV}`,
// Don't include `.env.local` for `test` environment
// since normally you expect tests to produce the same
// results for everyone
NODE_ENV !== 'test' && `env.local`,
'.env',
].filter(Boolean);
const configureEnv = () => {
// Load environment variables from .env* files. Suppress warnings using silent
// if this file is missing. dotenv will never modify any environment variables
// that have already been set.
// https://github.com/motdotla/dotenv
dotenvFiles.forEach(dotenvFile => {
if (fs.existsSync(dotenvFile)) {
console.log('Loading env from file:' + dotenvFile);
require('dotenv').config({ path: dotenvFile });
}
});
};
module.exports = {
configureEnv: configureEnv,
};

View file

@ -17,5 +17,4 @@ module.exports = {
matchPathname: mainJs.matchPathname, matchPathname: mainJs.matchPathname,
configureStore: mainJs.configureStore, configureStore: mainJs.configureStore,
routeConfiguration: mainJs.routeConfiguration, routeConfiguration: mainJs.routeConfiguration,
config: mainJs.config,
}; };

View file

@ -16,6 +16,9 @@
// This enables nice stacktraces from the minified production bundle // This enables nice stacktraces from the minified production bundle
require('source-map-support').install(); require('source-map-support').install();
// Configure process.env with .env.* files
require('./env').configureEnv();
const express = require('express'); const express = require('express');
const helmet = require('helmet'); const helmet = require('helmet');
const compression = require('compression'); const compression = require('compression');
@ -35,12 +38,11 @@ const { sitemapStructure } = require('./sitemap');
const csp = require('./csp'); const csp = require('./csp');
const buildPath = path.resolve(__dirname, '..', 'build'); const buildPath = path.resolve(__dirname, '..', 'build');
const env = process.env.REACT_APP_ENV || 'production'; const env = process.env.REACT_APP_ENV;
const dev = process.env.REACT_APP_ENV === 'development'; const dev = process.env.REACT_APP_ENV === 'development';
const PORT = process.env.PORT || 4000; const PORT = process.env.PORT;
const CLIENT_ID = const CLIENT_ID = process.env.REACT_APP_SHARETRIBE_SDK_CLIENT_ID;
process.env.REACT_APP_SHARETRIBE_SDK_CLIENT_ID || '08ec69f6-d37e-414d-83eb-324e94afddf0'; const BASE_URL = process.env.REACT_APP_SHARETRIBE_SDK_BASE_URL;
const BASE_URL = process.env.REACT_APP_SHARETRIBE_SDK_BASE_URL || 'http://localhost:8088';
const USING_SSL = process.env.REACT_APP_SHARETRIBE_USING_SSL === 'true'; const USING_SSL = process.env.REACT_APP_SHARETRIBE_USING_SSL === 'true';
const TRUST_PROXY = process.env.SERVER_SHARETRIBE_TRUST_PROXY || null; const TRUST_PROXY = process.env.SERVER_SHARETRIBE_TRUST_PROXY || null;
const CSP = process.env.REACT_APP_CSP; const CSP = process.env.REACT_APP_CSP;

View file

@ -1,9 +1,8 @@
const path = require('path'); const path = require('path');
const moment = require('moment'); const moment = require('moment');
const { config } = require('./importer');
const buildPath = path.resolve(__dirname, '..', 'build'); const buildPath = path.resolve(__dirname, '..', 'build');
const PORT = process.env.PORT || 4000; const PORT = process.env.PORT;
const USING_SSL = process.env.REACT_APP_SHARETRIBE_USING_SSL === 'true'; const USING_SSL = process.env.REACT_APP_SHARETRIBE_USING_SSL === 'true';
/** /**
@ -44,17 +43,17 @@ const port = rootURL => {
}; };
/** /**
* Return a structure for sitemap.xml and robots.txt to be * Return a structure for sitemap.xml and robots.txt to be used by the
* used by the express-sitemap library. Uses the canonical * express-sitemap library. Uses the canonical URL value from env
* URL value from config for domain and port information. * config for domain and port information.
*/ */
exports.sitemapStructure = () => { exports.sitemapStructure = () => {
const now = moment().format('YYYY-MM-DD'); const now = moment().format('YYYY-MM-DD');
return { return {
url: domain(config.canonicalRootURL), url: domain(process.env.REACT_APP_CANONICAL_ROOT_URL),
http: USING_SSL ? 'https' : 'http', http: USING_SSL ? 'https' : 'http',
port: port(config.canonicalRootURL), port: port(process.env.REACT_APP_CANONICAL_ROOT_URL),
sitemap: path.join(buildPath, 'static', 'sitemap.xml'), sitemap: path.join(buildPath, 'static', 'sitemap.xml'),
robots: path.join(buildPath, 'robots.txt'), robots: path.join(buildPath, 'robots.txt'),
sitemapSubmission: '/static/sitemap.xml', sitemapSubmission: '/static/sitemap.xml',

View file

@ -1,6 +1,6 @@
import * as custom from './marketplace-custom-config.js'; import * as custom from './marketplace-custom-config.js';
const env = process.env.REACT_APP_ENV || 'production'; const env = process.env.REACT_APP_ENV;
const dev = process.env.REACT_APP_ENV === 'development'; const dev = process.env.REACT_APP_ENV === 'development';
// If you want to change the language, remember to also change the // If you want to change the language, remember to also change the
@ -33,9 +33,9 @@ const bookingUnitType = 'line-item/night';
// react-scripts) require using the REACT_APP_ prefix to avoid // react-scripts) require using the REACT_APP_ prefix to avoid
// exposing server secrets to the client side. // exposing server secrets to the client side.
const sdkClientId = process.env.REACT_APP_SHARETRIBE_SDK_CLIENT_ID; const sdkClientId = process.env.REACT_APP_SHARETRIBE_SDK_CLIENT_ID;
const sdkBaseUrl = process.env.REACT_APP_SHARETRIBE_SDK_BASE_URL || 'http://localhost:8088'; const sdkBaseUrl = process.env.REACT_APP_SHARETRIBE_SDK_BASE_URL;
const currency = process.env.REACT_APP_SHARETRIBE_MARKETPLACE_CURRENCY || 'USD'; const currency = process.env.REACT_APP_SHARETRIBE_MARKETPLACE_CURRENCY;
// Sentry DSN (Data Source Name), a client key for authenticating calls to Sentry // Sentry DSN (Data Source Name), a client key for authenticating calls to Sentry
const sentryDsn = process.env.REACT_APP_PUBLIC_SENTRY_DSN; const sentryDsn = process.env.REACT_APP_PUBLIC_SENTRY_DSN;
@ -219,7 +219,7 @@ const postalCode = '00100';
const streetAddress = 'Bulevardi 14'; const streetAddress = 'Bulevardi 14';
// Canonical root url is needed in social media sharing and SEO optimization purposes. // Canonical root url is needed in social media sharing and SEO optimization purposes.
const canonicalRootURL = process.env.REACT_APP_CANONICAL_ROOT_URL || 'http://localhost:3000'; const canonicalRootURL = process.env.REACT_APP_CANONICAL_ROOT_URL;
// Site title is needed in meta tags (bots and social media sharing reads those) // Site title is needed in meta tags (bots and social media sharing reads those)
const siteTitle = 'Saunatime'; const siteTitle = 'Saunatime';