mirror of
https://github.com/usual2970/certimate.git
synced 2025-10-05 05:54:53 +00:00
feat: support specified format on deployment to local/ssh
This commit is contained in:
@@ -102,7 +102,7 @@ const DeployToHuaweiCloudCDN = () => {
|
||||
onValueChange={(value) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.resourceType = value?.trim();
|
||||
draft.config.resourceType = value;
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
|
@@ -6,6 +6,7 @@ import { produce } from "immer";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { useDeployEditContext } from "./DeployEdit";
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -20,8 +21,10 @@ const DeployToLocal = () => {
|
||||
setDeploy({
|
||||
...data,
|
||||
config: {
|
||||
format: "pem",
|
||||
certPath: "/etc/nginx/ssl/nginx.crt",
|
||||
keyPath: "/etc/nginx/ssl/nginx.key",
|
||||
pfxPassword: "",
|
||||
shell: "sh",
|
||||
preCommand: "",
|
||||
command: "sudo service nginx reload",
|
||||
@@ -34,15 +37,34 @@ const DeployToLocal = () => {
|
||||
setError({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z.object({
|
||||
certPath: z.string().min(1, t("domain.deployment.form.file_cert_path.placeholder")),
|
||||
keyPath: z.string().min(1, t("domain.deployment.form.file_key_path.placeholder")),
|
||||
shell: z.union([z.literal("sh"), z.literal("cmd"), z.literal("powershell")], {
|
||||
message: t("domain.deployment.form.shell.placeholder"),
|
||||
}),
|
||||
preCommand: z.string().optional(),
|
||||
command: z.string().optional(),
|
||||
});
|
||||
const formSchema = z
|
||||
.object({
|
||||
format: z.union([z.literal("pem"), z.literal("pfx")], {
|
||||
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(),
|
||||
shell: z.union([z.literal("sh"), z.literal("cmd"), z.literal("powershell")], {
|
||||
message: t("domain.deployment.form.shell.placeholder"),
|
||||
}),
|
||||
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"],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(data.config);
|
||||
@@ -67,6 +89,26 @@ const DeployToLocal = () => {
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data.config?.format === "pem") {
|
||||
if (data.config.certPath && data.config.certPath.endsWith(".pfx")) {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.certPath = data.config!.certPath.replace(/.pfx$/, ".crt");
|
||||
});
|
||||
setDeploy(newData);
|
||||
}
|
||||
} else if (data.config?.format === "pfx") {
|
||||
if (data.config.certPath && data.config.certPath.endsWith(".crt")) {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.certPath = data.config!.certPath.replace(/.crt$/, ".pfx");
|
||||
});
|
||||
setDeploy(newData);
|
||||
}
|
||||
}
|
||||
}, [data.config?.format]);
|
||||
|
||||
const getOptionCls = (val: string) => {
|
||||
if (data.config?.shell === val) {
|
||||
return "border-primary dark:border-primary";
|
||||
@@ -78,6 +120,31 @@ const DeployToLocal = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_format.label")}</Label>
|
||||
<Select
|
||||
value={data?.config?.format}
|
||||
onValueChange={(value) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.format = value;
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("domain.deployment.form.file_format.placeholder")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="pem">PEM</SelectItem>
|
||||
<SelectItem value="pfx">PFX</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.format}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_cert_path.label")}</Label>
|
||||
<Input
|
||||
@@ -95,22 +162,47 @@ const DeployToLocal = () => {
|
||||
<div className="text-red-600 text-sm mt-1">{error?.certPath}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_key_path.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_key_path.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.keyPath}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.keyPath = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.keyPath}</div>
|
||||
</div>
|
||||
{data.config?.format === "pem" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_key_path.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_key_path.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.keyPath}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.keyPath = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.keyPath}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{data.config?.format === "pfx" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_pfx_password.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_pfx_password.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.pfxPassword}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.pfxPassword = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.pfxPassword}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.shell.label")}</Label>
|
||||
|
@@ -5,6 +5,7 @@ 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";
|
||||
|
||||
@@ -18,8 +19,10 @@ const DeployToSSH = () => {
|
||||
setDeploy({
|
||||
...data,
|
||||
config: {
|
||||
format: "pem",
|
||||
certPath: "/etc/nginx/ssl/nginx.crt",
|
||||
keyPath: "/etc/nginx/ssl/nginx.key",
|
||||
pfxPassword: "",
|
||||
preCommand: "",
|
||||
command: "sudo service nginx reload",
|
||||
},
|
||||
@@ -31,12 +34,31 @@ const DeployToSSH = () => {
|
||||
setError({});
|
||||
}, []);
|
||||
|
||||
const formSchema = z.object({
|
||||
certPath: z.string().min(1, t("domain.deployment.form.file_cert_path.placeholder")),
|
||||
keyPath: z.string().min(1, t("domain.deployment.form.file_key_path.placeholder")),
|
||||
preCommand: z.string().optional(),
|
||||
command: z.string().optional(),
|
||||
});
|
||||
const formSchema = z
|
||||
.object({
|
||||
format: z.union([z.literal("pem"), z.literal("pfx")], {
|
||||
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(),
|
||||
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"],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const res = formSchema.safeParse(data.config);
|
||||
@@ -59,9 +81,54 @@ const DeployToSSH = () => {
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data.config?.format === "pem") {
|
||||
if (data.config.certPath && data.config.certPath.endsWith(".pfx")) {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.certPath = data.config!.certPath.replace(/.pfx$/, ".crt");
|
||||
});
|
||||
setDeploy(newData);
|
||||
}
|
||||
} else if (data.config?.format === "pfx") {
|
||||
if (data.config.certPath && data.config.certPath.endsWith(".crt")) {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.certPath = data.config!.certPath.replace(/.crt$/, ".pfx");
|
||||
});
|
||||
setDeploy(newData);
|
||||
}
|
||||
}
|
||||
}, [data.config?.format]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col space-y-8">
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_format.label")}</Label>
|
||||
<Select
|
||||
value={data?.config?.format}
|
||||
onValueChange={(value) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.format = value;
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("domain.deployment.form.file_format.placeholder")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="pem">PEM</SelectItem>
|
||||
<SelectItem value="pfx">PFX</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.format}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_cert_path.label")}</Label>
|
||||
<Input
|
||||
@@ -79,22 +146,47 @@ const DeployToSSH = () => {
|
||||
<div className="text-red-600 text-sm mt-1">{error?.certPath}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_key_path.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_key_path.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.keyPath}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.keyPath = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.keyPath}</div>
|
||||
</div>
|
||||
{data.config?.format === "pem" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_key_path.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_key_path.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.keyPath}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.keyPath = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.keyPath}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{data.config?.format === "pfx" ? (
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_pfx_password.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_pfx_password.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.pfxPassword}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.pfxPassword = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.pfxPassword}</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.shell_pre_command.label")}</Label>
|
||||
|
@@ -86,10 +86,14 @@
|
||||
"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.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.shell.label": "Shell",
|
||||
"domain.deployment.form.shell.placeholder": "Please select shell environment",
|
||||
"domain.deployment.form.shell.option.sh.label": "POSIX Bash (Linux)",
|
||||
|
@@ -86,10 +86,14 @@
|
||||
"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.file_key_path.label": "私钥保存路径",
|
||||
"domain.deployment.form.file_key_path.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.shell.label": "Shell",
|
||||
"domain.deployment.form.shell.placeholder": "请选择命令执行环境",
|
||||
"domain.deployment.form.shell_pre_command.label": "前置命令",
|
||||
|
@@ -209,7 +209,7 @@ const Dashboard = () => {
|
||||
{t("history.log")}
|
||||
</SheetTitle>
|
||||
</SheetHeader>
|
||||
<div className="bg-gray-950 text-stone-100 p-5 text-sm h-[80dvh]">
|
||||
<div className="bg-gray-950 text-stone-100 p-5 text-sm h-[80dvh] overflow-y-auto">
|
||||
{deployment.log.check && (
|
||||
<>
|
||||
{deployment.log.check.map((item: Log) => {
|
||||
|
@@ -104,7 +104,7 @@ const History = () => {
|
||||
{t("history.log")}
|
||||
</SheetTitle>
|
||||
</SheetHeader>
|
||||
<div className="bg-gray-950 text-stone-100 p-5 text-sm h-[80dvh]">
|
||||
<div className="bg-gray-950 text-stone-100 p-5 text-sm h-[80dvh] overflow-y-auto">
|
||||
{deployment.log.check && (
|
||||
<>
|
||||
{deployment.log.check.map((item: Log) => {
|
||||
|
Reference in New Issue
Block a user