mirror of https://github.com/usual2970/certimate
				
				
				
			feat(ui): TextFileInput
							parent
							
								
									355059df3c
								
							
						
					
					
						commit
						04abf9dd76
					
				| 
						 | 
				
			
			@ -0,0 +1,51 @@
 | 
			
		|||
import { type ChangeEvent, useRef } from "react";
 | 
			
		||||
import { useTranslation } from "react-i18next";
 | 
			
		||||
import { UploadOutlined as UploadOutlinedIcon } from "@ant-design/icons";
 | 
			
		||||
import { Button, type ButtonProps, Input, Space, type UploadProps } from "antd";
 | 
			
		||||
import { type TextAreaProps } from "antd/es/input/TextArea";
 | 
			
		||||
 | 
			
		||||
import { mergeCls } from "@/utils/css";
 | 
			
		||||
import { readFileContent } from "@/utils/file";
 | 
			
		||||
 | 
			
		||||
export interface TextFileInputProps extends Omit<TextAreaProps, "onChange"> {
 | 
			
		||||
  accept?: UploadProps["accept"];
 | 
			
		||||
  uploadButtonProps?: Omit<ButtonProps, "disabled" | "onClick">;
 | 
			
		||||
  uploadText?: string;
 | 
			
		||||
  onChange?: (value: string) => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const TextFileInput = ({ className, style, accept, disabled, readOnly, uploadText, uploadButtonProps, onChange, ...props }: TextFileInputProps) => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  const fileInputRef = useRef<HTMLInputElement>(null);
 | 
			
		||||
 | 
			
		||||
  const handleButtonClick = () => {
 | 
			
		||||
    if (fileInputRef.current) {
 | 
			
		||||
      fileInputRef.current.click();
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleFileChange = async (e: ChangeEvent<HTMLInputElement>) => {
 | 
			
		||||
    const { files } = e.target as HTMLInputElement;
 | 
			
		||||
    if (files?.length) {
 | 
			
		||||
      const value = await readFileContent(files[0]);
 | 
			
		||||
      onChange?.(value);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Space className={mergeCls("w-full", className)} style={style} direction="vertical" size="small">
 | 
			
		||||
      <Input.TextArea {...props} disabled={disabled} readOnly={readOnly} onChange={(e) => onChange?.(e.target.value)} />
 | 
			
		||||
      {!readOnly && (
 | 
			
		||||
        <>
 | 
			
		||||
          <Button {...uploadButtonProps} block disabled={disabled} icon={<UploadOutlinedIcon />} onClick={handleButtonClick}>
 | 
			
		||||
            {uploadText ?? t("common.text.import_from_file")}
 | 
			
		||||
          </Button>
 | 
			
		||||
          <input ref={fileInputRef} type="file" style={{ display: "none" }} accept={accept} onChange={handleFileChange} />
 | 
			
		||||
        </>
 | 
			
		||||
      )}
 | 
			
		||||
    </Space>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default TextFileInput;
 | 
			
		||||
| 
						 | 
				
			
			@ -1,12 +1,10 @@
 | 
			
		|||
import { useEffect, useState } from "react";
 | 
			
		||||
import { useTranslation } from "react-i18next";
 | 
			
		||||
import { UploadOutlined as UploadOutlinedIcon } from "@ant-design/icons";
 | 
			
		||||
import { Button, Form, type FormInstance, Input, Upload, type UploadFile, type UploadProps } from "antd";
 | 
			
		||||
import { Form, type FormInstance } from "antd";
 | 
			
		||||
import { createSchemaFieldRule } from "antd-zod";
 | 
			
		||||
import { z } from "zod";
 | 
			
		||||
 | 
			
		||||
import TextFileInput from "@/components/TextFileInput";
 | 
			
		||||
import { type AccessConfigForKubernetes } from "@/domain/access";
 | 
			
		||||
import { readFileContent } from "@/utils/file";
 | 
			
		||||
 | 
			
		||||
type AccessFormKubernetesConfigFieldValues = Nullish<AccessConfigForKubernetes>;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -34,24 +32,6 @@ const AccessFormKubernetesConfig = ({ form: formInst, formName, disabled, initia
 | 
			
		|||
  });
 | 
			
		||||
  const formRule = createSchemaFieldRule(formSchema);
 | 
			
		||||
 | 
			
		||||
  const fieldKubeConfig = Form.useWatch("kubeConfig", formInst);
 | 
			
		||||
  const [fieldKubeFileList, setFieldKubeFileList] = useState<UploadFile[]>([]);
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    setFieldKubeFileList(initialValues?.kubeConfig?.trim() ? [{ uid: "-1", name: "kubeconfig", status: "done" }] : []);
 | 
			
		||||
  }, [initialValues?.kubeConfig]);
 | 
			
		||||
 | 
			
		||||
  const handleKubeFileChange: UploadProps["onChange"] = async ({ file }) => {
 | 
			
		||||
    if (file && file.status !== "removed") {
 | 
			
		||||
      formInst.setFieldValue("kubeConfig", await readFileContent(file.originFileObj ?? (file as unknown as File)));
 | 
			
		||||
      setFieldKubeFileList([file]);
 | 
			
		||||
    } else {
 | 
			
		||||
      formInst.setFieldValue("kubeConfig", "");
 | 
			
		||||
      setFieldKubeFileList([]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onValuesChange?.(formInst.getFieldsValue(true));
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
 | 
			
		||||
    onValuesChange?.(values);
 | 
			
		||||
  };
 | 
			
		||||
| 
						 | 
				
			
			@ -65,16 +45,13 @@ const AccessFormKubernetesConfig = ({ form: formInst, formName, disabled, initia
 | 
			
		|||
      name={formName}
 | 
			
		||||
      onValuesChange={handleFormChange}
 | 
			
		||||
    >
 | 
			
		||||
      <Form.Item name="kubeConfig" noStyle rules={[formRule]}>
 | 
			
		||||
        <Input.TextArea autoComplete="new-password" hidden placeholder={t("access.form.k8s_kubeconfig.placeholder")} value={fieldKubeConfig} />
 | 
			
		||||
      </Form.Item>
 | 
			
		||||
      <Form.Item
 | 
			
		||||
        name="kubeConfig"
 | 
			
		||||
        label={t("access.form.k8s_kubeconfig.label")}
 | 
			
		||||
        rules={[formRule]}
 | 
			
		||||
        tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.k8s_kubeconfig.tooltip") }}></span>}
 | 
			
		||||
      >
 | 
			
		||||
        <Upload beforeUpload={() => false} fileList={fieldKubeFileList} maxCount={1} onChange={handleKubeFileChange}>
 | 
			
		||||
          <Button icon={<UploadOutlinedIcon />}>{t("access.form.k8s_kubeconfig.upload")}</Button>
 | 
			
		||||
        </Upload>
 | 
			
		||||
        <TextFileInput allowClear autoSize={{ minRows: 3, maxRows: 10 }} placeholder={t("access.form.k8s_kubeconfig.placeholder")} />
 | 
			
		||||
      </Form.Item>
 | 
			
		||||
    </Form>
 | 
			
		||||
  );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,10 @@
 | 
			
		|||
import { useEffect, useState } from "react";
 | 
			
		||||
import { useTranslation } from "react-i18next";
 | 
			
		||||
import { UploadOutlined as UploadOutlinedIcon } from "@ant-design/icons";
 | 
			
		||||
import { Button, Form, type FormInstance, Input, InputNumber, Upload, type UploadFile, type UploadProps } from "antd";
 | 
			
		||||
import { Form, type FormInstance, Input, InputNumber } from "antd";
 | 
			
		||||
import { createSchemaFieldRule } from "antd-zod";
 | 
			
		||||
import { z } from "zod";
 | 
			
		||||
 | 
			
		||||
import TextFileInput from "@/components/TextFileInput";
 | 
			
		||||
import { type AccessConfigForSSH } from "@/domain/access";
 | 
			
		||||
import { readFileContent } from "@/utils/file";
 | 
			
		||||
import { validDomainName, validIPv4Address, validIPv6Address, validPortNumber } from "@/utils/validators";
 | 
			
		||||
 | 
			
		||||
type AccessFormSSHConfigFieldValues = Nullish<AccessConfigForSSH>;
 | 
			
		||||
| 
						 | 
				
			
			@ -59,24 +57,6 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
 | 
			
		|||
  });
 | 
			
		||||
  const formRule = createSchemaFieldRule(formSchema);
 | 
			
		||||
 | 
			
		||||
  const fieldKey = Form.useWatch("key", formInst);
 | 
			
		||||
  const [fieldKeyFileList, setFieldKeyFileList] = useState<UploadFile[]>([]);
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    setFieldKeyFileList(initialValues?.key?.trim() ? [{ uid: "-1", name: "sshkey", status: "done" }] : []);
 | 
			
		||||
  }, [initialValues?.key]);
 | 
			
		||||
 | 
			
		||||
  const handleKeyFileChange: UploadProps["onChange"] = async ({ file }) => {
 | 
			
		||||
    if (file && file.status !== "removed") {
 | 
			
		||||
      formInst.setFieldValue("key", await readFileContent(file.originFileObj ?? (file as unknown as File)));
 | 
			
		||||
      setFieldKeyFileList([file]);
 | 
			
		||||
    } else {
 | 
			
		||||
      formInst.setFieldValue("key", "");
 | 
			
		||||
      setFieldKeyFileList([]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onValuesChange?.(formInst.getFieldsValue(true));
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
 | 
			
		||||
    onValuesChange?.(values);
 | 
			
		||||
  };
 | 
			
		||||
| 
						 | 
				
			
			@ -104,48 +84,36 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
 | 
			
		|||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div className="flex space-x-2">
 | 
			
		||||
        <div className="w-1/2">
 | 
			
		||||
          <Form.Item name="username" label={t("access.form.ssh_username.label")} rules={[formRule]}>
 | 
			
		||||
            <Input autoComplete="new-password" placeholder={t("access.form.ssh_username.placeholder")} />
 | 
			
		||||
          </Form.Item>
 | 
			
		||||
        </div>
 | 
			
		||||
      <Form.Item name="username" label={t("access.form.ssh_username.label")} rules={[formRule]}>
 | 
			
		||||
        <Input autoComplete="new-password" placeholder={t("access.form.ssh_username.placeholder")} />
 | 
			
		||||
      </Form.Item>
 | 
			
		||||
 | 
			
		||||
        <div className="w-1/2">
 | 
			
		||||
          <Form.Item
 | 
			
		||||
            name="password"
 | 
			
		||||
            label={t("access.form.ssh_password.label")}
 | 
			
		||||
            rules={[formRule]}
 | 
			
		||||
            tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ssh_password.tooltip") }}></span>}
 | 
			
		||||
          >
 | 
			
		||||
            <Input.Password autoComplete="new-password" placeholder={t("access.form.ssh_password.placeholder")} />
 | 
			
		||||
          </Form.Item>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <Form.Item
 | 
			
		||||
        name="password"
 | 
			
		||||
        label={t("access.form.ssh_password.label")}
 | 
			
		||||
        rules={[formRule]}
 | 
			
		||||
        tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ssh_password.tooltip") }}></span>}
 | 
			
		||||
      >
 | 
			
		||||
        <Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.ssh_password.placeholder")} />
 | 
			
		||||
      </Form.Item>
 | 
			
		||||
 | 
			
		||||
      <div className="flex space-x-2">
 | 
			
		||||
        <div className="w-1/2">
 | 
			
		||||
          <Form.Item name="key" noStyle rules={[formRule]}>
 | 
			
		||||
            <Input.TextArea autoComplete="new-password" hidden placeholder={t("access.form.ssh_key.placeholder")} value={fieldKey} />
 | 
			
		||||
          </Form.Item>
 | 
			
		||||
          <Form.Item label={t("access.form.ssh_key.label")} tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ssh_key.tooltip") }}></span>}>
 | 
			
		||||
            <Upload beforeUpload={() => false} fileList={fieldKeyFileList} maxCount={1} onChange={handleKeyFileChange}>
 | 
			
		||||
              <Button icon={<UploadOutlinedIcon />}>{t("access.form.ssh_key.upload")}</Button>
 | 
			
		||||
            </Upload>
 | 
			
		||||
          </Form.Item>
 | 
			
		||||
        </div>
 | 
			
		||||
      <Form.Item
 | 
			
		||||
        name="key"
 | 
			
		||||
        label={t("access.form.ssh_key.label")}
 | 
			
		||||
        rules={[formRule]}
 | 
			
		||||
        tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ssh_key.tooltip") }}></span>}
 | 
			
		||||
      >
 | 
			
		||||
        <TextFileInput allowClear autoSize={{ minRows: 1, maxRows: 5 }} placeholder={t("access.form.ssh_key.placeholder")} />
 | 
			
		||||
      </Form.Item>
 | 
			
		||||
 | 
			
		||||
        <div className="w-1/2">
 | 
			
		||||
          <Form.Item
 | 
			
		||||
            name="keyPassphrase"
 | 
			
		||||
            label={t("access.form.ssh_key_passphrase.label")}
 | 
			
		||||
            rules={[formRule]}
 | 
			
		||||
            tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ssh_key_passphrase.tooltip") }}></span>}
 | 
			
		||||
          >
 | 
			
		||||
            <Input.Password autoComplete="new-password" placeholder={t("access.form.ssh_key_passphrase.placeholder")} />
 | 
			
		||||
          </Form.Item>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <Form.Item
 | 
			
		||||
        name="keyPassphrase"
 | 
			
		||||
        label={t("access.form.ssh_key_passphrase.label")}
 | 
			
		||||
        rules={[formRule]}
 | 
			
		||||
        tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ssh_key_passphrase.tooltip") }}></span>}
 | 
			
		||||
      >
 | 
			
		||||
        <Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.ssh_key_passphrase.placeholder")} />
 | 
			
		||||
      </Form.Item>
 | 
			
		||||
    </Form>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -177,7 +177,7 @@ const NotifyNodeConfigForm = forwardRef<NotifyNodeConfigFormInstance, NotifyNode
 | 
			
		|||
        </Form.Item>
 | 
			
		||||
 | 
			
		||||
        <Form.Item name="message" label={t("workflow_node.notify.form.message.label")} rules={[formRule]}>
 | 
			
		||||
          <Input.TextArea autoSize={{ minRows: 3, maxRows: 5 }} placeholder={t("workflow_node.notify.form.message.placeholder")} />
 | 
			
		||||
          <Input.TextArea autoSize={{ minRows: 3, maxRows: 10 }} placeholder={t("workflow_node.notify.form.message.placeholder")} />
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
 | 
			
		||||
        <Form.Item className="mb-0" htmlFor="null">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,15 +1,14 @@
 | 
			
		|||
import { forwardRef, memo, useImperativeHandle } from "react";
 | 
			
		||||
import { useTranslation } from "react-i18next";
 | 
			
		||||
import { UploadOutlined as UploadOutlinedIcon } from "@ant-design/icons";
 | 
			
		||||
import { Button, Form, type FormInstance, Input, Upload, type UploadProps } from "antd";
 | 
			
		||||
import { Form, type FormInstance, Input } from "antd";
 | 
			
		||||
import { createSchemaFieldRule } from "antd-zod";
 | 
			
		||||
import { z } from "zod";
 | 
			
		||||
 | 
			
		||||
import { validateCertificate, validatePrivateKey } from "@/api/certificates";
 | 
			
		||||
import TextFileInput from "@/components/TextFileInput";
 | 
			
		||||
import { type WorkflowNodeConfigForUpload } from "@/domain/workflow";
 | 
			
		||||
import { useAntdForm } from "@/hooks";
 | 
			
		||||
import { getErrMsg } from "@/utils/error";
 | 
			
		||||
import { readFileContent } from "@/utils/file";
 | 
			
		||||
 | 
			
		||||
type UploadNodeConfigFormFieldValues = Partial<WorkflowNodeConfigForUpload>;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -70,65 +69,53 @@ const UploadNodeConfigForm = forwardRef<UploadNodeConfigFormInstance, UploadNode
 | 
			
		|||
      } as UploadNodeConfigFormInstance;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const handleCertificateFileChange: UploadProps["onChange"] = async ({ file }) => {
 | 
			
		||||
      if (file && file.status !== "removed") {
 | 
			
		||||
        const certificate = await readFileContent(file.originFileObj ?? (file as unknown as File));
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
          const resp = await validateCertificate(certificate);
 | 
			
		||||
          formInst.setFields([
 | 
			
		||||
            {
 | 
			
		||||
              name: "domains",
 | 
			
		||||
              value: resp.data.domains,
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              name: "certificate",
 | 
			
		||||
              value: certificate,
 | 
			
		||||
            },
 | 
			
		||||
          ]);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
          formInst.setFields([
 | 
			
		||||
            {
 | 
			
		||||
              name: "domains",
 | 
			
		||||
              value: "",
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              name: "certificate",
 | 
			
		||||
              value: "",
 | 
			
		||||
              errors: [getErrMsg(e)],
 | 
			
		||||
            },
 | 
			
		||||
          ]);
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        formInst.setFieldValue("certificate", "");
 | 
			
		||||
    const handleCertificateChange = async (value: string) => {
 | 
			
		||||
      try {
 | 
			
		||||
        const resp = await validateCertificate(value);
 | 
			
		||||
        formInst.setFields([
 | 
			
		||||
          {
 | 
			
		||||
            name: "domains",
 | 
			
		||||
            value: resp.data.domains,
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            name: "certificate",
 | 
			
		||||
            value: value,
 | 
			
		||||
          },
 | 
			
		||||
        ]);
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        formInst.setFields([
 | 
			
		||||
          {
 | 
			
		||||
            name: "domains",
 | 
			
		||||
            value: "",
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            name: "certificate",
 | 
			
		||||
            value: value,
 | 
			
		||||
            errors: [getErrMsg(e)],
 | 
			
		||||
          },
 | 
			
		||||
        ]);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      onValuesChange?.(formInst.getFieldsValue(true));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const handlePrivateKeyFileChange: UploadProps["onChange"] = async ({ file }) => {
 | 
			
		||||
      if (file && file.status !== "removed") {
 | 
			
		||||
        const privateKey = await readFileContent(file.originFileObj ?? (file as unknown as File));
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
          await validatePrivateKey(privateKey);
 | 
			
		||||
          formInst.setFields([
 | 
			
		||||
            {
 | 
			
		||||
              name: "privateKey",
 | 
			
		||||
              value: privateKey,
 | 
			
		||||
            },
 | 
			
		||||
          ]);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
          formInst.setFields([
 | 
			
		||||
            {
 | 
			
		||||
              name: "privateKey",
 | 
			
		||||
              value: "",
 | 
			
		||||
              errors: [getErrMsg(e)],
 | 
			
		||||
            },
 | 
			
		||||
          ]);
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        formInst.setFieldValue("privateKey", "");
 | 
			
		||||
    const handlePrivateKeyChange = async (value: string) => {
 | 
			
		||||
      try {
 | 
			
		||||
        await validatePrivateKey(value);
 | 
			
		||||
        formInst.setFields([
 | 
			
		||||
          {
 | 
			
		||||
            name: "privateKey",
 | 
			
		||||
            value: value,
 | 
			
		||||
          },
 | 
			
		||||
        ]);
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        formInst.setFields([
 | 
			
		||||
          {
 | 
			
		||||
            name: "privateKey",
 | 
			
		||||
            value: value,
 | 
			
		||||
            errors: [getErrMsg(e)],
 | 
			
		||||
          },
 | 
			
		||||
        ]);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      onValuesChange?.(formInst.getFieldsValue(true));
 | 
			
		||||
| 
						 | 
				
			
			@ -141,23 +128,19 @@ const UploadNodeConfigForm = forwardRef<UploadNodeConfigFormInstance, UploadNode
 | 
			
		|||
        </Form.Item>
 | 
			
		||||
 | 
			
		||||
        <Form.Item name="certificate" label={t("workflow_node.upload.form.certificate.label")} rules={[formRule]}>
 | 
			
		||||
          <Input.TextArea readOnly autoSize={{ minRows: 5, maxRows: 5 }} placeholder={t("workflow_node.upload.form.certificate.placeholder")} />
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
 | 
			
		||||
        <Form.Item>
 | 
			
		||||
          <Upload beforeUpload={() => false} maxCount={1} onChange={handleCertificateFileChange}>
 | 
			
		||||
            <Button icon={<UploadOutlinedIcon />}>{t("workflow_node.upload.form.certificate.button")}</Button>
 | 
			
		||||
          </Upload>
 | 
			
		||||
          <TextFileInput
 | 
			
		||||
            autoSize={{ minRows: 3, maxRows: 10 }}
 | 
			
		||||
            placeholder={t("workflow_node.upload.form.certificate.placeholder")}
 | 
			
		||||
            onChange={handleCertificateChange}
 | 
			
		||||
          />
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
 | 
			
		||||
        <Form.Item name="privateKey" label={t("workflow_node.upload.form.private_key.label")} rules={[formRule]}>
 | 
			
		||||
          <Input.TextArea readOnly autoSize={{ minRows: 5, maxRows: 5 }} placeholder={t("workflow_node.upload.form.private_key.placeholder")} />
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
 | 
			
		||||
        <Form.Item>
 | 
			
		||||
          <Upload beforeUpload={() => false} maxCount={1} onChange={handlePrivateKeyFileChange}>
 | 
			
		||||
            <Button icon={<UploadOutlinedIcon />}>{t("workflow_node.upload.form.private_key.button")}</Button>
 | 
			
		||||
          </Upload>
 | 
			
		||||
          <TextFileInput
 | 
			
		||||
            autoSize={{ minRows: 3, maxRows: 10 }}
 | 
			
		||||
            placeholder={t("workflow_node.upload.form.private_key.placeholder")}
 | 
			
		||||
            onChange={handlePrivateKeyChange}
 | 
			
		||||
          />
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
      </Form>
 | 
			
		||||
    );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -230,7 +230,6 @@
 | 
			
		|||
  "access.form.jdcloud_access_key_secret.tooltip": "For more information, see <a href=\"https://docs.jdcloud.com/en/account-management/accesskey-management\" target=\"_blank\">https://docs.jdcloud.com/en/account-management/accesskey-management</a>",
 | 
			
		||||
  "access.form.k8s_kubeconfig.label": "KubeConfig",
 | 
			
		||||
  "access.form.k8s_kubeconfig.placeholder": "Please enter KubeConfig file",
 | 
			
		||||
  "access.form.k8s_kubeconfig.upload": "Choose File ...",
 | 
			
		||||
  "access.form.k8s_kubeconfig.tooltip": "For more information, see <a href=\"https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/\" target=\"_blank\">https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/</a><br><br>Leave it blank to use the Pod's ServiceAccount.",
 | 
			
		||||
  "access.form.larkbot_webhook_url.label": "Lark bot Webhook URL",
 | 
			
		||||
  "access.form.larkbot_webhook_url.placeholder": "Please enter Lark bot Webhook URL",
 | 
			
		||||
| 
						 | 
				
			
			@ -315,7 +314,6 @@
 | 
			
		|||
  "access.form.ssh_password.tooltip": "Required when using password to connect to SSH.",
 | 
			
		||||
  "access.form.ssh_key.label": "SSH key (Optional)",
 | 
			
		||||
  "access.form.ssh_key.placeholder": "Please enter SSH key",
 | 
			
		||||
  "access.form.ssh_key.upload": "Choose file ...",
 | 
			
		||||
  "access.form.ssh_key.tooltip": "Required when using key to connect to SSH.",
 | 
			
		||||
  "access.form.ssh_key_passphrase.label": "SSH key passphrase (Optional)",
 | 
			
		||||
  "access.form.ssh_key_passphrase.placeholder": "Please enter SSH key passphrase",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,7 @@
 | 
			
		|||
  "common.button.submit": "Submit",
 | 
			
		||||
 | 
			
		||||
  "common.text.copied": "Copied",
 | 
			
		||||
  "common.text.import_from_file": "Import from file ...",
 | 
			
		||||
  "common.text.nodata": "No data available",
 | 
			
		||||
  "common.text.operation_confirm": "Operation confirm",
 | 
			
		||||
  "common.text.operation_succeeded": "Operation succeeded",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -732,10 +732,8 @@
 | 
			
		|||
  "workflow_node.upload.form.domains.placholder": "Please select certificate file",
 | 
			
		||||
  "workflow_node.upload.form.certificate.label": "Certificate (PEM format)",
 | 
			
		||||
  "workflow_node.upload.form.certificate.placeholder": "-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----",
 | 
			
		||||
  "workflow_node.upload.form.certificate.button": "Choose file ...",
 | 
			
		||||
  "workflow_node.upload.form.private_key.label": "Private key (PEM format)",
 | 
			
		||||
  "workflow_node.upload.form.private_key.placeholder": "-----BEGIN (RSA|EC) PRIVATE KEY-----...-----END(RSA|EC) PRIVATE KEY-----",
 | 
			
		||||
  "workflow_node.upload.form.private_key.button": "Choose file ...",
 | 
			
		||||
 | 
			
		||||
  "workflow_node.notify.label": "Notification",
 | 
			
		||||
  "workflow_node.notify.form.subject.label": "Subject",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -223,8 +223,7 @@
 | 
			
		|||
  "access.form.jdcloud_access_key_secret.placeholder": "请输入京东云 AccessKeySecret",
 | 
			
		||||
  "access.form.jdcloud_access_key_secret.tooltip": "这是什么?请参阅 <a href=\"https://docs.jdcloud.com/cn/account-management/accesskey-management\" target=\"_blank\">https://docs.jdcloud.com/cn/account-management/accesskey-management</a>",
 | 
			
		||||
  "access.form.k8s_kubeconfig.label": "KubeConfig",
 | 
			
		||||
  "access.form.k8s_kubeconfig.placeholder": "请选择 KubeConfig 文件",
 | 
			
		||||
  "access.form.k8s_kubeconfig.upload": "选择文件",
 | 
			
		||||
  "access.form.k8s_kubeconfig.placeholder": "请输入 KubeConfig 文件内容",
 | 
			
		||||
  "access.form.k8s_kubeconfig.tooltip": "这是什么?请参阅 <a href=\"https://kubernetes.io/zh-cn/docs/concepts/configuration/organize-cluster-access-kubeconfig/\" target=\"_blank\">https://kubernetes.io/zh-cn/docs/concepts/configuration/organize-cluster-access-kubeconfig/</a><br><br>为空时,将使用 Pod 的 ServiceAccount 作为凭证。",
 | 
			
		||||
  "access.form.larkbot_webhook_url.label": "飞书群机器人 Webhook 地址",
 | 
			
		||||
  "access.form.larkbot_webhook_url.placeholder": "请输入飞书群机器人 Webhook 地址",
 | 
			
		||||
| 
						 | 
				
			
			@ -308,8 +307,7 @@
 | 
			
		|||
  "access.form.ssh_password.placeholder": "请输入密码",
 | 
			
		||||
  "access.form.ssh_password.tooltip": "使用密码连接到 SSH 时必填。<br>该字段与密钥文件字段二选一,如果同时填写优先使用 SSH 密钥登录。",
 | 
			
		||||
  "access.form.ssh_key.label": "SSH 密钥(可选)",
 | 
			
		||||
  "access.form.ssh_key.placeholder": "请输入 SSH 密钥文件",
 | 
			
		||||
  "access.form.ssh_key.upload": "选择文件",
 | 
			
		||||
  "access.form.ssh_key.placeholder": "请输入 SSH 密钥文件内容",
 | 
			
		||||
  "access.form.ssh_key.tooltip": "使用 SSH 密钥连接到 SSH 时必填。<br>该字段与密码字段二选一,如果同时填写优先使用 SSH 密钥登录。",
 | 
			
		||||
  "access.form.ssh_key_passphrase.label": "SSH 密钥口令(可选)",
 | 
			
		||||
  "access.form.ssh_key_passphrase.placeholder": "请输入 SSH 密钥口令",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,7 @@
 | 
			
		|||
  "common.button.submit": "提交",
 | 
			
		||||
 | 
			
		||||
  "common.text.copied": "已复制",
 | 
			
		||||
  "common.text.import_from_file": "从文件导入 ……",
 | 
			
		||||
  "common.text.nodata": "暂无数据",
 | 
			
		||||
  "common.text.operation_confirm": "操作确认",
 | 
			
		||||
  "common.text.operation_succeeded": "操作成功",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -731,10 +731,8 @@
 | 
			
		|||
  "workflow_node.upload.form.domains.placeholder": "上传证书文件后显示",
 | 
			
		||||
  "workflow_node.upload.form.certificate.label": "证书文件(PEM 格式)",
 | 
			
		||||
  "workflow_node.upload.form.certificate.placeholder": "-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----",
 | 
			
		||||
  "workflow_node.upload.form.certificate.button": "选择文件",
 | 
			
		||||
  "workflow_node.upload.form.private_key.label": "私钥文件(PEM 格式)",
 | 
			
		||||
  "workflow_node.upload.form.private_key.placeholder": "-----BEGIN (RSA|EC) PRIVATE KEY-----...-----END(RSA|EC) PRIVATE KEY-----",
 | 
			
		||||
  "workflow_node.upload.form.private_key.button": "选择文件",
 | 
			
		||||
 | 
			
		||||
  "workflow_node.notify.label": "推送通知",
 | 
			
		||||
  "workflow_node.notify.form.subject.label": "通知主题",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue