From 2561aea376ef28b6272261b09a045c60cf661604 Mon Sep 17 00:00:00 2001 From: Rory Abraham Date: Wed, 9 Feb 2022 13:03:17 -0800 Subject: [PATCH 1/8] Set up isProduction flag in Electron main process --- config/electron.config.js | 1 + desktop/main.js | 3 +++ 2 files changed, 4 insertions(+) diff --git a/config/electron.config.js b/config/electron.config.js index eef59beebd55..c3e57b31bacb 100644 --- a/config/electron.config.js +++ b/config/electron.config.js @@ -3,6 +3,7 @@ module.exports = { productName: 'New Expensify', extraMetadata: { main: './desktop/main.js', + isProduction: Boolean(process.env.SHOULD_DEPLOY_PRODUCTION), }, mac: { category: 'public.app-category.finance', diff --git a/desktop/main.js b/desktop/main.js index 7048db19f306..a4f12ecae248 100644 --- a/desktop/main.js +++ b/desktop/main.js @@ -14,6 +14,9 @@ const log = require('electron-log'); const ELECTRON_EVENTS = require('./ELECTRON_EVENTS'); const checkForUpdates = require('../src/libs/checkForUpdates'); +// This variable is injected into package.json by electron-builder via the extraMetadata field (specified in electron.config.js) +const {isProduction} = Boolean(require('../package.json')); + const isDev = process.env.NODE_ENV === 'development'; const port = process.env.PORT || 8080; From c77bbef0d8fbcb628278d6aa257797031840ec98 Mon Sep 17 00:00:00 2001 From: Rory Abraham Date: Wed, 9 Feb 2022 14:31:13 -0800 Subject: [PATCH 2/8] Fix CORS issues in electron application --- desktop/main.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/desktop/main.js b/desktop/main.js index a4f12ecae248..d69cc3f299b1 100644 --- a/desktop/main.js +++ b/desktop/main.js @@ -150,6 +150,27 @@ const mainWindow = (() => { titleBarStyle: 'hidden', }); + if (!isDev) { + const newDotURL = isProduction ? 'https://new.expensify.com' : 'https://staging.new.expensify.com' + + // Modify the request origin for requests sent to our API + const validDestinationFilters = {urls: ['https://*.expensify.com/*']}; + browserWindow.webContents.session.webRequest.onBeforeSendHeaders(validDestinationFilters, (details, callback) => { + // eslint-disable-next-line no-param-reassign + details.requestHeaders.origin = newDotURL; + // eslint-disable-next-line no-param-reassign + details.requestHeaders.referer = newDotURL; + callback({requestHeaders: details.requestHeaders}); + }); + + // Modify access-control-allow-origin header for the response + browserWindow.webContents.session.webRequest.onHeadersReceived(validDestinationFilters, (details, callback) => { + // eslint-disable-next-line no-param-reassign + details.responseHeaders['access-control-allow-origin'] = ['app://-']; + callback({ responseHeaders: details.responseHeaders }); + }); + } + // Prod and staging overwrite the app name in the electron-builder config, so only update it here for dev if (isDev) { browserWindow.setTitle('New Expensify'); From 759af3a808618ba96128b5888244696390e30984 Mon Sep 17 00:00:00 2001 From: Rory Abraham Date: Wed, 9 Feb 2022 14:43:45 -0800 Subject: [PATCH 3/8] Fix JS style --- desktop/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/desktop/main.js b/desktop/main.js index d69cc3f299b1..b14ebc3fc40c 100644 --- a/desktop/main.js +++ b/desktop/main.js @@ -151,7 +151,7 @@ const mainWindow = (() => { }); if (!isDev) { - const newDotURL = isProduction ? 'https://new.expensify.com' : 'https://staging.new.expensify.com' + const newDotURL = isProduction ? 'https://new.expensify.com' : 'https://staging.new.expensify.com'; // Modify the request origin for requests sent to our API const validDestinationFilters = {urls: ['https://*.expensify.com/*']}; @@ -167,7 +167,7 @@ const mainWindow = (() => { browserWindow.webContents.session.webRequest.onHeadersReceived(validDestinationFilters, (details, callback) => { // eslint-disable-next-line no-param-reassign details.responseHeaders['access-control-allow-origin'] = ['app://-']; - callback({ responseHeaders: details.responseHeaders }); + callback({responseHeaders: details.responseHeaders}); }); } From d3e7dd5162b2faa75c48b0b5104cd3b9ad98fb45 Mon Sep 17 00:00:00 2001 From: Rory Abraham Date: Wed, 9 Feb 2022 17:08:37 -0800 Subject: [PATCH 4/8] Move ENVIRONMENT constant to a CJS module --- desktop/ELECTRON_ENVIRONMENT.js | 0 src/CONST/ENVIRONMENT.js | 5 +++++ src/{CONST.js => CONST/index.js} | 11 ++++------- 3 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 desktop/ELECTRON_ENVIRONMENT.js create mode 100644 src/CONST/ENVIRONMENT.js rename src/{CONST.js => CONST/index.js} (99%) diff --git a/desktop/ELECTRON_ENVIRONMENT.js b/desktop/ELECTRON_ENVIRONMENT.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/CONST/ENVIRONMENT.js b/src/CONST/ENVIRONMENT.js new file mode 100644 index 000000000000..dd742b0f8245 --- /dev/null +++ b/src/CONST/ENVIRONMENT.js @@ -0,0 +1,5 @@ +module.exports = { + DEV: 'DEV', + STAGING: 'STG', + PRODUCTION: 'PROD', +}; diff --git a/src/CONST.js b/src/CONST/index.js similarity index 99% rename from src/CONST.js rename to src/CONST/index.js index b4c286cc41b4..d1937a84179a 100755 --- a/src/CONST.js +++ b/src/CONST/index.js @@ -1,6 +1,7 @@ import lodashGet from 'lodash/get'; import Config from 'react-native-config'; -import * as Url from './libs/Url'; +import ENVIRONMENT from './ENVIRONMENT'; +import * as Url from '../libs/Url'; const CLOUDFRONT_URL = 'https://d2k5nsl2zxldvw.cloudfront.net'; const ACTIVE_ENVIRONMENT_NEW_EXPENSIFY_URL = Url.addTrailingForwardSlash(lodashGet(Config, 'EXPENSIFY_URL_CASH', 'https://new.expensify.com')); @@ -390,12 +391,6 @@ const CONST = { ADMIN: 'admin@expensify.com', }, - ENVIRONMENT: { - DEV: 'DEV', - STAGING: 'STG', - PRODUCTION: 'PROD', - }, - // Used to delay the initial fetching of reportActions when the app first inits or reconnects (e.g. returning // from backgound). The times are based on how long it generally seems to take for the app to become interactive // in each scenario. @@ -614,4 +609,6 @@ const CONST = { }, }; +CONST.ENVIRONMENT = ENVIRONMENT; + export default CONST; From 3cf3eb96c1856fd738b44e5453b4d2ca8a7fec1a Mon Sep 17 00:00:00 2001 From: Rory Abraham Date: Wed, 9 Feb 2022 17:26:36 -0800 Subject: [PATCH 5/8] Create separate module for ELECTRON_ENVIRONMENT --- config/electron.config.js | 4 +++- desktop/ELECTRON_ENVIRONMENT.js | 31 +++++++++++++++++++++++++++++++ desktop/main.js | 19 ++++++++----------- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/config/electron.config.js b/config/electron.config.js index c3e57b31bacb..fa127435d093 100644 --- a/config/electron.config.js +++ b/config/electron.config.js @@ -1,9 +1,11 @@ +const ENVIRONMENT = require('../src/CONST/ENVIRONMENT'); + module.exports = { appId: 'com.expensifyreactnative.chat', productName: 'New Expensify', extraMetadata: { main: './desktop/main.js', - isProduction: Boolean(process.env.SHOULD_DEPLOY_PRODUCTION), + electronEnvironment: process.env.SHOULD_DEPLOY_PRODUCTION ? ENVIRONMENT.PRODUCTION : ENVIRONMENT.STAGING, }, mac: { category: 'public.app-category.finance', diff --git a/desktop/ELECTRON_ENVIRONMENT.js b/desktop/ELECTRON_ENVIRONMENT.js index e69de29bb2d1..eb3133895b0c 100644 --- a/desktop/ELECTRON_ENVIRONMENT.js +++ b/desktop/ELECTRON_ENVIRONMENT.js @@ -0,0 +1,31 @@ +// This variable is injected into package.json by electron-builder via the extraMetadata field (specified in electron.config.js) +// It will be `prod` on production, `staging` on staging, and `undefined` on dev (because dev doesn't use electron-builder) +const {electronEnvironment} = require('../package.json'); +const ENVIRONMENT = require('../src/CONST/ENVIRONMENT'); + +/** + * @returns {String} – One of ['PROD', 'STG', 'DEV'] + */ +function getEnvironment() { + // If we are on dev, then the NODE_ENV environment variable will be present (set by the executing shell in start.js) + if (process.env.NODE_ENV === 'development') { + return ENVIRONMENT.DEV; + } + + // Otherwise, use the environment injected into package.json by electron-builder + return electronEnvironment; +} + +function isDev() { + return getEnvironment() === ENVIRONMENT.DEV; +} + +function isProd() { + return getEnvironment() === ENVIRONMENT.PRODUCTION; +} + +module.exports = { + getEnvironment, + isDev, + isProd, +}; diff --git a/desktop/main.js b/desktop/main.js index b14ebc3fc40c..7d17277148c1 100644 --- a/desktop/main.js +++ b/desktop/main.js @@ -11,13 +11,10 @@ const serve = require('electron-serve'); const contextMenu = require('electron-context-menu'); const {autoUpdater} = require('electron-updater'); const log = require('electron-log'); +const ELECTRON_ENVIRONMENT = require('./ELECTRON_ENVIRONMENT'); const ELECTRON_EVENTS = require('./ELECTRON_EVENTS'); const checkForUpdates = require('../src/libs/checkForUpdates'); -// This variable is injected into package.json by electron-builder via the extraMetadata field (specified in electron.config.js) -const {isProduction} = Boolean(require('../package.json')); - -const isDev = process.env.NODE_ENV === 'development'; const port = process.env.PORT || 8080; /** @@ -44,7 +41,7 @@ autoUpdater.logger.transports.file.level = 'info'; _.assign(console, log.functions); // setup Hot reload -if (isDev) { +if (ELECTRON_ENVIRONMENT.isDev()) { try { require('electron-reloader')(module, { watchRenderer: false, @@ -127,12 +124,12 @@ const electronUpdater = browserWindow => ({ }); const mainWindow = (() => { - const loadURL = isDev + const loadURL = ELECTRON_ENVIRONMENT.isDev() ? win => win.loadURL(`http://localhost:${port}`) : serve({directory: `${__dirname}/../dist`}); // Prod and staging set the icon in the electron-builder config, so only update it here for dev - if (isDev) { + if (ELECTRON_ENVIRONMENT.isDev()) { app.dock.setIcon(`${__dirname}/icon-dev.png`); app.setName('New Expensify'); } @@ -150,8 +147,8 @@ const mainWindow = (() => { titleBarStyle: 'hidden', }); - if (!isDev) { - const newDotURL = isProduction ? 'https://new.expensify.com' : 'https://staging.new.expensify.com'; + if (!ELECTRON_ENVIRONMENT.isDev()) { + const newDotURL = ELECTRON_ENVIRONMENT.isProd() ? 'https://new.expensify.com' : 'https://staging.new.expensify.com'; // Modify the request origin for requests sent to our API const validDestinationFilters = {urls: ['https://*.expensify.com/*']}; @@ -172,7 +169,7 @@ const mainWindow = (() => { } // Prod and staging overwrite the app name in the electron-builder config, so only update it here for dev - if (isDev) { + if (ELECTRON_ENVIRONMENT.isDev()) { browserWindow.setTitle('New Expensify'); } @@ -310,7 +307,7 @@ const mainWindow = (() => { // Start checking for JS updates .then((browserWindow) => { - if (isDev) { + if (ELECTRON_ENVIRONMENT.isDev()) { return; } From e47cc83ac887edff0ddc2097a05c36e0df5b9dfd Mon Sep 17 00:00:00 2001 From: Rory Abraham Date: Wed, 9 Feb 2022 17:32:05 -0800 Subject: [PATCH 6/8] Correct comment to use PROD instead of production --- desktop/ELECTRON_ENVIRONMENT.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/ELECTRON_ENVIRONMENT.js b/desktop/ELECTRON_ENVIRONMENT.js index eb3133895b0c..a3a0737ac63e 100644 --- a/desktop/ELECTRON_ENVIRONMENT.js +++ b/desktop/ELECTRON_ENVIRONMENT.js @@ -1,5 +1,5 @@ // This variable is injected into package.json by electron-builder via the extraMetadata field (specified in electron.config.js) -// It will be `prod` on production, `staging` on staging, and `undefined` on dev (because dev doesn't use electron-builder) +// It will be `PROD` on production, `STG` on staging, and `undefined` on dev (because dev doesn't use electron-builder) const {electronEnvironment} = require('../package.json'); const ENVIRONMENT = require('../src/CONST/ENVIRONMENT'); From 5d7c54901af93562a54a1cac5aff4c1df4244e64 Mon Sep 17 00:00:00 2001 From: Rory Abraham Date: Thu, 10 Feb 2022 10:28:52 -0800 Subject: [PATCH 7/8] Add more detailed comment to explain CORS fix --- desktop/main.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/desktop/main.js b/desktop/main.js index 7d17277148c1..1ba82346db5c 100644 --- a/desktop/main.js +++ b/desktop/main.js @@ -147,10 +147,19 @@ const mainWindow = (() => { titleBarStyle: 'hidden', }); + /* + * The default origin of our Electron app is app://- instead of https://new.expensify.com or https://staging.new.expensify.com + * This causes CORS errors because the referer and origin headers are wrong and the API responds with an Access-Control-Allow-Origin that doesn't match app://- + * + * To fix this, we'll: + * + * 1. Modify headers on any outgoing requests to match the origin of our corresponding web environment. + * 2. Modify the Access-Control-Allow-Origin header of the response to match the "real" origin of our Electron app. + */ if (!ELECTRON_ENVIRONMENT.isDev()) { const newDotURL = ELECTRON_ENVIRONMENT.isProd() ? 'https://new.expensify.com' : 'https://staging.new.expensify.com'; - // Modify the request origin for requests sent to our API + // Modify the origin and referer for requests sent to our API const validDestinationFilters = {urls: ['https://*.expensify.com/*']}; browserWindow.webContents.session.webRequest.onBeforeSendHeaders(validDestinationFilters, (details, callback) => { // eslint-disable-next-line no-param-reassign From 0424b4564d4c5a11e4663e24dd1afde81764592d Mon Sep 17 00:00:00 2001 From: Rory Abraham Date: Thu, 10 Feb 2022 10:30:49 -0800 Subject: [PATCH 8/8] Don't export getEnvironment --- desktop/ELECTRON_ENVIRONMENT.js | 1 - 1 file changed, 1 deletion(-) diff --git a/desktop/ELECTRON_ENVIRONMENT.js b/desktop/ELECTRON_ENVIRONMENT.js index a3a0737ac63e..698e14b43078 100644 --- a/desktop/ELECTRON_ENVIRONMENT.js +++ b/desktop/ELECTRON_ENVIRONMENT.js @@ -25,7 +25,6 @@ function isProd() { } module.exports = { - getEnvironment, isDev, isProd, };