Skip to content

Commit a3625f0

Browse files
@W-19265242@ Configurable base paths for /mobify routes (PWA Kit 3.12 version) (#2892) (#3173)
* Initial restore of base path PR * Rename path functions * Update commerce-sdk-react callbacks and slas private proxy endpoint * Big update * Replace getAppOrigin calls with useAppOrigin * WIP - Filter the base path from all incoming requests to the server * Remove base path from proxied requests + cleanup * Use functions for proxy pathRewrite * A bit of PR cleanup * Lint cleanup * More cleanup * Fix tests * Fix issues found by CI * Lint for override asset * Turn off slas private proxy * Cleanup comments * Update changelog entries * Fix config * Limit base path removal to /mobify or express route * Adjust express filter * Lint * Fix dependency in generated apps * Comment cleanup * use envBasePath as a feature toggle for enabling the base path middleware * Enable basepath removal middlware to handle express routes defined as regexes * Apply suggestions * Adjust app config template * Use pathToRegexp rather than having manual regex interpretation * escape the base path to not allow regex * refactor * Remove ?* from callback url * Handle ? characters in express route * Cache express regexes * Handle properly formatted ()? express groups * Comment cleanup * Apply suggestions to reduce use of regex * Add base path to social login redirectUri * Lint * Update comments and test * Update test with more realistic scenario * Remove path-to-regexp * Refine convertExpressRouteToRegex further * Move express route to regex logic to separate file * Adjust logger and lock file * Revert package-lock changes --------- Signed-off-by: vcua-mobify <47404250+vcua-mobify@users.noreply.github.com> Co-authored-by: vcua-mobify <47404250+vcua-mobify@users.noreply.github.com>
1 parent afb6ea9 commit a3625f0

File tree

44 files changed

+731
-130
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+731
-130
lines changed

packages/commerce-sdk-react/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
## v3.5.0-dev (Jul 22, 2025)
2-
2+
- Add support for environment level base paths on /mobify routes [#2892](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2892)
33
- Update USID expiry to match SLAS refresh token expiry[#2854](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2854)
44

55
- [Bugfix] Skip deleting dwsid on shopper login if hybrid auth is enabled for current site. [#3151](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3151)

packages/commerce-sdk-react/src/auth/index.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ type Helpers = typeof helpers
4343
interface AuthConfig extends ApiClientConfigParams {
4444
redirectURI: string
4545
proxy: string
46+
privateClientProxyEndpoint?: string
4647
fetchOptions?: ShopperLoginTypes.FetchOptions
4748
fetchedToken?: string
4849
enablePWAKitPrivateClient?: boolean
@@ -247,12 +248,19 @@ class Auth {
247248
private hybridAuthEnabled: boolean
248249

249250
constructor(config: AuthConfig) {
250-
// Special endpoint for injecting SLAS private client secret.
251+
// Special proxy endpoint for injecting SLAS private client secret.
252+
// We prioritize config.privateClientProxyEndpoint since that allows us to use the new envBasePath feature
253+
// The preexisting hard coded privateClientEndpoint is kept here for now to prevent a breaking change.
254+
// TODO: We should remove this in the next major release so we do not have a hard coded proxy path inside commerce-sdk-react
251255
const baseUrl = config.proxy.split(MOBIFY_PATH)[0]
252256
const privateClientEndpoint = `${baseUrl}${SLAS_PRIVATE_PROXY_PATH}`
253257

254258
this.client = new ShopperLogin({
255-
proxy: config.enablePWAKitPrivateClient ? privateClientEndpoint : config.proxy,
259+
proxy: config.enablePWAKitPrivateClient
260+
? config.privateClientProxyEndpoint
261+
? config.privateClientProxyEndpoint
262+
: privateClientEndpoint
263+
: config.proxy,
256264
parameters: {
257265
clientId: config.clientId,
258266
organizationId: config.organizationId,
@@ -340,7 +348,9 @@ class Auth {
340348
this.passwordlessLoginCallbackURI = passwordlessLoginCallbackURI
341349
? isAbsoluteUrl(passwordlessLoginCallbackURI)
342350
? passwordlessLoginCallbackURI
343-
: `${baseUrl}${passwordlessLoginCallbackURI}`
351+
: // This fallback does not take into account the envBasePath feature
352+
// To set an env base path, config.passwordlessLoginCallbackURI must be an absolute url
353+
`${baseUrl}${passwordlessLoginCallbackURI}`
344354
: ''
345355

346356
this.hybridAuthEnabled = config.hybridAuthEnabled || false

packages/commerce-sdk-react/src/components/StorefrontPreview/utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export const detectStorefrontPreview = () => {
2525
export const getClientScript = () => {
2626
const parentOrigin = getParentOrigin() ?? 'https://runtime.commercecloud.com'
2727
return parentOrigin === DEVELOPMENT_ORIGIN
28+
// TODO: This will need to be updated to support base paths with storefront preview
2829
? `${parentOrigin}${LOCAL_BUNDLE_PATH}/static/storefront-preview.js`
2930
: `${parentOrigin}/cc/b2c/preview/preview.client.js`
3031
}

packages/commerce-sdk-react/src/constant.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,23 @@ export const IFRAME_HOST_ALLOW_LIST = Object.freeze([
1414
'https://runtime-admin-preview.mobify-storefront.com'
1515
])
1616

17-
// We hardcode these here since we don't want commerce-sdk-react to have a dependency on pwa-kit-runtime
17+
/* Deprecating the following path constants since, outside of storefront preview,
18+
* the paths they are used for can be passed in via the provider. */
19+
/**
20+
* @deprecated
21+
*/
1822
export const MOBIFY_PATH = '/mobify'
23+
/**
24+
* @deprecated
25+
*/
1926
export const PROXY_PATH = `${MOBIFY_PATH}/proxy`
27+
/**
28+
* @deprecated
29+
*/
2030
export const LOCAL_BUNDLE_PATH = `${MOBIFY_PATH}/bundle/development`
31+
/**
32+
* @deprecated
33+
*/
2134
export const SLAS_PRIVATE_PROXY_PATH = `${MOBIFY_PATH}/slas/private`
2235

2336
export const SLAS_SECRET_WARNING_MSG =

packages/commerce-sdk-react/src/provider.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export interface CommerceApiProviderProps extends ApiClientConfigParams {
4141
headers?: Record<string, string>
4242
fetchedToken?: string
4343
enablePWAKitPrivateClient?: boolean
44+
privateClientProxyEndpoint?: string
4445
clientSecret?: string
4546
silenceWarnings?: boolean
4647
logger?: Logger
@@ -138,6 +139,7 @@ const CommerceApiProvider = (props: CommerceApiProviderProps): ReactElement => {
138139
currency,
139140
fetchedToken,
140141
enablePWAKitPrivateClient,
142+
privateClientProxyEndpoint,
141143
clientSecret,
142144
silenceWarnings,
143145
logger,
@@ -164,6 +166,7 @@ const CommerceApiProvider = (props: CommerceApiProviderProps): ReactElement => {
164166
fetchOptions,
165167
fetchedToken,
166168
enablePWAKitPrivateClient,
169+
privateClientProxyEndpoint,
167170
clientSecret,
168171
silenceWarnings,
169172
logger: configLogger,
@@ -183,6 +186,7 @@ const CommerceApiProvider = (props: CommerceApiProviderProps): ReactElement => {
183186
fetchOptions,
184187
fetchedToken,
185188
enablePWAKitPrivateClient,
189+
privateClientProxyEndpoint,
186190
clientSecret,
187191
silenceWarnings,
188192
configLogger,
@@ -253,6 +257,11 @@ const CommerceApiProvider = (props: CommerceApiProviderProps): ReactElement => {
253257
fetchOptions
254258
}
255259

260+
// Special proxy endpoint for injecting SLAS private client secret.
261+
// This is only used by the ShopperLogin API as that is the only one that interacts with SLAS.
262+
// We prioritize config.privateClientProxyEndpoint since that allows us to use the new envBasePath feature
263+
// The preexisting hard coded privateClientEndpoint is kept here for now to prevent a breaking change.
264+
// TODO: We should remove this in the next major release so we do not have a hard coded proxy path inside commerce-sdk-react
256265
const baseUrl = config.proxy.split(MOBIFY_PATH)[0]
257266
const privateClientEndpoint = `${baseUrl}${SLAS_PRIVATE_PROXY_PATH}`
258267

@@ -264,7 +273,11 @@ const CommerceApiProvider = (props: CommerceApiProviderProps): ReactElement => {
264273
shopperGiftCertificates: new ShopperGiftCertificates(config),
265274
shopperLogin: new ShopperLogin({
266275
...config,
267-
proxy: enablePWAKitPrivateClient ? privateClientEndpoint : config.proxy
276+
proxy: enablePWAKitPrivateClient
277+
? privateClientProxyEndpoint
278+
? privateClientProxyEndpoint
279+
: privateClientEndpoint
280+
: config.proxy
268281
}),
269282
shopperOrders: new ShopperOrders(config),
270283
shopperProducts: new ShopperProducts(config),

packages/pwa-kit-create-app/CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
## v3.12.0-dev (Jul 28, 2025)
1+
## v3.12.0-dev (Jul 22, 2025)
22
- This feature introduces an AI-powered shopping assistant that integrates Salesforce Embedded Messaging Service with PWA Kit applications. The shopper agent provides real-time chat support, search assistance, and personalized shopping guidance directly within the e-commerce experience. [#2658](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2658)
3+
- Add support for environment level base paths on /mobify routes [#2892](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2892)
4+
- Add `--initGit` to automate git repo creation for the generated project [#2817](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2817)
5+
36

47
- Update _app-config generator template to include `hybridAuthEnabled` prop [#3151](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3151)
58

packages/pwa-kit-create-app/assets/bootstrap/js/config/default.js.hbs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ module.exports = {
123123
tenantId: '{{answers.project.dataCloud.tenantId}}'
124124
}
125125
},
126+
// Experimental: The base path for the app. This is the path that will be prepended to all /mobify routes,
127+
// callback routes, and Express routes.
128+
// Setting this to `/` or an empty string will result in the above routes not having a base path.
129+
envBasePath: '/',
126130
// This list contains server-side only libraries that you don't want to be compiled by webpack
127131
externals: [],
128132
// Page not found url for your app

packages/pwa-kit-create-app/assets/bootstrap/js/overrides/app/components/_app-config/index.jsx.hbs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,13 @@ import {
1818
resolveLocaleFromUrl
1919
} from '@salesforce/retail-react-app/app/utils/site-utils'
2020
import {getConfig} from '@salesforce/pwa-kit-runtime/utils/ssr-config'
21-
import {proxyBasePath} from '@salesforce/pwa-kit-runtime/utils/ssr-namespace-paths'
21+
import {
22+
getEnvBasePath,
23+
slasPrivateProxyPath
24+
} from '@salesforce/pwa-kit-runtime/utils/ssr-namespace-paths'
2225
import {createUrlTemplate} from '@salesforce/retail-react-app/app/utils/url'
2326
import createLogger from '@salesforce/pwa-kit-runtime/utils/logger-factory'
27+
import {isAbsoluteURL} from '@salesforce/retail-react-app/app/page-designer/utils'
2428

2529
import {CommerceApiProvider} from '@salesforce/commerce-sdk-react'
2630
import {withReactQuery} from '@salesforce/pwa-kit-react-sdk/ssr/universal/components/with-react-query'
@@ -68,6 +72,14 @@ const AppConfig = ({children, locals = {}}) => {
6872
supportedCountries: STORE_LOCATOR_SUPPORTED_COUNTRIES
6973
}
7074

75+
// Set absolute uris for CommerceApiProvider proxies and callbacks
76+
const redirectURI = `${appOrigin}${getEnvBasePath()}/callback`
77+
const proxy = `${appOrigin}${getEnvBasePath()}${commerceApiConfig.proxyPath}`
78+
const slasPrivateClientProxyEndpoint = `${appOrigin}${getEnvBasePath()}${slasPrivateProxyPath}`
79+
const passwordlessLoginCallbackURI = isAbsoluteURL(passwordlessCallback)
80+
? passwordlessCallback
81+
: `${appOrigin}${getEnvBasePath()}${passwordlessCallback}`
82+
7183
return (
7284
<CommerceApiProvider
7385
shortCode={commerceApiConfig.parameters.shortCode}
@@ -76,17 +88,20 @@ const AppConfig = ({children, locals = {}}) => {
7688
siteId={locals.site?.id}
7789
locale={locals.locale?.id}
7890
currency={locals.locale?.preferredCurrency}
79-
redirectURI={`${appOrigin}/callback`}
80-
proxy={`${appOrigin}${commerceApiConfig.proxyPath}`}
91+
redirectURI={redirectURI}
92+
proxy={proxy}
8193
headers={headers}
8294
defaultDnt={DEFAULT_DNT_STATE}
8395
logger={createLogger({packageName: 'commerce-sdk-react'})}
84-
passwordlessLoginCallbackURI={passwordlessCallback}
85-
{{#if answers.project.commerce.isSlasPrivate}}
86-
// Set 'enablePWAKitPrivateClient' to true use SLAS private client login flows.
96+
passwordlessLoginCallbackURI={passwordlessLoginCallbackURI}
97+
// Set 'enablePWAKitPrivateClient' to true to use SLAS private client login flows.
8798
// Make sure to also enable useSLASPrivateClient in ssr.js when enabling this setting.
99+
{{#if answers.project.commerce.isSlasPrivate}}
88100
enablePWAKitPrivateClient={true}
101+
{{else}}
102+
enablePWAKitPrivateClient={false}
89103
{{/if}}
104+
slasPrivateClientProxyEndpoint={slasPrivateClientProxyEndpoint}
90105
// Uncomment 'hybridAuthEnabled' if the current site has Hybrid Auth enabled. Do NOT set this flag for hybrid storefronts using Plugin SLAS.
91106
// hybridAuthEnabled={true}
92107
>

packages/pwa-kit-create-app/assets/bootstrap/js/overrides/app/main.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
*/
77
import {start, registerServiceWorker} from '@salesforce/pwa-kit-react-sdk/ssr/browser/main'
8+
import {getEnvBasePath} from '@salesforce/pwa-kit-runtime/utils/ssr-namespace-paths'
89

910
const main = () => {
1011
// The path to your service worker should match what is set up in ssr.js
11-
return Promise.all([start(), registerServiceWorker('/worker.js')])
12+
return Promise.all([start(), registerServiceWorker(`${getEnvBasePath()}/worker.js`)])
1213
}
1314

1415
main()

packages/pwa-kit-create-app/assets/bootstrap/js/overrides/app/ssr.js.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ const {handler} = runtime.createHandler(options, (app) => {
327327
)
328328

329329
// Handle the redirect from SLAS as to avoid error
330-
app.get('/callback?*', (req, res) => {
330+
app.get('/callback', (req, res) => {
331331
// This endpoint does nothing and is not expected to change
332332
// Thus we cache it for a year to maximize performance
333333
res.set('Cache-Control', `max-age=31536000`)

0 commit comments

Comments
 (0)