diff --git a/projects/storefrontapp/src/app/app.config.server.ts b/projects/storefrontapp/src/app/app.config.server.ts index e9fe2316464..19c7e479af9 100644 --- a/projects/storefrontapp/src/app/app.config.server.ts +++ b/projects/storefrontapp/src/app/app.config.server.ts @@ -10,14 +10,18 @@ import { mergeApplicationConfig, } from '@angular/core'; import { provideServerRendering } from '@angular/platform-server'; +import { provideConfigFactory } from '@spartacus/core'; import { TestConfigServerModule } from '@spartacus/setup/ssr'; import { appConfig } from './app.config'; import { AppServerModule } from './app.module.server'; +import { ssrConfigurationFactory } from './base-url-state'; const serverConfig: ApplicationConfig = { providers: [ provideServerRendering(), + provideConfigFactory(ssrConfigurationFactory), + importProvidersFrom(AppServerModule), importProvidersFrom( diff --git a/projects/storefrontapp/src/app/base-url-state.ts b/projects/storefrontapp/src/app/base-url-state.ts new file mode 100644 index 00000000000..0958c5512fb --- /dev/null +++ b/projects/storefrontapp/src/app/base-url-state.ts @@ -0,0 +1,79 @@ +/* + * SPDX-FileCopyrightText: 2026 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { inject, makeStateKey, TransferState } from '@angular/core'; +import { Meta } from '@angular/platform-browser'; +import { + Config, + OccConfig, + occServerConfigFromMetaTagFactory, + WindowRef, +} from '@spartacus/core'; + +/** + * State Transfer key for ssr-generated configuration + */ +export const ssrConfigurationState = makeStateKey( + 'ssrConfigurationState' +); + +/** + * Generate a configuration object for use in SSR context and set it as transfer + * state to be used in the browser context. + */ +export const ssrConfigurationFactory = (): OccConfig => { + const transferState = inject(TransferState); + const windowRef = inject(WindowRef); + const meta = inject(Meta); + + /* Read inputs */ + // read config value injected via HTML, could be used for fallback baseUrl + const injectedConfig = occServerConfigFromMetaTagFactory(meta); + const defaultHost = injectedConfig.backend?.occ?.baseUrl; + // read page origin or href + const pageOrigin = windowRef.location.origin; + + /* Base URL calculation logic */ + const subdomain = 'api'; + let calculatedBaseUrl: string | undefined; + if (pageOrigin) { + const origin = new URL(pageOrigin); + + // replace subdomain if it is a stage environment, otherwise add subdomain to host + calculatedBaseUrl = origin.host.includes('model-t.myhybris.cloud') + ? `https://${origin.host.replace(/^[^.]+/, subdomain)}` + : `https://${subdomain}.${origin.host}`; + calculatedBaseUrl = 'https://20.83.184.244:9002'; + } + + /* Return calculated base URL in configuration */ + const config = { + backend: { + occ: { + baseUrl: calculatedBaseUrl ?? defaultHost, + prefix: '/occ/v2/', + }, + }, + } satisfies OccConfig; + console.log(JSON.stringify(config, undefined, 2)); + + /* Transfer completed configuration to browser */ + transferState.set(ssrConfigurationState, config); + + return config; +}; + +/** + * Read and apply the ssr-generated configuration + */ +export const csrConfigurationFactory = (): OccConfig => { + const transferState = inject(TransferState); + const config = transferState.get(ssrConfigurationState, {}); + + console.log('state transfer config', JSON.stringify(config, undefined, 2)); + + return config; +}; diff --git a/projects/storefrontapp/src/app/private/private.providers.ts b/projects/storefrontapp/src/app/private/private.providers.ts index 66db4ac6f08..5cc1e167b39 100644 --- a/projects/storefrontapp/src/app/private/private.providers.ts +++ b/projects/storefrontapp/src/app/private/private.providers.ts @@ -21,7 +21,7 @@ export const privateProviders = makeEnvironmentProviders([ // Custom OCC backend configuration: backend: { occ: { - baseUrl: environment.occBaseUrl, + // baseUrl: environment.occBaseUrl, // disable this prefix: environment.occApiPrefix, }, media: { diff --git a/projects/storefrontapp/src/app/spartacus/spartacus-configuration.module.ts b/projects/storefrontapp/src/app/spartacus/spartacus-configuration.module.ts index 82e0b2dea6a..6877dd6670f 100644 --- a/projects/storefrontapp/src/app/spartacus/spartacus-configuration.module.ts +++ b/projects/storefrontapp/src/app/spartacus/spartacus-configuration.module.ts @@ -23,6 +23,7 @@ import { mediaConfig, } from '@spartacus/storefront'; import { environment } from '../../environments/environment'; +import { csrConfigurationFactory } from '../base-url-state'; import { spartacusB2bConfigurationProviders } from './spartacus-b2b-configuration.providers'; import { spartacusB2cConfigurationProviders } from './spartacus-b2c-configuration.providers'; @@ -67,6 +68,8 @@ const spartacusChannelSpecificConfigurationProviders = environment.b2b }), spartacusChannelSpecificConfigurationProviders, + + provideConfigFactory(csrConfigurationFactory), ], }) export class SpartacusConfigurationModule {} diff --git a/projects/storefrontapp/src/app/ssr-base-url.provider.ts b/projects/storefrontapp/src/app/ssr-base-url.provider.ts new file mode 100644 index 00000000000..aa89e15207e --- /dev/null +++ b/projects/storefrontapp/src/app/ssr-base-url.provider.ts @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2026 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { inject } from '@angular/core'; +import { Meta } from '@angular/platform-browser'; +import { + LoggerService, + OCC_BASE_URL_META_TAG_NAME, + OccConfig, + occServerConfigFromMetaTagFactory, + WindowRef, +} from '@spartacus/core'; +import { baseUrlMap, fallbackBaseUrl } from './ssr-baser-url-mappings'; + +/** + * Determine an OCC Base URL to use in SSR processing and set it to the OCC Base URL Meta tag. + * + * Allows for server-side assignment of OCC base URL without exposing url mappings. + */ +export const ssrBaseUrl = (): OccConfig => { + const meta = inject(Meta); + const windowRef = inject(WindowRef); + const logger = inject(LoggerService); + + /* read injected data */ + /** Will be the value injected at pod startup */ + const initialBaseUrl = + occServerConfigFromMetaTagFactory(meta).backend?.occ?.baseUrl; + logger.error(`initialBaseUrl: ${initialBaseUrl}`); + + /* read request data */ + const originUrl = new URL(windowRef.location.origin as string); + // const hrefUrl = new URL(windowRef.location.href as string); // If we need to read request path + logger.error( + `windowRef: ${JSON.stringify(windowRef.location, undefined, 2)}` + ); + + /* Business logic to determine baseUrl */ + // Uses a simple map, fallback to injected value, then hard-coded fallback + const baseUrl = + baseUrlMap[originUrl?.hostname] ?? initialBaseUrl ?? fallbackBaseUrl; + + /* Set meta tag for CSR configuration */ + meta.updateTag({ name: OCC_BASE_URL_META_TAG_NAME, content: baseUrl }); + logger.error(`set meta tag to: ${baseUrl}`); + + /* Return config partial for SSR configuration */ + const config = { + backend: { occ: { baseUrl } }, + } satisfies OccConfig; + logger.error(`adding config: ${JSON.stringify(config, undefined, 2)}`); + return config; +}; diff --git a/projects/storefrontapp/src/app/ssr-baser-url-mappings.ts b/projects/storefrontapp/src/app/ssr-baser-url-mappings.ts new file mode 100644 index 00000000000..9952ce1c74f --- /dev/null +++ b/projects/storefrontapp/src/app/ssr-baser-url-mappings.ts @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2026 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +export const fallbackBaseUrl = 'https://40.76.109.9:9002'; + +export const baseUrlMap: Record = { + 'apparel-storefront.com': 'https://api.apparel-storefront.com', + + // localhost: 'https://20.83.184.244:9002', + localhost: 'https://40.76.109.9:9002', + + 'spartacusstore.c432wmya2v-teamspart3-s3-public.model-t.myhybris.cloud': + 'https://api.c432wmya2v-teamspart3-s3-public.model-t.myhybris.cloud', + + 'spartacusstore.c432wmya2v-teamspart3-s2-public.model-t.myhybris.cloud': + 'https://api.c432wmya2v-teamspart3-s2-public.model-t.myhybris.cloud', + + 'b2bspastore.c432wmya2v-teamspart3-s2-public.model-t.myhybris.cloud': + 'https://api.c432wmya2v-teamspart3-s3-public.model-t.myhybris.cloud', + + 'electronics-storefront.com': 'https://20.83.184.244:9002', +};