Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions projects/storefrontapp/src/app/app.config.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
79 changes: 79 additions & 0 deletions projects/storefrontapp/src/app/base-url-state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* SPDX-FileCopyrightText: 2026 SAP Spartacus team <spartacus-team@sap.com>
*
* 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<Config>(
'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;
};
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -67,6 +68,8 @@ const spartacusChannelSpecificConfigurationProviders = environment.b2b
}),

spartacusChannelSpecificConfigurationProviders,

provideConfigFactory(csrConfigurationFactory),
],
})
export class SpartacusConfigurationModule {}
56 changes: 56 additions & 0 deletions projects/storefrontapp/src/app/ssr-base-url.provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* SPDX-FileCopyrightText: 2026 SAP Spartacus team <spartacus-team@sap.com>
*
* 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;
};
25 changes: 25 additions & 0 deletions projects/storefrontapp/src/app/ssr-baser-url-mappings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2026 SAP Spartacus team <spartacus-team@sap.com>
*
* SPDX-License-Identifier: Apache-2.0
*/

export const fallbackBaseUrl = 'https://40.76.109.9:9002';

export const baseUrlMap: Record<string, string> = {
'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',
};