+
+
+
+
{error?.format}
+
+
+
+
+
{
+ const newData = produce(data, (draft) => {
+ draft.config ??= {};
+ draft.config.certPath = e.target.value?.trim();
+ });
+ setDeploy(newData);
+ }}
+ />
+
{error?.certPath}
+
+
+ {data.config?.format === "pem" ? (
+
+
+
{
+ const newData = produce(data, (draft) => {
+ draft.config ??= {};
+ draft.config.keyPath = e.target.value?.trim();
+ });
+ setDeploy(newData);
+ }}
+ />
+
{error?.keyPath}
+
+ ) : (
+ <>>
+ )}
+
+ {data.config?.format === "pfx" ? (
+
+
+
{
+ const newData = produce(data, (draft) => {
+ draft.config ??= {};
+ draft.config.pfxPassword = e.target.value?.trim();
+ });
+ setDeploy(newData);
+ }}
+ />
+
{error?.pfxPassword}
+
+ ) : (
+ <>>
+ )}
+
+ {data.config?.format === "jks" ? (
+ <>
+
+
+
{
+ const newData = produce(data, (draft) => {
+ draft.config ??= {};
+ draft.config.jksAlias = e.target.value?.trim();
+ });
+ setDeploy(newData);
+ }}
+ />
+
{error?.jksAlias}
+
+
+
+
+
{
+ const newData = produce(data, (draft) => {
+ draft.config ??= {};
+ draft.config.jksKeypass = e.target.value?.trim();
+ });
+ setDeploy(newData);
+ }}
+ />
+
{error?.jksKeypass}
+
+
+
+
+
{
+ const newData = produce(data, (draft) => {
+ draft.config ??= {};
+ draft.config.jksStorepass = e.target.value?.trim();
+ });
+ setDeploy(newData);
+ }}
+ />
+
{error?.jksStorepass}
+
+ >
+ ) : (
+ <>>
+ )}
+
+
+
+
{
+ const newData = produce(data, (draft) => {
+ draft.config ??= {};
+ draft.config.shell = val;
+ });
+ setDeploy(newData);
+ }}
+ >
+
+
+
+
+
{error?.shell}
+
+
+
+
+
+
{error?.preCommand}
+
+
+
+
+
+
+
+ {t("domain.deployment.form.shell_preset_scripts.trigger")}
+
+
+ handleUsePresetScript("reload_nginx")}>
+ {t("domain.deployment.form.shell_preset_scripts.option.reload_nginx.label")}
+
+ handleUsePresetScript("binding_iis")}>
+ {t("domain.deployment.form.shell_preset_scripts.option.binding_iis.label")}
+
+
+
+
+
+
{error?.command}
+
+
+ >
+ );
+};
+
+export default DeployToLocal;
diff --git a/ui/src/components/certimate/DeployToSSH.tsx b/ui/src/components/certimate/DeployToSSH.tsx
index 80eb15fb..b59eeb72 100644
--- a/ui/src/components/certimate/DeployToSSH.tsx
+++ b/ui/src/components/certimate/DeployToSSH.tsx
@@ -1,29 +1,31 @@
import { useEffect } from "react";
import { useTranslation } from "react-i18next";
+import { z } from "zod";
import { produce } from "immer";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
+import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Textarea } from "@/components/ui/textarea";
import { useDeployEditContext } from "./DeployEdit";
const DeployToSSH = () => {
const { t } = useTranslation();
- const { setError } = useDeployEditContext();
- useEffect(() => {
- setError({});
- }, []);
-
- const { deploy: data, setDeploy } = useDeployEditContext();
+ const { deploy: data, setDeploy, error, setError } = useDeployEditContext();
useEffect(() => {
if (!data.id) {
setDeploy({
...data,
config: {
+ format: "pem",
certPath: "/etc/nginx/ssl/nginx.crt",
keyPath: "/etc/nginx/ssl/nginx.key",
+ pfxPassword: "",
+ jksAlias: "",
+ jksKeypass: "",
+ jksStorepass: "",
preCommand: "",
command: "sudo service nginx reload",
},
@@ -31,79 +33,287 @@ const DeployToSSH = () => {
}
}, []);
+ useEffect(() => {
+ setError({});
+ }, []);
+
+ const formSchema = z
+ .object({
+ format: z.union([z.literal("pem"), z.literal("pfx"), z.literal("jks")], {
+ message: t("domain.deployment.form.file_format.placeholder"),
+ }),
+ certPath: z
+ .string()
+ .min(1, t("domain.deployment.form.file_cert_path.placeholder"))
+ .max(255, t("common.errmsg.string_max", { max: 255 })),
+ keyPath: z
+ .string()
+ .min(0, t("domain.deployment.form.file_key_path.placeholder"))
+ .max(255, t("common.errmsg.string_max", { max: 255 })),
+ pfxPassword: z.string().optional(),
+ jksAlias: z.string().optional(),
+ jksKeypass: z.string().optional(),
+ jksStorepass: z.string().optional(),
+ preCommand: z.string().optional(),
+ command: z.string().optional(),
+ })
+ .refine((data) => (data.format === "pem" ? !!data.keyPath?.trim() : true), {
+ message: t("domain.deployment.form.file_key_path.placeholder"),
+ path: ["keyPath"],
+ })
+ .refine((data) => (data.format === "pfx" ? !!data.pfxPassword?.trim() : true), {
+ message: t("domain.deployment.form.file_pfx_password.placeholder"),
+ path: ["pfxPassword"],
+ })
+ .refine((data) => (data.format === "jks" ? !!data.jksAlias?.trim() : true), {
+ message: t("domain.deployment.form.file_jks_alias.placeholder"),
+ path: ["jksAlias"],
+ })
+ .refine((data) => (data.format === "jks" ? !!data.jksKeypass?.trim() : true), {
+ message: t("domain.deployment.form.file_jks_keypass.placeholder"),
+ path: ["jksKeypass"],
+ })
+ .refine((data) => (data.format === "jks" ? !!data.jksStorepass?.trim() : true), {
+ message: t("domain.deployment.form.file_jks_storepass.placeholder"),
+ path: ["jksStorepass"],
+ });
+
+ useEffect(() => {
+ const res = formSchema.safeParse(data.config);
+ if (!res.success) {
+ setError({
+ ...error,
+ format: res.error.errors.find((e) => e.path[0] === "format")?.message,
+ certPath: res.error.errors.find((e) => e.path[0] === "certPath")?.message,
+ keyPath: res.error.errors.find((e) => e.path[0] === "keyPath")?.message,
+ pfxPassword: res.error.errors.find((e) => e.path[0] === "pfxPassword")?.message,
+ jksAlias: res.error.errors.find((e) => e.path[0] === "jksAlias")?.message,
+ jksKeypass: res.error.errors.find((e) => e.path[0] === "jksKeypass")?.message,
+ jksStorepass: res.error.errors.find((e) => e.path[0] === "jksStorepass")?.message,
+ preCommand: res.error.errors.find((e) => e.path[0] === "preCommand")?.message,
+ command: res.error.errors.find((e) => e.path[0] === "command")?.message,
+ });
+ } else {
+ setError({
+ ...error,
+ format: undefined,
+ certPath: undefined,
+ keyPath: undefined,
+ pfxPassword: undefined,
+ jksAlias: undefined,
+ jksKeypass: undefined,
+ jksStorepass: undefined,
+ preCommand: undefined,
+ command: undefined,
+ });
+ }
+ }, [data]);
+
+ useEffect(() => {
+ if (data.config?.format === "pem") {
+ if (/(.pfx|.jks)$/.test(data.config.certPath)) {
+ const newData = produce(data, (draft) => {
+ draft.config ??= {};
+ draft.config.certPath = data.config!.certPath.replace(/(.pfx|.jks)$/, ".crt");
+ });
+ setDeploy(newData);
+ }
+ } else if (data.config?.format === "pfx") {
+ if (/(.crt|.jks)$/.test(data.config.certPath)) {
+ const newData = produce(data, (draft) => {
+ draft.config ??= {};
+ draft.config.certPath = data.config!.certPath.replace(/(.crt|.jks)$/, ".pfx");
+ });
+ setDeploy(newData);
+ }
+ } else if (data.config?.format === "jks") {
+ if (/(.crt|.pfx)$/.test(data.config.certPath)) {
+ const newData = produce(data, (draft) => {
+ draft.config ??= {};
+ draft.config.certPath = data.config!.certPath.replace(/(.crt|.pfx)$/, ".jks");
+ });
+ setDeploy(newData);
+ }
+ }
+ }, [data.config?.format]);
+
return (
<>
-
+
+
+
{error?.format}
+
+
+
+
{
const newData = produce(data, (draft) => {
- if (!draft.config) {
- draft.config = {};
- }
- draft.config.certPath = e.target.value;
+ draft.config ??= {};
+ draft.config.certPath = e.target.value?.trim();
});
setDeploy(newData);
}}
/>
+
{error?.certPath}
-
-
- {
- const newData = produce(data, (draft) => {
- if (!draft.config) {
- draft.config = {};
- }
- draft.config.keyPath = e.target.value;
- });
- setDeploy(newData);
- }}
- />
-
+ {data.config?.format === "pem" ? (
+
+
+
{
+ const newData = produce(data, (draft) => {
+ draft.config ??= {};
+ draft.config.keyPath = e.target.value?.trim();
+ });
+ setDeploy(newData);
+ }}
+ />
+
{error?.keyPath}
+
+ ) : (
+ <>>
+ )}
+
+ {data.config?.format === "pfx" ? (
+
+
+
{
+ const newData = produce(data, (draft) => {
+ draft.config ??= {};
+ draft.config.pfxPassword = e.target.value?.trim();
+ });
+ setDeploy(newData);
+ }}
+ />
+
{error?.pfxPassword}
+
+ ) : (
+ <>>
+ )}
+
+ {data.config?.format === "jks" ? (
+ <>
+
+
+
{
+ const newData = produce(data, (draft) => {
+ draft.config ??= {};
+ draft.config.jksAlias = e.target.value?.trim();
+ });
+ setDeploy(newData);
+ }}
+ />
+
{error?.jksAlias}
+
+
+
+
+
{
+ const newData = produce(data, (draft) => {
+ draft.config ??= {};
+ draft.config.jksKeypass = e.target.value?.trim();
+ });
+ setDeploy(newData);
+ }}
+ />
+
{error?.jksKeypass}
+
+
+
+
+
{
+ const newData = produce(data, (draft) => {
+ draft.config ??= {};
+ draft.config.jksStorepass = e.target.value?.trim();
+ });
+ setDeploy(newData);
+ }}
+ />
+
{error?.jksStorepass}
+
+ >
+ ) : (
+ <>>
+ )}
-
+
+
{error?.preCommand}
-
+
+
{error?.command}
>
diff --git a/ui/src/i18n/locales/en/nls.domain.json b/ui/src/i18n/locales/en/nls.domain.json
index c3a5a4b1..06e78c6d 100644
--- a/ui/src/i18n/locales/en/nls.domain.json
+++ b/ui/src/i18n/locales/en/nls.domain.json
@@ -120,14 +120,32 @@
"domain.deployment.form.huaweicloud_elb_loadbalancer_id.placeholder": "Please enter ELB loadbalancer ID",
"domain.deployment.form.huaweicloud_elb_listener_id.label": "Listener ID",
"domain.deployment.form.huaweicloud_elb_listener_id.placeholder": "Please enter ELB listener ID",
- "domain.deployment.form.ssh_key_path.label": "Private Key Save Path",
- "domain.deployment.form.ssh_key_path.placeholder": "Please enter private key save path",
- "domain.deployment.form.ssh_cert_path.label": "Certificate Save Path",
- "domain.deployment.form.ssh_cert_path.placeholder": "Please enter certificate save path",
- "domain.deployment.form.ssh_pre_command.label": "Pre-deployment Command",
- "domain.deployment.form.ssh_pre_command.placeholder": "Command to be executed before deploying the certificate",
- "domain.deployment.form.ssh_command.label": "Command",
- "domain.deployment.form.ssh_command.placeholder": "Please enter command",
+ "domain.deployment.form.file_format.label": "Certificate Format",
+ "domain.deployment.form.file_format.placeholder": "Please select certificate format",
+ "domain.deployment.form.file_cert_path.label": "Certificate Save Path",
+ "domain.deployment.form.file_cert_path.placeholder": "Please enter certificate save path",
+ "domain.deployment.form.file_key_path.label": "Private Key Save Path",
+ "domain.deployment.form.file_key_path.placeholder": "Please enter private key save path",
+ "domain.deployment.form.file_pfx_password.label": "PFX Output Password",
+ "domain.deployment.form.file_pfx_password.placeholder": "Please enter PFX output password",
+ "domain.deployment.form.file_jks_alias.label": "JKS Alias (KeyStore Alias)",
+ "domain.deployment.form.file_jks_alias.placeholder": "Please enter JKS alias",
+ "domain.deployment.form.file_jks_keypass.label": "JKS Key Password (KeyStore Keypass)",
+ "domain.deployment.form.file_jks_keypass.placeholder": "Please enter JKS key password",
+ "domain.deployment.form.file_jks_storepass.label": "JKS Store Password (KeyStore Storepass)",
+ "domain.deployment.form.file_jks_storepass.placeholder": "Please enter JKS store password",
+ "domain.deployment.form.shell.label": "Shell",
+ "domain.deployment.form.shell.placeholder": "Please select shell environment",
+ "domain.deployment.form.shell.option.sh.label": "POSIX Bash (Linux)",
+ "domain.deployment.form.shell.option.cmd.label": "CMD (Windows)",
+ "domain.deployment.form.shell.option.powershell.label": "PowerShell (Windows)",
+ "domain.deployment.form.shell_pre_command.label": "Pre-deployment Command",
+ "domain.deployment.form.shell_pre_command.placeholder": "Command to be executed before deploying the certificate",
+ "domain.deployment.form.shell_command.label": "Command",
+ "domain.deployment.form.shell_command.placeholder": "Please enter command",
+ "domain.deployment.form.shell_preset_scripts.trigger": "Use Preset Scripts",
+ "domain.deployment.form.shell_preset_scripts.option.reload_nginx.label": "Bash - Reload Nginx",
+ "domain.deployment.form.shell_preset_scripts.option.binding_iis.label": "PowerShell - Binding IIS",
"domain.deployment.form.k8s_namespace.label": "Namespace",
"domain.deployment.form.k8s_namespace.placeholder": "Please enter namespace",
"domain.deployment.form.k8s_secret_name.label": "Secret Name",
diff --git a/ui/src/i18n/locales/zh/nls.domain.json b/ui/src/i18n/locales/zh/nls.domain.json
index f6433bf4..7ccf94ba 100644
--- a/ui/src/i18n/locales/zh/nls.domain.json
+++ b/ui/src/i18n/locales/zh/nls.domain.json
@@ -104,8 +104,8 @@
"domain.deployment.form.tencent_clb_domain.label": "部署到域名(支持泛域名)",
"domain.deployment.form.tencent_clb_domain.placeholder": "请输入部署到的域名, 如未开启 SNI, 可置空忽略此项",
"domain.deployment.form.tencent_teo_zone_id.label": "Zone ID",
- "domain.deployment.form.tencent_teo_zone_id.placeholder": "请输入zoneid, 形如: zone-xxxxxxxxx",
- "domain.deployment.form.tencent_teo_domain.label": "部署到域名(支持泛域名, 应与服务器上配置的域名完全一致, 每行一个域名)",
+ "domain.deployment.form.tencent_teo_zone_id.placeholder": "请输入 Zone ID",
+ "domain.deployment.form.tencent_teo_domain.label": "部署到域名(支持泛域名, 应与服务器上配置的域名完全一致, 每行一个域名)",
"domain.deployment.form.tencent_teo_domain.placeholder": "请输入部署到的域名",
"domain.deployment.form.huaweicloud_elb_region.label": "地域",
"domain.deployment.form.huaweicloud_elb_region.placeholder": "请输入地域(如 cn-north-1)",
@@ -120,14 +120,29 @@
"domain.deployment.form.huaweicloud_elb_loadbalancer_id.placeholder": "请输入负载均衡器 ID",
"domain.deployment.form.huaweicloud_elb_listener_id.label": "监听器 ID",
"domain.deployment.form.huaweicloud_elb_listener_id.placeholder": "请输入监听器 ID",
- "domain.deployment.form.ssh_key_path.label": "私钥保存路径",
- "domain.deployment.form.ssh_key_path.placeholder": "请输入私钥保存路径",
- "domain.deployment.form.ssh_cert_path.label": "证书保存路径",
- "domain.deployment.form.ssh_cert_path.placeholder": "请输入证书保存路径",
- "domain.deployment.form.ssh_pre_command.label": "前置命令",
- "domain.deployment.form.ssh_pre_command.placeholder": "在部署证书前执行的命令",
- "domain.deployment.form.ssh_command.label": "命令",
- "domain.deployment.form.ssh_command.placeholder": "请输入要执行的命令",
+ "domain.deployment.form.file_format.label": "证书格式",
+ "domain.deployment.form.file_format.placeholder": "请选择证书格式",
+ "domain.deployment.form.file_cert_path.label": "证书保存路径",
+ "domain.deployment.form.file_cert_path.placeholder": "请输入证书保存路径",
+ "domain.deployment.form.file_key_path.label": "私钥保存路径",
+ "domain.deployment.form.file_key_path.placeholder": "请输入私钥保存路径",
+ "domain.deployment.form.file_pfx_password.label": "PFX 导出密码",
+ "domain.deployment.form.file_pfx_password.placeholder": "请输入 PFX 导出密码",
+ "domain.deployment.form.file_jks_alias.label": "JKS 别名(KeyStore Alias)",
+ "domain.deployment.form.file_jks_alias.placeholder": "请输入 JKS 别名",
+ "domain.deployment.form.file_jks_keypass.label": "JKS 私钥访问口令(KeyStore Keypass)",
+ "domain.deployment.form.file_jks_keypass.placeholder": "请输入 JKS 私钥访问口令",
+ "domain.deployment.form.file_jks_storepass.label": "JKS 密钥库存储口令(KeyStore Storepass)",
+ "domain.deployment.form.file_jks_storepass.placeholder": "请输入 JKS 密钥库存储口令",
+ "domain.deployment.form.shell.label": "Shell",
+ "domain.deployment.form.shell.placeholder": "请选择命令执行环境",
+ "domain.deployment.form.shell_pre_command.label": "前置命令",
+ "domain.deployment.form.shell_pre_command.placeholder": "在部署证书前执行的命令",
+ "domain.deployment.form.shell_command.label": "命令",
+ "domain.deployment.form.shell_command.placeholder": "请输入要执行的命令",
+ "domain.deployment.form.shell_preset_scripts.trigger": "使用预设脚本",
+ "domain.deployment.form.shell_preset_scripts.option.reload_nginx.label": "Bash - 重启 nginx",
+ "domain.deployment.form.shell_preset_scripts.option.binding_iis.label": "PowerShell - 导入并绑定到 IIS(需管理员权限)",
"domain.deployment.form.k8s_namespace.label": "命名空间",
"domain.deployment.form.k8s_namespace.placeholder": "请输入 K8S 命名空间",
"domain.deployment.form.k8s_secret_name.label": "Secret 名称",
diff --git a/ui/src/pages/dashboard/Dashboard.tsx b/ui/src/pages/dashboard/Dashboard.tsx
index a792e756..ff655b98 100644
--- a/ui/src/pages/dashboard/Dashboard.tsx
+++ b/ui/src/pages/dashboard/Dashboard.tsx
@@ -209,7 +209,7 @@ const Dashboard = () => {
{t("history.log")}
-