feat: improve workflow node configuration

This commit is contained in:
Fu Diwei
2025-01-06 23:46:14 +08:00
parent 155371cdd0
commit 84c36a4eec
70 changed files with 1799 additions and 875 deletions

View File

@@ -12,32 +12,32 @@ import DeployProviderPicker from "@/components/provider/DeployProviderPicker";
import DeployProviderSelect from "@/components/provider/DeployProviderSelect";
import { ACCESS_USAGES, DEPLOY_PROVIDERS, accessProvidersMap, deployProvidersMap } from "@/domain/provider";
import { type WorkflowNode, type WorkflowNodeConfigForDeploy } from "@/domain/workflow";
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
import { useAntdForm, useAntdFormName, useZustandShallowSelector } from "@/hooks";
import { useWorkflowStore } from "@/stores/workflow";
import DeployNodeConfigFormAliyunALBFields from "./DeployNodeConfigFormAliyunALBFields";
import DeployNodeConfigFormAliyunCDNFields from "./DeployNodeConfigFormAliyunCDNFields";
import DeployNodeConfigFormAliyunCLBFields from "./DeployNodeConfigFormAliyunCLBFields";
import DeployNodeConfigFormAliyunDCDNFields from "./DeployNodeConfigFormAliyunDCDNFields";
import DeployNodeConfigFormAliyunNLBFields from "./DeployNodeConfigFormAliyunNLBFields";
import DeployNodeConfigFormAliyunOSSFields from "./DeployNodeConfigFormAliyunOSSFields";
import DeployNodeConfigFormBaiduCloudCDNFields from "./DeployNodeConfigFormBaiduCloudCDNFields";
import DeployNodeConfigFormBytePlusCDNFields from "./DeployNodeConfigFormBytePlusCDNFields";
import DeployNodeConfigFormDogeCloudCDNFields from "./DeployNodeConfigFormDogeCloudCDNFields";
import DeployNodeConfigFormHuaweiCloudCDNFields from "./DeployNodeConfigFormHuaweiCloudCDNFields";
import DeployNodeConfigFormHuaweiCloudELBFields from "./DeployNodeConfigFormHuaweiCloudELBFields";
import DeployNodeConfigFormKubernetesSecretFields from "./DeployNodeConfigFormKubernetesSecretFields";
import DeployNodeConfigFormLocalFields from "./DeployNodeConfigFormLocalFields";
import DeployNodeConfigFormQiniuCDNFields from "./DeployNodeConfigFormQiniuCDNFields";
import DeployNodeConfigFormSSHFields from "./DeployNodeConfigFormSSHFields";
import DeployNodeConfigFormTencentCloudCDNFields from "./DeployNodeConfigFormTencentCloudCDNFields";
import DeployNodeConfigFormTencentCloudCLBFields from "./DeployNodeConfigFormTencentCloudCLBFields";
import DeployNodeConfigFormTencentCloudCOSFields from "./DeployNodeConfigFormTencentCloudCOSFields";
import DeployNodeConfigFormTencentCloudECDNFields from "./DeployNodeConfigFormTencentCloudECDNFields";
import DeployNodeConfigFormTencentCloudEOFields from "./DeployNodeConfigFormTencentCloudEOFields";
import DeployNodeConfigFormVolcEngineCDNFields from "./DeployNodeConfigFormVolcEngineCDNFields";
import DeployNodeConfigFormVolcEngineLiveFields from "./DeployNodeConfigFormVolcEngineLiveFields";
import DeployNodeConfigFormWebhookFields from "./DeployNodeConfigFormWebhookFields";
import DeployNodeConfigFormAliyunALBConfig from "./DeployNodeConfigFormAliyunALBConfig";
import DeployNodeConfigFormAliyunCDNConfig from "./DeployNodeConfigFormAliyunCDNConfig";
import DeployNodeConfigFormAliyunCLBConfig from "./DeployNodeConfigFormAliyunCLBConfig";
import DeployNodeConfigFormAliyunDCDNConfig from "./DeployNodeConfigFormAliyunDCDNConfig";
import DeployNodeConfigFormAliyunNLBConfig from "./DeployNodeConfigFormAliyunNLBConfig";
import DeployNodeConfigFormAliyunOSSConfig from "./DeployNodeConfigFormAliyunOSSConfig";
import DeployNodeConfigFormBaiduCloudCDNConfig from "./DeployNodeConfigFormBaiduCloudCDNConfig";
import DeployNodeConfigFormBytePlusCDNConfig from "./DeployNodeConfigFormBytePlusCDNConfig";
import DeployNodeConfigFormDogeCloudCDNConfig from "./DeployNodeConfigFormDogeCloudCDNConfig";
import DeployNodeConfigFormHuaweiCloudCDNConfig from "./DeployNodeConfigFormHuaweiCloudCDNConfig";
import DeployNodeConfigFormHuaweiCloudELBConfig from "./DeployNodeConfigFormHuaweiCloudELBConfig";
import DeployNodeConfigFormKubernetesSecretConfig from "./DeployNodeConfigFormKubernetesSecretConfig";
import DeployNodeConfigFormLocalConfig from "./DeployNodeConfigFormLocalConfig";
import DeployNodeConfigFormQiniuCDNConfig from "./DeployNodeConfigFormQiniuCDNConfig";
import DeployNodeConfigFormSSHConfig from "./DeployNodeConfigFormSSHConfig.tsx";
import DeployNodeConfigFormTencentCloudCDNConfig from "./DeployNodeConfigFormTencentCloudCDNConfig.tsx";
import DeployNodeConfigFormTencentCloudCLBConfig from "./DeployNodeConfigFormTencentCloudCLBConfig.tsx";
import DeployNodeConfigFormTencentCloudCOSConfig from "./DeployNodeConfigFormTencentCloudCOSConfig.tsx";
import DeployNodeConfigFormTencentCloudECDNConfig from "./DeployNodeConfigFormTencentCloudECDNConfig.tsx";
import DeployNodeConfigFormTencentCloudEOConfig from "./DeployNodeConfigFormTencentCloudEOConfig.tsx";
import DeployNodeConfigFormVolcEngineCDNConfig from "./DeployNodeConfigFormVolcEngineCDNConfig.tsx";
import DeployNodeConfigFormVolcEngineLiveConfig from "./DeployNodeConfigFormVolcEngineLiveConfig.tsx";
import DeployNodeConfigFormWebhookConfig from "./DeployNodeConfigFormWebhookConfig.tsx";
type DeployNodeConfigFormFieldValues = Partial<WorkflowNodeConfigForDeploy>;
@@ -74,75 +74,86 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
}, [nodeId]);
const formSchema = z.object({
certificate: z
.string({ message: t("workflow_node.deploy.form.certificate.placeholder") })
.nonempty(t("workflow_node.deploy.form.certificate.placeholder")),
provider: z.string({ message: t("workflow_node.deploy.form.provider.placeholder") }).nonempty(t("workflow_node.deploy.form.provider.placeholder")),
providerAccessId: z
.string({ message: t("workflow_node.deploy.form.provider_access.placeholder") })
.nonempty(t("workflow_node.deploy.form.provider_access.placeholder")),
certificate: z
.string({ message: t("workflow_node.deploy.form.certificate.placeholder") })
.nonempty(t("workflow_node.deploy.form.certificate.placeholder")),
providerConfig: z.any(),
});
const formRule = createSchemaFieldRule(formSchema);
const { form: formInst, formProps } = useAntdForm({
name: "workflowNodeDeployConfigForm",
initialValues: initialValues ?? initFormModel(),
});
const fieldProvider = Form.useWatch("provider", { form: formInst, preserve: true });
const formFieldsEl = useMemo(() => {
const [nestedFormInst] = Form.useForm();
const nestedFormName = useAntdFormName({ form: nestedFormInst, name: "workflowNodeDeployConfigFormProviderConfigForm" });
const nestedFormEl = useMemo(() => {
const nestedFormProps = {
form: nestedFormInst,
formName: nestedFormName,
disabled: disabled,
initialValues: initialValues?.providerConfig,
};
/*
注意:如果追加新的子组件,请保持以 ASCII 排序。
NOTICE: If you add new child component, please keep ASCII order.
*/
注意:如果追加新的子组件,请保持以 ASCII 排序。
NOTICE: If you add new child component, please keep ASCII order.
*/
switch (fieldProvider) {
case DEPLOY_PROVIDERS.ALIYUN_ALB:
return <DeployNodeConfigFormAliyunALBFields />;
return <DeployNodeConfigFormAliyunALBConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.ALIYUN_CLB:
return <DeployNodeConfigFormAliyunCLBFields />;
return <DeployNodeConfigFormAliyunCLBConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.ALIYUN_CDN:
return <DeployNodeConfigFormAliyunCDNFields />;
return <DeployNodeConfigFormAliyunCDNConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.ALIYUN_DCDN:
return <DeployNodeConfigFormAliyunDCDNFields />;
return <DeployNodeConfigFormAliyunDCDNConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.ALIYUN_NLB:
return <DeployNodeConfigFormAliyunNLBFields />;
return <DeployNodeConfigFormAliyunNLBConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.ALIYUN_OSS:
return <DeployNodeConfigFormAliyunOSSFields />;
return <DeployNodeConfigFormAliyunOSSConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.BAIDUCLOUD_CDN:
return <DeployNodeConfigFormBaiduCloudCDNFields />;
return <DeployNodeConfigFormBaiduCloudCDNConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.BYTEPLUS_CDN:
return <DeployNodeConfigFormBytePlusCDNFields />;
return <DeployNodeConfigFormBytePlusCDNConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.DOGECLOUD_CDN:
return <DeployNodeConfigFormDogeCloudCDNFields />;
return <DeployNodeConfigFormDogeCloudCDNConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.HUAWEICLOUD_CDN:
return <DeployNodeConfigFormHuaweiCloudCDNFields />;
return <DeployNodeConfigFormHuaweiCloudCDNConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.HUAWEICLOUD_ELB:
return <DeployNodeConfigFormHuaweiCloudELBFields />;
return <DeployNodeConfigFormHuaweiCloudELBConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.KUBERNETES_SECRET:
return <DeployNodeConfigFormKubernetesSecretFields />;
return <DeployNodeConfigFormKubernetesSecretConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.LOCAL:
return <DeployNodeConfigFormLocalFields />;
return <DeployNodeConfigFormLocalConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.QINIU_CDN:
return <DeployNodeConfigFormQiniuCDNFields />;
return <DeployNodeConfigFormQiniuCDNConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.SSH:
return <DeployNodeConfigFormSSHFields />;
return <DeployNodeConfigFormSSHConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.TENCENTCLOUD_CDN:
return <DeployNodeConfigFormTencentCloudCDNFields />;
return <DeployNodeConfigFormTencentCloudCDNConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.TENCENTCLOUD_CLB:
return <DeployNodeConfigFormTencentCloudCLBFields />;
return <DeployNodeConfigFormTencentCloudCLBConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.TENCENTCLOUD_COS:
return <DeployNodeConfigFormTencentCloudCOSFields />;
return <DeployNodeConfigFormTencentCloudCOSConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.TENCENTCLOUD_ECDN:
return <DeployNodeConfigFormTencentCloudECDNFields />;
return <DeployNodeConfigFormTencentCloudECDNConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.TENCENTCLOUD_EO:
return <DeployNodeConfigFormTencentCloudEOFields />;
return <DeployNodeConfigFormTencentCloudEOConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.VOLCENGINE_CDN:
return <DeployNodeConfigFormVolcEngineCDNFields />;
return <DeployNodeConfigFormVolcEngineCDNConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.VOLCENGINE_LIVE:
return <DeployNodeConfigFormVolcEngineLiveFields />;
return <DeployNodeConfigFormVolcEngineLiveConfig {...nestedFormProps} />;
case DEPLOY_PROVIDERS.WEBHOOK:
return <DeployNodeConfigFormWebhookFields />;
return <DeployNodeConfigFormWebhookConfig {...nestedFormProps} />;
}
}, [fieldProvider]);
}, [disabled, initialValues?.providerConfig, fieldProvider, nestedFormInst, nestedFormName]);
const handleProviderPick = (value: string) => {
formInst.setFieldValue("provider", value);
@@ -175,6 +186,13 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
}
};
const handleFormProviderChange = (name: string) => {
if (name === nestedFormName) {
formInst.setFieldValue("providerConfig", nestedFormInst.getFieldsValue());
onValuesChange?.(formInst.getFieldsValue(true));
}
};
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values as DeployNodeConfigFormFieldValues);
};
@@ -188,101 +206,105 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
return formInst.resetFields(fields);
},
validateFields: (nameList, config) => {
return formInst.validateFields(nameList, config);
const t1 = formInst.validateFields(nameList, config);
const t2 = nestedFormInst.validateFields(undefined, config);
return Promise.all([t1, t2]).then(() => t1);
},
} as DeployNodeConfigFormInstance;
});
return (
<Form className={className} style={style} {...formProps} disabled={disabled} layout="vertical" scrollToFirstError onValuesChange={handleFormChange}>
<Show when={!!fieldProvider} fallback={<DeployProviderPicker onSelect={handleProviderPick} />}>
<Form.Item name="provider" label={t("workflow_node.deploy.form.provider.label")} rules={[formRule]}>
<DeployProviderSelect
allowClear
disabled
placeholder={t("workflow_node.deploy.form.provider.placeholder")}
showSearch
onSelect={handleProviderSelect}
/>
</Form.Item>
<Form.Item className="mb-0">
<label className="mb-1 block">
<div className="flex w-full items-center justify-between gap-4">
<div className="max-w-full grow truncate">
<span>{t("workflow_node.deploy.form.provider_access.label")}</span>
<Tooltip title={t("workflow_node.deploy.form.provider_access.tooltip")}>
<Typography.Text className="ms-1" type="secondary">
<QuestionCircleOutlinedIcon />
</Typography.Text>
</Tooltip>
</div>
<div className="text-right">
<AccessEditModal
data={{ provider: deployProvidersMap.get(fieldProvider!)?.provider }}
preset="add"
trigger={
<Button size="small" type="link">
<PlusOutlinedIcon />
{t("workflow_node.deploy.form.provider_access.button")}
</Button>
}
afterSubmit={(record) => {
const provider = accessProvidersMap.get(record.provider);
if (ACCESS_USAGES.ALL === provider?.usage || ACCESS_USAGES.DEPLOY === provider?.usage) {
formInst.setFieldValue("providerAccessId", record.id);
}
}}
/>
</div>
</div>
</label>
<Form.Item name="providerAccessId" rules={[formRule]}>
<AccessSelect
placeholder={t("workflow_node.deploy.form.provider_access.placeholder")}
filter={(record) => {
if (fieldProvider) {
return deployProvidersMap.get(fieldProvider)?.provider === record.provider;
}
const provider = accessProvidersMap.get(record.provider);
return ACCESS_USAGES.ALL === provider?.usage || ACCESS_USAGES.APPLY === provider?.usage;
}}
<Form.Provider onFormChange={handleFormProviderChange}>
<Form className={className} style={style} {...formProps} disabled={disabled} layout="vertical" scrollToFirstError onValuesChange={handleFormChange}>
<Show when={!!fieldProvider} fallback={<DeployProviderPicker onSelect={handleProviderPick} />}>
<Form.Item name="provider" label={t("workflow_node.deploy.form.provider.label")} rules={[formRule]}>
<DeployProviderSelect
allowClear
disabled
placeholder={t("workflow_node.deploy.form.provider.placeholder")}
showSearch
onSelect={handleProviderSelect}
/>
</Form.Item>
</Form.Item>
<Form.Item
name="certificate"
label={t("workflow_node.deploy.form.certificate.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.certificate.tooltip") }}></span>}
>
<Select
options={previousNodes.map((item) => {
return {
label: item.name,
options: item.outputs?.map((output) => {
return {
label: `${item.name} - ${output.label}`,
value: `${item.id}#${output.name}`,
};
}),
};
})}
placeholder={t("workflow_node.deploy.form.certificate.placeholder")}
/>
</Form.Item>
<Form.Item className="mb-0">
<label className="mb-1 block">
<div className="flex w-full items-center justify-between gap-4">
<div className="max-w-full grow truncate">
<span>{t("workflow_node.deploy.form.provider_access.label")}</span>
<Tooltip title={t("workflow_node.deploy.form.provider_access.tooltip")}>
<Typography.Text className="ms-1" type="secondary">
<QuestionCircleOutlinedIcon />
</Typography.Text>
</Tooltip>
</div>
<div className="text-right">
<AccessEditModal
data={{ provider: deployProvidersMap.get(fieldProvider!)?.provider }}
preset="add"
trigger={
<Button size="small" type="link">
<PlusOutlinedIcon />
{t("workflow_node.deploy.form.provider_access.button")}
</Button>
}
afterSubmit={(record) => {
const provider = accessProvidersMap.get(record.provider);
if (ACCESS_USAGES.ALL === provider?.usage || ACCESS_USAGES.DEPLOY === provider?.usage) {
formInst.setFieldValue("providerAccessId", record.id);
}
}}
/>
</div>
</div>
</label>
<Form.Item name="providerAccessId" rules={[formRule]}>
<AccessSelect
placeholder={t("workflow_node.deploy.form.provider_access.placeholder")}
filter={(record) => {
if (fieldProvider) {
return deployProvidersMap.get(fieldProvider)?.provider === record.provider;
}
<Divider className="my-1">
<Typography.Text className="text-xs font-normal" type="secondary">
{t("workflow_node.deploy.form.params_config.label")}
</Typography.Text>
</Divider>
const provider = accessProvidersMap.get(record.provider);
return ACCESS_USAGES.ALL === provider?.usage || ACCESS_USAGES.APPLY === provider?.usage;
}}
/>
</Form.Item>
</Form.Item>
{formFieldsEl}
</Show>
</Form>
<Form.Item
name="certificate"
label={t("workflow_node.deploy.form.certificate.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.certificate.tooltip") }}></span>}
>
<Select
options={previousNodes.map((item) => {
return {
label: item.name,
options: item.outputs?.map((output) => {
return {
label: `${item.name} - ${output.label}`,
value: `${item.id}#${output.name}`,
};
}),
};
})}
placeholder={t("workflow_node.deploy.form.certificate.placeholder")}
/>
</Form.Item>
<Divider className="my-1">
<Typography.Text className="text-xs font-normal" type="secondary">
{t("workflow_node.deploy.form.params_config.label")}
</Typography.Text>
</Divider>
</Show>
</Form>
<Show when={!!fieldProvider}>{nestedFormEl}</Show>
</Form.Provider>
);
}
);