[DURACOM-288] Refactoring configuration to transfer state

This commit is contained in:
Giuseppe Digilio
2025-01-09 18:46:22 +01:00
parent a7276b8a30
commit cfba84ce31
9 changed files with 45 additions and 26 deletions

View File

@@ -270,10 +270,8 @@ function serverSideRender(req, res, next, sendToUser: boolean = true) {
.then((html) => { .then((html) => {
if (hasValue(html)) { if (hasValue(html)) {
// Replace REST URL with UI URL // Replace REST URL with UI URL
if (environment.ui.replaceRestUrl && REST_BASE_URL !== environment.rest.baseUrl) { if (environment.ssr.replaceRestUrl && REST_BASE_URL !== environment.rest.baseUrl) {
const t0 = Date.now();
html = html.replace(new RegExp(REST_BASE_URL, 'g'), environment.rest.baseUrl); html = html.replace(new RegExp(REST_BASE_URL, 'g'), environment.rest.baseUrl);
console.log(`Replaced all SSR URLs in HTML in ${Date.now() - t0}ms`); // todo: remove this
} }
// save server side rendered page to cache (if any are enabled) // save server side rendered page to cache (if any are enabled)

View File

@@ -52,9 +52,6 @@ export class DefaultAppConfig implements AppConfig {
// Trust X-FORWARDED-* headers from proxies // Trust X-FORWARDED-* headers from proxies
useProxies: true, useProxies: true,
transferState: true,
replaceRestUrl: false,
}; };
// The REST API server settings // The REST API server settings

View File

@@ -21,6 +21,24 @@ export interface SSRConfig extends Config {
*/ */
inlineCriticalCss: boolean; inlineCriticalCss: boolean;
/**
* Enable state transfer from the server-side application to the client-side application.
* Defaults to true.
*
* Note: When using an external application cache layer, it's recommended not to transfer the state to avoid caching it.
* Disabling it ensures that dynamic state information is not inadvertently cached, which can improve security and
* ensure that users always use the most up-to-date state.
*/
transferState: boolean;
/**
* When a different REST base URL is used for the server-side application, the generated state contains references to
* REST resources with the internal URL configured, so it is not transferred to the client application, by default.
* Enabling this setting transfers the state to the client application and replaces internal URLs with the public
* URLs used by the client application.
*/
replaceRestUrl: boolean;
/** /**
* Paths to enable SSR for. Defaults to the home page and paths in the sitemap. * Paths to enable SSR for. Defaults to the home page and paths in the sitemap.
*/ */

View File

@@ -13,7 +13,4 @@ export class UIServerConfig extends ServerConfig {
// Trust X-FORWARDED-* headers from proxies // Trust X-FORWARDED-* headers from proxies
useProxies: boolean; useProxies: boolean;
transferState: boolean;
replaceRestUrl: boolean;
} }

View File

@@ -8,6 +8,8 @@ export const environment: Partial<BuildConfig> = {
enabled: true, enabled: true,
enablePerformanceProfiler: false, enablePerformanceProfiler: false,
inlineCriticalCss: false, inlineCriticalCss: false,
transferState: true,
replaceRestUrl: false,
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ], paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ],
enableSearchComponent: false, enableSearchComponent: false,
enableBrowseComponent: false, enableBrowseComponent: false,

View File

@@ -12,6 +12,8 @@ export const environment: BuildConfig = {
enabled: true, enabled: true,
enablePerformanceProfiler: false, enablePerformanceProfiler: false,
inlineCriticalCss: false, inlineCriticalCss: false,
transferState: true,
replaceRestUrl: false,
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ], paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ],
enableSearchComponent: false, enableSearchComponent: false,
enableBrowseComponent: false, enableBrowseComponent: false,

View File

@@ -13,6 +13,8 @@ export const environment: Partial<BuildConfig> = {
enabled: false, enabled: false,
enablePerformanceProfiler: false, enablePerformanceProfiler: false,
inlineCriticalCss: false, inlineCriticalCss: false,
transferState: true,
replaceRestUrl: false,
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ], paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ],
enableSearchComponent: false, enableSearchComponent: false,
enableBrowseComponent: false, enableBrowseComponent: false,

View File

@@ -54,6 +54,7 @@ import {
APP_CONFIG_STATE, APP_CONFIG_STATE,
AppConfig, AppConfig,
} from '../../config/app-config.interface'; } from '../../config/app-config.interface';
import { BuildConfig } from '../../config/build-config.interface';
import { extendEnvironmentWithAppConfig } from '../../config/config.util'; import { extendEnvironmentWithAppConfig } from '../../config/config.util';
import { DefaultAppConfig } from '../../config/default-app-config'; import { DefaultAppConfig } from '../../config/default-app-config';
import { environment } from '../../environments/environment'; import { environment } from '../../environments/environment';
@@ -70,7 +71,7 @@ export class BrowserInitService extends InitService {
protected store: Store<AppState>, protected store: Store<AppState>,
protected correlationIdService: CorrelationIdService, protected correlationIdService: CorrelationIdService,
protected transferState: TransferState, protected transferState: TransferState,
@Inject(APP_CONFIG) protected appConfig: AppConfig, @Inject(APP_CONFIG) protected appConfig: BuildConfig,
protected translate: TranslateService, protected translate: TranslateService,
protected localeService: LocaleService, protected localeService: LocaleService,
protected angulartics2DSpace: Angulartics2DSpace, protected angulartics2DSpace: Angulartics2DSpace,
@@ -113,9 +114,7 @@ export class BrowserInitService extends InitService {
protected init(): () => Promise<boolean> { protected init(): () => Promise<boolean> {
return async () => { return async () => {
if (this.appConfig.ui.transferState) {
await this.loadAppState(); await this.loadAppState();
}
this.checkAuthenticationToken(); this.checkAuthenticationToken();
this.externalAuthCheck(); this.externalAuthCheck();
this.initCorrelationId(); this.initCorrelationId();
@@ -147,7 +146,7 @@ export class BrowserInitService extends InitService {
*/ */
private async loadAppState(): Promise<boolean> { private async loadAppState(): Promise<boolean> {
// The app state can be transferred only when SSR and CSR are using the same base url for the REST API // The app state can be transferred only when SSR and CSR are using the same base url for the REST API
if (this.appConfig.ui.transferState && (!this.appConfig.rest.hasSsrBaseUrl || this.appConfig.ui.replaceRestUrl)) { if (this.appConfig.ssr.transferState) {
const state = this.transferState.get<any>(InitService.NGRX_STATE, null); const state = this.transferState.get<any>(InitService.NGRX_STATE, null);
this.transferState.remove(InitService.NGRX_STATE); this.transferState.remove(InitService.NGRX_STATE);
this.store.dispatch(new StoreAction(StoreActionTypes.REHYDRATE, state)); this.store.dispatch(new StoreAction(StoreActionTypes.REHYDRATE, state));

View File

@@ -21,7 +21,10 @@ import { LocaleService } from '../../app/core/locale/locale.service';
import { HeadTagService } from '../../app/core/metadata/head-tag.service'; import { HeadTagService } from '../../app/core/metadata/head-tag.service';
import { CorrelationIdService } from '../../app/correlation-id/correlation-id.service'; import { CorrelationIdService } from '../../app/correlation-id/correlation-id.service';
import { InitService } from '../../app/init.service'; import { InitService } from '../../app/init.service';
import { isNotEmpty } from '../../app/shared/empty.util'; import {
isEmpty,
isNotEmpty,
} from '../../app/shared/empty.util';
import { MenuService } from '../../app/shared/menu/menu.service'; import { MenuService } from '../../app/shared/menu/menu.service';
import { ThemeService } from '../../app/shared/theme-support/theme.service'; import { ThemeService } from '../../app/shared/theme-support/theme.service';
import { Angulartics2DSpace } from '../../app/statistics/angulartics/dspace-provider'; import { Angulartics2DSpace } from '../../app/statistics/angulartics/dspace-provider';
@@ -30,6 +33,7 @@ import {
APP_CONFIG_STATE, APP_CONFIG_STATE,
AppConfig, AppConfig,
} from '../../config/app-config.interface'; } from '../../config/app-config.interface';
import { BuildConfig } from '../../config/build-config.interface';
import { environment } from '../../environments/environment'; import { environment } from '../../environments/environment';
/** /**
@@ -41,7 +45,7 @@ export class ServerInitService extends InitService {
protected store: Store<AppState>, protected store: Store<AppState>,
protected correlationIdService: CorrelationIdService, protected correlationIdService: CorrelationIdService,
protected transferState: TransferState, protected transferState: TransferState,
@Inject(APP_CONFIG) protected appConfig: AppConfig, @Inject(APP_CONFIG) protected appConfig: BuildConfig,
protected translate: TranslateService, protected translate: TranslateService,
protected localeService: LocaleService, protected localeService: LocaleService,
protected angulartics2DSpace: Angulartics2DSpace, protected angulartics2DSpace: Angulartics2DSpace,
@@ -68,9 +72,7 @@ export class ServerInitService extends InitService {
return async () => { return async () => {
this.checkAuthenticationToken(); this.checkAuthenticationToken();
this.saveAppConfigForCSR(); this.saveAppConfigForCSR();
if (this.appConfig.ui.transferState) {
this.saveAppState(); this.saveAppState();
}
this.initCorrelationId(); this.initCorrelationId();
this.checkEnvironment(); this.checkEnvironment();
@@ -92,6 +94,7 @@ export class ServerInitService extends InitService {
* @private * @private
*/ */
private saveAppState() { private saveAppState() {
if (this.appConfig.ssr.transferState && (isEmpty(this.appConfig.rest.ssrBaseUrl) || this.appConfig.ssr.replaceRestUrl)) {
this.transferState.onSerialize(InitService.NGRX_STATE, () => { this.transferState.onSerialize(InitService.NGRX_STATE, () => {
let state; let state;
this.store.pipe(take(1)).subscribe((saveState: any) => { this.store.pipe(take(1)).subscribe((saveState: any) => {
@@ -101,6 +104,7 @@ export class ServerInitService extends InitService {
return state; return state;
}); });
} }
}
private saveAppConfigForCSR(): void { private saveAppConfigForCSR(): void {
if (isNotEmpty(environment.rest.ssrBaseUrl) && environment.rest.baseUrl !== environment.rest.ssrBaseUrl) { if (isNotEmpty(environment.rest.ssrBaseUrl) && environment.rest.baseUrl !== environment.rest.ssrBaseUrl) {