From e1044dbae1770cc3c41db6d87d912f6d87d26c98 Mon Sep 17 00:00:00 2001 From: Charles Zhao Date: Mon, 11 Nov 2024 10:42:41 +0800 Subject: [PATCH] feat(console): support console url with wildcard tenant id (#6780) --- .../src/containers/TenantAccess/index.tsx | 22 ++++++++++++++----- .../SubscriptionDataProvider/index.tsx | 3 --- .../console/src/contexts/TenantsProvider.tsx | 7 ++++++ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/packages/console/src/containers/TenantAccess/index.tsx b/packages/console/src/containers/TenantAccess/index.tsx index 781b225c9..b779d6026 100644 --- a/packages/console/src/containers/TenantAccess/index.tsx +++ b/packages/console/src/containers/TenantAccess/index.tsx @@ -1,12 +1,13 @@ import { useLogto } from '@logto/react'; import { useContext, useEffect } from 'react'; -import { Outlet } from 'react-router-dom'; +import { Outlet, useLocation } from 'react-router-dom'; import { useSWRConfig } from 'swr'; // Used in the docs -// eslint-disable-next-line unused-imports/no-unused-imports -import type ProtectedRoutes from '@/containers/ProtectedRoutes'; -import { TenantsContext } from '@/contexts/TenantsProvider'; + +import { isCloud } from '@/consts/env'; +import { reservedTenantIdWildcard, TenantsContext } from '@/contexts/TenantsProvider'; +import useUserDefaultTenantId from '@/hooks/use-user-default-tenant-id'; /** * The container that ensures the user has access to the current tenant. When the user is @@ -43,6 +44,8 @@ export default function TenantAccess() { const { isAuthenticated } = useLogto(); const { currentTenant, currentTenantId } = useContext(TenantsContext); const { mutate } = useSWRConfig(); + const { pathname } = useLocation(); + const { defaultTenantId } = useUserDefaultTenantId(); // Clean the cache when the current tenant ID changes. This is required because the // SWR cache key is not tenant-aware. @@ -68,13 +71,20 @@ export default function TenantAccess() { isAuthenticated && currentTenantId && // The current tenant is unavailable to the user, maybe a deleted tenant or a tenant that - // the user has no access to. Fall back to the home page. + // the user has no access to. Try with prepended default tenant ID, or if it's not available, + // redirect to the home page. !currentTenant ) { + if (isCloud && defaultTenantId && currentTenantId === reservedTenantIdWildcard) { + // eslint-disable-next-line @silverhand/fp/no-mutation + window.location.href = pathname.replace(reservedTenantIdWildcard, defaultTenantId); + return; + } + // eslint-disable-next-line @silverhand/fp/no-mutation window.location.href = '/'; } - }, [currentTenant, currentTenantId, isAuthenticated]); + }, [currentTenant, currentTenantId, isAuthenticated, pathname, defaultTenantId]); return ; } diff --git a/packages/console/src/contexts/SubscriptionDataProvider/index.tsx b/packages/console/src/contexts/SubscriptionDataProvider/index.tsx index b99af40d7..ea726657d 100644 --- a/packages/console/src/contexts/SubscriptionDataProvider/index.tsx +++ b/packages/console/src/contexts/SubscriptionDataProvider/index.tsx @@ -7,9 +7,6 @@ import { defaultSubscriptionQuota, defaultSubscriptionUsage, } from '@/consts'; -// Used in the docs -// eslint-disable-next-line unused-imports/no-unused-imports -import TenantAccess from '@/containers/TenantAccess'; import { type FullContext } from './types'; diff --git a/packages/console/src/contexts/TenantsProvider.tsx b/packages/console/src/contexts/TenantsProvider.tsx index 04fe0d02e..f7fdff34a 100644 --- a/packages/console/src/contexts/TenantsProvider.tsx +++ b/packages/console/src/contexts/TenantsProvider.tsx @@ -34,6 +34,13 @@ const reservedRoutes: Readonly = Object.freeze([ ...Object.values(GlobalRoute), ]); +/** + * The reserved tenant ID wildcard for the default tenant. Useful when specifying a console URL in + * the documentation or other places where the tenant ID is not known. Will be replaced with the + * actual default tenant ID in the runtime. + */ +export const reservedTenantIdWildcard = 'default'; + /** @see {@link TenantsProvider} for why `useSWR()` is not applicable for this context. */ type Tenants = { tenants: readonly TenantResponse[];