mirror of
https://github.com/logto-io/logto.git
synced 2025-03-17 22:31:28 -05:00
feat(console): add custom domain notes for endpoints and social callback uri (#4034)
This commit is contained in:
parent
9fa2bc9235
commit
6cac3ee3f9
21 changed files with 157 additions and 32 deletions
40
packages/console/src/hooks/use-custom-domain.ts
Normal file
40
packages/console/src/hooks/use-custom-domain.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { type Domain } from '@logto/schemas';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import { customDomainSyncInterval } from '@/consts/custom-domain';
|
||||
import { isCloud } from '@/consts/env';
|
||||
|
||||
import { type RequestError } from './use-api';
|
||||
|
||||
const useCustomDomain = (autoSync = false) => {
|
||||
const { data, error, mutate } = useSWR<Domain[], RequestError>(
|
||||
isCloud && 'api/domains',
|
||||
conditional(
|
||||
autoSync && {
|
||||
refreshInterval: customDomainSyncInterval * 1000,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
const isLoading = !data && !error;
|
||||
|
||||
/**
|
||||
* 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];
|
||||
|
||||
return {
|
||||
data: customDomain,
|
||||
isLoading,
|
||||
mutate: (domain?: Domain) => {
|
||||
if (domain) {
|
||||
void mutate([domain]);
|
||||
return;
|
||||
}
|
||||
void mutate();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default useCustomDomain;
|
|
@ -3,6 +3,7 @@ import {
|
|||
type SnakeCaseOidcConfig,
|
||||
ApplicationType,
|
||||
customClientMetadataGuard,
|
||||
DomainStatus,
|
||||
} from '@logto/schemas';
|
||||
import { appendPath } from '@silverhand/essentials';
|
||||
import { useContext } from 'react';
|
||||
|
@ -10,6 +11,7 @@ import { useFormContext } from 'react-hook-form';
|
|||
import { Trans, useTranslation } from 'react-i18next';
|
||||
|
||||
import CopyToClipboard from '@/components/CopyToClipboard';
|
||||
import DynamicT from '@/components/DynamicT';
|
||||
import FormCard from '@/components/FormCard';
|
||||
import FormField from '@/components/FormField';
|
||||
import Switch from '@/components/Switch';
|
||||
|
@ -17,6 +19,8 @@ import TextInput from '@/components/TextInput';
|
|||
import TextLink from '@/components/TextLink';
|
||||
import { openIdProviderConfigPath } from '@/consts/oidc';
|
||||
import { AppEndpointsContext } from '@/contexts/AppEndpointsProvider';
|
||||
import useCustomDomain from '@/hooks/use-custom-domain';
|
||||
import { applyDomain } from '@/utils/domain';
|
||||
|
||||
import * as styles from '../index.module.scss';
|
||||
|
||||
|
@ -40,6 +44,10 @@ function AdvancedSettings({ applicationType, oidcConfig }: Props) {
|
|||
min: minTtl,
|
||||
max: maxTtl,
|
||||
});
|
||||
const { data: customDomain } = useCustomDomain();
|
||||
|
||||
const tryApplyCustomDomain = (url: string) =>
|
||||
customDomain?.status === DomainStatus.Active ? applyDomain(url, customDomain.domain) : url;
|
||||
|
||||
return (
|
||||
<FormCard
|
||||
|
@ -51,7 +59,7 @@ function AdvancedSettings({ applicationType, oidcConfig }: Props) {
|
|||
<FormField title="application_details.config_endpoint">
|
||||
<CopyToClipboard
|
||||
className={styles.textField}
|
||||
value={appendPath(userEndpoint, openIdProviderConfigPath).href}
|
||||
value={tryApplyCustomDomain(appendPath(userEndpoint, openIdProviderConfigPath).href)}
|
||||
variant="border"
|
||||
/>
|
||||
</FormField>
|
||||
|
@ -76,24 +84,35 @@ function AdvancedSettings({ applicationType, oidcConfig }: Props) {
|
|||
>
|
||||
<CopyToClipboard
|
||||
className={styles.textField}
|
||||
value={oidcConfig.authorization_endpoint}
|
||||
value={tryApplyCustomDomain(oidcConfig.authorization_endpoint)}
|
||||
variant="border"
|
||||
/>
|
||||
</FormField>
|
||||
<FormField title="application_details.token_endpoint">
|
||||
<CopyToClipboard
|
||||
className={styles.textField}
|
||||
value={oidcConfig.token_endpoint}
|
||||
value={tryApplyCustomDomain(oidcConfig.token_endpoint)}
|
||||
variant="border"
|
||||
/>
|
||||
</FormField>
|
||||
<FormField title="application_details.user_info_endpoint">
|
||||
<CopyToClipboard
|
||||
className={styles.textField}
|
||||
value={oidcConfig.userinfo_endpoint}
|
||||
value={tryApplyCustomDomain(oidcConfig.userinfo_endpoint)}
|
||||
variant="border"
|
||||
/>
|
||||
</FormField>
|
||||
{customDomain?.status === DomainStatus.Active && userEndpoint && (
|
||||
<div className={styles.customEndpointNotes}>
|
||||
<DynamicT
|
||||
forKey="domain.custom_endpoint_note"
|
||||
interpolation={{
|
||||
custom: customDomain.domain,
|
||||
default: new URL(userEndpoint).host,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{[ApplicationType.Traditional, ApplicationType.SPA].includes(applicationType) && (
|
||||
<FormField title="application_details.always_issue_refresh_token">
|
||||
<Switch
|
||||
|
|
|
@ -79,3 +79,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.customEndpointNotes {
|
||||
margin-top: _.unit(6);
|
||||
font: var(--font-body-2);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
import type { ConnectorConfigFormItem } from '@logto/connector-kit';
|
||||
import { ConnectorType } from '@logto/connector-kit';
|
||||
import { DomainStatus } from '@logto/schemas';
|
||||
import { useContext } from 'react';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import CodeEditor from '@/components/CodeEditor';
|
||||
import CopyToClipboard from '@/components/CopyToClipboard';
|
||||
import DynamicT from '@/components/DynamicT';
|
||||
import FormField from '@/components/FormField';
|
||||
import { AppEndpointsContext } from '@/contexts/AppEndpointsProvider';
|
||||
import useCustomDomain from '@/hooks/use-custom-domain';
|
||||
import { applyDomain } from '@/utils/domain';
|
||||
import { jsonValidator } from '@/utils/validator';
|
||||
|
||||
import type { ConnectorFormType } from '../../types';
|
||||
|
@ -29,17 +33,36 @@ function ConfigForm({ formItems, className, connectorId, connectorType }: Props)
|
|||
formState: { errors },
|
||||
} = useFormContext<ConnectorFormType>();
|
||||
const { userEndpoint } = useContext(AppEndpointsContext);
|
||||
const { data: customDomain } = useCustomDomain();
|
||||
const callbackUri = new URL(`/callback/${connectorId}`, userEndpoint).toString();
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
{connectorType === ConnectorType.Social && (
|
||||
<FormField title="connectors.guide.callback_uri">
|
||||
<FormField
|
||||
title="connectors.guide.callback_uri"
|
||||
tip={t('connectors.guide.callback_uri_description')}
|
||||
>
|
||||
<CopyToClipboard
|
||||
className={styles.copyToClipboard}
|
||||
variant="border"
|
||||
value={new URL(`/callback/${connectorId}`, userEndpoint).toString()}
|
||||
value={
|
||||
customDomain?.status === DomainStatus.Active
|
||||
? applyDomain(callbackUri, customDomain.domain)
|
||||
: callbackUri
|
||||
}
|
||||
/>
|
||||
<div className={styles.description}>{t('connectors.guide.callback_uri_description')}</div>
|
||||
{customDomain?.status === DomainStatus.Active && userEndpoint && (
|
||||
<div className={styles.description}>
|
||||
<DynamicT
|
||||
forKey="domain.custom_social_callback_url_note"
|
||||
interpolation={{
|
||||
custom: customDomain.domain,
|
||||
default: new URL(userEndpoint).host,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</FormField>
|
||||
)}
|
||||
{formItems ? (
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
import { withAppInsights } from '@logto/app-insights/react';
|
||||
import { type Domain } from '@logto/schemas';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import FormCard from '@/components/FormCard';
|
||||
import FormField from '@/components/FormField';
|
||||
import PageMeta from '@/components/PageMeta';
|
||||
import { customDomainSyncInterval } from '@/consts/custom-domain';
|
||||
import { type RequestError } from '@/hooks/use-api';
|
||||
import useCustomDomain from '@/hooks/use-custom-domain';
|
||||
|
||||
import AddDomainForm from './AddDomainForm';
|
||||
import CustomDomain from './CustomDomain';
|
||||
|
@ -15,15 +11,7 @@ import DefaultDomain from './DefaultDomain';
|
|||
import * as styles from './index.module.scss';
|
||||
|
||||
function TenantDomainSettings() {
|
||||
const { data, error, mutate } = useSWR<Domain[], RequestError>('api/domains', {
|
||||
refreshInterval: customDomainSyncInterval * 1000,
|
||||
});
|
||||
|
||||
const isLoading = !data && !error;
|
||||
/**
|
||||
* 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 { data: customDomain, isLoading, mutate } = useCustomDomain(true);
|
||||
|
||||
if (isLoading) {
|
||||
return null;
|
||||
|
@ -38,18 +26,9 @@ function TenantDomainSettings() {
|
|||
>
|
||||
<FormField title="domain.custom.custom_domain_field">
|
||||
{customDomain ? (
|
||||
<CustomDomain
|
||||
customDomain={customDomain}
|
||||
onDeleteCustomDomain={() => {
|
||||
void mutate();
|
||||
}}
|
||||
/>
|
||||
<CustomDomain customDomain={customDomain} onDeleteCustomDomain={mutate} />
|
||||
) : (
|
||||
<AddDomainForm
|
||||
onCustomDomainAdded={(domain) => {
|
||||
void mutate([domain]);
|
||||
}}
|
||||
/>
|
||||
<AddDomainForm onCustomDomainAdded={mutate} />
|
||||
)}
|
||||
</FormField>
|
||||
</FormCard>
|
||||
|
|
1
packages/console/src/utils/domain.ts
Normal file
1
packages/console/src/utils/domain.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export const applyDomain = (url: string, domain: string) => url.replace(new URL(url).host, domain);
|
|
@ -47,6 +47,10 @@ const domain = {
|
|||
'Logto bietet eine vorkonfigurierte Standarddomain, die ohne zusätzliche Einrichtung verwendet werden kann. Diese Standarddomain dient als Backup-Option, auch wenn Sie eine benutzerdefinierte Domain aktiviert haben.',
|
||||
default_domain_field: 'Logto Standard-Domain',
|
||||
},
|
||||
custom_endpoint_note:
|
||||
'Sie können den Domainnamen dieser Endpunkte anpassen, wie Sie möchten. Wählen Sie entweder "{{custom}}" oder "{{default}}".',
|
||||
custom_social_callback_url_note:
|
||||
'Sie können den Domainnamen dieser URI anpassen, um mit dem Endpunkt Ihrer Anwendung übereinzustimmen. Wählen Sie entweder "{{custom}}" oder "{{default}}".',
|
||||
};
|
||||
|
||||
export default domain;
|
||||
|
|
|
@ -47,6 +47,10 @@ const domain = {
|
|||
'Logto offers a pre-configured default domain, ready to use without any additional setup. This default domain serves as a backup option even if you enabled a custom domain.', // UNTRANSLATED
|
||||
default_domain_field: 'Logto default domain',
|
||||
},
|
||||
custom_endpoint_note:
|
||||
'You can customize the domain name of these endpoints as your required. Choose either "{{custom}}" or "{{default}}".',
|
||||
custom_social_callback_url_note:
|
||||
'You can customize the domain name of this URI to match your application’s endpoint. Choose either "{{custom}}" or "{{default}}".',
|
||||
};
|
||||
|
||||
export default domain;
|
||||
|
|
|
@ -47,6 +47,10 @@ const domain = {
|
|||
'Logto ofrece un dominio predeterminado preconfigurado, listo para usar sin ninguna configuración adicional. Este dominio predeterminado sirve como opción de respaldo incluso si habilitó un dominio personalizado.',
|
||||
default_domain_field: 'Dominio predeterminado de Logto',
|
||||
},
|
||||
custom_endpoint_note:
|
||||
'Puede personalizar el nombre de dominio de estos puntos finales según sea necesario. Elija "{{custom}}" o "{{default}}".',
|
||||
custom_social_callback_url_note:
|
||||
'Puede personalizar el nombre de dominio de esta URI para que coincida con el punto final de su aplicación. Elija "{{custom}}" o "{{default}}".',
|
||||
};
|
||||
|
||||
export default domain;
|
||||
|
|
|
@ -47,6 +47,10 @@ const domain = {
|
|||
'Logto offre un domaine par défaut préconfiguré, prêt à être utilisé sans aucune configuration supplémentaire. Ce domaine par défaut sert de solution de secours même si vous avez activé un domaine personnalisé.',
|
||||
default_domain_field: 'Domaine par défaut de Logto',
|
||||
},
|
||||
custom_endpoint_note:
|
||||
'Vous pouvez personnaliser le nom de domaine de ces points de terminaison selon vos besoins. Choisissez soit "{{custom}}" ou "{{default}}".',
|
||||
custom_social_callback_url_note:
|
||||
'Vous pouvez personnaliser le nom de domaine de cette URI pour correspondre au point de terminaison de votre application. Choisissez soit "{{custom}}" ou "{{default}}".',
|
||||
};
|
||||
|
||||
export default domain;
|
||||
|
|
|
@ -47,6 +47,10 @@ const domain = {
|
|||
"Logto offre un dominio preconfigurato predefinito, pronto all'uso senza alcuna configurazione aggiuntiva. Questo dominio predefinito serve come opzione di backup anche se hai abilitato un dominio personalizzato.",
|
||||
default_domain_field: 'Dominio predefinito Logto',
|
||||
},
|
||||
custom_endpoint_note:
|
||||
'Puoi personalizzare il nome di dominio di questi endpoint come richiesto. Scegli "{{custom}}" o "{{default}}".',
|
||||
custom_social_callback_url_note:
|
||||
'Puoi personalizzare il nome di dominio di questo URI per corrispondere all\'endpoint della tua applicazione. Scegli "{{custom}}" o "{{default}}".',
|
||||
};
|
||||
|
||||
export default domain;
|
||||
|
|
|
@ -46,6 +46,10 @@ const domain = {
|
|||
'Logtoは、追加のセットアップなしで使用できる事前に構成されたデフォルトドメインを提供しています。このデフォルトドメインは、カスタムドメインを有効にしていない場合でもバックアップオプションとして機能します。',
|
||||
default_domain_field: 'Logtoデフォルトドメイン',
|
||||
},
|
||||
custom_endpoint_note:
|
||||
'これらのエンドポイントのドメイン名を必要に応じてカスタマイズできます。 "{{custom}}" または "{{default}}" のいずれかを選択してください。',
|
||||
custom_social_callback_url_note:
|
||||
'このURIのドメイン名をアプリケーションのエンドポイントに合わせてカスタマイズできます。 "{{custom}}" または "{{default}}" のいずれかを選択してください。',
|
||||
};
|
||||
|
||||
export default domain;
|
||||
|
|
|
@ -46,6 +46,10 @@ const domain = {
|
|||
'Logto는 추가 구성 없이 사용할 수 있는 미리 구성된 기본 도메인을 제공합니다. 이 기본 도메인은 사용자 지정 도메인을 사용하지 않더라도 백업 옵션으로 사용됩니다.',
|
||||
default_domain_field: 'Logto 기본 도메인',
|
||||
},
|
||||
custom_endpoint_note:
|
||||
'이 엔드포인트의 도메인 이름을 필요에 따라 사용자 정의할 수 있습니다. "{{custom}}" 또는 "{{default}}" 중 하나를 선택하세요.',
|
||||
custom_social_callback_url_note:
|
||||
'이 URI의 도메인 이름을 애플리케이션 엔드포인트와 일치하도록 사용자 정의할 수 있습니다. "{{custom}}" 또는 "{{default}}" 중 하나를 선택하세요.',
|
||||
};
|
||||
|
||||
export default domain;
|
||||
|
|
|
@ -47,6 +47,10 @@ const domain = {
|
|||
'Logto oferuje prekonfigurowaną domenę domyślną, gotową do użycia bez dodatkowej konfiguracji. Ta domyślna domena służy jako opcja zapasowa, nawet jeśli włączyłeś niestandardową domenę.',
|
||||
default_domain_field: 'Domyślna domena Logto',
|
||||
},
|
||||
custom_endpoint_note:
|
||||
'Możesz dostosować nazwę domeny tych punktów końcowych według swoich wymagań. Wybierz "{{custom}}" lub "{{default}}".',
|
||||
custom_social_callback_url_note:
|
||||
'Możesz dostosować nazwę domeny tego adresu URI, aby dopasować ją do punktu końcowego Twojej aplikacji. Wybierz "{{custom}}" lub "{{default}}".',
|
||||
};
|
||||
|
||||
export default domain;
|
||||
|
|
|
@ -47,6 +47,10 @@ const domain = {
|
|||
'Logto oferece um domínio padrão pré-configurado, pronto para uso sem nenhuma configuração adicional. Este domínio padrão serve como uma opção de backup mesmo se você habilitou um domínio personalizado.',
|
||||
default_domain_field: 'Domínio padrão do Logto',
|
||||
},
|
||||
custom_endpoint_note:
|
||||
'Você pode personalizar o nome de domínio desses endpoints conforme necessário. Escolha "{{custom}}" ou "{{default}}".',
|
||||
custom_social_callback_url_note:
|
||||
'Você pode personalizar o nome de domínio desta URI para corresponder ao endpoint de seu aplicativo. Escolha "{{custom}}" ou "{{default}}".',
|
||||
};
|
||||
|
||||
export default domain;
|
||||
|
|
|
@ -47,6 +47,10 @@ const domain = {
|
|||
'Logto oferece um domínio predefinido pré-configurado, pronto para usar sem qualquer configuração adicional. Este domínio predefinido serve como opção de backup mesmo que tenha ativado um domínio personalizado.',
|
||||
default_domain_field: 'Domínio predefinido da Logto',
|
||||
},
|
||||
custom_endpoint_note:
|
||||
'Pode personalizar o nome de domínio desses endpoints conforme necessário. Escolha "{{custom}}" ou "{{default}}".',
|
||||
custom_social_callback_url_note:
|
||||
'Pode personalizar o nome de domínio deste URI para corresponder ao endpoint da sua aplicação. Escolha "{{custom}}" ou "{{default}}".',
|
||||
};
|
||||
|
||||
export default domain;
|
||||
|
|
|
@ -46,6 +46,10 @@ const domain = {
|
|||
'Logto предлагает предварительно настроенный домен по умолчанию, готовый к использованию без дополнительной настройки. Этот домен по умолчанию служит как резервный вариант, даже если вы включили пользовательский домен.',
|
||||
default_domain_field: 'Домен по умолчанию Logto',
|
||||
},
|
||||
custom_endpoint_note:
|
||||
'Вы можете настроить имя домена этих конечных точек по своему усмотрению. Выберите "{{custom}}" или "{{default}}".',
|
||||
custom_social_callback_url_note:
|
||||
'Вы можете настроить имя домена этого URI, чтобы соответствовать конечной точке вашего приложения. Выберите "{{custom}}" или "{{default}}".',
|
||||
};
|
||||
|
||||
export default domain;
|
||||
|
|
|
@ -47,6 +47,10 @@ const domain = {
|
|||
'Logto, ek bir yapılandırma olmadan kullanıma hazır önceden yapılandırılmış bir varsayılan alan adı sunar. Bu varsayılan alan adı, özel bir alan adı etkinleştirdiyseniz bile yedek seçeneği olarak hizmet verir.',
|
||||
default_domain_field: 'Logto varsayılan alan adı',
|
||||
},
|
||||
custom_endpoint_note:
|
||||
'Bu uç noktaların alan adını özelleştirebilirsiniz. "{{custom}}" veya "{{default}}" seçeneklerinden birini seçin.',
|
||||
custom_social_callback_url_note:
|
||||
'Bu URI\'nin alan adını uygulamanızın uç noktasıyla eşleştirmek için özelleştirebilirsiniz. "{{custom}}" veya "{{default}}" seçeneklerinden birini seçin.',
|
||||
};
|
||||
|
||||
export default domain;
|
||||
|
|
|
@ -42,6 +42,9 @@ const domain = {
|
|||
'Logto 提供了一个预配置的默认域名,无需任何其他设置即可使用。即使您启用了自定义域名,此默认域名也可作为备用选项。',
|
||||
default_domain_field: 'Logto 默认域名',
|
||||
},
|
||||
custom_endpoint_note: '您可以根据需要自定义这些端点的域名。选择 "{{custom}}" 或 "{{default}}"。',
|
||||
custom_social_callback_url_note:
|
||||
'您可以根据需要自定义此 URI 的域名,以匹配您的应用程序端点。选择 "{{custom}}" 或 "{{default}}"。',
|
||||
};
|
||||
|
||||
export default domain;
|
||||
|
|
|
@ -42,6 +42,9 @@ const domain = {
|
|||
'Logto提供預配置的默認域名,無需進行任何其他設置即可使用。即使啟用了自定義域名,此默認域名也可作為備用選項。',
|
||||
default_domain_field: 'Logto 默認域名',
|
||||
},
|
||||
custom_endpoint_note: '您可以根據需要自定義這些端點的域名。選擇“{{custom}}”或“{{default}}”。',
|
||||
custom_social_callback_url_note:
|
||||
'您可以自定義此URI的域名以匹配您的應用程序端點。選擇“{{custom}}”或“{{default}}”。',
|
||||
};
|
||||
|
||||
export default domain;
|
||||
|
|
|
@ -42,6 +42,9 @@ const domain = {
|
|||
'Logto 提供預先配置的預設網域,無需進行任何其他設置即可使用。即使啟用了自訂網域,此預設網域也可作為備用選項。',
|
||||
default_domain_field: 'Logto 默認網域',
|
||||
},
|
||||
custom_endpoint_note: '您可以根據需要自定義這些端點的域名。選擇“{{custom}}”或“{{default}}”。',
|
||||
custom_social_callback_url_note:
|
||||
'您可以自定義此 URI 的域名以匹配您的應用程序端點。選擇“{{custom}}”或“{{default}}”。',
|
||||
};
|
||||
|
||||
export default domain;
|
||||
|
|
Loading…
Add table
Reference in a new issue