3d57c90e04
Closes #12303 ### What’s Changed - Replace auto‐save with explicit Save / Cancel Webhook forms now use manual “Save” and “Cancel” buttons instead of the old debounced auto‐save/update. - Separate “New” and “Detail” routes Two dedicated paths `/settings/webhooks/new` for creation and /`settings/webhooks/:webhookId` for editing, making the UX clearer. - URL hint & normalization If a user omits the http(s):// scheme, we display a “Will be saved as https://…” hint and automatically default to HTTPS. - Centralized validation with Zod Introduced a `webhookFormSchema` for client‐side URL, operations, and secret validation. - Storybook coverage Added stories for both “New Webhook” and “Webhook Detail” - Unit tests Added tests for the new `useWebhookForm` hook
46 lines
1.1 KiB
TypeScript
46 lines
1.1 KiB
TypeScript
import { getAbsoluteUrl } from '@/utils/url/getAbsoluteUrl';
|
|
import { isValidHostname } from '@/utils/url/isValidHostname';
|
|
import { z } from 'zod';
|
|
|
|
export const absoluteUrlSchema = z.string().transform((value, ctx) => {
|
|
const trimmedValue = value.trim();
|
|
const absoluteUrl = getAbsoluteUrl(trimmedValue);
|
|
|
|
const valueWithoutProtocol = absoluteUrl
|
|
.replace('https://', '')
|
|
.replace('http://', '');
|
|
|
|
if (/^\d+(?:\/[a-zA-Z]*)?$/.test(valueWithoutProtocol)) {
|
|
// if the hostname is a number, it's not a valid url
|
|
// if we let URL() parse it, it will throw cast an IP address and we lose the information
|
|
ctx.addIssue({
|
|
code: z.ZodIssueCode.custom,
|
|
message: 'domain is not a valid url',
|
|
});
|
|
|
|
return z.NEVER;
|
|
}
|
|
|
|
try {
|
|
const url = new URL(absoluteUrl);
|
|
|
|
if (isValidHostname(url.hostname)) {
|
|
return absoluteUrl;
|
|
}
|
|
|
|
ctx.addIssue({
|
|
code: z.ZodIssueCode.custom,
|
|
message: 'domain is not a valid url',
|
|
});
|
|
|
|
return z.NEVER;
|
|
} catch (error) {
|
|
ctx.addIssue({
|
|
code: z.ZodIssueCode.custom,
|
|
message: 'domain is not a valid url',
|
|
});
|
|
|
|
return z.NEVER;
|
|
}
|
|
});
|