feat(): enable custom domain usage (#9911)
# Content - Introduce the `workspaceUrls` property. It contains two sub-properties: `customUrl, subdomainUrl`. These endpoints are used to access the workspace. Even if the `workspaceUrls` is invalid for multiple reasons, the `subdomainUrl` remains valid. - Introduce `ResolveField` workspaceEndpoints to avoid unnecessary URL computation on the frontend part. - Add a `forceSubdomainUrl` to avoid custom URL using a query parameter
This commit is contained in:
@ -1,20 +1,12 @@
|
||||
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
|
||||
export const useBuildWorkspaceUrl = () => {
|
||||
const domainConfiguration = useRecoilValue(domainConfigurationState);
|
||||
|
||||
const buildWorkspaceUrl = (
|
||||
subdomain: string,
|
||||
endpoint: string,
|
||||
pathname?: string,
|
||||
searchParams?: Record<string, string>,
|
||||
searchParams?: Record<string, string | boolean>,
|
||||
) => {
|
||||
const url = new URL(window.location.href);
|
||||
|
||||
if (subdomain.length !== 0) {
|
||||
url.hostname = `${subdomain}.${domainConfiguration.frontDomain}`;
|
||||
}
|
||||
const url = new URL(endpoint);
|
||||
|
||||
if (isDefined(pathname)) {
|
||||
url.pathname = pathname;
|
||||
@ -22,7 +14,7 @@ export const useBuildWorkspaceUrl = () => {
|
||||
|
||||
if (isDefined(searchParams)) {
|
||||
Object.entries(searchParams).forEach(([key, value]) =>
|
||||
url.searchParams.set(key, value),
|
||||
url.searchParams.set(key, value.toString()),
|
||||
);
|
||||
}
|
||||
return url.toString();
|
||||
|
||||
@ -5,9 +5,9 @@ import { useRedirectToDefaultDomain } from '@/domain-manager/hooks/useRedirectTo
|
||||
import { workspaceAuthProvidersState } from '@/workspace/states/workspaceAuthProvidersState';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
import { useGetPublicWorkspaceDataBySubdomainQuery } from '~/generated/graphql';
|
||||
import { useGetPublicWorkspaceDataByDomainQuery } from '~/generated/graphql';
|
||||
|
||||
export const useGetPublicWorkspaceDataBySubdomain = () => {
|
||||
export const useGetPublicWorkspaceDataByDomain = () => {
|
||||
const { isDefaultDomain } = useIsCurrentLocationOnDefaultDomain();
|
||||
const isMultiWorkspaceEnabled = useRecoilValue(isMultiWorkspaceEnabledState);
|
||||
const setWorkspaceAuthProviders = useSetRecoilState(
|
||||
@ -19,15 +19,15 @@ export const useGetPublicWorkspaceDataBySubdomain = () => {
|
||||
workspacePublicDataState,
|
||||
);
|
||||
|
||||
const { loading, data, error } = useGetPublicWorkspaceDataBySubdomainQuery({
|
||||
const { loading, data, error } = useGetPublicWorkspaceDataByDomainQuery({
|
||||
skip:
|
||||
(isMultiWorkspaceEnabled && isDefaultDomain) ||
|
||||
isDefined(workspacePublicData),
|
||||
onCompleted: (data) => {
|
||||
setWorkspaceAuthProviders(
|
||||
data.getPublicWorkspaceDataBySubdomain.authProviders,
|
||||
data.getPublicWorkspaceDataByDomain.authProviders,
|
||||
);
|
||||
setWorkspacePublicDataState(data.getPublicWorkspaceDataBySubdomain);
|
||||
setWorkspacePublicDataState(data.getPublicWorkspaceDataByDomain);
|
||||
},
|
||||
onError: (error) => {
|
||||
// eslint-disable-next-line no-console
|
||||
@ -38,7 +38,7 @@ export const useGetPublicWorkspaceDataBySubdomain = () => {
|
||||
|
||||
return {
|
||||
loading,
|
||||
data: data?.getPublicWorkspaceDataBySubdomain,
|
||||
data: data?.getPublicWorkspaceDataByDomain,
|
||||
error,
|
||||
};
|
||||
};
|
||||
@ -4,7 +4,7 @@ import { domainConfigurationState } from '@/domain-manager/states/domainConfigur
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
|
||||
export const useIsCurrentLocationOnAWorkspaceSubdomain = () => {
|
||||
export const useIsCurrentLocationOnAWorkspace = () => {
|
||||
const { defaultDomain } = useReadDefaultDomainFromConfiguration();
|
||||
|
||||
const isMultiWorkspaceEnabled = useRecoilValue(isMultiWorkspaceEnabledState);
|
||||
@ -18,10 +18,10 @@ export const useIsCurrentLocationOnAWorkspaceSubdomain = () => {
|
||||
throw new Error('frontDomain and defaultSubdomain are required');
|
||||
}
|
||||
|
||||
const isOnAWorkspaceSubdomain =
|
||||
const isOnAWorkspace =
|
||||
isMultiWorkspaceEnabled && window.location.hostname !== defaultDomain;
|
||||
|
||||
return {
|
||||
isOnAWorkspaceSubdomain,
|
||||
isOnAWorkspace,
|
||||
};
|
||||
};
|
||||
@ -8,7 +8,7 @@ export const useLastAuthenticatedWorkspaceDomain = () => {
|
||||
lastAuthenticatedWorkspaceDomainState,
|
||||
);
|
||||
const setLastAuthenticateWorkspaceDomainWithCookieAttributes = (
|
||||
params: { workspaceId: string; subdomain: string } | null,
|
||||
params: { workspaceId: string; workspaceUrl: string } | null,
|
||||
) => {
|
||||
setLastAuthenticatedWorkspaceDomain({
|
||||
...(params ? params : {}),
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
import { useIsCurrentLocationOnAWorkspaceSubdomain } from '@/domain-manager/hooks/useIsCurrentLocationOnAWorkspaceSubdomain';
|
||||
import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
|
||||
export const useReadWorkspaceSubdomainFromCurrentLocation = () => {
|
||||
const domainConfiguration = useRecoilValue(domainConfigurationState);
|
||||
const { isOnAWorkspaceSubdomain } =
|
||||
useIsCurrentLocationOnAWorkspaceSubdomain();
|
||||
|
||||
if (!isDefined(domainConfiguration.frontDomain)) {
|
||||
throw new Error('frontDomain is not defined');
|
||||
}
|
||||
|
||||
const workspaceSubdomain = isOnAWorkspaceSubdomain
|
||||
? window.location.hostname.replace(
|
||||
`.${domainConfiguration.frontDomain}`,
|
||||
'',
|
||||
)
|
||||
: null;
|
||||
|
||||
return {
|
||||
workspaceSubdomain,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,11 @@
|
||||
import { useIsCurrentLocationOnAWorkspace } from '@/domain-manager/hooks/useIsCurrentLocationOnAWorkspace';
|
||||
|
||||
export const useReadWorkspaceUrlFromCurrentLocation = () => {
|
||||
const { isOnAWorkspace } = useIsCurrentLocationOnAWorkspace();
|
||||
|
||||
return {
|
||||
currentLocationHostname: isOnAWorkspace
|
||||
? window.location.hostname
|
||||
: undefined,
|
||||
};
|
||||
};
|
||||
@ -9,12 +9,12 @@ export const useRedirectToWorkspaceDomain = () => {
|
||||
const { redirect } = useRedirect();
|
||||
|
||||
const redirectToWorkspaceDomain = (
|
||||
subdomain: string,
|
||||
baseUrl: string,
|
||||
pathname?: string,
|
||||
searchParams?: Record<string, string>,
|
||||
searchParams?: Record<string, string | boolean>,
|
||||
) => {
|
||||
if (!isMultiWorkspaceEnabled) return;
|
||||
redirect(buildWorkspaceUrl(subdomain, pathname, searchParams));
|
||||
redirect(buildWorkspaceUrl(baseUrl, pathname, searchParams));
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@ -3,7 +3,7 @@ import { cookieStorageEffect } from '~/utils/recoil-effects';
|
||||
|
||||
export const lastAuthenticatedWorkspaceDomainState = createState<
|
||||
| {
|
||||
subdomain: string;
|
||||
workspaceUrl: string;
|
||||
workspaceId: string;
|
||||
cookieAttributes?: Cookies.CookieAttributes;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user