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 @@
+