mirror of
https://github.com/woodchen-ink/certimate.git
synced 2025-07-18 17:31:55 +08:00
feat: support jks format
This commit is contained in:
parent
009e8fb976
commit
b47a1a13cb
1
go.mod
1
go.mod
@ -19,6 +19,7 @@ require (
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.114
|
||||
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61
|
||||
github.com/nikoksr/notify v1.0.0
|
||||
github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0
|
||||
github.com/pkg/sftp v1.13.6
|
||||
github.com/pocketbase/dbx v1.10.1
|
||||
github.com/pocketbase/pocketbase v0.22.18
|
||||
|
2
go.sum
2
go.sum
@ -398,6 +398,8 @@ github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
|
||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
|
||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
|
||||
github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0 h1:2nosf3P75OZv2/ZO/9Px5ZgZ5gbKrzA3joN1QMfOGMQ=
|
||||
github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0/go.mod h1:lAVhWwbNaveeJmxrxuSTxMgKpF6DjnuVpn6T8WiBwYQ=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
|
@ -1,11 +1,15 @@
|
||||
package deployer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pavlo-v-chernykh/keystore-go/v4"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
"software.sslmate.com/src/go-pkcs12"
|
||||
|
||||
@ -187,7 +191,7 @@ func getDeployVariables(conf domain.DeployConfig) map[string]string {
|
||||
return rs
|
||||
}
|
||||
|
||||
func convertPemToPfx(certificate string, privateKey string, password string) ([]byte, error) {
|
||||
func convertPEMToPFX(certificate string, privateKey string, password string) ([]byte, error) {
|
||||
cert, err := x509.ParseCertificateFromPEM(certificate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -205,3 +209,38 @@ func convertPemToPfx(certificate string, privateKey string, password string) ([]
|
||||
|
||||
return pfxData, nil
|
||||
}
|
||||
|
||||
func convertPEMToJKS(certificate string, privateKey string, alias string, keypass string, storepass string) ([]byte, error) {
|
||||
certBlock, _ := pem.Decode([]byte(certificate))
|
||||
if certBlock == nil {
|
||||
return nil, errors.New("failed to decode certificate PEM")
|
||||
}
|
||||
|
||||
privkeyBlock, _ := pem.Decode([]byte(privateKey))
|
||||
if privkeyBlock == nil {
|
||||
return nil, errors.New("failed to decode private key PEM")
|
||||
}
|
||||
|
||||
ks := keystore.New()
|
||||
entry := keystore.PrivateKeyEntry{
|
||||
CreationTime: time.Now(),
|
||||
PrivateKey: privkeyBlock.Bytes,
|
||||
CertificateChain: []keystore.Certificate{
|
||||
{
|
||||
Type: "X509",
|
||||
Content: certBlock.Bytes,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := ks.SetPrivateKeyEntry(alias, entry, []byte(keypass)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := ks.Store(&buf, []byte(storepass)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
@ -17,6 +17,18 @@ type LocalDeployer struct {
|
||||
infos []string
|
||||
}
|
||||
|
||||
const (
|
||||
certFormatPEM = "pem"
|
||||
certFormatPFX = "pfx"
|
||||
certFormatJKS = "jks"
|
||||
)
|
||||
|
||||
const (
|
||||
shellEnvSh = "sh"
|
||||
shellEnvCmd = "cmd"
|
||||
shellEnvPowershell = "powershell"
|
||||
)
|
||||
|
||||
func NewLocalDeployer(option *DeployerOption) (Deployer, error) {
|
||||
return &LocalDeployer{
|
||||
option: option,
|
||||
@ -50,8 +62,8 @@ func (d *LocalDeployer) Deploy(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// 写入证书和私钥文件
|
||||
switch d.option.DeployConfig.GetConfigOrDefaultAsString("format", "pem") {
|
||||
case "pem":
|
||||
switch d.option.DeployConfig.GetConfigOrDefaultAsString("format", certFormatPEM) {
|
||||
case certFormatPEM:
|
||||
if err := fs.WriteFileString(d.option.DeployConfig.GetConfigAsString("certPath"), d.option.Certificate.Certificate); err != nil {
|
||||
return fmt.Errorf("failed to save certificate file: %w", err)
|
||||
}
|
||||
@ -64,8 +76,12 @@ func (d *LocalDeployer) Deploy(ctx context.Context) error {
|
||||
|
||||
d.infos = append(d.infos, toStr("保存私钥成功", nil))
|
||||
|
||||
case "pfx":
|
||||
pfxData, err := convertPemToPfx(d.option.Certificate.Certificate, d.option.Certificate.PrivateKey, d.option.DeployConfig.GetConfigAsString("pfxPassword"))
|
||||
case certFormatPFX:
|
||||
pfxData, err := convertPEMToPFX(
|
||||
d.option.Certificate.Certificate,
|
||||
d.option.Certificate.PrivateKey,
|
||||
d.option.DeployConfig.GetConfigAsString("pfxPassword"),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert pem to pfx %w", err)
|
||||
}
|
||||
@ -74,6 +90,24 @@ func (d *LocalDeployer) Deploy(ctx context.Context) error {
|
||||
return fmt.Errorf("failed to save certificate file: %w", err)
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("保存证书成功", nil))
|
||||
|
||||
case certFormatJKS:
|
||||
jksData, err := convertPEMToJKS(
|
||||
d.option.Certificate.Certificate,
|
||||
d.option.Certificate.PrivateKey,
|
||||
d.option.DeployConfig.GetConfigAsString("jksAlias"),
|
||||
d.option.DeployConfig.GetConfigAsString("jksKeypass"),
|
||||
d.option.DeployConfig.GetConfigAsString("jksStorepass"),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert pem to pfx %w", err)
|
||||
}
|
||||
|
||||
if err := fs.WriteFile(d.option.DeployConfig.GetConfigAsString("certPath"), jksData); err != nil {
|
||||
return fmt.Errorf("failed to save certificate file: %w", err)
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("保存证书成功", nil))
|
||||
}
|
||||
|
||||
@ -95,13 +129,13 @@ func (d *LocalDeployer) execCommand(command string) (string, string, error) {
|
||||
var cmd *exec.Cmd
|
||||
|
||||
switch d.option.DeployConfig.GetConfigAsString("shell") {
|
||||
case "sh":
|
||||
case shellEnvSh:
|
||||
cmd = exec.Command("sh", "-c", command)
|
||||
|
||||
case "cmd":
|
||||
case shellEnvCmd:
|
||||
cmd = exec.Command("cmd", "/C", command)
|
||||
|
||||
case "powershell":
|
||||
case shellEnvPowershell:
|
||||
cmd = exec.Command("powershell", "-Command", command)
|
||||
|
||||
case "":
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
||||
"github.com/usual2970/certimate/internal/domain"
|
||||
"github.com/usual2970/certimate/internal/pkg/utils/fs"
|
||||
)
|
||||
|
||||
type SSHDeployer struct {
|
||||
@ -61,8 +62,8 @@ func (d *SSHDeployer) Deploy(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// 上传证书和私钥文件
|
||||
switch d.option.DeployConfig.GetConfigOrDefaultAsString("format", "pem") {
|
||||
case "pem":
|
||||
switch d.option.DeployConfig.GetConfigOrDefaultAsString("format", certFormatPEM) {
|
||||
case certFormatPEM:
|
||||
if err := d.writeSftpFileString(client, d.option.DeployConfig.GetConfigAsString("certPath"), d.option.Certificate.Certificate); err != nil {
|
||||
return fmt.Errorf("failed to upload certificate file: %w", err)
|
||||
}
|
||||
@ -75,8 +76,12 @@ func (d *SSHDeployer) Deploy(ctx context.Context) error {
|
||||
|
||||
d.infos = append(d.infos, toStr("SSH 上传私钥成功", nil))
|
||||
|
||||
case "pfx":
|
||||
pfxData, err := convertPemToPfx(d.option.Certificate.Certificate, d.option.Certificate.PrivateKey, d.option.DeployConfig.GetConfigAsString("pfxPassword"))
|
||||
case certFormatPFX:
|
||||
pfxData, err := convertPEMToPFX(
|
||||
d.option.Certificate.Certificate,
|
||||
d.option.Certificate.PrivateKey,
|
||||
d.option.DeployConfig.GetConfigAsString("pfxPassword"),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert pem to pfx %w", err)
|
||||
}
|
||||
@ -86,6 +91,24 @@ func (d *SSHDeployer) Deploy(ctx context.Context) error {
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("SSH 上传证书成功", nil))
|
||||
|
||||
case certFormatJKS:
|
||||
jksData, err := convertPEMToJKS(
|
||||
d.option.Certificate.Certificate,
|
||||
d.option.Certificate.PrivateKey,
|
||||
d.option.DeployConfig.GetConfigAsString("jksAlias"),
|
||||
d.option.DeployConfig.GetConfigAsString("jksKeypass"),
|
||||
d.option.DeployConfig.GetConfigAsString("jksStorepass"),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert pem to pfx %w", err)
|
||||
}
|
||||
|
||||
if err := fs.WriteFile(d.option.DeployConfig.GetConfigAsString("certPath"), jksData); err != nil {
|
||||
return fmt.Errorf("failed to save certificate file: %w", err)
|
||||
}
|
||||
|
||||
d.infos = append(d.infos, toStr("保存证书成功", nil))
|
||||
}
|
||||
|
||||
// 执行命令
|
||||
|
@ -26,6 +26,9 @@ const DeployToLocal = () => {
|
||||
certPath: "/etc/nginx/ssl/nginx.crt",
|
||||
keyPath: "/etc/nginx/ssl/nginx.key",
|
||||
pfxPassword: "",
|
||||
jksAlias: "",
|
||||
jksKeypass: "",
|
||||
jksStorepass: "",
|
||||
shell: "sh",
|
||||
preCommand: "",
|
||||
command: "sudo service nginx reload",
|
||||
@ -40,7 +43,7 @@ const DeployToLocal = () => {
|
||||
|
||||
const formSchema = z
|
||||
.object({
|
||||
format: z.union([z.literal("pem"), z.literal("pfx")], {
|
||||
format: z.union([z.literal("pem"), z.literal("pfx"), z.literal("jks")], {
|
||||
message: t("domain.deployment.form.file_format.placeholder"),
|
||||
}),
|
||||
certPath: z
|
||||
@ -52,6 +55,9 @@ const DeployToLocal = () => {
|
||||
.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(),
|
||||
shell: z.union([z.literal("sh"), z.literal("cmd"), z.literal("powershell")], {
|
||||
message: t("domain.deployment.form.shell.placeholder"),
|
||||
}),
|
||||
@ -65,6 +71,18 @@ const DeployToLocal = () => {
|
||||
.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(() => {
|
||||
@ -72,8 +90,13 @@ const DeployToLocal = () => {
|
||||
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,
|
||||
shell: res.error.errors.find((e) => e.path[0] === "shell")?.message,
|
||||
preCommand: res.error.errors.find((e) => e.path[0] === "preCommand")?.message,
|
||||
command: res.error.errors.find((e) => e.path[0] === "command")?.message,
|
||||
@ -81,8 +104,13 @@ const DeployToLocal = () => {
|
||||
} else {
|
||||
setError({
|
||||
...error,
|
||||
format: undefined,
|
||||
certPath: undefined,
|
||||
keyPath: undefined,
|
||||
pfxPassword: undefined,
|
||||
jksAlias: undefined,
|
||||
jksKeypass: undefined,
|
||||
jksStorepass: undefined,
|
||||
shell: undefined,
|
||||
preCommand: undefined,
|
||||
command: undefined,
|
||||
@ -92,18 +120,26 @@ const DeployToLocal = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (data.config?.format === "pem") {
|
||||
if (data.config.certPath && data.config.certPath.endsWith(".pfx")) {
|
||||
if (/(.pfx|.jks)$/.test(data.config.certPath)) {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.certPath = data.config!.certPath.replace(/.pfx$/, ".crt");
|
||||
draft.config.certPath = data.config!.certPath.replace(/(.pfx|.jks)$/, ".crt");
|
||||
});
|
||||
setDeploy(newData);
|
||||
}
|
||||
} else if (data.config?.format === "pfx") {
|
||||
if (data.config.certPath && data.config.certPath.endsWith(".crt")) {
|
||||
if (/(.crt|.jks)$/.test(data.config.certPath)) {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.certPath = data.config!.certPath.replace(/.crt$/, ".pfx");
|
||||
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);
|
||||
}
|
||||
@ -194,6 +230,7 @@ Remove-Item -Path "$pfxPath" -Force
|
||||
<SelectGroup>
|
||||
<SelectItem value="pem">PEM</SelectItem>
|
||||
<SelectItem value="pfx">PFX</SelectItem>
|
||||
<SelectItem value="jks">JKS</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
@ -259,6 +296,63 @@ Remove-Item -Path "$pfxPath" -Force
|
||||
<></>
|
||||
)}
|
||||
|
||||
{data.config?.format === "jks" ? (
|
||||
<>
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_jks_alias.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_jks_alias.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.jksAlias}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.jksAlias = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.jksAlias}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_jks_keypass.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_jks_keypass.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.jksKeypass}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.jksKeypass = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.jksKeypass}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_jks_storepass.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_jks_storepass.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.jksStorepass}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.jksStorepass = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.jksStorepass}</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.shell.label")}</Label>
|
||||
<RadioGroup
|
||||
|
@ -23,6 +23,9 @@ const DeployToSSH = () => {
|
||||
certPath: "/etc/nginx/ssl/nginx.crt",
|
||||
keyPath: "/etc/nginx/ssl/nginx.key",
|
||||
pfxPassword: "",
|
||||
jksAlias: "",
|
||||
jksKeypass: "",
|
||||
jksStorepass: "",
|
||||
preCommand: "",
|
||||
command: "sudo service nginx reload",
|
||||
},
|
||||
@ -36,7 +39,7 @@ const DeployToSSH = () => {
|
||||
|
||||
const formSchema = z
|
||||
.object({
|
||||
format: z.union([z.literal("pem"), z.literal("pfx")], {
|
||||
format: z.union([z.literal("pem"), z.literal("pfx"), z.literal("jks")], {
|
||||
message: t("domain.deployment.form.file_format.placeholder"),
|
||||
}),
|
||||
certPath: z
|
||||
@ -48,6 +51,9 @@ const DeployToSSH = () => {
|
||||
.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(),
|
||||
})
|
||||
@ -58,6 +64,18 @@ const DeployToSSH = () => {
|
||||
.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(() => {
|
||||
@ -65,16 +83,26 @@ const DeployToSSH = () => {
|
||||
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,
|
||||
});
|
||||
@ -83,18 +111,26 @@ const DeployToSSH = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (data.config?.format === "pem") {
|
||||
if (data.config.certPath && data.config.certPath.endsWith(".pfx")) {
|
||||
if (/(.pfx|.jks)$/.test(data.config.certPath)) {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.certPath = data.config!.certPath.replace(/.pfx$/, ".crt");
|
||||
draft.config.certPath = data.config!.certPath.replace(/(.pfx|.jks)$/, ".crt");
|
||||
});
|
||||
setDeploy(newData);
|
||||
}
|
||||
} else if (data.config?.format === "pfx") {
|
||||
if (data.config.certPath && data.config.certPath.endsWith(".crt")) {
|
||||
if (/(.crt|.jks)$/.test(data.config.certPath)) {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.certPath = data.config!.certPath.replace(/.crt$/, ".pfx");
|
||||
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);
|
||||
}
|
||||
@ -123,6 +159,7 @@ const DeployToSSH = () => {
|
||||
<SelectGroup>
|
||||
<SelectItem value="pem">PEM</SelectItem>
|
||||
<SelectItem value="pfx">PFX</SelectItem>
|
||||
<SelectItem value="jks">JKS</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
@ -188,6 +225,63 @@ const DeployToSSH = () => {
|
||||
<></>
|
||||
)}
|
||||
|
||||
{data.config?.format === "jks" ? (
|
||||
<>
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_jks_alias.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_jks_alias.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.jksAlias}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.jksAlias = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.jksAlias}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_jks_keypass.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_jks_keypass.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.jksKeypass}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.jksKeypass = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.jksKeypass}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.file_jks_storepass.label")}</Label>
|
||||
<Input
|
||||
placeholder={t("domain.deployment.form.file_jks_storepass.placeholder")}
|
||||
className="w-full mt-1"
|
||||
value={data?.config?.jksStorepass}
|
||||
onChange={(e) => {
|
||||
const newData = produce(data, (draft) => {
|
||||
draft.config ??= {};
|
||||
draft.config.jksStorepass = e.target.value?.trim();
|
||||
});
|
||||
setDeploy(newData);
|
||||
}}
|
||||
/>
|
||||
<div className="text-red-600 text-sm mt-1">{error?.jksStorepass}</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<Label>{t("domain.deployment.form.shell_pre_command.label")}</Label>
|
||||
<Textarea
|
||||
|
@ -128,6 +128,12 @@
|
||||
"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)",
|
||||
|
@ -128,6 +128,12 @@
|
||||
"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": "前置命令",
|
||||
|
Loading…
x
Reference in New Issue
Block a user