import { forwardRef, useImperativeHandle, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { Form, type FormInstance, Input } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; import AccessProviderPicker from "@/components/provider/AccessProviderPicker"; import AccessProviderSelect from "@/components/provider/AccessProviderSelect"; import Show from "@/components/Show"; import { type AccessModel } from "@/domain/access"; import { ACCESS_PROVIDERS, ACCESS_USAGES, type AccessProvider } from "@/domain/provider"; import { useAntdForm, useAntdFormName } from "@/hooks"; import AccessForm1PanelConfig from "./AccessForm1PanelConfig"; import AccessFormACMECAConfig from "./AccessFormACMECAConfig"; import AccessFormACMEHttpReqConfig from "./AccessFormACMEHttpReqConfig"; import AccessFormAliyunConfig from "./AccessFormAliyunConfig"; import AccessFormAWSConfig from "./AccessFormAWSConfig"; import AccessFormAzureConfig from "./AccessFormAzureConfig"; import AccessFormBaiduCloudConfig from "./AccessFormBaiduCloudConfig"; import AccessFormBaishanConfig from "./AccessFormBaishanConfig"; import AccessFormBaotaPanelConfig from "./AccessFormBaotaPanelConfig"; import AccessFormBaotaWAFConfig from "./AccessFormBaotaWAFConfig"; import AccessFormBunnyConfig from "./AccessFormBunnyConfig"; import AccessFormBytePlusConfig from "./AccessFormBytePlusConfig"; import AccessFormCacheFlyConfig from "./AccessFormCacheFlyConfig"; import AccessFormCdnflyConfig from "./AccessFormCdnflyConfig"; import AccessFormCloudflareConfig from "./AccessFormCloudflareConfig"; import AccessFormClouDNSConfig from "./AccessFormClouDNSConfig"; import AccessFormCMCCCloudConfig from "./AccessFormCMCCCloudConfig"; import AccessFormConstellixConfig from "./AccessFormConstellixConfig"; import AccessFormDeSECConfig from "./AccessFormDeSECConfig"; import AccessFormDigitalOceanConfig from "./AccessFormDigitalOceanConfig"; import AccessFormDingTalkBotConfig from "./AccessFormDingTalkBotConfig"; import AccessFormDiscordBotConfig from "./AccessFormDiscordBotConfig"; import AccessFormDNSLAConfig from "./AccessFormDNSLAConfig"; import AccessFormDogeCloudConfig from "./AccessFormDogeCloudConfig"; import AccessFormDuckDNSConfig from "./AccessFormDuckDNSConfig"; import AccessFormDynv6Config from "./AccessFormDynv6Config"; import AccessFormEdgioConfig from "./AccessFormEdgioConfig"; import AccessFormEmailConfig from "./AccessFormEmailConfig"; import AccessFormFlexCDNConfig from "./AccessFormFlexCDNConfig"; import AccessFormGcoreConfig from "./AccessFormGcoreConfig"; import AccessFormGnameConfig from "./AccessFormGnameConfig"; import AccessFormGoDaddyConfig from "./AccessFormGoDaddyConfig"; import AccessFormGoEdgeConfig from "./AccessFormGoEdgeConfig"; import AccessFormGoogleTrustServicesConfig from "./AccessFormGoogleTrustServicesConfig"; import AccessFormHetznerConfig from "./AccessFormHetznerConfig"; import AccessFormHuaweiCloudConfig from "./AccessFormHuaweiCloudConfig"; import AccessFormJDCloudConfig from "./AccessFormJDCloudConfig"; import AccessFormKubernetesConfig from "./AccessFormKubernetesConfig"; import AccessFormLarkBotConfig from "./AccessFormLarkBotConfig"; import AccessFormLeCDNConfig from "./AccessFormLeCDNConfig"; import AccessFormMattermostConfig from "./AccessFormMattermostConfig"; import AccessFormNamecheapConfig from "./AccessFormNamecheapConfig"; import AccessFormNameDotComConfig from "./AccessFormNameDotComConfig"; import AccessFormNameSiloConfig from "./AccessFormNameSiloConfig"; import AccessFormNetcupConfig from "./AccessFormNetcupConfig"; import AccessFormNetlifyConfig from "./AccessFormNetlifyConfig"; import AccessFormNS1Config from "./AccessFormNS1Config"; import AccessFormPorkbunConfig from "./AccessFormPorkbunConfig"; import AccessFormPowerDNSConfig from "./AccessFormPowerDNSConfig"; import AccessFormProxmoxVEConfig from "./AccessFormProxmoxVEConfig"; import AccessFormQiniuConfig from "./AccessFormQiniuConfig"; import AccessFormRainYunConfig from "./AccessFormRainYunConfig"; import AccessFormRatPanelConfig from "./AccessFormRatPanelConfig"; import AccessFormSafeLineConfig from "./AccessFormSafeLineConfig"; import AccessFormSlackBotConfig from "./AccessFormSlackBotConfig"; import AccessFormSSHConfig from "./AccessFormSSHConfig"; import AccessFormSSLComConfig from "./AccessFormSSLComConfig"; import AccessFormTelegramBotConfig from "./AccessFormTelegramBotConfig"; import AccessFormTencentCloudConfig from "./AccessFormTencentCloudConfig"; import AccessFormUCloudConfig from "./AccessFormUCloudConfig"; import AccessFormUniCloudConfig from "./AccessFormUniCloudConfig"; import AccessFormUpyunConfig from "./AccessFormUpyunConfig"; import AccessFormVercelConfig from "./AccessFormVercelConfig"; import AccessFormVolcEngineConfig from "./AccessFormVolcEngineConfig"; import AccessFormWangsuConfig from "./AccessFormWangsuConfig"; import AccessFormWebhookConfig from "./AccessFormWebhookConfig"; import AccessFormWeComBotConfig from "./AccessFormWeComBotConfig"; import AccessFormWestcnConfig from "./AccessFormWestcnConfig"; import AccessFormZeroSSLConfig from "./AccessFormZeroSSLConfig"; type AccessFormFieldValues = Partial>; type AccessFormScenes = "add" | "edit"; type AccessFormUsages = "both-dns-hosting" | "ca-only" | "notification-only"; export type AccessFormProps = { className?: string; style?: React.CSSProperties; disabled?: boolean; initialValues?: AccessFormFieldValues; scene: AccessFormScenes; usage?: AccessFormUsages; onValuesChange?: (values: AccessFormFieldValues) => void; }; export type AccessFormInstance = { getFieldsValue: () => ReturnType["getFieldsValue"]>; resetFields: FormInstance["resetFields"]; validateFields: FormInstance["validateFields"]; }; const AccessForm = forwardRef(({ className, style, disabled, initialValues, usage, scene, onValuesChange }, ref) => { const { t } = useTranslation(); const formSchema = z.object({ name: z .string({ message: t("access.form.name.placeholder") }) .trim() .min(1, t("access.form.name.placeholder")) .max(64, t("common.errmsg.string_max", { max: 64 })), provider: z.nativeEnum(ACCESS_PROVIDERS, { message: usage === "ca-only" ? t("access.form.certificate_authority.placeholder") : usage === "notification-only" ? t("access.form.notification_channel.placeholder") : t("access.form.provider.placeholder"), }), config: z.any(), reserve: z.string().nullish(), }); const formRule = createSchemaFieldRule(formSchema); const { form: formInst, formProps } = useAntdForm({ name: "accessForm", initialValues: initialValues, }); const providerFilter = useMemo(() => { switch (usage) { case "both-dns-hosting": return (record: AccessProvider) => record.usages.includes(ACCESS_USAGES.DNS) || record.usages.includes(ACCESS_USAGES.HOSTING); case "ca-only": return (record: AccessProvider) => record.usages.includes(ACCESS_USAGES.CA); case "notification-only": return (record: AccessProvider) => record.usages.includes(ACCESS_USAGES.NOTIFICATION); } return undefined; }, [usage]); const providerLabel = useMemo(() => { switch (usage) { case "ca-only": return t("access.form.certificate_authority.label"); case "notification-only": return t("access.form.notification_channel.label"); } return t("access.form.provider.label"); }, [usage]); const providerPlaceholder = useMemo(() => { switch (usage) { case "ca-only": return t("access.form.certificate_authority.placeholder"); case "notification-only": return t("access.form.notification_channel.placeholder"); } return t("access.form.provider.placeholder"); }, [usage]); const providerTooltip = useMemo(() => { switch (usage) { case "both-dns-hosting": return ; } return undefined; }, [usage]); const fieldProvider = Form.useWatch["provider"]>("provider", formInst); const [fieldProviderPicked, setFieldProviderPicked] = useState(initialValues?.provider); // bugfix: Form.useWatch 在条件渲染下不生效,这里用单独的变量存放 Picker 组件选择的值 const [nestedFormInst] = Form.useForm(); const nestedFormName = useAntdFormName({ form: nestedFormInst, name: "accessConfigForm" }); const nestedFormEl = useMemo(() => { const nestedFormProps = { form: nestedFormInst, formName: nestedFormName, disabled: disabled, initialValues: initialValues?.config, }; /* 注意:如果追加新的子组件,请保持以 ASCII 排序。 NOTICE: If you add new child component, please keep ASCII order. */ switch (fieldProvider) { case ACCESS_PROVIDERS["1PANEL"]: return ; case ACCESS_PROVIDERS.ACMECA: return ; case ACCESS_PROVIDERS.ACMEHTTPREQ: return ; case ACCESS_PROVIDERS.ALIYUN: return ; case ACCESS_PROVIDERS.AWS: return ; case ACCESS_PROVIDERS.AZURE: return ; case ACCESS_PROVIDERS.BAIDUCLOUD: return ; case ACCESS_PROVIDERS.BAISHAN: return ; case ACCESS_PROVIDERS.BAOTAPANEL: return ; case ACCESS_PROVIDERS.BAOTAWAF: return ; case ACCESS_PROVIDERS.BUNNY: return ; case ACCESS_PROVIDERS.BYTEPLUS: return ; case ACCESS_PROVIDERS.CACHEFLY: return ; case ACCESS_PROVIDERS.CDNFLY: return ; case ACCESS_PROVIDERS.CLOUDFLARE: return ; case ACCESS_PROVIDERS.CLOUDNS: return ; case ACCESS_PROVIDERS.CMCCCLOUD: return ; case ACCESS_PROVIDERS.CONSTELLIX: return ; case ACCESS_PROVIDERS.DESEC: return ; case ACCESS_PROVIDERS.DIGITALOCEAN: return ; case ACCESS_PROVIDERS.DINGTALKBOT: return ; case ACCESS_PROVIDERS.DISCORDBOT: return ; case ACCESS_PROVIDERS.DNSLA: return ; case ACCESS_PROVIDERS.DOGECLOUD: return ; case ACCESS_PROVIDERS.DUCKDNS: return ; case ACCESS_PROVIDERS.DYNV6: return ; case ACCESS_PROVIDERS.EDGIO: return ; case ACCESS_PROVIDERS.EMAIL: return ; case ACCESS_PROVIDERS.FLEXCDN: return ; case ACCESS_PROVIDERS.GCORE: return ; case ACCESS_PROVIDERS.GNAME: return ; case ACCESS_PROVIDERS.GODADDY: return ; case ACCESS_PROVIDERS.GOEDGE: return ; case ACCESS_PROVIDERS.GOOGLETRUSTSERVICES: return ; case ACCESS_PROVIDERS.HETZNER: return ; case ACCESS_PROVIDERS.HUAWEICLOUD: return ; case ACCESS_PROVIDERS.JDCLOUD: return ; case ACCESS_PROVIDERS.KUBERNETES: return ; case ACCESS_PROVIDERS.LARKBOT: return ; case ACCESS_PROVIDERS.LECDN: return ; case ACCESS_PROVIDERS.MATTERMOST: return ; case ACCESS_PROVIDERS.NAMECHEAP: return ; case ACCESS_PROVIDERS.NAMEDOTCOM: return ; case ACCESS_PROVIDERS.NAMESILO: return ; case ACCESS_PROVIDERS.NETCUP: return ; case ACCESS_PROVIDERS.NETLIFY: return ; case ACCESS_PROVIDERS.NS1: return ; case ACCESS_PROVIDERS.PORKBUN: return ; case ACCESS_PROVIDERS.POWERDNS: return ; case ACCESS_PROVIDERS.PROXMOXVE: return ; case ACCESS_PROVIDERS.QINIU: return ; case ACCESS_PROVIDERS.RAINYUN: return ; case ACCESS_PROVIDERS.RATPANEL: return ; case ACCESS_PROVIDERS.SAFELINE: return ; case ACCESS_PROVIDERS.SLACKBOT: return ; case ACCESS_PROVIDERS.SSH: return ; case ACCESS_PROVIDERS.TELEGRAMBOT: return ; case ACCESS_PROVIDERS.SSLCOM: return ; case ACCESS_PROVIDERS.TENCENTCLOUD: return ; case ACCESS_PROVIDERS.UCLOUD: return ; case ACCESS_PROVIDERS.UNICLOUD: return ; case ACCESS_PROVIDERS.UPYUN: return ; case ACCESS_PROVIDERS.VERCEL: return ; case ACCESS_PROVIDERS.VOLCENGINE: return ; case ACCESS_PROVIDERS.WANGSU: return ; case ACCESS_PROVIDERS.WEBHOOK: return ( ); case ACCESS_PROVIDERS.WECOMBOT: return ; case ACCESS_PROVIDERS.WESTCN: return ; case ACCESS_PROVIDERS.ZEROSSL: return ; } }, [usage, disabled, initialValues?.config, fieldProvider, nestedFormInst, nestedFormName]); const handleProviderPick = (value: string) => { setFieldProviderPicked(value); formInst.setFieldValue("provider", value); onValuesChange?.(formInst.getFieldsValue(true)); }; const handleFormProviderChange = (name: string) => { if (name === nestedFormName) { formInst.setFieldValue("config", nestedFormInst.getFieldsValue()); onValuesChange?.(formInst.getFieldsValue(true)); } }; const handleFormChange = (_: unknown, values: AccessFormFieldValues) => { onValuesChange?.(values); }; useImperativeHandle(ref, () => { return { getFieldsValue: () => { const values = formInst.getFieldsValue(true); values.config = nestedFormInst.getFieldsValue(); values.reserve = usage === "ca-only" ? "ca" : usage === "notification-only" ? "notification" : undefined; return values; }, resetFields: (fields) => { formInst.resetFields(fields); if (!!fields && fields.includes("config")) { nestedFormInst.resetFields(fields); } }, validateFields: (nameList, config) => { const t1 = formInst.validateFields(nameList, config); const t2 = nestedFormInst.validateFields(undefined, config); return Promise.all([t1, t2]).then(() => t1); }, } as AccessFormInstance; }); return (
} >
{nestedFormEl}
); }); export default AccessForm;