import { useEffect, useState } from "react"; import { flushSync } from "react-dom"; import { useTranslation } from "react-i18next"; import { Button, Form, Input, InputNumber, Upload, type FormInstance, type UploadFile, type UploadProps } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; import { Upload as UploadIcon } from "lucide-react"; import { type SSHAccessConfig } from "@/domain/access"; import { readFileContent } from "@/utils/file"; type AccessEditFormSSHConfigModelType = Partial; export type AccessEditFormSSHConfigProps = { form: FormInstance; disabled?: boolean; loading?: boolean; model?: AccessEditFormSSHConfigModelType; onModelChange?: (model: AccessEditFormSSHConfigModelType) => void; }; const initModel = () => { return { host: "127.0.0.1", port: 22, username: "root", } as AccessEditFormSSHConfigModelType; }; const AccessEditFormSSHConfig = ({ form, disabled, loading, model, onModelChange }: AccessEditFormSSHConfigProps) => { const { t } = useTranslation(); const formSchema = z.object({ host: z.string().refine( (str) => { const reIpv4 = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; const reIpv6 = /^([\da-fA-F]{1,4}:){6}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)|::([\da−fA−F]1,4:)0,4((25[0−5]|2[0−4]\d|[01]?\d\d?)\.)3(25[0−5]|2[0−4]\d|[01]?\d\d?)|::([\da−fA−F]1,4:)0,4((25[0−5]|2[0−4]\d|[01]?\d\d?)\.)3(25[0−5]|2[0−4]\d|[01]?\d\d?)|^([\da-fA-F]{1,4}:):([\da-fA-F]{1,4}:){0,3}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)|([\da−fA−F]1,4:)2:([\da−fA−F]1,4:)0,2((25[0−5]|2[0−4]\d|[01]?\d\d?)\.)3(25[0−5]|2[0−4]\d|[01]?\d\d?)|([\da−fA−F]1,4:)2:([\da−fA−F]1,4:)0,2((25[0−5]|2[0−4]\d|[01]?\d\d?)\.)3(25[0−5]|2[0−4]\d|[01]?\d\d?)|^([\da-fA-F]{1,4}:){3}:([\da-fA-F]{1,4}:){0,1}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)|([\da−fA−F]1,4:)4:((25[0−5]|2[0−4]\d|[01]?\d\d?)\.)3(25[0−5]|2[0−4]\d|[01]?\d\d?)|([\da−fA−F]1,4:)4:((25[0−5]|2[0−4]\d|[01]?\d\d?)\.)3(25[0−5]|2[0−4]\d|[01]?\d\d?)|^([\da-fA-F]{1,4}:){7}[\da-fA-F]{1,4}|:((:[\da−fA−F]1,4)1,6|:)|:((:[\da−fA−F]1,4)1,6|:)|^[\da-fA-F]{1,4}:((:[\da-fA-F]{1,4}){1,5}|:)|([\da−fA−F]1,4:)2((:[\da−fA−F]1,4)1,4|:)|([\da−fA−F]1,4:)2((:[\da−fA−F]1,4)1,4|:)|^([\da-fA-F]{1,4}:){3}((:[\da-fA-F]{1,4}){1,3}|:)|([\da−fA−F]1,4:)4((:[\da−fA−F]1,4)1,2|:)|([\da−fA−F]1,4:)4((:[\da−fA−F]1,4)1,2|:)|^([\da-fA-F]{1,4}:){5}:([\da-fA-F]{1,4})?|([\da−fA−F]1,4:)6:|([\da−fA−F]1,4:)6:/; const reDomain = /^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/; return reIpv4.test(str) || reIpv6.test(str) || reDomain.test(str); }, { message: t("common.errmsg.host_invalid") } ), port: z .number() .int() .gte(1, t("common.errmsg.port_invalid")) .lte(65535, t("common.errmsg.port_invalid")) .transform((v) => +v), username: z .string() .min(1, "access.form.ssh_username.placeholder") .max(64, t("common.errmsg.string_max", { max: 64 })), password: z .string() .min(0, "access.form.ssh_password.placeholder") .max(64, t("common.errmsg.string_max", { max: 64 })) .nullish(), key: z .string() .min(0, "access.form.ssh_key.placeholder") .max(20480, t("common.errmsg.string_max", { max: 20480 })) .nullish(), keyPassphrase: z .string() .min(0, "access.form.ssh_key_passphrase.placeholder") .max(20480, t("common.errmsg.string_max", { max: 20480 })) .nullish() .refine((v) => !v || form.getFieldValue("key"), { message: t("access.form.ssh_key.placeholder") }), }); const formRule = createSchemaFieldRule(formSchema); const [initialValues, setInitialValues] = useState>>(model ?? initModel()); useEffect(() => { setInitialValues(model ?? initModel()); setKeyFileList(model?.key?.trim() ? [{ uid: "-1", name: "sshkey", status: "done" }] : []); }, [model]); const [keyFileList, setKeyFileList] = useState([]); const handleFormChange = (_: unknown, fields: AccessEditFormSSHConfigModelType) => { onModelChange?.(fields); }; const handleUploadChange: UploadProps["onChange"] = async ({ file }) => { if (file && file.status !== "removed") { form.setFieldValue("kubeConfig", (await readFileContent(file.originFileObj ?? (file as unknown as File))).trim()); setKeyFileList([file]); } else { form.setFieldValue("kubeConfig", ""); setKeyFileList([]); } flushSync(() => onModelChange?.(form.getFieldsValue())); }; return (
} >
} >
} >
); }; export default AccessEditFormSSHConfig;