Migrate url tooling to twenty-shared (#10440)
Migrate and unify URL tooling in twenty-shared. We now have: - isValidHostname which follows our own business rules - a zod schema that can be re-used in different context and leverages is isValidHostname - isValidUrl on top of the zod schema - a getAbsoluteURl and getHostname on top of the zod schema I have added a LOT of tests to cover all the cases I've found Also fixes: https://github.com/twentyhq/twenty/issues/10147
This commit is contained in:
@ -7,8 +7,8 @@ import { z } from 'zod';
|
||||
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isValidHostname } from 'twenty-shared';
|
||||
import { Button } from 'twenty-ui';
|
||||
import { isDomain } from '~/utils/is-domain';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
@ -43,12 +43,15 @@ export const SettingsAccountsBlocklistInput = ({
|
||||
.trim()
|
||||
.email(t`Invalid email or domain`)
|
||||
.or(
|
||||
z
|
||||
.string()
|
||||
.refine(
|
||||
(value) => value.startsWith('@') && isDomain(value.slice(1)),
|
||||
t`Invalid email or domain`,
|
||||
),
|
||||
z.string().refine(
|
||||
(value) =>
|
||||
value.startsWith('@') &&
|
||||
isValidHostname(value.slice(1), {
|
||||
allowIp: false,
|
||||
allowLocalhost: false,
|
||||
}),
|
||||
t`Invalid email or domain`,
|
||||
),
|
||||
)
|
||||
.refine(
|
||||
(value) => !blockedEmailOrDomainList.includes(value),
|
||||
|
||||
@ -5,7 +5,7 @@ import { IconChevronRight } from 'twenty-ui';
|
||||
import { Webhook } from '@/settings/developers/types/webhook/Webhook';
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { getUrlHostname } from '~/utils/url/getUrlHostname';
|
||||
import { getUrlHostnameOrThrow } from 'twenty-shared';
|
||||
|
||||
export const StyledApisFieldTableRow = styled(TableRow)`
|
||||
grid-template-columns: 1fr 28px;
|
||||
@ -39,7 +39,7 @@ export const SettingsDevelopersWebhookTableRow = ({
|
||||
return (
|
||||
<StyledApisFieldTableRow to={to}>
|
||||
<StyledUrlTableCell>
|
||||
{getUrlHostname(fieldItem.targetUrl, { keepPath: true })}
|
||||
{getUrlHostnameOrThrow(fieldItem.targetUrl)}
|
||||
</StyledUrlTableCell>
|
||||
<StyledIconTableCell>
|
||||
<StyledIconChevronRight
|
||||
|
||||
@ -1,18 +1,16 @@
|
||||
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useState } from 'react';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { Webhook } from '@/settings/developers/types/webhook/Webhook';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
import { WebhookOperationType } from '~/pages/settings/developers/webhooks/types/WebhookOperationsType';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
import { WEBHOOK_EMPTY_OPERATION } from '~/pages/settings/developers/webhooks/constants/WebhookEmptyOperation';
|
||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { Webhook } from '@/settings/developers/types/webhook/Webhook';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { useState } from 'react';
|
||||
import { getUrlHostnameOrThrow, isDefined, isValidUrl } from 'twenty-shared';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
import { isValidUrl } from '~/utils/url/isValidUrl';
|
||||
import { getUrlHostname } from '~/utils/url/getUrlHostname';
|
||||
import { WEBHOOK_EMPTY_OPERATION } from '~/pages/settings/developers/webhooks/constants/WebhookEmptyOperation';
|
||||
import { WebhookOperationType } from '~/pages/settings/developers/webhooks/types/WebhookOperationsType';
|
||||
|
||||
type WebhookFormData = {
|
||||
targetUrl: string;
|
||||
@ -106,9 +104,7 @@ export const useWebhookUpdateForm = ({
|
||||
const isTargetUrlValid = isValidUrl(trimmedUrl);
|
||||
setIsTargetUrlValid(isTargetUrlValid);
|
||||
if (isTargetUrlValid) {
|
||||
setTitle(
|
||||
getUrlHostname(trimmedUrl, { keepPath: true }) || 'New Webhook',
|
||||
);
|
||||
setTitle(getUrlHostnameOrThrow(trimmedUrl) || 'New Webhook');
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -177,9 +173,7 @@ export const useWebhookUpdateForm = ({
|
||||
operations,
|
||||
secret: data.secret,
|
||||
});
|
||||
setTitle(
|
||||
getUrlHostname(data.targetUrl, { keepPath: true }) || 'New Webhook',
|
||||
);
|
||||
setTitle(getUrlHostnameOrThrow(data.targetUrl) || 'New Webhook');
|
||||
setLoading(false);
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user