@vertesia/ui 1.5.0-dev.20260713.151751Z → 1.5.0-dev.20260717.131047Z
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/env/index.d.ts +19 -0
- package/lib/env/index.d.ts.map +1 -1
- package/lib/env/index.js +6 -0
- package/lib/env/index.js.map +1 -1
- package/lib/features/agent/chat/ArtifactsTab.d.ts.map +1 -1
- package/lib/features/agent/chat/ArtifactsTab.js +65 -11
- package/lib/features/agent/chat/ArtifactsTab.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentConversation.js +51 -48
- package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
- package/lib/features/agent/chat/hooks/useArtifacts.d.ts +8 -1
- package/lib/features/agent/chat/hooks/useArtifacts.d.ts.map +1 -1
- package/lib/features/agent/chat/hooks/useArtifacts.js +28 -10
- package/lib/features/agent/chat/hooks/useArtifacts.js.map +1 -1
- package/lib/features/document-viewer/UniversalDocumentViewer.d.ts +73 -0
- package/lib/features/document-viewer/UniversalDocumentViewer.d.ts.map +1 -0
- package/lib/features/document-viewer/UniversalDocumentViewer.js +415 -0
- package/lib/features/document-viewer/UniversalDocumentViewer.js.map +1 -0
- package/lib/features/document-viewer/index.d.ts +2 -0
- package/lib/features/document-viewer/index.d.ts.map +1 -0
- package/lib/features/document-viewer/index.js +2 -0
- package/lib/features/document-viewer/index.js.map +1 -0
- package/lib/features/index.d.ts +1 -0
- package/lib/features/index.d.ts.map +1 -1
- package/lib/features/index.js +1 -0
- package/lib/features/index.js.map +1 -1
- package/lib/features/permissions/UserPermissionsProvider.d.ts +2 -3
- package/lib/features/permissions/UserPermissionsProvider.d.ts.map +1 -1
- package/lib/features/permissions/UserPermissionsProvider.js +31 -38
- package/lib/features/permissions/UserPermissionsProvider.js.map +1 -1
- package/lib/features/store/objects/components/ContentOverview.d.ts.map +1 -1
- package/lib/features/store/objects/components/ContentOverview.js +73 -13
- package/lib/features/store/objects/components/ContentOverview.js.map +1 -1
- package/lib/i18n/locales/ar.json +2 -0
- package/lib/i18n/locales/de.json +2 -0
- package/lib/i18n/locales/en.json +2 -0
- package/lib/i18n/locales/es.json +2 -0
- package/lib/i18n/locales/fr.json +2 -0
- package/lib/i18n/locales/it.json +2 -0
- package/lib/i18n/locales/ja.json +2 -0
- package/lib/i18n/locales/ko.json +2 -0
- package/lib/i18n/locales/pt.json +2 -0
- package/lib/i18n/locales/ru.json +2 -0
- package/lib/i18n/locales/tr.json +2 -0
- package/lib/i18n/locales/zh-TW.json +2 -0
- package/lib/i18n/locales/zh.json +2 -0
- package/lib/router/HistoryNavigator.d.ts.map +1 -1
- package/lib/router/HistoryNavigator.js +4 -1
- package/lib/router/HistoryNavigator.js.map +1 -1
- package/lib/router/Nav.d.ts.map +1 -1
- package/lib/router/Nav.js +7 -1
- package/lib/router/Nav.js.map +1 -1
- package/lib/router/Router.d.ts.map +1 -1
- package/lib/router/Router.js +19 -4
- package/lib/router/Router.js.map +1 -1
- package/lib/router/path.d.ts +15 -0
- package/lib/router/path.d.ts.map +1 -1
- package/lib/router/path.js +43 -0
- package/lib/router/path.js.map +1 -1
- package/lib/session/UserSession.d.ts +6 -0
- package/lib/session/UserSession.d.ts.map +1 -1
- package/lib/session/UserSession.js +31 -9
- package/lib/session/UserSession.js.map +1 -1
- package/lib/session/UserSessionProvider.d.ts.map +1 -1
- package/lib/session/UserSessionProvider.js +111 -49
- package/lib/session/UserSessionProvider.js.map +1 -1
- package/lib/session/auth/composable.d.ts.map +1 -1
- package/lib/session/auth/composable.js +52 -3
- package/lib/session/auth/composable.js.map +1 -1
- package/lib/session/auth/domainRouting.d.ts +25 -0
- package/lib/session/auth/domainRouting.d.ts.map +1 -1
- package/lib/session/auth/domainRouting.js +36 -0
- package/lib/session/auth/domainRouting.js.map +1 -1
- package/lib/shell/login/SignInRestrictedEnvStep.d.ts +1 -1
- package/lib/shell/login/SignInRestrictedEnvStep.js +1 -1
- package/lib/shell/login/SigninScreen.js +9 -1
- package/lib/shell/login/SigninScreen.js.map +1 -1
- package/lib/shell/login/productionUrl.d.ts +0 -14
- package/lib/shell/login/productionUrl.d.ts.map +1 -1
- package/lib/shell/login/productionUrl.js +14 -5
- package/lib/shell/login/productionUrl.js.map +1 -1
- package/lib/vertesia-ui-core.js +3 -3
- package/lib/vertesia-ui-core.js.map +1 -1
- package/lib/vertesia-ui-env.js +1 -1
- package/lib/vertesia-ui-env.js.map +1 -1
- package/lib/vertesia-ui-features.js +28 -28
- package/lib/vertesia-ui-features.js.map +1 -1
- package/lib/vertesia-ui-i18n.js +13 -13
- package/lib/vertesia-ui-router.js +1 -1
- package/lib/vertesia-ui-router.js.map +1 -1
- package/lib/vertesia-ui-session.js +1 -1
- package/lib/vertesia-ui-session.js.map +1 -1
- package/lib/vertesia-ui-shell.js +1 -1
- package/lib/vertesia-ui-shell.js.map +1 -1
- package/lib/vertesia-ui-widgets.js +2 -2
- package/lib/vertesia-ui-widgets.js.map +1 -1
- package/llms.txt +70 -70
- package/package.json +8 -7
- package/src/env/index.ts +24 -0
- package/src/features/agent/chat/ArtifactsTab.tsx +155 -21
- package/src/features/agent/chat/ModernAgentConversation.tsx +53 -49
- package/src/features/agent/chat/hooks/useArtifacts.ts +44 -11
- package/src/features/document-viewer/UniversalDocumentViewer.test.tsx +123 -0
- package/src/features/document-viewer/UniversalDocumentViewer.tsx +746 -0
- package/src/features/document-viewer/index.ts +1 -0
- package/src/features/index.ts +1 -0
- package/src/features/permissions/UserPermissionsProvider.tsx +36 -39
- package/src/features/store/objects/components/ContentOverview.tsx +99 -26
- package/src/i18n/locales/ar.json +2 -0
- package/src/i18n/locales/de.json +2 -0
- package/src/i18n/locales/en.json +2 -0
- package/src/i18n/locales/es.json +2 -0
- package/src/i18n/locales/fr.json +2 -0
- package/src/i18n/locales/it.json +2 -0
- package/src/i18n/locales/ja.json +2 -0
- package/src/i18n/locales/ko.json +2 -0
- package/src/i18n/locales/pt.json +2 -0
- package/src/i18n/locales/ru.json +2 -0
- package/src/i18n/locales/tr.json +2 -0
- package/src/i18n/locales/zh-TW.json +2 -0
- package/src/i18n/locales/zh.json +2 -0
- package/src/router/HistoryNavigator.ts +4 -1
- package/src/router/Nav.tsx +7 -1
- package/src/router/Router.tsx +18 -4
- package/src/router/path.test.ts +87 -0
- package/src/router/path.ts +38 -0
- package/src/session/UserSession.ts +32 -9
- package/src/session/UserSessionProvider.tsx +138 -62
- package/src/session/auth/composable.test.ts +111 -0
- package/src/session/auth/composable.ts +58 -3
- package/src/session/auth/domainRouting.test.ts +28 -1
- package/src/session/auth/domainRouting.ts +38 -0
- package/src/shell/login/SignInRestrictedEnvStep.tsx +1 -1
- package/src/shell/login/SigninScreen.tsx +9 -1
- package/src/shell/login/productionUrl.test.ts +8 -5
- package/src/shell/login/productionUrl.ts +15 -5
|
@@ -17,6 +17,44 @@ interface ComposableTokenResponse {
|
|
|
17
17
|
message?: string;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
function normalizeIssuer(value: string | undefined): string | undefined {
|
|
21
|
+
return value?.replace(/\/+$/, '');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function decodeToken(token: string): AuthTokenPayload {
|
|
25
|
+
return jwtDecode(token) as AuthTokenPayload;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isVertesiaIssuedToken(token: string | undefined): token is string {
|
|
29
|
+
if (!token) return false;
|
|
30
|
+
try {
|
|
31
|
+
const decoded = decodeToken(token) as AuthTokenPayload & { iss?: string };
|
|
32
|
+
return normalizeIssuer(decoded.iss) === normalizeIssuer(Env.endpoints.sts);
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function canUseVertesiaTokenDirectly(token: string, accountId?: string, projectId?: string): boolean {
|
|
39
|
+
const decoded = decodeToken(token);
|
|
40
|
+
const hasAuthorizationClaims = Boolean(
|
|
41
|
+
decoded.permissions?.length ||
|
|
42
|
+
decoded.account_roles?.length ||
|
|
43
|
+
decoded.project_roles?.length ||
|
|
44
|
+
decoded.apps?.length,
|
|
45
|
+
);
|
|
46
|
+
if (!hasAuthorizationClaims) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
if (accountId && decoded.account?.id !== accountId) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if (projectId && decoded.project?.id !== projectId) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
|
|
20
58
|
export async function fetchComposableToken(
|
|
21
59
|
getIdToken: () => Promise<string | null | undefined>,
|
|
22
60
|
accountId?: string,
|
|
@@ -319,23 +357,40 @@ export async function getComposableToken(
|
|
|
319
357
|
const selectedAccount = accountId ?? localStorage.getItem(LastSelectedAccountId_KEY) ?? undefined;
|
|
320
358
|
const selectedProject =
|
|
321
359
|
projectId ?? localStorage.getItem(`${LastSelectedProjectId_KEY}-${selectedAccount}`) ?? undefined;
|
|
360
|
+
const devAuthToken = Env.isLocalDev ? Env.devAuthToken : undefined;
|
|
361
|
+
const suppliedToken = devAuthToken ?? initToken ?? AUTH_TOKEN_RAW;
|
|
322
362
|
|
|
323
363
|
//token is still valid for more than 5 minutes
|
|
324
364
|
if (!forceRefresh && AUTH_TOKEN_RAW && AUTH_TOKEN && AUTH_TOKEN.exp > Date.now() / 1000 + 300) {
|
|
325
365
|
return { rawToken: AUTH_TOKEN_RAW, token: AUTH_TOKEN, error: false };
|
|
326
366
|
}
|
|
327
367
|
|
|
368
|
+
if (
|
|
369
|
+
!forceRefresh &&
|
|
370
|
+
isVertesiaIssuedToken(suppliedToken) &&
|
|
371
|
+
canUseVertesiaTokenDirectly(suppliedToken, selectedAccount, selectedProject)
|
|
372
|
+
) {
|
|
373
|
+
AUTH_TOKEN_RAW = suppliedToken;
|
|
374
|
+
AUTH_TOKEN = decodeToken(AUTH_TOKEN_RAW);
|
|
375
|
+
if (!AUTH_TOKEN.exp) {
|
|
376
|
+
throw new Error('Invalid composable token');
|
|
377
|
+
}
|
|
378
|
+
return { rawToken: AUTH_TOKEN_RAW, token: AUTH_TOKEN, error: false };
|
|
379
|
+
}
|
|
380
|
+
|
|
328
381
|
//token is close to expire, refresh it
|
|
329
|
-
if (!useInternalAuth && getFirebaseAuth().currentUser) {
|
|
382
|
+
if (!devAuthToken && !useInternalAuth && getFirebaseAuth().currentUser) {
|
|
330
383
|
//we have a firebase user, get the token from there
|
|
331
384
|
AUTH_TOKEN_RAW = await fetchComposableTokenFromFirebaseToken(selectedAccount, selectedProject);
|
|
332
|
-
} else if (initToken || AUTH_TOKEN_RAW) {
|
|
385
|
+
} else if (!devAuthToken && (initToken || AUTH_TOKEN_RAW)) {
|
|
333
386
|
// we have a token already and no firebase user, refresh it
|
|
334
387
|
AUTH_TOKEN_RAW = await fetchComposableToken(
|
|
335
388
|
() => Promise.resolve(initToken ?? AUTH_TOKEN_RAW),
|
|
336
389
|
selectedAccount,
|
|
337
390
|
selectedProject,
|
|
338
391
|
);
|
|
392
|
+
} else if (devAuthToken) {
|
|
393
|
+
AUTH_TOKEN_RAW = devAuthToken;
|
|
339
394
|
}
|
|
340
395
|
|
|
341
396
|
if (!AUTH_TOKEN_RAW) {
|
|
@@ -348,7 +403,7 @@ export async function getComposableToken(
|
|
|
348
403
|
throw new Error('Cannot acquire a composable token');
|
|
349
404
|
}
|
|
350
405
|
|
|
351
|
-
AUTH_TOKEN =
|
|
406
|
+
AUTH_TOKEN = decodeToken(AUTH_TOKEN_RAW);
|
|
352
407
|
|
|
353
408
|
if (!AUTH_TOKEN?.exp || !AUTH_TOKEN_RAW) {
|
|
354
409
|
console.error('Invalid composable token', AUTH_TOKEN);
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { afterEach, describe, expect, it } from 'vitest';
|
|
2
|
-
import { shouldRedirectToCentralAuth, shouldUseFirebaseAuth } from './domainRouting';
|
|
2
|
+
import { mountRootUrl, shouldRedirectToCentralAuth, shouldUseFirebaseAuth } from './domainRouting';
|
|
3
|
+
|
|
4
|
+
// Stub document.baseURI to simulate the served `<base href>` (deep gateway mount) or its absence.
|
|
5
|
+
function setBaseURI(baseURI: string) {
|
|
6
|
+
(globalThis as { document?: unknown }).document = { baseURI };
|
|
7
|
+
}
|
|
3
8
|
|
|
4
9
|
describe('domainRouting', () => {
|
|
5
10
|
afterEach(() => {
|
|
6
11
|
delete (globalThis as { window?: unknown }).window;
|
|
12
|
+
delete (globalThis as { document?: unknown }).document;
|
|
7
13
|
});
|
|
8
14
|
|
|
9
15
|
it("uses Firebase auth when AUTH_MODE is 'firebase'", () => {
|
|
@@ -23,4 +29,25 @@ describe('domainRouting', () => {
|
|
|
23
29
|
expect(shouldUseFirebaseAuth()).toBe(false);
|
|
24
30
|
expect(shouldRedirectToCentralAuth()).toBe(true);
|
|
25
31
|
});
|
|
32
|
+
|
|
33
|
+
describe('mountRootUrl', () => {
|
|
34
|
+
it('returns the deep gateway mount root (dropping any deep route / query / hash)', () => {
|
|
35
|
+
const mount = 'https://gw.example.com/tenants/05948c_5ed5f4/apps/furniture-catalog/versions/v1/app/';
|
|
36
|
+
setBaseURI(mount);
|
|
37
|
+
expect(mountRootUrl().toString()).toBe(mount);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('strips an existing query and hash so callers control the reload target', () => {
|
|
41
|
+
setBaseURI('https://gw.example.com/tenants/t/apps/a/versions/v/app/');
|
|
42
|
+
const url = mountRootUrl();
|
|
43
|
+
url.searchParams.set('a', 'acct1');
|
|
44
|
+
url.searchParams.set('p', 'proj1');
|
|
45
|
+
expect(url.toString()).toBe('https://gw.example.com/tenants/t/apps/a/versions/v/app/?a=acct1&p=proj1');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('is the origin root for the Studio UI (no <base> element)', () => {
|
|
49
|
+
setBaseURI('https://studio.vertesia.io/');
|
|
50
|
+
expect(mountRootUrl().toString()).toBe('https://studio.vertesia.io/');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
26
53
|
});
|
|
@@ -11,3 +11,41 @@ export function shouldUseFirebaseAuth(_hostname?: string) {
|
|
|
11
11
|
export function shouldRedirectToCentralAuth(_hostname?: string) {
|
|
12
12
|
return !shouldUseFirebaseAuth();
|
|
13
13
|
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The page URL to return to after a central-auth round-trip (`redirect_uri`).
|
|
17
|
+
*
|
|
18
|
+
* Apps served under a deep gateway mount carry a `<base href="/tenants/<t>/apps/<app>/.../app/">`
|
|
19
|
+
* that the app-gateway injects at serve time. The in-app router rewrites the address bar relative
|
|
20
|
+
* to the origin rather than that mount, so by the time the auth flow runs `window.location` can
|
|
21
|
+
* read as the bare origin `/`. Building `redirect_uri` from that drops the app path, and the
|
|
22
|
+
* post-login token bounces back to a URL that serves no app.
|
|
23
|
+
*
|
|
24
|
+
* Prefer the live location when it still sits under the mount (preserves the in-app deep route);
|
|
25
|
+
* otherwise fall back to `document.baseURI` — the mount, which the router cannot clobber. With no
|
|
26
|
+
* `<base>` element (the Studio UI), `document.baseURI` is the document URL and its `pathname` is a
|
|
27
|
+
* prefix of the live location, so the live URL is used unchanged — no behavior change there.
|
|
28
|
+
*/
|
|
29
|
+
export function authReturnUrl(): URL {
|
|
30
|
+
const base = new URL(document.baseURI);
|
|
31
|
+
const current = new URL(window.location.href);
|
|
32
|
+
const target = current.pathname.startsWith(base.pathname) ? current : base;
|
|
33
|
+
target.hash = '';
|
|
34
|
+
return target;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The app's mount root URL — the served `<base href>` (or the origin root for the Studio UI).
|
|
39
|
+
*
|
|
40
|
+
* Used for full-reload navigations that intentionally reset to the app root (logout, account /
|
|
41
|
+
* project switch). Building these from a bare `/` drops a gateway-mounted app off its mount and
|
|
42
|
+
* lands on a URL that serves no app; resolving against `document.baseURI` keeps the reload inside
|
|
43
|
+
* the mount. For the Studio UI (no `<base>` element) `document.baseURI` is the origin root, so the
|
|
44
|
+
* behavior is unchanged.
|
|
45
|
+
*/
|
|
46
|
+
export function mountRootUrl(): URL {
|
|
47
|
+
const url = new URL(document.baseURI);
|
|
48
|
+
url.hash = '';
|
|
49
|
+
url.search = '';
|
|
50
|
+
return url;
|
|
51
|
+
}
|
|
@@ -15,7 +15,7 @@ interface SignInRestrictedEnvStepProps {
|
|
|
15
15
|
const AUTO_REDIRECT_SECONDS = 15;
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* Shown when the STS rejects sign-in because the current environment (
|
|
18
|
+
* Shown when the STS rejects sign-in because the current environment (any non-production tier) is
|
|
19
19
|
* restricted to early-access users. Offers a redirect to the production web application — in the
|
|
20
20
|
* user's own region — and a way to sign in with a different account. If the user takes no action, it
|
|
21
21
|
* redirects automatically after {@link AUTO_REDIRECT_SECONDS}. See
|
|
@@ -89,7 +89,15 @@ function SigninScreenImpl({
|
|
|
89
89
|
|
|
90
90
|
useEffect(() => {
|
|
91
91
|
if (!preservePath) {
|
|
92
|
-
|
|
92
|
+
// Reset to the app's mount root, not the bare origin. A gateway-mounted app carries a
|
|
93
|
+
// served `<base href>` deep mount; collapsing to '/' drops the app off that mount (the
|
|
94
|
+
// bare origin serves no app) and the address bar can no longer be reloaded. This effect
|
|
95
|
+
// also flashes briefly for already-authenticated users during the loading transition, so
|
|
96
|
+
// a bare '/' here loses the URL even on a normal login. Deriving the root from
|
|
97
|
+
// `document.baseURI` keeps it inside the mount; for the origin-served Studio UI the
|
|
98
|
+
// pathname is '/', so the behavior is unchanged.
|
|
99
|
+
const mountRoot = new URL(document.baseURI).pathname || '/';
|
|
100
|
+
history.replaceState({}, '', mountRoot);
|
|
93
101
|
}
|
|
94
102
|
}, [preservePath]);
|
|
95
103
|
|
|
@@ -2,17 +2,20 @@ import { describe, expect, it } from 'vitest';
|
|
|
2
2
|
import { getProductionAppUrl } from './productionUrl.js';
|
|
3
3
|
|
|
4
4
|
describe('getProductionAppUrl', () => {
|
|
5
|
-
it('sends
|
|
5
|
+
it('sends every production region to its regional site (one cloud.{region} pattern)', () => {
|
|
6
|
+
// us1 is not special-cased — it uses the same regional pattern as every other region.
|
|
7
|
+
expect(getProductionAppUrl('us1')).toBe('https://cloud.us1.vertesia.io/');
|
|
8
|
+
expect(getProductionAppUrl('us2')).toBe('https://cloud.us2.vertesia.io/');
|
|
6
9
|
expect(getProductionAppUrl('eu1')).toBe('https://cloud.eu1.vertesia.io/');
|
|
7
10
|
expect(getProductionAppUrl('jp1')).toBe('https://cloud.jp1.vertesia.io/');
|
|
8
11
|
});
|
|
9
12
|
|
|
10
|
-
it('sends
|
|
11
|
-
|
|
12
|
-
expect(getProductionAppUrl('
|
|
13
|
+
it('sends a dev region to the production region it mirrors, not the dev host', () => {
|
|
14
|
+
// dev1 mirrors us1: a rejected user must land on production, never back on cloud.dev1.
|
|
15
|
+
expect(getProductionAppUrl('dev1')).toBe('https://cloud.us1.vertesia.io/');
|
|
13
16
|
});
|
|
14
17
|
|
|
15
|
-
it('falls back to the canonical
|
|
18
|
+
it('falls back to the canonical non-regional site when the region is unknown', () => {
|
|
16
19
|
expect(getProductionAppUrl(undefined)).toBe('https://cloud.vertesia.io/');
|
|
17
20
|
expect(getProductionAppUrl('')).toBe('https://cloud.vertesia.io/');
|
|
18
21
|
});
|
|
@@ -4,15 +4,25 @@
|
|
|
4
4
|
* they do have access — in their own region, never another one (an EU user must not land on the US
|
|
5
5
|
* site, and vice versa).
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Every production region follows one URL pattern — `https://cloud.{region}.vertesia.io/` (e.g.
|
|
8
|
+
* `us1`, `us2`, `eu1`, `jp1`) — with no region special-cased. Only the legacy non-regional
|
|
9
|
+
* deployment has no region and falls back to the apex `https://cloud.vertesia.io/`.
|
|
10
|
+
*
|
|
11
|
+
* Dev/ephemeral regions have no production site of their own — they mirror a production region — so
|
|
12
|
+
* they are mapped to it first. Sending a rejected user to the dev host (e.g. `cloud.dev1.vertesia.io`)
|
|
13
|
+
* would just bounce them back to the same restricted environment. `dev1` (us-central1) mirrors the
|
|
14
|
+
* `us1` production region.
|
|
10
15
|
*
|
|
11
16
|
* Region is preferred over parsing the current hostname because it is well-defined for every serving
|
|
12
17
|
* pattern (including the direct-routing `*.ui.{region}.vertesia.io` hosts, which carry no
|
|
13
18
|
* `preview.`/`preprod.` prefix to strip). Callers pass `Env.region` from `@vertesia/ui/env`.
|
|
14
19
|
*/
|
|
20
|
+
const DEV_REGION_TO_PRODUCTION_REGION: Record<string, string> = {
|
|
21
|
+
// dev1 (us-central1) is a dev mirror of the us1 production region.
|
|
22
|
+
dev1: 'us1',
|
|
23
|
+
};
|
|
24
|
+
|
|
15
25
|
export function getProductionAppUrl(region?: string): string {
|
|
16
|
-
const
|
|
17
|
-
return
|
|
26
|
+
const prodRegion = (region && DEV_REGION_TO_PRODUCTION_REGION[region]) || region;
|
|
27
|
+
return prodRegion ? `https://cloud.${prodRegion}.vertesia.io/` : 'https://cloud.vertesia.io/';
|
|
18
28
|
}
|