diff --git a/package.json b/package.json index b6f8f6eecc..c1fd82627e 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "_majorVersionBump": "gulp majorVersionBump", "serve": "node serve-proxy.js . -p 8000 -c-1", "serveLocalAccount": "node serve-proxy.js . -p 8000 -c-1 --localAccount", + "serveStagingAccount": "node serve-proxy.js . -p 8000 -c-1 --stagingAccount", "_serveWithWebCacheHelp": "echo !!!Make sure to npm run release:dev/stageing/prod before testing the cache!!!", "serveWithWebCache": "npm run _releaseWebCache && npm run _serveWithWebCacheHelp && http-server ./dist -p 8000 -c-1", "serveExternal": "node serve-proxy.js . -p 8000 -a 0.0.0.0 --log-ip -c-1", diff --git a/serve-proxy.js b/serve-proxy.js index b2323667fc..33484fbd13 100644 --- a/serve-proxy.js +++ b/serve-proxy.js @@ -7,8 +7,12 @@ const path = require('path'); const fs = require('fs'); const httpProxy = require('http-proxy'); +const ACCOUNT_PROD = 'https://account.phcode.dev'; +const ACCOUNT_STAGING = 'https://account-stage.phcode.dev'; +const ACCOUNT_DEV = 'http://localhost:5000'; + // Account server configuration - switch between local and production -let accountServer = 'https://account.phcode.dev'; // Production +let accountServer = ACCOUNT_PROD; // Production // Set to local development server if --localAccount flag is provided // Default configuration @@ -24,6 +28,8 @@ let config = { // Parse command line arguments function parseArgs() { const args = process.argv.slice(2); + let hasLocalAccount = false; + let hasStagingAccount = false; for (let i = 0; i < args.length; i++) { const arg = args[i]; @@ -46,11 +52,21 @@ function parseArgs() { } else if (arg === '--log-ip') { config.logIp = true; } else if (arg === '--localAccount') { - accountServer = 'http://localhost:5000'; + hasLocalAccount = true; + accountServer = ACCOUNT_DEV; + } else if (arg === '--stagingAccount') { + hasStagingAccount = true; + accountServer = ACCOUNT_STAGING; } else if (!arg.startsWith('-')) { config.root = path.resolve(arg); } } + + // Check for mutually exclusive flags + if (hasLocalAccount && hasStagingAccount) { + console.error('Error: --localAccount and --stagingAccount cannot be used together'); + process.exit(1); + } } // Create proxy server @@ -81,7 +97,7 @@ proxy.on('proxyReq', (proxyReq, req) => { // Transform referer from localhost:8000 to phcode.dev if (originalReferer && originalReferer.includes('localhost:8000')) { - const newReferer = originalReferer.replace(/localhost:8000/g, 'phcode.dev'); + const newReferer = originalReferer.replace(/http:\/\/localhost:8000/g, 'https://phcode.dev'); proxyReq.setHeader('Referer', newReferer); } else if (!originalReferer) { proxyReq.setHeader('Referer', 'https://phcode.dev/'); @@ -89,7 +105,7 @@ proxy.on('proxyReq', (proxyReq, req) => { // Transform origin from localhost:8000 to phcode.dev if (originalOrigin && originalOrigin.includes('localhost:8000')) { - const newOrigin = originalOrigin.replace(/localhost:8000/g, 'phcode.dev'); + const newOrigin = originalOrigin.replace(/http:\/\/localhost:8000/g, 'https://phcode.dev'); proxyReq.setHeader('Origin', newOrigin); } @@ -254,6 +270,31 @@ const server = http.createServer((req, res) => { return; } + // Handle proxy config request + if (parsedUrl.pathname === '/proxy/config') { + const configResponse = { + accountURL: accountServer + '/' + }; + + if (!config.silent) { + console.log(`[CONFIG] ${req.method} ${parsedUrl.pathname} -> ${JSON.stringify(configResponse)}`); + } + + const headers = { + 'Content-Type': 'application/json' + }; + + if (config.cors) { + headers['Access-Control-Allow-Origin'] = '*'; + headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'; + headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization, Cache-Control'; + } + + res.writeHead(200, headers); + res.end(JSON.stringify(configResponse)); + return; + } + // Check if this is a proxy request if (parsedUrl.pathname.startsWith('/proxy/accounts')) { // Extract the path after /proxy/accounts @@ -288,8 +329,11 @@ const server = http.createServer((req, res) => { } if (!config.silent) { - const clientIp = req.headers['x-forwarded-for'] || req.connection.remoteAddress; - console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}${config.logIp ? ` (${clientIp})` : ''}`); + // Skip logging PWA asset requests to reduce noise. chrome somehoe sends this every second to dev server. + if (!parsedUrl.pathname.includes('/assets/pwa/')) { + const clientIp = req.headers['x-forwarded-for'] || req.connection.remoteAddress; + console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}${config.logIp ? ` (${clientIp})` : ''}`); + } } // Handle directory requests without trailing slash diff --git a/src/brackets.config.staging.json b/src/brackets.config.staging.json index 6ba54f9810..68fd51439d 100644 --- a/src/brackets.config.staging.json +++ b/src/brackets.config.staging.json @@ -10,5 +10,6 @@ "bugsnagEnv" : "staging", "app_notification_url" : "https://updates.phcode.io/appNotifications/staging/", "app_update_url" : "https://updates.phcode.io/tauri/update-latest-pre-release.json", - "promotions_url" : "https://promotions.phcode.dev/dev/" + "promotions_url" : "https://promotions.phcode.dev/dev/", + "account_url" : "https://account-stage.phcode.dev/" } diff --git a/src/index.html b/src/index.html index 13f8831710..830fa6a5e5 100644 --- a/src/index.html +++ b/src/index.html @@ -446,7 +446,22 @@ loadJS('verify-dependencies-loaded.js', null, document.body); } - function _startRequireLoop() { + async function _startRequireLoop() { + // If running on localhost, fetch proxy config to get dynamic account URL + if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') { + try { + const response = await fetch('/proxy/config'); + if (response.ok) { + const config = await response.json(); + if (config.accountURL) { + window.AppConfig.config.account_url = config.accountURL; + console.log('Applied dynamic account URL from proxy:', config.accountURL); + } + } + } catch (error) { + console.warn('Failed to fetch proxy config, using default account URL:', error); + } + } loadJS('thirdparty/requirejs/require.js', _requireDone, document.body, "main"); } if(window.PhStore){ diff --git a/src/services/login-desktop.js b/src/services/login-desktop.js index e7058654e6..251fff301e 100644 --- a/src/services/login-desktop.js +++ b/src/services/login-desktop.js @@ -80,9 +80,19 @@ define(function (require, exports, module) { * For desktop apps, this directly uses the configured account URL */ function getAccountBaseURL() { + if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') { + return '/proxy/accounts'; + } return Phoenix.config.account_url.replace(/\/$/, ''); // Remove trailing slash } + /** + * Get the account website URL for opening browser tabs + */ + function _getAccountWebURL() { + return Phoenix.config.account_url; + } + const ERR_RETRY_LATER = "retry_later"; const ERR_INVALID = "invalid"; @@ -96,7 +106,7 @@ define(function (require, exports, module) { * never rejects. */ async function _resolveAPIKey(apiKey, validationCode) { - const resolveURL = `${Phoenix.config.account_url}resolveAppSessionID?appSessionID=${apiKey}&validationCode=${validationCode}`; + const resolveURL = `${getAccountBaseURL()}/resolveAppSessionID?appSessionID=${apiKey}&validationCode=${validationCode}`; if (!navigator.onLine) { return {err: ERR_RETRY_LATER}; } @@ -196,7 +206,7 @@ define(function (require, exports, module) { const authPortURL = _getAutoAuthPortURL(); const platformStr = PLATFORM_STRINGS[Phoenix.platform] || Phoenix.platform; const appName = encodeURIComponent(`${Strings.APP_NAME} Desktop on ${platformStr}`); - const resolveURL = `${Phoenix.config.account_url}getAppAuthSession?autoAuthPort=${authPortURL}&appName=${appName}`; + const resolveURL = `${getAccountBaseURL()}/getAppAuthSession?autoAuthPort=${authPortURL}&appName=${appName}`; // {"isSuccess":true,"appSessionID":"a uuid...","validationCode":"SWXP07"} try { if(Phoenix.isTestWindow && fetchFn === fetch){ @@ -254,7 +264,7 @@ define(function (require, exports, module) { } const {appSessionID, validationCode} = appAuthSession; await setAutoVerificationCode(validationCode); - const appSignInURL = `${Phoenix.config.account_url}authorizeApp?appSessionID=${appSessionID}`; + const appSignInURL = `${_getAccountWebURL()}authorizeApp?appSessionID=${appSessionID}`; // Show dialog with validation code const dialogData = { @@ -350,7 +360,7 @@ define(function (require, exports, module) { } async function signOutAccount() { - const resolveURL = `${Phoenix.config.account_url}logoutSession`; + const resolveURL = `${getAccountBaseURL()}/logoutSession`; try { let input = { appSessionID: userProfile.apiKey @@ -378,7 +388,7 @@ define(function (require, exports, module) { Strings.SIGNED_OUT_FAILED_MESSAGE ); dialog.done(() => { - NativeApp.openURLInDefaultBrowser(Phoenix.config.account_url + "#advanced"); + NativeApp.openURLInDefaultBrowser(_getAccountWebURL() + "#advanced"); }); Metrics.countEvent(Metrics.EVENT_TYPE.AUTH, 'logoutFail', Phoenix.platform); return; @@ -399,7 +409,7 @@ define(function (require, exports, module) { Strings.SIGNED_OUT_FAILED_MESSAGE ); dialog.done(() => { - NativeApp.openURLInDefaultBrowser(Phoenix.config.account_url + "#advanced"); + NativeApp.openURLInDefaultBrowser(_getAccountWebURL() + "#advanced"); }); Metrics.countEvent(Metrics.EVENT_TYPE.AUTH, 'getAppAuth', Phoenix.platform); logger.reportError(error, "Failed to call logout calling" + resolveURL); diff --git a/src/services/manage-licenses.js b/src/services/manage-licenses.js index 9d8e4cec86..1c67aa0146 100644 --- a/src/services/manage-licenses.js +++ b/src/services/manage-licenses.js @@ -44,6 +44,9 @@ define(function (require, exports, module) { * Get the API base URL for license operations */ function _getAPIBaseURL() { + if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') { + return '/proxy/accounts'; + } return Phoenix.config.account_url.replace(/\/$/, ''); // Remove trailing slash } diff --git a/src/services/readme-login-browser-no_dist.md b/src/services/readme-login-browser-no_dist.md index f9b684c652..837dc4b2b3 100644 --- a/src/services/readme-login-browser-no_dist.md +++ b/src/services/readme-login-browser-no_dist.md @@ -99,9 +99,8 @@ function _getAccountBaseURL() { For testing with a local account server instance: 1. **Configure Proxy Server:** - - Edit `serve-proxy.js` - - **Comment out:** `const ACCOUNT_SERVER = 'https://account.phcode.dev'; // Production` - - **Uncomment:** `const ACCOUNT_SERVER = 'http://localhost:5000'; // Local development` + - use `npm run serveLocalAccount` to serve phoenix repo server, instead of using npm run serve command. + - use `npm run serveStagingAccount` to use the staging endpoint. To get access to staging server, contact team. 2. **Setup Local Account Server:** - Start your local account development stack on `localhost:5000` @@ -111,10 +110,12 @@ Now just visit login server at `http://localhost:5000` and login. It should work at https://localhost:8000/src when you run phoenix code dev server via `npm run serve`. This works without any manual cookie copy needed as the dev server sets cookies localhost wide. But if that didnt work, please see manual cookie copy instructions below. + +For staging server at https://account-stage.phcode.dev , you may need to copy cookie manually like below: 1. **Login and Copy Session:** - - Navigate to `http://localhost:5000` in browser - - Login with test credentials + - Navigate to `http://localhost:5000` in browser. (if staging, use https://account-stage.phcode.dev) + - Login with your credentials - Copy `session` cookie value from DevTools 2. **Set Cookie in Phoenix App:** diff --git a/src/services/readme-login-desktop-no_dist.md b/src/services/readme-login-desktop-no_dist.md index 1e7ee1cc23..97673f1197 100644 --- a/src/services/readme-login-desktop-no_dist.md +++ b/src/services/readme-login-desktop-no_dist.md @@ -241,25 +241,21 @@ pref.on('change', _verifyLogin); For testing desktop authentication with a local account server: -1. **Configure Account URL:** - - Edit `src/config.json` - - Change `account_url` from `https://account.phcode.dev/` to `http://localhost:5000/` (or your local server URL) +1. **Configure Proxy Server:** + - use `npm run serveLocalAccount` to serve phoenix repo server, instead of using npm run serve command. + - use `npm run serveStagingAccount` to use the staging endpoint. To get access to staging server, contact team. -2. **Rebuild Application:** - ```bash - npm run build - ``` - -3. **Start Your Local Account Server:** - - Ensure your local account server is running on the configured port (e.g., localhost:5000) - - Verify all authentication endpoints are properly configured +2. **Setup Local Account Server:** + - Start your local account development stack on `localhost:5000` + - Ensure all login endpoints are properly configured -4. **Test Desktop Authentication:** - - Desktop app will now use your local server for all authentication calls - - Verification codes and API key resolution will go through your local server +3. **Test Desktop Authentication:** + - Desktop app will now use your local/staging server for all authentication calls + - Verification codes and API key resolution will go through your local/staging server - Auto-verification will attempt to connect to your local account service -**Note:** Unlike browser testing which requires proxy server configuration, desktop apps simply use the `account_url` directly from config.json. +**Note:** Like browser testing which requires proxy server configuration, desktop apps also use the proxy server +for communication with backend. ## Troubleshooting @@ -326,4 +322,4 @@ if(resolveResponse.userDetails) { For desktop implementation details, see the source code in `src/services/login-desktop.js`. For browser authentication, see `src/services/login-browser.js` and `readme-login-browser-no_dist.md`. -For deeper understanding of the Kernel Mode Trust security architecture and secure credential storage implementation, refer to the Kernel Mode Trust source files (out of scope for this document). \ No newline at end of file +For deeper understanding of the Kernel Mode Trust security architecture and secure credential storage implementation, refer to the Kernel Mode Trust source files (out of scope for this document). diff --git a/src/utils/Global.js b/src/utils/Global.js index ba44641e52..e4fb5ed120 100644 --- a/src/utils/Global.js +++ b/src/utils/Global.js @@ -28,8 +28,7 @@ define(function (require, exports, module) { - var configJSON = require("text!config.json"), - UrlParams = require("utils/UrlParams").UrlParams; + const UrlParams = require("utils/UrlParams").UrlParams; // Define core brackets namespace if it isn't already defined // @@ -57,7 +56,7 @@ define(function (require, exports, module) { // Parse src/config.json try { - global.brackets.metadata = JSON.parse(configJSON); + global.brackets.metadata = window.AppConfig; global.brackets.config = global.brackets.metadata.config; } catch (err) { console.log(err); diff --git a/test/SpecRunner.html b/test/SpecRunner.html index feaaf3ecd2..e4c2d73356 100644 --- a/test/SpecRunner.html +++ b/test/SpecRunner.html @@ -148,6 +148,7 @@ +