import { useTranslation } from "react-i18next"; import { DownOutlined as DownOutlinedIcon } from "@ant-design/icons"; import { Button, Dropdown, Form, type FormInstance, Input, Select, Switch } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; import CodeInput from "@/components/CodeInput"; import Show from "@/components/Show"; import { CERTIFICATE_FORMATS } from "@/domain/certificate"; import { initPresetScript as _initPresetScript } from "./DeployNodeConfigFormLocalConfig"; type DeployNodeConfigFormSSHConfigFieldValues = Nullish<{ format: string; certPath: string; certPathForServerOnly?: string; certPathForIntermediaOnly?: string; keyPath?: string; pfxPassword?: string; jksAlias?: string; jksKeypass?: string; jksStorepass?: string; preCommand?: string; postCommand?: string; useSCP?: boolean; }>; export type DeployNodeConfigFormSSHConfigProps = { form: FormInstance; formName: string; disabled?: boolean; initialValues?: DeployNodeConfigFormSSHConfigFieldValues; onValuesChange?: (values: DeployNodeConfigFormSSHConfigFieldValues) => void; }; const FORMAT_PEM = CERTIFICATE_FORMATS.PEM; const FORMAT_PFX = CERTIFICATE_FORMATS.PFX; const FORMAT_JKS = CERTIFICATE_FORMATS.JKS; const initFormModel = (): DeployNodeConfigFormSSHConfigFieldValues => { return { format: FORMAT_PEM, certPath: "/etc/ssl/certimate/cert.crt", keyPath: "/etc/ssl/certimate/cert.key", }; }; const initPresetScript = ( key: Parameters[0] | "sh_replace_synologydsm_ssl" | "sh_replace_fnos_ssl", params?: Parameters[1] ) => { switch (key) { case "sh_replace_synologydsm_ssl": return `# *** 需要 root 权限 *** # 脚本参考 https://github.com/catchdave/ssl-certs/blob/main/replace_synology_ssl_certs.sh # 请将以下变量替换为实际值 $tmpFullchainPath = "${params?.certPath || ""}" # 证书文件路径(与表单中保持一致) $tmpCertPath = "${params?.certPathForServerOnly || ""}" # 服务器证书文件路径(与表单中保持一致) $tmpKeyPath = "${params?.keyPath || ""}" # 私钥文件路径(与表单中保持一致) DEBUG=1 error_exit() { echo "[ERROR] $1"; exit 1; } warn() { echo "[WARN] $1"; } info() { echo "[INFO] $1"; } debug() { [[ "\${DEBUG}" ]] && echo "[DEBUG] $1"; } certs_src_dir="/usr/syno/etc/certificate/system/default" target_cert_dirs=( "/usr/syno/etc/certificate/system/FQDN" "/usr/local/etc/certificate/ScsiTarget/pkg-scsi-plugin-server/" "/usr/local/etc/certificate/SynologyDrive/SynologyDrive/" "/usr/local/etc/certificate/WebDAVServer/webdav/" "/usr/local/etc/certificate/ActiveBackup/ActiveBackup/" "/usr/syno/etc/certificate/smbftpd/ftpd/") # 获取证书目录 default_dir_name=$(/dev/null && /usr/syno/bin/synopkg restart ScsiTarget /usr/syno/bin/synopkg is_onoff SynologyDrive 1>/dev/null && /usr/syno/bin/synopkg restart SynologyDrive /usr/syno/bin/synopkg is_onoff WebDAVServer 1>/dev/null && /usr/syno/bin/synopkg restart WebDAVServer /usr/syno/bin/synopkg is_onoff ActiveBackup 1>/dev/null && /usr/syno/bin/synopkg restart ActiveBackup if ! /usr/syno/bin/synow3tool --gen-all && sudo /usr/syno/bin/synosystemctl restart nginx; then warn "nginx failed to restart" fi info "Completed" `.trim(); case "sh_replace_fnos_ssl": return `# *** 需要 root 权限 *** # 脚本参考 https://github.com/lfgyx/fnos_certificate_update/blob/main/src/update_cert.sh # 请将以下变量替换为实际值 # 飞牛证书实际存放路径请在 \`/usr/trim/etc/network_cert_all.conf\` 中查看,注意不要修改文件名 $tmpFullchainPath = "${params?.certPath || ""}" # 证书文件路径(与表单中保持一致) $tmpCertPath = "${params?.certPathForServerOnly || ""}" # 服务器证书文件路径(与表单中保持一致) $tmpKeyPath = "${params?.keyPath || ""}" # 私钥文件路径(与表单中保持一致) $fnFullchainPath = "/usr/trim/var/trim_connect/ssls/example.com/1234567890/fullchain.crt" # 飞牛证书文件路径 $fnCertPath = "/usr/trim/var/trim_connect/ssls/example.com/1234567890/example.com.crt" # 飞牛服务器证书文件路径 $fnKeyPath = "/usr/trim/var/trim_connect/ssls/example.com/1234567890/example.com.key" # 飞牛私钥文件路径 $domain = "" # 域名 # 复制文件 cp -rf "$tmpFullchainPath" "$fnFullchainPath" cp -rf "$tmpCertPath" "$fnCertPath" cp -rf "$tmpKeyPath" "$fnKeyPath" chmod 755 "$fnCertPath" chmod 755 "$fnKeyPath" chmod 755 "$fnFullchainPath" # 更新数据库 NEW_EXPIRY_DATE=$(openssl x509 -enddate -noout -in "$fnCertPath" | sed "s/^.*=\\(.*\\)$/\\1/") NEW_EXPIRY_TIMESTAMP=$(date -d "$NEW_EXPIRY_DATE" +%s%3N) psql -U postgres -d trim_connect -c "UPDATE cert SET valid_to=$NEW_EXPIRY_TIMESTAMP WHERE domain='$domain'" # 重启服务 systemctl restart webdav.service systemctl restart smbftpd.service systemctl restart trim_nginx.service `.trim(); } return _initPresetScript(key as Parameters[0], params); }; const DeployNodeConfigFormSSHConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: DeployNodeConfigFormSSHConfigProps) => { const { t } = useTranslation(); const formSchema = z.object({ format: z.union([z.literal(FORMAT_PEM), z.literal(FORMAT_PFX), z.literal(FORMAT_JKS)], { message: t("workflow_node.deploy.form.ssh_format.placeholder"), }), certPath: z .string() .min(1, t("workflow_node.deploy.form.ssh_cert_path.tooltip")) .max(256, t("common.errmsg.string_max", { max: 256 })) .trim(), keyPath: z .string() .max(256, t("common.errmsg.string_max", { max: 256 })) .trim() .nullish() .refine((v) => fieldFormat !== FORMAT_PEM || !!v?.trim(), { message: t("workflow_node.deploy.form.ssh_key_path.tooltip") }), certPathForServerOnly: z .string() .max(256, t("common.errmsg.string_max", { max: 256 })) .trim() .nullish(), certPathForIntermediaOnly: z .string() .max(256, t("common.errmsg.string_max", { max: 256 })) .trim() .nullish(), pfxPassword: z .string() .max(64, t("common.errmsg.string_max", { max: 256 })) .trim() .nullish() .refine((v) => fieldFormat !== FORMAT_PFX || !!v?.trim(), { message: t("workflow_node.deploy.form.ssh_pfx_password.tooltip") }), jksAlias: z .string() .max(64, t("common.errmsg.string_max", { max: 256 })) .trim() .nullish() .refine((v) => fieldFormat !== FORMAT_JKS || !!v?.trim(), { message: t("workflow_node.deploy.form.ssh_jks_alias.tooltip") }), jksKeypass: z .string() .max(64, t("common.errmsg.string_max", { max: 256 })) .trim() .nullish() .refine((v) => fieldFormat !== FORMAT_JKS || !!v?.trim(), { message: t("workflow_node.deploy.form.ssh_jks_keypass.tooltip") }), jksStorepass: z .string() .max(64, t("common.errmsg.string_max", { max: 256 })) .trim() .nullish() .refine((v) => fieldFormat !== FORMAT_JKS || !!v?.trim(), { message: t("workflow_node.deploy.form.ssh_jks_storepass.tooltip") }), preCommand: z .string() .max(20480, t("common.errmsg.string_max", { max: 20480 })) .nullish(), postCommand: z .string() .max(20480, t("common.errmsg.string_max", { max: 20480 })) .nullish(), useSCP: z.boolean().nullish(), }); const formRule = createSchemaFieldRule(formSchema); const fieldFormat = Form.useWatch("format", formInst); const fieldCertPath = Form.useWatch("certPath", formInst); const handleFormatSelect = (value: string) => { if (fieldFormat === value) return; switch (value) { case FORMAT_PEM: { if (/(.pfx|.jks)$/.test(fieldCertPath)) { formInst.setFieldValue("certPath", fieldCertPath.replace(/(.pfx|.jks)$/, ".crt")); } } break; case FORMAT_PFX: { if (/(.crt|.jks)$/.test(fieldCertPath)) { formInst.setFieldValue("certPath", fieldCertPath.replace(/(.crt|.jks)$/, ".pfx")); } } break; case FORMAT_JKS: { if (/(.crt|.pfx)$/.test(fieldCertPath)) { formInst.setFieldValue("certPath", fieldCertPath.replace(/(.crt|.pfx)$/, ".jks")); } } break; } }; const handlePresetPreScriptClick = (key: string) => { switch (key) { case "sh_backup_files": case "ps_backup_files": { const presetScriptParams = { certPath: formInst.getFieldValue("certPath"), keyPath: formInst.getFieldValue("keyPath"), }; formInst.setFieldValue("preCommand", initPresetScript(key, presetScriptParams)); } break; } }; const handlePresetPostScriptClick = (key: string) => { switch (key) { case "sh_reload_nginx": { formInst.setFieldValue("postCommand", initPresetScript(key)); } break; case "sh_replace_synologydsm_ssl": case "sh_replace_fnos_ssl": { const presetScriptParams = { certPath: formInst.getFieldValue("certPath"), certPathForServerOnly: formInst.getFieldValue("certPathForServerOnly"), certPathForIntermediaOnly: formInst.getFieldValue("certPathForIntermediaOnly"), keyPath: formInst.getFieldValue("keyPath"), }; formInst.setFieldValue("postCommand", initPresetScript(key, presetScriptParams)); } break; case "ps_binding_iis": case "ps_binding_netsh": case "ps_binding_rdp": { const presetScriptParams = { certPath: formInst.getFieldValue("certPath"), pfxPassword: formInst.getFieldValue("pfxPassword"), }; formInst.setFieldValue("postCommand", initPresetScript(key, presetScriptParams)); } break; } }; const handleFormChange = (_: unknown, values: z.infer) => { onValuesChange?.(values); }; return (
} > } > } > } > } > } > } > } > } >
); }; export default DeployNodeConfigFormSSHConfig;