From 29d1b0af43a1c499605d2c802eb03454261fdfa2 Mon Sep 17 00:00:00 2001 From: Grzegorz Krajniak Date: Thu, 18 Sep 2025 14:56:13 +0200 Subject: [PATCH 1/9] add support for local development --- src/portal-options/auth-config-provider.ts | 11 +++++++++++ ...content-configuration-service-providers.service.ts | 8 +++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/portal-options/auth-config-provider.ts b/src/portal-options/auth-config-provider.ts index e996345..5170810 100644 --- a/src/portal-options/auth-config-provider.ts +++ b/src/portal-options/auth-config-provider.ts @@ -3,6 +3,7 @@ import { AuthConfigService, DiscoveryService, EnvAuthConfigService, + EnvService, ServerAuthVariables, } from '@openmfp/portal-server-lib'; import type { Request } from 'express'; @@ -14,6 +15,7 @@ export class PMAuthConfigProvider implements AuthConfigService { constructor( private discoveryService: DiscoveryService, private envEuthConfigService: EnvAuthConfigService, + private envService: EnvService, ) {} async getAuthConfig(request: Request): Promise { @@ -75,6 +77,15 @@ export class PMAuthConfigProvider implements AuthConfigService { const subDomain = request.hostname.split('.')[0]; const clientId = process.env['OIDC_CLIENT_ID_DEFAULT']; const baseDomain = process.env['BASE_DOMAINS_DEFAULT']; + + const { isLocal } = this.envService.getEnv(); + if (isLocal) { + return { + organization: 'openmfp', + baseDomain: request.hostname, + }; + } + return { organization: request.hostname === baseDomain ? clientId : subDomain, baseDomain, diff --git a/src/portal-options/service-providers/content-configuration-service-providers.service.ts b/src/portal-options/service-providers/content-configuration-service-providers.service.ts index ed266b9..83bbae9 100644 --- a/src/portal-options/service-providers/content-configuration-service-providers.service.ts +++ b/src/portal-options/service-providers/content-configuration-service-providers.service.ts @@ -2,16 +2,21 @@ import { RequestContext } from '../openmfp-request-context-provider.js'; import { contentConfigurationsQuery } from './contentconfigurations-query.js'; import { ContentConfigurationQueryResponse } from './models/contentconfigurations.js'; import { welcomeNodeConfig } from './models/welcome-node-config.js'; +import { Injectable } from '@nestjs/common'; import { ContentConfiguration, + EnvService, ServiceProviderResponse, ServiceProviderService, } from '@openmfp/portal-server-lib'; import { GraphQLClient } from 'graphql-request'; +@Injectable() export class ContentConfigurationServiceProvidersService implements ServiceProviderService { + constructor(private envService: EnvService) {} + async getServiceProviders( token: string, entities: string[], @@ -22,7 +27,8 @@ export class ContentConfigurationServiceProvidersService throw new Error('Token is required'); } - if (!context.isSubDomain) { + const { isLocal } = this.envService.getEnv(); + if (!context.isSubDomain && !isLocal) { return welcomeNodeConfig; } From 6a981d237cd5d1eabc634eb34b6debd464c6da7e Mon Sep 17 00:00:00 2001 From: Grzegorz Krajniak Date: Thu, 18 Sep 2025 15:19:41 +0200 Subject: [PATCH 2/9] add support for local development --- .../auth-config-provider.spec.ts | 22 ++++++++++++++++++- ...guration-service-providers.service.spec.ts | 19 +++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/portal-options/auth-config-provider.spec.ts b/src/portal-options/auth-config-provider.spec.ts index dfb94f3..c3fd204 100644 --- a/src/portal-options/auth-config-provider.spec.ts +++ b/src/portal-options/auth-config-provider.spec.ts @@ -3,6 +3,7 @@ import { HttpException } from '@nestjs/common'; import { DiscoveryService, EnvAuthConfigService, + EnvService, } from '@openmfp/portal-server-lib'; import type { Request } from 'express'; import { mock } from 'jest-mock-extended'; @@ -11,11 +12,18 @@ describe('PMAuthConfigProvider', () => { let provider: PMAuthConfigProvider; let discoveryService: jest.Mocked; let envAuthConfigService: jest.Mocked; + let mockEnvService: jest.Mocked; beforeEach(() => { discoveryService = mock(); envAuthConfigService = mock(); - provider = new PMAuthConfigProvider(discoveryService, envAuthConfigService); + mockEnvService = mock(); + mockEnvService.getEnv.mockReturnValue({ isLocal: false }); + provider = new PMAuthConfigProvider( + discoveryService, + envAuthConfigService, + mockEnvService, + ); jest.resetModules(); process.env = { AUTH_SERVER_URL_DEFAULT: 'authUrl', @@ -95,4 +103,16 @@ describe('PMAuthConfigProvider', () => { baseDomain: 'example.com', }); }); + + it('getDomain should return defaults for local developmentn', () => { + const req = { hostname: 'localhost' } as Request; + mockEnvService.getEnv.mockReturnValue({ isLocal: true }); + + const result = provider.getDomain(req); + + expect(result).toEqual({ + organization: 'openmfp', + baseDomain: 'localhost', + }); + }); }); diff --git a/src/portal-options/service-providers/content-configuration-service-providers.service.spec.ts b/src/portal-options/service-providers/content-configuration-service-providers.service.spec.ts index b77020e..ccab42c 100644 --- a/src/portal-options/service-providers/content-configuration-service-providers.service.spec.ts +++ b/src/portal-options/service-providers/content-configuration-service-providers.service.spec.ts @@ -1,6 +1,9 @@ import { RequestContext } from '../openmfp-request-context-provider.js'; import { ContentConfigurationServiceProvidersService } from './content-configuration-service-providers.service.js'; +import { welcomeNodeConfig } from './models/welcome-node-config.js'; +import { EnvService } from '@openmfp/portal-server-lib'; import { GraphQLClient } from 'graphql-request'; +import { mock } from 'jest-mock-extended'; jest.mock('graphql-request', () => { return { @@ -16,10 +19,13 @@ jest.mock('graphql-request', () => { describe('ContentConfigurationServiceProvidersService', () => { let service: ContentConfigurationServiceProvidersService; let mockClient: jest.Mocked; + let mockEnvService: jest.Mocked; let context: RequestContext; beforeEach(() => { - service = new ContentConfigurationServiceProvidersService(); + mockEnvService = mock(); + mockEnvService.getEnv.mockReturnValue({ isLocal: false }); + service = new ContentConfigurationServiceProvidersService(mockEnvService); mockClient = new GraphQLClient('') as any; (GraphQLClient as jest.Mock).mockReturnValue(mockClient); context = { @@ -44,6 +50,17 @@ describe('ContentConfigurationServiceProvidersService', () => { ).rejects.toThrow('Context with organization is required'); }); + it('throws if context organization is missing', async () => { + context.isSubDomain = false; + const result = await service.getServiceProviders( + 'token', + ['entity'], + context, + ); + + expect(result).toEqual(welcomeNodeConfig); + }); + it('returns parsed content configurations', async () => { mockClient.request.mockResolvedValue({ ui_platform_mesh_io: { From b7191fde01c2fe269277669b685a2e20ba1c8054 Mon Sep 17 00:00:00 2001 From: Grzegorz Krajniak Date: Thu, 18 Sep 2025 17:18:40 +0200 Subject: [PATCH 3/9] add log for the crd gateway url --- .../content-configuration-service-providers.service.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/portal-options/service-providers/content-configuration-service-providers.service.ts b/src/portal-options/service-providers/content-configuration-service-providers.service.ts index 83bbae9..50aa9cc 100644 --- a/src/portal-options/service-providers/content-configuration-service-providers.service.ts +++ b/src/portal-options/service-providers/content-configuration-service-providers.service.ts @@ -44,6 +44,8 @@ export class ContentConfigurationServiceProvidersService url = url.replace('/graphql', `:${context.account}/graphql`); } + console.log(`Calculated crd gateway api url: ${url}`); + const client = new GraphQLClient(url, { headers: { Authorization: `Bearer ${token}`, From ad536a870eeab02e2efe0ce381af1f6721bf88dc Mon Sep 17 00:00:00 2001 From: Grzegorz Krajniak Date: Fri, 10 Oct 2025 13:06:17 +0200 Subject: [PATCH 4/9] Add support for localhost development --- .../auth-config-provider.spec.ts | 31 ----------- src/portal-options/auth-config-provider.ts | 9 +--- .../pm-portal-context.service.spec.ts | 39 ++++---------- .../pm-portal-context.service.ts | 12 ++--- .../pm-request-context-provider.spec.ts | 13 ++--- .../pm-request-context-provider.ts | 11 ++-- ...guration-service-providers.service.spec.ts | 7 +-- ...configuration-service-providers.service.ts | 6 +-- src/portal-options/utils/domain.spec.ts | 54 +++++-------------- src/portal-options/utils/domain.ts | 9 +--- 10 files changed, 49 insertions(+), 142 deletions(-) diff --git a/src/portal-options/auth-config-provider.spec.ts b/src/portal-options/auth-config-provider.spec.ts index 615bdab..224953b 100644 --- a/src/portal-options/auth-config-provider.spec.ts +++ b/src/portal-options/auth-config-provider.spec.ts @@ -1,5 +1,4 @@ import { PMAuthConfigProvider } from './auth-config-provider.js'; -import { getDomainAndOrganization } from './utils/domain.js'; import { HttpException } from '@nestjs/common'; import { DiscoveryService, @@ -102,34 +101,4 @@ describe('PMAuthConfigProvider', () => { await expect(provider.getAuthConfig(req)).rejects.toThrow(HttpException); }); - - it('getDomain should return organization and baseDomain', () => { - const req = { hostname: 'foo.example.com' } as Request; - const result = getDomainAndOrganization(req); - expect(result).toEqual({ - organization: 'foo', - baseDomain: 'example.com', - }); - }); - - it('getDomain should return clientId if hostname equals baseDomain', () => { - const req = { hostname: 'example.com' } as Request; - const result = getDomainAndOrganization(req); - expect(result).toEqual({ - organization: 'client123', - baseDomain: 'example.com', - }); - }); - - it('getDomain should return defaults for local developmentn', () => { - const req = { hostname: 'localhost' } as Request; - mockEnvService.getEnv.mockReturnValue({ isLocal: true }); - - const result = provider.getDomain(req); - - expect(result).toEqual({ - organization: 'openmfp', - baseDomain: 'localhost', - }); - }); }); diff --git a/src/portal-options/auth-config-provider.ts b/src/portal-options/auth-config-provider.ts index 10d258b..b524647 100644 --- a/src/portal-options/auth-config-provider.ts +++ b/src/portal-options/auth-config-provider.ts @@ -1,3 +1,4 @@ +import { getOrganization } from './utils/domain.js'; import { CoreV1Api, KubeConfig } from '@kubernetes/client-node'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { @@ -19,13 +20,7 @@ export class PMAuthConfigProvider implements AuthConfigService { async getAuthConfig(request: Request): Promise { const baseDomain = process.env['BASE_DOMAINS_DEFAULT']; - - const subDomain = request.hostname.split('.')[0]; - const isSubdomain = request.hostname !== baseDomain; - - const clientId = isSubdomain - ? subDomain - : process.env['OIDC_CLIENT_ID_DEFAULT']; + const clientId = getOrganization(request); const clientSecret = await this.getClientSecret(clientId); const oidcUrl = process.env[`DISCOVERY_ENDPOINT`]?.replace( diff --git a/src/portal-options/pm-portal-context.service.spec.ts b/src/portal-options/pm-portal-context.service.spec.ts index 56cb064..970e955 100644 --- a/src/portal-options/pm-portal-context.service.spec.ts +++ b/src/portal-options/pm-portal-context.service.spec.ts @@ -1,9 +1,10 @@ import { PMPortalContextService } from './pm-portal-context.service.js'; import { KcpKubernetesService } from './services/kcp-k8s.service.js'; -import { getDomainAndOrganization } from './utils/domain.js'; +import { getOrganization } from './utils/domain.js'; import { Test, TestingModule } from '@nestjs/testing'; import { Request } from 'express'; import { mock } from 'jest-mock-extended'; +import process from 'node:process'; jest.mock('@kubernetes/client-node', () => { class KubeConfig { @@ -23,22 +24,19 @@ jest.mock('@kubernetes/client-node', () => { }); jest.mock('./utils/domain.js', () => ({ - getDomainAndOrganization: jest.fn(), + getOrganization: jest.fn(), })); describe('PMPortalContextService', () => { let service: PMPortalContextService; let kcpKubernetesServiceMock: jest.Mocked; - const mockedGetDomainAndOrganization = jest.mocked(getDomainAndOrganization); + const mockedGetDomainAndOrganization = jest.mocked(getOrganization); let mockRequest: any; beforeEach(async () => { kcpKubernetesServiceMock = mock(); - mockedGetDomainAndOrganization.mockReturnValue({ - baseDomain: 'example.com', - organization: 'test-org', - }); + mockedGetDomainAndOrganization.mockReturnValue('test-org'); const module: TestingModule = await Test.createTestingModule({ providers: [ @@ -87,10 +85,7 @@ describe('PMPortalContextService', () => { process.env.OTHER_ENV_VAR = 'should-be-ignored'; try { - mockedGetDomainAndOrganization.mockReturnValue({ - baseDomain: 'example.com', - organization: 'test-org', - }); + mockedGetDomainAndOrganization.mockReturnValue('test-org'); const result = await service.getContextValues(mockRequest as Request); @@ -110,10 +105,7 @@ describe('PMPortalContextService', () => { process.env.OPENMFP_PORTAL_CONTEXT_MULTIPLE_SNAKE_CASE_KEYS = 'value2'; try { - mockedGetDomainAndOrganization.mockReturnValue({ - baseDomain: 'example.com', - organization: 'test-org', - }); + mockedGetDomainAndOrganization.mockReturnValue('test-org'); const result = await service.getContextValues(mockRequest as Request); @@ -132,10 +124,7 @@ describe('PMPortalContextService', () => { 'https://${org-subdomain}api.example.com/${org-name}/graphql'; try { - mockedGetDomainAndOrganization.mockReturnValue({ - baseDomain: 'example.com', - organization: 'test-org', - }); + mockedGetDomainAndOrganization.mockReturnValue('test-org'); mockRequest.hostname = 'subdomain.example.com'; @@ -154,11 +143,8 @@ describe('PMPortalContextService', () => { 'https://${org-subdomain}api.example.com/${org-name}/graphql'; try { - mockedGetDomainAndOrganization.mockReturnValue({ - baseDomain: 'example.com', - organization: 'test-org', - }); - + mockedGetDomainAndOrganization.mockReturnValue('test-org'); + process.env['BASE_DOMAINS_DEFAULT'] = 'example.com'; mockRequest.hostname = 'example.com'; const result = await service.getContextValues(mockRequest as Request); @@ -177,10 +163,7 @@ describe('PMPortalContextService', () => { process.env.OPENMFP_PORTAL_CONTEXT_VALID_KEY = 'valid-value'; try { - mockedGetDomainAndOrganization.mockReturnValue({ - baseDomain: 'example.com', - organization: 'test-org', - }); + mockedGetDomainAndOrganization.mockReturnValue('test-org'); const result = await service.getContextValues(mockRequest as Request); diff --git a/src/portal-options/pm-portal-context.service.ts b/src/portal-options/pm-portal-context.service.ts index 632ec09..171cc26 100644 --- a/src/portal-options/pm-portal-context.service.ts +++ b/src/portal-options/pm-portal-context.service.ts @@ -1,5 +1,5 @@ import { KcpKubernetesService } from './services/kcp-k8s.service.js'; -import { getDomainAndOrganization } from './utils/domain.js'; +import { getOrganization } from './utils/domain.js'; import { Injectable } from '@nestjs/common'; import { PortalContextProvider } from '@openmfp/portal-server-lib'; import type { Request } from 'express'; @@ -31,7 +31,7 @@ export class PMPortalContextService implements PortalContextProvider { } private addKcpWorkspaceUrl(request, portalContext) { - const { organization } = getDomainAndOrganization(request); + const organization = getOrganization(request); const account = request.query?.['core_platform-mesh_io_account']; portalContext.kcpWorkspaceUrl = this.kcpKubernetesService @@ -43,12 +43,12 @@ export class PMPortalContextService implements PortalContextProvider { request: Request, portalContext: Record, ): void { - const org = getDomainAndOrganization(request); - const subDomain = - request.hostname === org.baseDomain ? '' : `${org.organization}.`; + const org = getOrganization(request); + const baseDomain = process.env['BASE_DOMAINS_DEFAULT']; + const subDomain = request.hostname !== baseDomain ? `${org}.` : ''; portalContext.crdGatewayApiUrl = portalContext.crdGatewayApiUrl ?.replace('${org-subdomain}', subDomain) - .replace('${org-name}', org.organization); + .replace('${org-name}', org); } private toCamelCase(text: string): string { diff --git a/src/portal-options/pm-request-context-provider.spec.ts b/src/portal-options/pm-request-context-provider.spec.ts index 507ef57..793af5e 100644 --- a/src/portal-options/pm-request-context-provider.spec.ts +++ b/src/portal-options/pm-request-context-provider.spec.ts @@ -1,6 +1,6 @@ import { PMPortalContextService } from './pm-portal-context.service.js'; import { PMRequestContextProvider } from './pm-request-context-provider.js'; -import { getDomainAndOrganization } from './utils/domain.js'; +import { getOrganization } from './utils/domain.js'; import type { Request } from 'express'; import { mock } from 'jest-mock-extended'; @@ -22,20 +22,17 @@ jest.mock('@kubernetes/client-node', () => { }); jest.mock('./utils/domain.js', () => ({ - getDomainAndOrganization: jest.fn(), + getOrganization: jest.fn(), })); describe('PMRequestContextProvider', () => { let provider: PMRequestContextProvider; const portalContextService = mock(); - const mockedGetDomainAndOrganization = jest.mocked(getDomainAndOrganization); + const mockedGetOrganization = jest.mocked(getOrganization); beforeEach(() => { jest.resetAllMocks(); - mockedGetDomainAndOrganization.mockReturnValue({ - organization: 'org1', - baseDomain: 'org1.example.com', - }); + mockedGetOrganization.mockReturnValue('org1'); ( portalContextService.getContextValues as unknown as jest.Mock ).mockResolvedValue({ @@ -62,7 +59,7 @@ describe('PMRequestContextProvider', () => { organization: 'org1', }); - expect(mockedGetDomainAndOrganization).toHaveBeenCalledWith(req); + expect(mockedGetOrganization).toHaveBeenCalledWith(req); expect(portalContextService.getContextValues).toHaveBeenCalledWith(req); }); }); diff --git a/src/portal-options/pm-request-context-provider.ts b/src/portal-options/pm-request-context-provider.ts index 42e6f6b..72259f9 100644 --- a/src/portal-options/pm-request-context-provider.ts +++ b/src/portal-options/pm-request-context-provider.ts @@ -1,5 +1,5 @@ import { PMPortalContextService } from './pm-portal-context.service.js'; -import { getDomainAndOrganization } from './utils/domain.js'; +import { getOrganization } from './utils/domain.js'; import { Injectable } from '@nestjs/common'; import { RequestContextProvider } from '@openmfp/portal-server-lib'; import type { Request } from 'express'; @@ -16,12 +16,15 @@ export class PMRequestContextProvider implements RequestContextProvider { constructor(private pmPortalContextService: PMPortalContextService) {} async getContextValues(request: Request): Promise { - const domainData = getDomainAndOrganization(request); + const organization = getOrganization(request); + const baseDomain = process.env['BASE_DOMAINS_DEFAULT']; return { ...request.query, ...(await this.pmPortalContextService.getContextValues(request)), - organization: domainData.organization, - isSubDomain: request.hostname !== domainData.baseDomain, + organization, + isSubDomain: + request.hostname.includes('localhost') || + request.hostname !== baseDomain, }; } } diff --git a/src/portal-options/service-providers/content-configuration-service-providers.service.spec.ts b/src/portal-options/service-providers/content-configuration-service-providers.service.spec.ts index 6757094..a0e2dbe 100644 --- a/src/portal-options/service-providers/content-configuration-service-providers.service.spec.ts +++ b/src/portal-options/service-providers/content-configuration-service-providers.service.spec.ts @@ -1,9 +1,7 @@ import { RequestContext } from '../pm-request-context-provider.js'; import { ContentConfigurationServiceProvidersService } from './content-configuration-service-providers.service.js'; import { welcomeNodeConfig } from './models/welcome-node-config.js'; -import { EnvService } from '@openmfp/portal-server-lib'; import { GraphQLClient } from 'graphql-request'; -import { mock } from 'jest-mock-extended'; jest.mock('graphql-request', () => { return { @@ -19,13 +17,10 @@ jest.mock('graphql-request', () => { describe('ContentConfigurationServiceProvidersService', () => { let service: ContentConfigurationServiceProvidersService; let mockClient: jest.Mocked; - let mockEnvService: jest.Mocked; let context: RequestContext; beforeEach(() => { - mockEnvService = mock(); - mockEnvService.getEnv.mockReturnValue({ isLocal: false }); - service = new ContentConfigurationServiceProvidersService(mockEnvService); + service = new ContentConfigurationServiceProvidersService(); mockClient = new GraphQLClient('') as any; (GraphQLClient as jest.Mock).mockReturnValue(mockClient); context = { diff --git a/src/portal-options/service-providers/content-configuration-service-providers.service.ts b/src/portal-options/service-providers/content-configuration-service-providers.service.ts index 7b9a708..540be03 100644 --- a/src/portal-options/service-providers/content-configuration-service-providers.service.ts +++ b/src/portal-options/service-providers/content-configuration-service-providers.service.ts @@ -5,7 +5,6 @@ import { welcomeNodeConfig } from './models/welcome-node-config.js'; import { Injectable } from '@nestjs/common'; import { ContentConfiguration, - EnvService, ServiceProviderResponse, ServiceProviderService, } from '@openmfp/portal-server-lib'; @@ -15,8 +14,6 @@ import { GraphQLClient } from 'graphql-request'; export class ContentConfigurationServiceProvidersService implements ServiceProviderService { - constructor(private envService: EnvService) {} - async getServiceProviders( token: string, entities: string[], @@ -27,8 +24,7 @@ export class ContentConfigurationServiceProvidersService throw new Error('Token is required'); } - const { isLocal } = this.envService.getEnv(); - if (!context.isSubDomain && !isLocal) { + if (!context.isSubDomain) { return welcomeNodeConfig; } diff --git a/src/portal-options/utils/domain.spec.ts b/src/portal-options/utils/domain.spec.ts index b1c5d39..8695bc3 100644 --- a/src/portal-options/utils/domain.spec.ts +++ b/src/portal-options/utils/domain.spec.ts @@ -1,7 +1,7 @@ -import { getDomainAndOrganization } from './domain.js'; +import { getOrganization } from './domain.js'; import type { Request } from 'express'; -describe('getDomainAndOrganization', () => { +describe('getOrganization', () => { const OLD_ENV = process.env; beforeEach(() => { @@ -18,56 +18,30 @@ describe('getDomainAndOrganization', () => { const makeReq = (hostname: string): Request => ({ hostname }) as unknown as Request; - it('returns subdomain as organization when hostname is not base domain', () => { - const req = makeReq('test-org.example.com'); - const result = getDomainAndOrganization(req); - expect(result).toEqual({ - organization: 'test-org', - baseDomain: 'example.com', - }); + it('returns subdomain when hostname is not base domain', () => { + const req = makeReq('team1.example.com'); + expect(getOrganization(req)).toBe('team1'); }); - it('returns client id as organization when hostname equals base domain', () => { + it('returns client id when hostname equals base domain', () => { const req = makeReq('example.com'); - const result = getDomainAndOrganization(req); - expect(result).toEqual({ - organization: 'default-client', - baseDomain: 'example.com', - }); + expect(getOrganization(req)).toBe('default-client'); }); - it('handles single-label hostnames', () => { + it('handles single-label hostname', () => { const req = makeReq('localhost'); - const result = getDomainAndOrganization(req); - expect(result).toEqual({ - organization: 'localhost', - baseDomain: 'example.com', - }); + expect(getOrganization(req)).toBe('localhost'); }); - it('handles multi-level subdomains', () => { + it('handles multi-level subdomain', () => { const req = makeReq('alpha.beta.example.com'); - const result = getDomainAndOrganization(req); - expect(result).toEqual({ - organization: 'alpha', - baseDomain: 'example.com', - }); + expect(getOrganization(req)).toBe('alpha'); }); - it('propagates updated env values', () => { + it('reflects updated env values', () => { process.env.OIDC_CLIENT_ID_DEFAULT = 'another-client'; process.env.BASE_DOMAINS_DEFAULT = 'corp.example.org'; - const req1 = makeReq('corp.example.org'); - const req2 = makeReq('dev.corp.example.org'); - - expect(getDomainAndOrganization(req1)).toEqual({ - organization: 'another-client', - baseDomain: 'corp.example.org', - }); - - expect(getDomainAndOrganization(req2)).toEqual({ - organization: 'dev', - baseDomain: 'corp.example.org', - }); + expect(getOrganization(makeReq('corp.example.org'))).toBe('another-client'); + expect(getOrganization(makeReq('dev.corp.example.org'))).toBe('dev'); }); }); diff --git a/src/portal-options/utils/domain.ts b/src/portal-options/utils/domain.ts index df8256f..7fc36c0 100644 --- a/src/portal-options/utils/domain.ts +++ b/src/portal-options/utils/domain.ts @@ -1,13 +1,8 @@ import type { Request } from 'express'; -export const getDomainAndOrganization = ( - request: Request, -): { organization?: string; baseDomain?: string } => { +export const getOrganization = (request: Request): string => { const subDomain = request.hostname.split('.')[0]; const clientId = process.env['OIDC_CLIENT_ID_DEFAULT']; const baseDomain = process.env['BASE_DOMAINS_DEFAULT']; - return { - organization: request.hostname === baseDomain ? clientId : subDomain, - baseDomain, - }; + return request.hostname !== baseDomain ? subDomain : clientId; }; From 0ede1923a1e11cbe1ff2803f677984370d7a68c0 Mon Sep 17 00:00:00 2001 From: Grzegorz Krajniak Date: Fri, 10 Oct 2025 13:07:44 +0200 Subject: [PATCH 5/9] Add support for localhost development --- src/portal-options/auth-config-provider.spec.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/portal-options/auth-config-provider.spec.ts b/src/portal-options/auth-config-provider.spec.ts index 224953b..07cbfc5 100644 --- a/src/portal-options/auth-config-provider.spec.ts +++ b/src/portal-options/auth-config-provider.spec.ts @@ -3,7 +3,6 @@ import { HttpException } from '@nestjs/common'; import { DiscoveryService, EnvAuthConfigService, - EnvService, } from '@openmfp/portal-server-lib'; import type { Request } from 'express'; import { mock } from 'jest-mock-extended'; @@ -29,13 +28,10 @@ describe('PMAuthConfigProvider', () => { let provider: PMAuthConfigProvider; let discoveryService: jest.Mocked; let envAuthConfigService: jest.Mocked; - let mockEnvService: jest.Mocked; beforeEach(() => { discoveryService = mock(); envAuthConfigService = mock(); - mockEnvService = mock(); - mockEnvService.getEnv.mockReturnValue({ isLocal: false }); provider = new PMAuthConfigProvider(discoveryService); jest.resetModules(); process.env = { From 6d0d3d27bb7a5bbccb6fcbe005447637fd54045c Mon Sep 17 00:00:00 2001 From: Grzegorz Krajniak Date: Fri, 10 Oct 2025 13:15:51 +0200 Subject: [PATCH 6/9] Add support for localhost development --- src/portal-options/pm-request-context-provider.ts | 6 +++--- .../content-configuration-service-providers.service.ts | 2 +- .../kubernetes-service-providers.service.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/portal-options/pm-request-context-provider.ts b/src/portal-options/pm-request-context-provider.ts index 72259f9..cc5ecd7 100644 --- a/src/portal-options/pm-request-context-provider.ts +++ b/src/portal-options/pm-request-context-provider.ts @@ -9,6 +9,7 @@ export interface RequestContext extends Record { organization: string; crdGatewayApiUrl?: string; isSubDomain: boolean; + isLocalhost: boolean; } @Injectable() @@ -22,9 +23,8 @@ export class PMRequestContextProvider implements RequestContextProvider { ...request.query, ...(await this.pmPortalContextService.getContextValues(request)), organization, - isSubDomain: - request.hostname.includes('localhost') || - request.hostname !== baseDomain, + isSubDomain: request.hostname !== baseDomain, + isLocalhost: request.hostname.includes('localhost'), }; } } diff --git a/src/portal-options/service-providers/content-configuration-service-providers.service.ts b/src/portal-options/service-providers/content-configuration-service-providers.service.ts index 540be03..792e284 100644 --- a/src/portal-options/service-providers/content-configuration-service-providers.service.ts +++ b/src/portal-options/service-providers/content-configuration-service-providers.service.ts @@ -24,7 +24,7 @@ export class ContentConfigurationServiceProvidersService throw new Error('Token is required'); } - if (!context.isSubDomain) { + if (!context.isSubDomain && !context.isLocalhost) { return welcomeNodeConfig; } diff --git a/src/portal-options/service-providers/kubernetes-service-providers.service.ts b/src/portal-options/service-providers/kubernetes-service-providers.service.ts index 2d8d6af..5e2f9ef 100644 --- a/src/portal-options/service-providers/kubernetes-service-providers.service.ts +++ b/src/portal-options/service-providers/kubernetes-service-providers.service.ts @@ -24,7 +24,7 @@ export class KubernetesServiceProvidersService throw new Error('Token is required'); } - if (!context.isSubDomain) { + if (!context.isSubDomain && !context.isLocalhost) { return welcomeNodeConfig; } From d31540471fe0a069702b8d872b51f61afc4304dd Mon Sep 17 00:00:00 2001 From: Grzegorz Krajniak Date: Mon, 13 Oct 2025 15:31:33 +0200 Subject: [PATCH 7/9] fix tests --- src/portal-options/pm-request-context-provider.ts | 2 -- .../content-configuration-service-providers.service.ts | 2 +- .../service-providers/kubernetes-service-providers.service.ts | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/portal-options/pm-request-context-provider.ts b/src/portal-options/pm-request-context-provider.ts index cc5ecd7..3f1bfb3 100644 --- a/src/portal-options/pm-request-context-provider.ts +++ b/src/portal-options/pm-request-context-provider.ts @@ -9,7 +9,6 @@ export interface RequestContext extends Record { organization: string; crdGatewayApiUrl?: string; isSubDomain: boolean; - isLocalhost: boolean; } @Injectable() @@ -24,7 +23,6 @@ export class PMRequestContextProvider implements RequestContextProvider { ...(await this.pmPortalContextService.getContextValues(request)), organization, isSubDomain: request.hostname !== baseDomain, - isLocalhost: request.hostname.includes('localhost'), }; } } diff --git a/src/portal-options/service-providers/content-configuration-service-providers.service.ts b/src/portal-options/service-providers/content-configuration-service-providers.service.ts index 792e284..540be03 100644 --- a/src/portal-options/service-providers/content-configuration-service-providers.service.ts +++ b/src/portal-options/service-providers/content-configuration-service-providers.service.ts @@ -24,7 +24,7 @@ export class ContentConfigurationServiceProvidersService throw new Error('Token is required'); } - if (!context.isSubDomain && !context.isLocalhost) { + if (!context.isSubDomain) { return welcomeNodeConfig; } diff --git a/src/portal-options/service-providers/kubernetes-service-providers.service.ts b/src/portal-options/service-providers/kubernetes-service-providers.service.ts index 5e2f9ef..2d8d6af 100644 --- a/src/portal-options/service-providers/kubernetes-service-providers.service.ts +++ b/src/portal-options/service-providers/kubernetes-service-providers.service.ts @@ -24,7 +24,7 @@ export class KubernetesServiceProvidersService throw new Error('Token is required'); } - if (!context.isSubDomain && !context.isLocalhost) { + if (!context.isSubDomain) { return welcomeNodeConfig; } From 173a727858b8cd64563743dc775badaa90c78333 Mon Sep 17 00:00:00 2001 From: Grzegorz Krajniak Date: Mon, 13 Oct 2025 17:06:49 +0200 Subject: [PATCH 8/9] fix tests --- .../service-providers/models/welcome-node-config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/portal-options/service-providers/models/welcome-node-config.ts b/src/portal-options/service-providers/models/welcome-node-config.ts index 3b1a5c0..bfce8e8 100644 --- a/src/portal-options/service-providers/models/welcome-node-config.ts +++ b/src/portal-options/service-providers/models/welcome-node-config.ts @@ -18,6 +18,7 @@ export const welcomeNodeConfig: ServiceProviderResponse = { pathSegment: 'welcome', hideFromNav: true, hideSideNav: true, + showBreadcrumbs: false, order: 1, url: '/assets/platform-mesh-portal-ui-wc.js#welcome-view', webcomponent: { From 4cf35cfec646ce0c664170d928a6debc6c367c87 Mon Sep 17 00:00:00 2001 From: Grzegorz Krajniak Date: Tue, 14 Oct 2025 10:23:19 +0200 Subject: [PATCH 9/9] update deps --- package-lock.json | 109 +++++++++++++++------------------------------- package.json | 4 +- 2 files changed, 37 insertions(+), 76 deletions(-) diff --git a/package-lock.json b/package-lock.json index ae0ca39..9bd91dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@nestjs/axios": ">=3.0.0", "@nestjs/common": ">=10.0.0", "@nestjs/serve-static": ">=4.0.0", - "@openmfp/portal-server-lib": ">=0.160.1", + "@openmfp/portal-server-lib": ">=0.160.2", "axios": ">=1.7.7", "express": ">=4.21.1", "rxjs": ">=7.8.1" @@ -51,7 +51,7 @@ "@nestjs/core": ">=10.0.0", "@nestjs/platform-express": ">=10.0.0", "@nestjs/serve-static": ">=4.0.0", - "@openmfp/portal-server-lib": ">=0.160.1", + "@openmfp/portal-server-lib": ">=0.160.2", "axios": ">=1.7.7", "cookie-parser": ">=1.4.7", "express": ">=4.21.1", @@ -248,6 +248,7 @@ "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -974,7 +975,6 @@ "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", "license": "MIT", - "peer": true, "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } @@ -1752,6 +1752,7 @@ "integrity": "sha512-teNTPZ8yZe3ahbYnvnVRDeOjr+3pu2uiAtNtrEsiMjVPPj+cXd5E/fr8BL7v/T7F31vYdEHrI5cC/2OoO/vM9A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/environment": "30.1.2", "@jest/expect": "30.1.2", @@ -2083,7 +2084,6 @@ "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", "integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==", "license": "MIT", - "peer": true, "engines": { "node": ">= 10.16.0" }, @@ -2096,7 +2096,6 @@ "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz", "integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 10.16.0" }, @@ -2232,6 +2231,7 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -2416,6 +2416,7 @@ "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.6.tgz", "integrity": "sha512-krKwLLcFmeuKDqngG2N/RuZHCs2ycsKcxWIDgcm7i1lf3sQ0iG03ci+DsP/r3FcT/eJDFsIHnKtNta2LIi7PzQ==", "license": "MIT", + "peer": true, "dependencies": { "file-type": "21.0.0", "iterare": "1.2.1", @@ -2634,7 +2635,6 @@ "resolved": "https://registry.npmjs.org/@nuxt/opencollective/-/opencollective-0.4.1.tgz", "integrity": "sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==", "license": "MIT", - "peer": true, "dependencies": { "consola": "^3.2.3" }, @@ -2682,9 +2682,9 @@ } }, "node_modules/@openmfp/portal-server-lib": { - "version": "0.160.1", - "resolved": "https://npm.pkg.github.com/download/@openmfp/portal-server-lib/0.160.1/1ab77e6d1e5b052c55fcc20b86293e3ff1eb339a", - "integrity": "sha512-2gO8N/kcz/DOnNtwMA6bnjntDYc/RTVtQNRHG7D2Nw0IY62sn6Pf7hWNThhJcMSOUAcwlwI1EeOQaxqvrKc1gg==", + "version": "0.160.2", + "resolved": "https://npm.pkg.github.com/download/@openmfp/portal-server-lib/0.160.2/110b28fa55ff1e683285b6d1755b982b747b5a7d", + "integrity": "sha512-S9pmoGE8y5x3cZXSCFEDaOMK6PxdnkjhsuNuJaxHSubkRUe2XPU1mr9xsEMdGXpHz1Nmf6w7k1L1/MP1HIR+vA==", "license": "ISC", "dependencies": { "@nestjs/axios": ">=3.0.0", @@ -2996,8 +2996,7 @@ "version": "4.0.9", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -3018,6 +3017,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.9.tgz", "integrity": "sha512-5yBtK0k/q8PjkMXbTfeIEP/XVYnz1R9qZJ3yUicdEW7ppdDJfe+MqXEhpqDL3mtn4Wvs1u0KLEG0RXzCgNpsSg==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -3027,7 +3027,6 @@ "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", "license": "MIT", - "peer": true, "dependencies": { "@types/node": "*", "form-data": "^4.0.4" @@ -3045,7 +3044,6 @@ "resolved": "https://registry.npmjs.org/@types/stream-buffers/-/stream-buffers-3.0.7.tgz", "integrity": "sha512-azOCy05sXVXrO+qklf0c/B07H/oHaIuDDAiHPVwlk3A9Ek+ksHyTeMajLZl3r76FxpPpxem//4Te61G1iW3Giw==", "license": "MIT", - "peer": true, "dependencies": { "@types/node": "*" } @@ -3143,6 +3141,7 @@ "integrity": "sha512-EHrrEsyhOhxYt8MTg4zTF+DJMuNBzWwgvvOYNj/zm1vnaD/IC5zCXFehZv94Piqa2cRFfXrTFxIvO95L7Qc/cw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.44.1", "@typescript-eslint/types": "8.44.1", @@ -3825,6 +3824,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3873,7 +3873,6 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "license": "MIT", - "peer": true, "engines": { "node": ">= 14" } @@ -3884,6 +3883,7 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -4043,8 +4043,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/arg": { "version": "4.1.3", @@ -4084,6 +4083,7 @@ "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", "license": "MIT", + "peer": true, "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", @@ -4094,8 +4094,7 @@ "version": "1.6.7", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/babel-jest": { "version": "30.1.2", @@ -4399,6 +4398,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001735", "electron-to-chromium": "^1.5.204", @@ -4470,7 +4470,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "peer": true, "dependencies": { "streamsearch": "^1.1.0" }, @@ -4597,6 +4596,7 @@ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "readdirp": "^4.0.1" }, @@ -4877,7 +4877,6 @@ "node >= 6.0" ], "license": "MIT", - "peer": true, "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", @@ -4890,7 +4889,6 @@ "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", "license": "MIT", - "peer": true, "engines": { "node": "^14.18.0 || >=16.10.0" } @@ -4925,7 +4923,6 @@ "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", "license": "MIT", - "peer": true, "dependencies": { "cookie": "0.7.2", "cookie-signature": "1.0.6" @@ -4938,8 +4935,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/cookie-signature": { "version": "1.2.2", @@ -4969,7 +4965,6 @@ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "license": "MIT", - "peer": true, "dependencies": { "object-assign": "^4", "vary": "^1" @@ -5193,7 +5188,6 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", "license": "MIT", - "peer": true, "dependencies": { "once": "^1.4.0" } @@ -5309,6 +5303,7 @@ "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5571,6 +5566,7 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", "license": "MIT", + "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", @@ -5631,8 +5627,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/fast-glob": { "version": "3.3.3", @@ -6315,7 +6310,6 @@ "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-1.2.0.tgz", "integrity": "sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==", "license": "MIT", - "peer": true, "engines": { "node": ">=14" } @@ -6481,7 +6475,6 @@ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", "license": "MIT", - "peer": true, "engines": { "node": ">= 12" } @@ -6629,7 +6622,6 @@ "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", "license": "MIT", - "peer": true, "peerDependencies": { "ws": "*" } @@ -6735,8 +6727,7 @@ "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest": { "version": "30.1.3", @@ -6744,6 +6735,7 @@ "integrity": "sha512-Ry+p2+NLk6u8Agh5yVqELfUJvRfV51hhVBRIB5yZPY7mU0DGBmOuFG5GebZbMbm86cdQNK0fhJuDX8/1YorISQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "30.1.3", "@jest/types": "30.0.5", @@ -7585,7 +7577,6 @@ "resolved": "https://registry.npmjs.org/jose/-/jose-6.0.13.tgz", "integrity": "sha512-Yms4GpbmdANamS51kKK6w4hRlKx8KTxbWyAAKT/MhUMtqbIqh5mb2HjhTNUbk7TFL8/MBB5zWSDohL7ed4k/UA==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/panva" } @@ -7705,7 +7696,6 @@ "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.3.0.tgz", "integrity": "sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==", "license": "MIT", - "peer": true, "dependencies": { "@jsep-plugin/assignment": "^1.3.0", "@jsep-plugin/regex": "^1.0.4", @@ -8111,7 +8101,6 @@ "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", "license": "MIT", - "peer": true, "dependencies": { "append-field": "^1.0.0", "busboy": "^1.6.0", @@ -8130,7 +8119,6 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -8140,7 +8128,6 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -8150,7 +8137,6 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "license": "MIT", - "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -8163,7 +8149,6 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "license": "MIT", - "peer": true, "dependencies": { "minimist": "^1.2.6" }, @@ -8176,7 +8161,6 @@ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "license": "MIT", - "peer": true, "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -8271,7 +8255,6 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "license": "MIT", - "peer": true, "dependencies": { "whatwg-url": "^5.0.0" }, @@ -8445,7 +8428,6 @@ "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.7.0.tgz", "integrity": "sha512-Q52wTPUWPsVLVVmTViXPQFMW2h2xv2jnDGxypjpelCFKaOjLsm7AxYuOk1oQgFm95VNDbuggasu9htXrz6XwKw==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/panva" } @@ -8455,7 +8437,6 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -8514,7 +8495,6 @@ "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.6.4.tgz", "integrity": "sha512-PLWVhRksRnNH05sqeuCX/PR+1J70NyZcAcPske+FeF732KKONd3v0p5Utx1ro1iLfCglH8B3/+dA1vqIHDoIiA==", "license": "MIT", - "peer": true, "dependencies": { "jose": "^6.0.12", "oauth4webapi": "^3.7.0" @@ -8886,6 +8866,7 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -8965,7 +8946,6 @@ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", "license": "MIT", - "peer": true, "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -9209,8 +9189,7 @@ "version": "1.5.4", "resolved": "https://registry.npmjs.org/rfc4648/-/rfc4648-1.5.4.tgz", "integrity": "sha512-rRg/6Lb+IGfJqO05HZkN50UtY7K/JhxJag1kP23+zyMfrvoB0B7RWv06MbOzoc79RgCdNTiUaNsTT1AJZ7Z+cg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/rimraf": { "version": "6.0.1", @@ -9277,6 +9256,7 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -9528,7 +9508,6 @@ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" @@ -9539,7 +9518,6 @@ "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", "license": "MIT", - "peer": true, "dependencies": { "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" @@ -9554,7 +9532,6 @@ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", "license": "MIT", - "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", @@ -9639,7 +9616,6 @@ "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.3.tgz", "integrity": "sha512-pqMqwQCso0PBJt2PQmDO0cFj0lyqmiwOMiMSkVtRokl7e+ZTRYgDHKnuZNbqjiJXgsg4nuqtD/zxuo9KqTp0Yw==", "license": "Unlicense", - "peer": true, "engines": { "node": ">= 0.10.0" } @@ -9648,7 +9624,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "peer": true, "engines": { "node": ">=10.0.0" } @@ -9658,7 +9633,6 @@ "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz", "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==", "license": "MIT", - "peer": true, "dependencies": { "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" @@ -9979,7 +9953,6 @@ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.0.tgz", "integrity": "sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w==", "license": "MIT", - "peer": true, "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" @@ -9994,7 +9967,6 @@ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "license": "MIT", - "peer": true, "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", @@ -10061,6 +10033,7 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -10210,7 +10183,6 @@ "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "b4a": "^1.6.4" } @@ -10276,8 +10248,7 @@ "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/tree-kill": { "version": "1.2.2", @@ -10410,6 +10381,7 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -10549,8 +10521,7 @@ "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/typescript": { "version": "5.9.2", @@ -10558,6 +10529,7 @@ "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10843,8 +10815,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause", - "peer": true + "license": "BSD-2-Clause" }, "node_modules/webpack": { "version": "5.101.3", @@ -10940,7 +10911,6 @@ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -10959,7 +10929,6 @@ "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -10973,7 +10942,6 @@ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -10988,7 +10956,6 @@ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "engines": { "node": ">=4.0" } @@ -10998,8 +10965,7 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/webpack/node_modules/mime-db": { "version": "1.52.0", @@ -11007,7 +10973,6 @@ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -11018,7 +10983,6 @@ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -11032,7 +10996,6 @@ "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -11052,7 +11015,6 @@ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "license": "MIT", - "peer": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -11225,7 +11187,6 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.4" } diff --git a/package.json b/package.json index 32f7ede..950ccbe 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "@nestjs/core": ">=10.0.0", "@nestjs/platform-express": ">=10.0.0", "@nestjs/serve-static": ">=4.0.0", - "@openmfp/portal-server-lib": ">=0.160.1", + "@openmfp/portal-server-lib": ">=0.160.2", "axios": ">=1.7.7", "cookie-parser": ">=1.4.7", "express": ">=4.21.1", @@ -48,7 +48,7 @@ "@nestjs/axios": ">=3.0.0", "@nestjs/common": ">=10.0.0", "@nestjs/serve-static": ">=4.0.0", - "@openmfp/portal-server-lib": ">=0.160.1", + "@openmfp/portal-server-lib": ">=0.160.2", "axios": ">=1.7.7", "express": ">=4.21.1", "rxjs": ">=7.8.1"