From 3b835d184ca5557af2e525755a24c4155cfe1ff8 Mon Sep 17 00:00:00 2001 From: erdemcaygor Date: Mon, 12 May 2025 13:17:48 +0300 Subject: [PATCH] local storage service refactored according to ssr --- .../src/lib/strategies/content.strategy.ts | 6 ++- .../core/src/lib/strategies/dom.strategy.ts | 37 ++++++++++--------- .../core/src/lib/utils/multi-tenancy-utils.ts | 20 ++++++++-- .../providers/oauth-module-config.provider.ts | 6 ++- .../src/lib/strategies/auth-flow-strategy.ts | 10 ++--- .../src/lib/utils/clear-o-auth-storage.ts | 5 ++- .../packages/oauth/src/lib/utils/index.ts | 1 - .../oauth/src/lib/utils/storage.factory.ts | 31 ++++++++++++++-- 8 files changed, 79 insertions(+), 37 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/strategies/content.strategy.ts b/npm/ng-packs/packages/core/src/lib/strategies/content.strategy.ts index 34d8fcbac8..cc279f7048 100644 --- a/npm/ng-packs/packages/core/src/lib/strategies/content.strategy.ts +++ b/npm/ng-packs/packages/core/src/lib/strategies/content.strategy.ts @@ -1,5 +1,7 @@ import { CONTENT_SECURITY_STRATEGY, ContentSecurityStrategy } from './content-security.strategy'; import { DOM_STRATEGY, DomStrategy } from './dom.strategy'; +import { inject } from '@angular/core'; +import { DOCUMENT } from '@angular/common'; export type ElementOptions = Partial<{ [key in keyof T]: T[key]; @@ -35,7 +37,7 @@ export abstract class ContentStrategy { createElement(): HTMLStyleElement { - const element = document.createElement('style'); + const element = inject(DOCUMENT).createElement('style'); element.textContent = this.content; return element; @@ -44,7 +46,7 @@ export class StyleContentStrategy extends ContentStrategy { export class ScriptContentStrategy extends ContentStrategy { createElement(): HTMLScriptElement { - const element = document.createElement('script'); + const element = inject(DOCUMENT).createElement('script'); element.textContent = this.content; return element; diff --git a/npm/ng-packs/packages/core/src/lib/strategies/dom.strategy.ts b/npm/ng-packs/packages/core/src/lib/strategies/dom.strategy.ts index 4fbe18d235..343e989b48 100644 --- a/npm/ng-packs/packages/core/src/lib/strategies/dom.strategy.ts +++ b/npm/ng-packs/packages/core/src/lib/strategies/dom.strategy.ts @@ -1,28 +1,29 @@ +import { inject, PLATFORM_ID } from '@angular/core'; +import { isPlatformBrowser, DOCUMENT } from '@angular/common'; + export class DomStrategy { + private readonly isBrowser: boolean; + constructor( - public target: HTMLElement = document.head, + private targetFactory: () => HTMLElement, public position: InsertPosition = 'beforeend', - ) {} + ) { + const platformId = inject(PLATFORM_ID); + this.isBrowser = isPlatformBrowser(platformId); + } insertElement(element: T) { - this.target.insertAdjacentElement(this.position, element); + if (this.isBrowser) { + const target = this.targetFactory(); + target.insertAdjacentElement(this.position, element); + } } } export const DOM_STRATEGY = { - AfterElement(element: HTMLElement) { - return new DomStrategy(element, 'afterend'); - }, - AppendToBody() { - return new DomStrategy(document.body, 'beforeend'); - }, - AppendToHead() { - return new DomStrategy(document.head, 'beforeend'); - }, - BeforeElement(element: HTMLElement) { - return new DomStrategy(element, 'beforebegin'); - }, - PrependToHead() { - return new DomStrategy(document.head, 'afterbegin'); - }, + AfterElement: (el: HTMLElement) => new DomStrategy(() => el, 'afterend'), + BeforeElement: (el: HTMLElement) => new DomStrategy(() => el, 'beforebegin'), + AppendToBody: () => new DomStrategy(() => inject(DOCUMENT).body, 'beforeend'), + AppendToHead: () => new DomStrategy(() => inject(DOCUMENT).head, 'beforeend'), + PrependToHead: () => new DomStrategy(() => inject(DOCUMENT).head, 'afterbegin'), }; diff --git a/npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts index cd1b9fe50e..278e15c521 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts @@ -1,6 +1,7 @@ -import { Injector } from '@angular/core'; +import { Injector, inject, PLATFORM_ID } from '@angular/core'; import clone from 'just-clone'; import { Environment } from '../models/environment'; +import { DOCUMENT, isPlatformBrowser } from '@angular/common'; import { FindTenantResultDto } from '../proxy/volo/abp/asp-net-core/mvc/multi-tenancy/models'; import { EnvironmentService } from '../services/environment.service'; @@ -10,6 +11,7 @@ import { firstValueFrom } from 'rxjs'; import { TENANT_NOT_FOUND_BY_NAME } from '../tokens/tenant-not-found-by-name'; import { HttpErrorResponse } from '@angular/common/http'; +const platformId = inject(PLATFORM_ID); const tenancyPlaceholder = '{0}'; function getCurrentTenancyName(appBaseUrl: string): string { @@ -17,11 +19,21 @@ function getCurrentTenancyName(appBaseUrl: string): string { const parseTokens = createTokenParser(appBaseUrl); const token = tenancyPlaceholder.replace(/[}{]/g, ''); - return parseTokens(window.location.href)[token]?.[0]; + const tokenValue = isPlatformBrowser(platformId) + ? parseTokens(inject(DOCUMENT).defaultView?.location.href)[token]?.[0] + : undefined; + return tokenValue; } -function getCurrentTenancyNameFromUrl(tenantKey: string) { - const urlParams = new URLSearchParams(window.location.search); +export function getCurrentTenancyNameFromUrl(tenantKey: string): string | null { + const platformId = inject(PLATFORM_ID); + + if (!isPlatformBrowser(platformId)) { + return null; + } + + const search = inject(DOCUMENT).defaultView?.location.search; + const urlParams = new URLSearchParams(search); return urlParams.get(tenantKey); } diff --git a/npm/ng-packs/packages/oauth/src/lib/providers/oauth-module-config.provider.ts b/npm/ng-packs/packages/oauth/src/lib/providers/oauth-module-config.provider.ts index 92ecc86012..30fa46a825 100644 --- a/npm/ng-packs/packages/oauth/src/lib/providers/oauth-module-config.provider.ts +++ b/npm/ng-packs/packages/oauth/src/lib/providers/oauth-module-config.provider.ts @@ -15,7 +15,7 @@ import { AbpOAuthGuard, abpOAuthGuard } from '../guards'; import { OAuthConfigurationHandler } from '../handlers'; import { OAuthApiInterceptor } from '../interceptors'; import { AbpOAuthService, OAuthErrorFilterService } from '../services'; -import { pipeToLogin, checkAccessToken } from '../utils'; +import { pipeToLogin, checkAccessToken, oAuthStorageFactory } from '../utils'; import { NavigateToManageProfileProvider } from './navigate-to-manage-profile.provider'; export function provideAbpOAuth() { @@ -36,6 +36,10 @@ export function provideAbpOAuth() { provide: ApiInterceptor, useClass: OAuthApiInterceptor, }, + { + provide: OAuthStorage, + useFactory: oAuthStorageFactory, + }, { provide: PIPE_TO_LOGIN_FN_KEY, useValue: pipeToLogin, diff --git a/npm/ng-packs/packages/oauth/src/lib/strategies/auth-flow-strategy.ts b/npm/ng-packs/packages/oauth/src/lib/strategies/auth-flow-strategy.ts index 02e5dd4ea3..b38e1d4792 100644 --- a/npm/ng-packs/packages/oauth/src/lib/strategies/auth-flow-strategy.ts +++ b/npm/ng-packs/packages/oauth/src/lib/strategies/auth-flow-strategy.ts @@ -1,4 +1,4 @@ -import { Injector } from '@angular/core'; +import { inject, Injector } from '@angular/core'; import { HttpErrorResponse } from '@angular/common/http'; import { Params, Router } from '@angular/router'; @@ -23,7 +23,6 @@ import { } from '@abp/ng.core'; import { clearOAuthStorage } from '../utils/clear-o-auth-storage'; -import { oAuthStorage } from '../utils/oauth-storage'; import { OAuthErrorFilterService } from '../services'; import { isTokenExpired } from '../utils'; import { RememberMeService } from '../services/remember-me.service'; @@ -74,8 +73,8 @@ export abstract class AuthFlowStrategy { async init(): Promise { if (this.oAuthConfig.clientId) { - const shouldClear = shouldStorageClear(this.oAuthConfig.clientId, oAuthStorage); - if (shouldClear) clearOAuthStorage(oAuthStorage); + const shouldClear = shouldStorageClear(this.oAuthConfig.clientId); + if (shouldClear) clearOAuthStorage(); } this.oAuthService.configure(this.oAuthConfig); this.oAuthService.events @@ -142,7 +141,8 @@ export abstract class AuthFlowStrategy { } } -function shouldStorageClear(clientId: string, storage: OAuthStorage): boolean { +function shouldStorageClear(clientId: string): boolean { + const storage = inject(OAuthStorage); const key = 'abpOAuthClientId'; if (!storage.getItem(key)) { storage.setItem(key, clientId); diff --git a/npm/ng-packs/packages/oauth/src/lib/utils/clear-o-auth-storage.ts b/npm/ng-packs/packages/oauth/src/lib/utils/clear-o-auth-storage.ts index d9b662c833..4b820d1963 100644 --- a/npm/ng-packs/packages/oauth/src/lib/utils/clear-o-auth-storage.ts +++ b/npm/ng-packs/packages/oauth/src/lib/utils/clear-o-auth-storage.ts @@ -1,7 +1,8 @@ import { OAuthStorage } from 'angular-oauth2-oidc'; -import { oAuthStorage } from './oauth-storage'; +import { inject } from '@angular/core'; -export function clearOAuthStorage(storage: OAuthStorage = oAuthStorage) { +export function clearOAuthStorage() { + const storage = inject(OAuthStorage); const keys = [ 'access_token', 'id_token', diff --git a/npm/ng-packs/packages/oauth/src/lib/utils/index.ts b/npm/ng-packs/packages/oauth/src/lib/utils/index.ts index 63dfd03fe8..fdf7c5598a 100644 --- a/npm/ng-packs/packages/oauth/src/lib/utils/index.ts +++ b/npm/ng-packs/packages/oauth/src/lib/utils/index.ts @@ -1,4 +1,3 @@ -export * from './oauth-storage'; export * from './storage.factory'; export * from './auth-utils'; export * from './clear-o-auth-storage'; diff --git a/npm/ng-packs/packages/oauth/src/lib/utils/storage.factory.ts b/npm/ng-packs/packages/oauth/src/lib/utils/storage.factory.ts index 5d76ecf244..2194abdf69 100644 --- a/npm/ng-packs/packages/oauth/src/lib/utils/storage.factory.ts +++ b/npm/ng-packs/packages/oauth/src/lib/utils/storage.factory.ts @@ -1,6 +1,29 @@ -import { OAuthStorage } from 'angular-oauth2-oidc'; -import { oAuthStorage } from './oauth-storage'; +import { inject, PLATFORM_ID } from '@angular/core'; +import { isPlatformBrowser } from '@angular/common'; -export function storageFactory(): OAuthStorage { - return oAuthStorage; +export class MockStorage implements Storage { + private data = new Map(); + get length() { + return this.data.size; + } + clear() { + this.data.clear(); + } + getItem(key: string) { + return this.data.get(key) || null; + } + key(index: number) { + return Array.from(this.data.keys())[index] || null; + } + removeItem(key: string) { + this.data.delete(key); + } + setItem(key: string, value: string) { + this.data.set(key, value); + } +} + +export function oAuthStorageFactory(): Storage { + const platformId = inject(PLATFORM_ID); + return isPlatformBrowser(platformId) ? localStorage : new MockStorage(); }