0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

fix(console): should display custom domain as alternative endpoint in app details and guides (#4501)

fix(console): should display custom domain as alternative endpoint in app guide and details
This commit is contained in:
Charles Zhao 2023-09-15 09:57:41 +08:00 committed by GitHub
parent e1fac554db
commit 926da108e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 35 additions and 31 deletions

View file

@ -12,7 +12,6 @@ import DynamicT from '@/ds-components/DynamicT';
import FormField from '@/ds-components/FormField'; import FormField from '@/ds-components/FormField';
import useCustomDomain from '@/hooks/use-custom-domain'; import useCustomDomain from '@/hooks/use-custom-domain';
import type { ConnectorFormType } from '@/types/connector'; import type { ConnectorFormType } from '@/types/connector';
import { applyDomain } from '@/utils/domain';
import { jsonValidator } from '@/utils/validator'; import { jsonValidator } from '@/utils/validator';
import ConfigFormFields from './ConfigFormFields'; import ConfigFormFields from './ConfigFormFields';
@ -32,7 +31,7 @@ function ConfigForm({ formItems, className, connectorId, connectorType }: Props)
formState: { errors }, formState: { errors },
} = useFormContext<ConnectorFormType>(); } = useFormContext<ConnectorFormType>();
const { tenantEndpoint } = useContext(AppDataContext); const { tenantEndpoint } = useContext(AppDataContext);
const { data: customDomain } = useCustomDomain(); const { data: customDomain, applyDomain: applyCustomDomain } = useCustomDomain();
const callbackUri = new URL(`/callback/${connectorId}`, tenantEndpoint).toString(); const callbackUri = new URL(`/callback/${connectorId}`, tenantEndpoint).toString();
return ( return (
@ -45,11 +44,7 @@ function ConfigForm({ formItems, className, connectorId, connectorType }: Props)
<CopyToClipboard <CopyToClipboard
className={styles.copyToClipboard} className={styles.copyToClipboard}
variant="border" variant="border"
value={ value={applyCustomDomain(callbackUri)}
customDomain?.status === DomainStatus.Active
? applyDomain(callbackUri, customDomain.domain)
: callbackUri
}
/> />
{customDomain?.status === DomainStatus.Active && tenantEndpoint && ( {customDomain?.status === DomainStatus.Active && tenantEndpoint && (
<div className={styles.description}> <div className={styles.description}>

View file

@ -1,9 +1,11 @@
import { type Domain } from '@logto/schemas'; import { DomainStatus, type Domain } from '@logto/schemas';
import { conditional } from '@silverhand/essentials'; import { conditional } from '@silverhand/essentials';
import { useCallback, useMemo } from 'react';
import useSWR from 'swr'; import useSWR from 'swr';
import { customDomainSyncInterval } from '@/consts/custom-domain'; import { customDomainSyncInterval } from '@/consts/custom-domain';
import { isCloud } from '@/consts/env'; import { isCloud } from '@/consts/env';
import { isAbsoluteUrl } from '@/utils/url';
import { type RequestError } from './use-api'; import { type RequestError } from './use-api';
@ -22,18 +24,30 @@ const useCustomDomain = (autoSync = false) => {
/** /**
* Note: we can only create a custom domain, and we don't have a default id for it, so the first element of the array is the custom domain. * Note: we can only create a custom domain, and we don't have a default id for it, so the first element of the array is the custom domain.
*/ */
const customDomain = conditional(!isLoading && data)?.[0]; const customDomain = useMemo(() => data?.[0], [data]);
const mutateDomain = useCallback(
(domain?: Domain) => {
void mutate(domain ? [domain] : undefined);
},
[mutate]
);
const applyDomain = useCallback(
(url: string) => {
if (customDomain?.status !== DomainStatus.Active || !isAbsoluteUrl(url)) {
return url;
}
return url.replace(new URL(url).host, customDomain.domain);
},
[customDomain]
);
return { return {
data: customDomain, data: customDomain,
isLoading, isLoading,
mutate: (domain?: Domain) => { mutate: mutateDomain,
if (domain) { applyDomain,
void mutate([domain]);
return;
}
void mutate();
},
}; };
}; };

View file

@ -20,7 +20,6 @@ import Switch from '@/ds-components/Switch';
import TextInput from '@/ds-components/TextInput'; import TextInput from '@/ds-components/TextInput';
import TextLink from '@/ds-components/TextLink'; import TextLink from '@/ds-components/TextLink';
import useCustomDomain from '@/hooks/use-custom-domain'; import useCustomDomain from '@/hooks/use-custom-domain';
import { applyDomain } from '@/utils/domain';
import * as styles from '../index.module.scss'; import * as styles from '../index.module.scss';
@ -44,10 +43,7 @@ function AdvancedSettings({ app: { type }, oidcConfig }: Props) {
min: minTtl, min: minTtl,
max: maxTtl, max: maxTtl,
}); });
const { data: customDomain } = useCustomDomain(); const { data: customDomain, applyDomain: applyCustomDomain } = useCustomDomain();
const tryApplyCustomDomain = (url: string) =>
customDomain?.status === DomainStatus.Active ? applyDomain(url, customDomain.domain) : url;
return ( return (
<FormCard <FormCard
@ -59,7 +55,7 @@ function AdvancedSettings({ app: { type }, oidcConfig }: Props) {
<FormField title="application_details.config_endpoint"> <FormField title="application_details.config_endpoint">
<CopyToClipboard <CopyToClipboard
className={styles.textField} className={styles.textField}
value={tryApplyCustomDomain(appendPath(tenantEndpoint, openIdProviderConfigPath).href)} value={applyCustomDomain(appendPath(tenantEndpoint, openIdProviderConfigPath).href)}
variant="border" variant="border"
/> />
</FormField> </FormField>
@ -84,21 +80,21 @@ function AdvancedSettings({ app: { type }, oidcConfig }: Props) {
> >
<CopyToClipboard <CopyToClipboard
className={styles.textField} className={styles.textField}
value={tryApplyCustomDomain(oidcConfig.authorization_endpoint)} value={applyCustomDomain(oidcConfig.authorization_endpoint)}
variant="border" variant="border"
/> />
</FormField> </FormField>
<FormField title="application_details.token_endpoint"> <FormField title="application_details.token_endpoint">
<CopyToClipboard <CopyToClipboard
className={styles.textField} className={styles.textField}
value={tryApplyCustomDomain(oidcConfig.token_endpoint)} value={applyCustomDomain(oidcConfig.token_endpoint)}
variant="border" variant="border"
/> />
</FormField> </FormField>
<FormField title="application_details.user_info_endpoint"> <FormField title="application_details.user_info_endpoint">
<CopyToClipboard <CopyToClipboard
className={styles.textField} className={styles.textField}
value={tryApplyCustomDomain(oidcConfig.userinfo_endpoint)} value={applyCustomDomain(oidcConfig.userinfo_endpoint)}
variant="border" variant="border"
/> />
</FormField> </FormField>

View file

@ -21,8 +21,6 @@ function AppGuide({ className, guideId, app, isCompact, onClose }: Props) {
const isCustomDomainActive = customDomain?.status === DomainStatus.Active; const isCustomDomainActive = customDomain?.status === DomainStatus.Active;
const guide = guides.find(({ id }) => id === guideId); const guide = guides.find(({ id }) => id === guideId);
const GuideComponent = guide?.Component;
const memorizedContext = useMemo( const memorizedContext = useMemo(
() => () =>
conditional( conditional(
@ -32,7 +30,7 @@ function AppGuide({ className, guideId, app, isCompact, onClose }: Props) {
Logo: guide.Logo, Logo: guide.Logo,
app, app,
endpoint: tenantEndpoint?.toString() ?? '', endpoint: tenantEndpoint?.toString() ?? '',
alternativeEndpoint: conditional(isCustomDomainActive && tenantEndpoint?.toString()), alternativeEndpoint: conditional(isCustomDomainActive && customDomain.domain),
redirectUris: app.oidcClientMetadata.redirectUris, redirectUris: app.oidcClientMetadata.redirectUris,
postLogoutRedirectUris: app.oidcClientMetadata.postLogoutRedirectUris, postLogoutRedirectUris: app.oidcClientMetadata.postLogoutRedirectUris,
isCompact: Boolean(isCompact), isCompact: Boolean(isCompact),
@ -42,7 +40,7 @@ function AppGuide({ className, guideId, app, isCompact, onClose }: Props) {
}, },
} }
) satisfies GuideContextType | undefined, ) satisfies GuideContextType | undefined,
[guide, app, tenantEndpoint, isCustomDomainActive, isCompact] [guide, app, tenantEndpoint, isCustomDomainActive, customDomain?.domain, isCompact]
); );
return memorizedContext ? ( return memorizedContext ? (

View file

@ -17,6 +17,7 @@ import {
} from '@/ds-components/MultiTextInput/utils'; } from '@/ds-components/MultiTextInput/utils';
import TextInput from '@/ds-components/TextInput'; import TextInput from '@/ds-components/TextInput';
import TextLink from '@/ds-components/TextLink'; import TextLink from '@/ds-components/TextLink';
import useCustomDomain from '@/hooks/use-custom-domain';
import useDocumentationUrl from '@/hooks/use-documentation-url'; import useDocumentationUrl from '@/hooks/use-documentation-url';
import * as styles from '../index.module.scss'; import * as styles from '../index.module.scss';
@ -27,6 +28,7 @@ type Props = {
function Settings({ data }: Props) { function Settings({ data }: Props) {
const { tenantEndpoint } = useContext(AppDataContext); const { tenantEndpoint } = useContext(AppDataContext);
const { applyDomain: applyCustomDomain } = useCustomDomain();
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const { getDocumentationUrl } = useDocumentationUrl(); const { getDocumentationUrl } = useDocumentationUrl();
@ -62,7 +64,7 @@ function Settings({ data }: Props) {
{tenantEndpoint && ( {tenantEndpoint && (
<FormField title="application_details.logto_endpoint"> <FormField title="application_details.logto_endpoint">
<CopyToClipboard <CopyToClipboard
value={tenantEndpoint.href} value={applyCustomDomain(tenantEndpoint.href)}
variant="border" variant="border"
className={styles.textField} className={styles.textField}
/> />

View file

@ -1 +0,0 @@
export const applyDomain = (url: string, domain: string) => url.replace(new URL(url).host, domain);