diff --git a/config/proxyConfig.js b/config/proxyConfig.js
new file mode 100644
index 000000000000..fa09c436461f
--- /dev/null
+++ b/config/proxyConfig.js
@@ -0,0 +1,9 @@
+/**
+ * These are the base API roots used to send requests to the proxy.
+ * We only specify for staging URLs as API requests are sent to the production
+ * servers by default.
+ */
+module.exports = {
+ STAGING: '/staging/',
+ STAGING_SECURE: '/staging-secure/',
+};
diff --git a/config/webpack/webpack.dev.js b/config/webpack/webpack.dev.js
index 0b48c4e5f35a..2867d857ee04 100644
--- a/config/webpack/webpack.dev.js
+++ b/config/webpack/webpack.dev.js
@@ -21,6 +21,7 @@ module.exports = (env = {}) => portfinder.getPortPromise({port: BASE_PORT})
: {
proxy: {
'/api': 'http://[::1]:9000',
+ '/staging': 'http://[::1]:9000',
'/chat-attachments': 'http://[::1]:9000',
},
};
diff --git a/src/CONFIG.js b/src/CONFIG.js
index 6b8d191b514e..d430f9d9883a 100644
--- a/src/CONFIG.js
+++ b/src/CONFIG.js
@@ -84,4 +84,5 @@ export default {
DEV_PORT: process.env.PORT || 8080,
E2E_TESTING: lodashGet(Config, 'E2E_TESTING', 'false') === 'true',
SEND_CRASH_REPORTS: lodashGet(Config, 'SEND_CRASH_REPORTS', 'false') === 'true',
+ IS_USING_WEB_PROXY: getPlatform() === 'web' && useWebProxy,
};
diff --git a/src/components/TestToolMenu.js b/src/components/TestToolMenu.js
index c1512a805bf6..eaae9aec9846 100644
--- a/src/components/TestToolMenu.js
+++ b/src/components/TestToolMenu.js
@@ -15,6 +15,7 @@ import networkPropTypes from './networkPropTypes';
import compose from '../libs/compose';
import {withNetwork} from './OnyxProvider';
import * as ApiUtils from '../libs/ApiUtils';
+import CONFIG from '../CONFIG';
const propTypes = {
/** User object in Onyx */
@@ -42,15 +43,18 @@ const TestToolMenu = props => (
{/* Option to switch between staging and default api endpoints.
- This enables QA and internal testers to take advantage of sandbox environments for 3rd party services like Plaid and Onfido. */}
-
- User.setShouldUseStagingServer(
- !lodashGet(props, 'user.shouldUseStagingServer', ApiUtils.isUsingStagingApi()),
- )}
- />
-
+ This enables QA, internal testers and external devs to take advantage of sandbox environments for 3rd party services like Plaid and Onfido.
+ This toggle is not rendered for internal devs as they make environment changes directly to the .env file. */}
+ {!CONFIG.IS_USING_LOCAL_WEB && (
+
+ User.setShouldUseStagingServer(
+ !lodashGet(props, 'user.shouldUseStagingServer', ApiUtils.isUsingStagingApi()),
+ )}
+ />
+
+ )}
{/* When toggled the app will be forced offline. */}
diff --git a/src/libs/ApiUtils.js b/src/libs/ApiUtils.js
index 7d7792480901..08533c83b076 100644
--- a/src/libs/ApiUtils.js
+++ b/src/libs/ApiUtils.js
@@ -4,6 +4,7 @@ import ONYXKEYS from '../ONYXKEYS';
import CONFIG from '../CONFIG';
import CONST from '../CONST';
import * as Environment from './Environment/Environment';
+import proxyConfig from '../../config/proxyConfig';
// To avoid rebuilding native apps, native apps use production config for both staging and prod
// We use the async environment check because it works on all platforms
@@ -17,8 +18,8 @@ Environment.getEnvironment()
Onyx.connect({
key: ONYXKEYS.USER,
callback: (val) => {
- // Toggling between APIs is not allowed on production
- if (ENV_NAME === CONST.ENVIRONMENT.PRODUCTION) {
+ // Toggling between APIs is not allowed on production and internal dev environment
+ if (ENV_NAME === CONST.ENVIRONMENT.PRODUCTION || CONFIG.IS_USING_LOCAL_WEB) {
shouldUseStagingServer = false;
return;
}
@@ -41,6 +42,11 @@ function getApiRoot(request) {
const shouldUseSecure = lodashGet(request, 'shouldUseSecure', false);
if (shouldUseStagingServer) {
+ if (CONFIG.IS_USING_WEB_PROXY) {
+ return shouldUseSecure
+ ? proxyConfig.STAGING_SECURE
+ : proxyConfig.STAGING;
+ }
return shouldUseSecure
? CONFIG.EXPENSIFY.STAGING_SECURE_API_ROOT
: CONFIG.EXPENSIFY.STAGING_API_ROOT;
diff --git a/web/proxy.js b/web/proxy.js
index 768963d0810c..3bce08514600 100644
--- a/web/proxy.js
+++ b/web/proxy.js
@@ -1,21 +1,18 @@
const http = require('http');
const https = require('https');
+const proxyConfig = require('../config/proxyConfig');
require('dotenv').config();
if (process.env.USE_WEB_PROXY === 'false') {
process.stdout.write('Skipping proxy as USE_WEB_PROXY was set to false.\n');
process.exit();
}
-
-let host = 'www.expensify.com';
-
-// If we are testing against the staging API then we must use the correct host here or nothing with work.
-if (/staging/.test(process.env.EXPENSIFY_URL)) {
- host = 'staging.expensify.com';
-}
+const host = 'www.expensify.com';
+const stagingHost = 'staging.expensify.com';
+const stagingSecureHost = 'staging-secure.expensify.com';
// eslint-disable-next-line no-console
-console.log(`Creating proxy with host: ${host}`);
+console.log(`Creating proxy with host: ${host} for production API and ${stagingHost} for staging API`);
/**
* Local proxy server that hits the production endpoint
@@ -24,13 +21,36 @@ console.log(`Creating proxy with host: ${host}`);
* environment that has no local API.
*/
const server = http.createServer((request, response) => {
+ let hostname = host;
+ let requestPath = request.url;
+
+ /**
+ * When a request is matching a proxy config path we might direct it to a different host (e.g. staging)
+ * For requests matching proxy config patterns we replace the mapping url (prefix) with the actual path.
+ * This is done because the staging api root is only intended for the proxy,
+ * the actual server request must use the /api path.
+ * For example,
+ * /api?command=OpenReport => request sent to production server
+ * /staging/api?command=OpenReport => request sent to staging server
+ * /staging-secure/api?command=OpenReport => request sent to secure staging server
+ * /chat-attachments/46545... => request sent to production server
+ * /staging/chat-attachments/46545... => request sent to staging server
+ */
+ if (request.url.startsWith(proxyConfig.STAGING_SECURE)) {
+ hostname = stagingSecureHost;
+ requestPath = request.url.replace(proxyConfig.STAGING_SECURE, '/');
+ } else if (request.url.startsWith(proxyConfig.STAGING)) {
+ hostname = stagingHost;
+ requestPath = request.url.replace(proxyConfig.STAGING, '/');
+ }
+
const proxyRequest = https.request({
- hostname: host,
+ hostname,
method: 'POST',
- path: request.url,
+ path: requestPath,
headers: {
...request.headers,
- host,
+ host: hostname,
'user-agent': request.headers['user-agent'].concat(' Development-NewDot/1.0'),
},
port: 443,