mirror of
https://github.com/certimate-go/certimate.git
synced 2026-06-22 21:05:48 +08:00
feat: support uploading certificates from local paths or urls
This commit is contained in:
parent
cfe3f6cc01
commit
32cd10d06c
@ -165,9 +165,9 @@ func (c WorkflowNodeConfig) AsBizApply() WorkflowNodeConfigForBizApply {
|
||||
|
||||
func (c WorkflowNodeConfig) AsBizUpload() WorkflowNodeConfigForBizUpload {
|
||||
return WorkflowNodeConfigForBizUpload{
|
||||
Source: xmaps.GetOrDefaultString(c, "source", "form"),
|
||||
Certificate: xmaps.GetString(c, "certificate"),
|
||||
PrivateKey: xmaps.GetString(c, "privateKey"),
|
||||
Domains: xmaps.GetString(c, "domains"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,9 +234,9 @@ type WorkflowNodeConfigForBizApply struct {
|
||||
}
|
||||
|
||||
type WorkflowNodeConfigForBizUpload struct {
|
||||
Certificate string `json:"certificate"` // 证书 PEM 内容
|
||||
PrivateKey string `json:"privateKey"` // 私钥 PEM 内容
|
||||
Domains string `json:"domains,omitempty"`
|
||||
Source string `json:"source"` // 证书来源(零值时默认值 "form")
|
||||
Certificate string `json:"certificate"` // 证书,根据证书来源决定是 PEM 内容 / 文件路径 / URL
|
||||
PrivateKey string `json:"privateKey"` // 私钥,根据证书来源决定是 PEM 内容 / 文件路径 / URL
|
||||
}
|
||||
|
||||
type WorkflowNodeConfigForBizMonitor struct {
|
||||
|
||||
@ -1,13 +1,22 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/certimate-go/certimate/internal/domain"
|
||||
"github.com/certimate-go/certimate/internal/repository"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
)
|
||||
|
||||
/**
|
||||
@ -26,6 +35,12 @@ type bizUploadNodeExecutor struct {
|
||||
wfoutputRepo workflowOutputRepository
|
||||
}
|
||||
|
||||
const (
|
||||
BizUploadSourceForm = "form"
|
||||
BizUploadSourceLocal = "local"
|
||||
BizUploadSourceURL = "url"
|
||||
)
|
||||
|
||||
func (ne *bizUploadNodeExecutor) Execute(execCtx *NodeExecutionContext) (*NodeExecutionResult, error) {
|
||||
execRes := newNodeExecutionResult(execCtx.Node)
|
||||
|
||||
@ -50,8 +65,102 @@ func (ne *bizUploadNodeExecutor) Execute(execCtx *NodeExecutionContext) (*NodeEx
|
||||
return execRes, nil
|
||||
} else if reason != "" {
|
||||
ne.logger.Info(fmt.Sprintf("re-upload, because %s", reason))
|
||||
} else {
|
||||
} else if lastCertificate != nil {
|
||||
ne.logger.Info("no found last uploaded certificate, begin to upload")
|
||||
} else {
|
||||
ne.logger.Info("try to upload")
|
||||
}
|
||||
|
||||
// 获取证书及私钥
|
||||
var certPEM, privkeyPEM string
|
||||
switch nodeCfg.Source {
|
||||
case BizUploadSourceForm:
|
||||
{
|
||||
certPEM = nodeCfg.Certificate
|
||||
privkeyPEM = nodeCfg.PrivateKey
|
||||
}
|
||||
|
||||
case BizUploadSourceLocal:
|
||||
{
|
||||
certData, err := os.ReadFile(nodeCfg.Certificate)
|
||||
if err != nil {
|
||||
return execRes, fmt.Errorf("failed to read certificate file from local path: %w", err)
|
||||
} else {
|
||||
certPEM = string(certData)
|
||||
}
|
||||
|
||||
privkeyData, err := os.ReadFile(nodeCfg.PrivateKey)
|
||||
if err != nil {
|
||||
return execRes, fmt.Errorf("failed to read private key file from local path: %w", err)
|
||||
} else {
|
||||
privkeyPEM = string(privkeyData)
|
||||
}
|
||||
}
|
||||
|
||||
case BizUploadSourceURL:
|
||||
{
|
||||
client := resty.New()
|
||||
client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
|
||||
|
||||
certResp, err := client.NewRequest().Get(nodeCfg.Certificate)
|
||||
if err != nil || certResp.IsError() {
|
||||
return execRes, fmt.Errorf("failed to download certificate from URL: %w", err)
|
||||
} else {
|
||||
certPEM = string(certResp.Body())
|
||||
}
|
||||
|
||||
privkeyResp, err := client.NewRequest().Get(nodeCfg.PrivateKey)
|
||||
if err != nil || privkeyResp.IsError() {
|
||||
return execRes, fmt.Errorf("failed to download private key from URL: %w", err)
|
||||
} else {
|
||||
privkeyPEM = string(privkeyResp.Body())
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return execRes, fmt.Errorf("unsupported upload source: '%s'", nodeCfg.Source)
|
||||
}
|
||||
|
||||
// 验证证书
|
||||
certX509, err := certcrypto.ParsePEMCertificate([]byte(certPEM))
|
||||
if err != nil {
|
||||
return execRes, err
|
||||
} else if time.Now().After(certX509.NotAfter) {
|
||||
ne.logger.Warn(fmt.Sprintf("the uploaded certificate has expired at %s", certX509.NotAfter.UTC().Format(time.RFC3339)))
|
||||
}
|
||||
|
||||
// 验证私钥
|
||||
privkey, err := certcrypto.ParsePEMPrivateKey([]byte(privkeyPEM))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
matched := false
|
||||
switch pub := certX509.PublicKey.(type) {
|
||||
case *rsa.PublicKey:
|
||||
p, ok := privkey.(*rsa.PrivateKey)
|
||||
matched = ok && pub.Equal(p.Public())
|
||||
case *ecdsa.PublicKey:
|
||||
p, ok := privkey.(*ecdsa.PrivateKey)
|
||||
matched = ok && pub.Equal(p.Public())
|
||||
case ed25519.PublicKey:
|
||||
p, ok := privkey.(ed25519.PrivateKey)
|
||||
matched = ok && pub.Equal(p.Public())
|
||||
default:
|
||||
matched = false
|
||||
}
|
||||
|
||||
if !matched {
|
||||
return nil, fmt.Errorf("the uploaded private key does not match the uploaded certificate")
|
||||
}
|
||||
}
|
||||
|
||||
// 二次检测是否可以跳过执行
|
||||
if lastCertificate != nil {
|
||||
lastCertX509, err := xcert.ParseCertificateFromPEM(lastCertificate.Certificate)
|
||||
if err == nil && xcert.EqualCertificates(certX509, lastCertX509) {
|
||||
ne.logger.Info("skip this uploading, because the last uploaded certificate already exists")
|
||||
return execRes, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 保存证书实体
|
||||
@ -61,7 +170,7 @@ func (ne *bizUploadNodeExecutor) Execute(execCtx *NodeExecutionContext) (*NodeEx
|
||||
WorkflowRunId: execCtx.RunId,
|
||||
WorkflowNodeId: execCtx.Node.Id,
|
||||
}
|
||||
certificate.PopulateFromPEM(nodeCfg.Certificate, nodeCfg.PrivateKey)
|
||||
certificate.PopulateFromPEM(certPEM, privkeyPEM)
|
||||
if certificate, err := ne.certificateRepo.Save(execCtx.ctx, certificate); err != nil {
|
||||
ne.logger.Warn("could not save certificate")
|
||||
return execRes, err
|
||||
@ -72,7 +181,7 @@ func (ne *bizUploadNodeExecutor) Execute(execCtx *NodeExecutionContext) (*NodeEx
|
||||
// 节点输出
|
||||
execRes.AddOutputWithPersistent(stateIOTypeRef, "certificate", fmt.Sprintf("%s#%s", domain.CollectionNameCertificate, certificate.Id), "string")
|
||||
execRes.AddVariableWithScope(execCtx.Node.Id, stateVarKeyNodeSkipped, false, "boolean")
|
||||
execRes.AddVariableWithScope(execCtx.Node.Id, stateVarKeyCertificateValidity, true, "boolean")
|
||||
execRes.AddVariableWithScope(execCtx.Node.Id, stateVarKeyCertificateValidity, time.Now().After(certificate.ValidityNotAfter), "boolean")
|
||||
execRes.AddVariableWithScope(execCtx.Node.Id, stateVarKeyCertificateDaysLeft, int32(time.Until(certificate.ValidityNotAfter).Hours()/24), "number")
|
||||
|
||||
ne.logger.Info("uploading completed")
|
||||
@ -104,11 +213,22 @@ func (ne *bizUploadNodeExecutor) checkCanSkip(execCtx *NodeExecutionContext, las
|
||||
// 比较和上次上传时的关键配置(即影响证书上传的)参数是否一致
|
||||
lastNodeCfg := lastOutput.NodeConfig.AsBizUpload()
|
||||
|
||||
if strings.TrimSpace(thisNodeCfg.Certificate) != strings.TrimSpace(lastNodeCfg.Certificate) {
|
||||
return false, "the configuration item 'Certificate' changed"
|
||||
if thisNodeCfg.Source != lastNodeCfg.Source {
|
||||
return false, "the configuration item 'Source' changed"
|
||||
}
|
||||
if strings.TrimSpace(thisNodeCfg.PrivateKey) != strings.TrimSpace(lastNodeCfg.PrivateKey) {
|
||||
return false, "the configuration item 'PrivateKey' changed"
|
||||
|
||||
switch thisNodeCfg.Source {
|
||||
case BizUploadSourceForm:
|
||||
if strings.TrimSpace(thisNodeCfg.Certificate) != strings.TrimSpace(lastNodeCfg.Certificate) {
|
||||
return false, "the configuration item 'Certificate' changed"
|
||||
}
|
||||
if strings.TrimSpace(thisNodeCfg.PrivateKey) != strings.TrimSpace(lastNodeCfg.PrivateKey) {
|
||||
return false, "the configuration item 'PrivateKey' changed"
|
||||
}
|
||||
|
||||
default:
|
||||
// 本地或远程文件来源,需实际下载后才能比较
|
||||
return false, ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1006,6 +1006,30 @@ func init() {
|
||||
},
|
||||
})
|
||||
|
||||
case "upload":
|
||||
if _, ok := current.Config["source"].(string); !ok {
|
||||
current.Config["source"] = "form"
|
||||
}
|
||||
|
||||
temp = append(temp, &dWorkflowNode{
|
||||
Id: current.Id,
|
||||
Type: "bizUpload",
|
||||
Data: dWorkflowNodeData{
|
||||
Name: current.Name,
|
||||
Config: current.Config,
|
||||
},
|
||||
})
|
||||
|
||||
case "monitor":
|
||||
temp = append(temp, &dWorkflowNode{
|
||||
Id: current.Id,
|
||||
Type: "bizMonitor",
|
||||
Data: dWorkflowNodeData{
|
||||
Name: current.Name,
|
||||
Config: current.Config,
|
||||
},
|
||||
})
|
||||
|
||||
case "deploy":
|
||||
if s, ok := current.Config["certificate"].(string); ok {
|
||||
current.Config["certificateOutputNodeId"] = strings.Split(s, "#")[0]
|
||||
@ -1021,10 +1045,14 @@ func init() {
|
||||
},
|
||||
})
|
||||
|
||||
case "upload", "monitor", "notify":
|
||||
case "notify":
|
||||
if _, ok := current.Config["channel"].(string); ok {
|
||||
delete(current.Config, "channel")
|
||||
}
|
||||
|
||||
temp = append(temp, &dWorkflowNode{
|
||||
Id: current.Id,
|
||||
Type: "biz" + strings.Title(current.Type),
|
||||
Type: "bizNotify",
|
||||
Data: dWorkflowNodeData{
|
||||
Name: current.Name,
|
||||
Config: current.Config,
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
import { useMemo } from "react";
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { type FlowNodeEntity, getNodeForm } from "@flowgram.ai/fixed-layout-editor";
|
||||
import { type AnchorProps, Form, type FormInstance, Input } from "antd";
|
||||
import { type AnchorProps, Form, type FormInstance, Input, Radio } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { validateCertificate, validatePrivateKey } from "@/api/certificates";
|
||||
import Show from "@/components/Show";
|
||||
import TextFileInput from "@/components/TextFileInput";
|
||||
import Tips from "@/components/Tips";
|
||||
import { type WorkflowNodeConfigForBizUpload, defaultNodeConfigForBizUpload } from "@/domain/workflow";
|
||||
import { useAntdForm } from "@/hooks";
|
||||
import { getErrMsg } from "@/utils/error";
|
||||
@ -19,6 +21,10 @@ export interface BizUploadNodeConfigFormProps {
|
||||
node: FlowNodeEntity;
|
||||
}
|
||||
|
||||
const UPLOAD_SOURCE_FORM = "form" as const;
|
||||
const UPLOAD_SOURCE_LOCAL = "local" as const;
|
||||
const UPLOAD_SOURCE_URL = "url" as const;
|
||||
|
||||
const BizUploadNodeConfigForm = ({ node, ...props }: BizUploadNodeConfigFormProps) => {
|
||||
if (node.flowNodeType !== NodeType.BizUpload) {
|
||||
console.warn(`[certimate] current workflow node type is not: ${NodeType.BizUpload}`);
|
||||
@ -38,7 +44,21 @@ const BizUploadNodeConfigForm = ({ node, ...props }: BizUploadNodeConfigFormProp
|
||||
initialValues: initialValues ?? getInitialValues(),
|
||||
});
|
||||
|
||||
const handleCertificateChange = async (value: string) => {
|
||||
const fieldSource = Form.useWatch("source", { form: formInst, preserve: true });
|
||||
|
||||
const handleSourceChange = (value: string) => {
|
||||
if (value === initialValues?.source) {
|
||||
formInst.resetFields(["certificate", "privateKey", "domains"]);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
formInst.setFieldValue("certificate", "");
|
||||
formInst.setFieldValue("privateKey", "");
|
||||
formInst.setFieldValue("domains", "");
|
||||
}, 0);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCertificatePEMChange = async (value: string) => {
|
||||
try {
|
||||
const resp = await validateCertificate(value);
|
||||
formInst.setFields([
|
||||
@ -66,7 +86,7 @@ const BizUploadNodeConfigForm = ({ node, ...props }: BizUploadNodeConfigFormProp
|
||||
}
|
||||
};
|
||||
|
||||
const handlePrivateKeyChange = async (value: string) => {
|
||||
const handlePrivateKeyPEMChange = async (value: string) => {
|
||||
try {
|
||||
await validatePrivateKey(value);
|
||||
formInst.setFields([
|
||||
@ -90,25 +110,63 @@ const BizUploadNodeConfigForm = ({ node, ...props }: BizUploadNodeConfigFormProp
|
||||
<NodeFormContextProvider value={{ node }}>
|
||||
<Form {...formProps} clearOnDestroy={true} form={formInst} layout="vertical" preserve={false} scrollToFirstError>
|
||||
<div id="parameters" data-anchor="parameters">
|
||||
<Form.Item name="domains" label={t("workflow_node.upload.form.domains.label")} rules={[formRule]}>
|
||||
<Input variant="filled" placeholder={t("workflow_node.upload.form.domains.placeholder")} readOnly />
|
||||
<Form.Item name="source" label={t("workflow_node.upload.form.source.label")} rules={[formRule]}>
|
||||
<Radio.Group block onChange={(e) => handleSourceChange(e.target.value)}>
|
||||
<Radio.Button value={UPLOAD_SOURCE_FORM}>{t("workflow_node.upload.form.source.option.form.label")}</Radio.Button>
|
||||
<Radio.Button value={UPLOAD_SOURCE_LOCAL}>{t("workflow_node.upload.form.source.option.local.label")}</Radio.Button>
|
||||
<Radio.Button value={UPLOAD_SOURCE_URL}>{t("workflow_node.upload.form.source.option.url.label")}</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="certificate" label={t("workflow_node.upload.form.certificate.label")} rules={[formRule]}>
|
||||
<TextFileInput
|
||||
autoSize={{ minRows: 3, maxRows: 10 }}
|
||||
placeholder={t("workflow_node.upload.form.certificate.placeholder")}
|
||||
onChange={handleCertificateChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Show when={fieldSource === UPLOAD_SOURCE_FORM}>
|
||||
<Form.Item name="domains" label={t("workflow_node.upload.form.domains.label")} rules={[formRule]}>
|
||||
<Input variant="filled" placeholder={t("workflow_node.upload.form.domains.placeholder")} readOnly />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="privateKey" label={t("workflow_node.upload.form.private_key.label")} rules={[formRule]}>
|
||||
<TextFileInput
|
||||
autoSize={{ minRows: 3, maxRows: 10 }}
|
||||
placeholder={t("workflow_node.upload.form.private_key.placeholder")}
|
||||
onChange={handlePrivateKeyChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="certificate" label={t("workflow_node.upload.form.certificate_pem.label")} rules={[formRule]}>
|
||||
<TextFileInput
|
||||
autoSize={{ minRows: 3, maxRows: 10 }}
|
||||
placeholder={t("workflow_node.upload.form.certificate_pem.placeholder")}
|
||||
onChange={handleCertificatePEMChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="privateKey" label={t("workflow_node.upload.form.private_key_pem.label")} rules={[formRule]}>
|
||||
<TextFileInput
|
||||
autoSize={{ minRows: 3, maxRows: 10 }}
|
||||
placeholder={t("workflow_node.upload.form.private_key_pem.placeholder")}
|
||||
onChange={handlePrivateKeyPEMChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Show>
|
||||
|
||||
<Show when={fieldSource === UPLOAD_SOURCE_LOCAL}>
|
||||
<Form.Item>
|
||||
<Tips message={t("workflow_node.upload.form.guide")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="certificate" label={t("workflow_node.upload.form.certificate_path.label")} rules={[formRule]}>
|
||||
<Input placeholder={t("workflow_node.upload.form.certificate_path.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="privateKey" label={t("workflow_node.upload.form.private_key_path.label")} rules={[formRule]}>
|
||||
<Input placeholder={t("workflow_node.upload.form.private_key_path.placeholder")} />
|
||||
</Form.Item>
|
||||
</Show>
|
||||
|
||||
<Show when={fieldSource === UPLOAD_SOURCE_URL}>
|
||||
<Form.Item>
|
||||
<Tips message={t("workflow_node.upload.form.guide")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="certificate" label={t("workflow_node.upload.form.certificate_url.label")} rules={[formRule]}>
|
||||
<Input placeholder={t("workflow_node.upload.form.certificate_url.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="privateKey" label={t("workflow_node.upload.form.private_key_url.label")} rules={[formRule]}>
|
||||
<Input placeholder={t("workflow_node.upload.form.private_key_url.placeholder")} />
|
||||
</Form.Item>
|
||||
</Show>
|
||||
</div>
|
||||
</Form>
|
||||
</NodeFormContextProvider>
|
||||
@ -127,6 +185,7 @@ const getAnchorItems = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
source: UPLOAD_SOURCE_FORM,
|
||||
certificate: "",
|
||||
privateKey: "",
|
||||
...defaultNodeConfigForBizUpload(),
|
||||
@ -136,17 +195,86 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
|
||||
const { t } = i18n;
|
||||
|
||||
return z.object({
|
||||
domains: z.string().nullish(),
|
||||
certificate: z
|
||||
.string()
|
||||
.min(1, t("workflow_node.upload.form.certificate.placeholder"))
|
||||
.max(20480, t("common.errmsg.string_max", { max: 20480 })),
|
||||
privateKey: z
|
||||
.string()
|
||||
.min(1, t("workflow_node.upload.form.private_key.placeholder"))
|
||||
.max(20480, t("common.errmsg.string_max", { max: 20480 })),
|
||||
});
|
||||
return z
|
||||
.object({
|
||||
source: z.string(t("workflow_node.upload.form.source.placeholder")).nonempty(t("workflow_node.upload.form.source.placeholder")),
|
||||
certificate: z.string().max(20480, t("common.errmsg.string_max", { max: 20480 })),
|
||||
privateKey: z.string().max(20480, t("common.errmsg.string_max", { max: 20480 })),
|
||||
domains: z.string().nullish(),
|
||||
})
|
||||
.superRefine((values, ctx) => {
|
||||
switch (values.source) {
|
||||
case UPLOAD_SOURCE_FORM:
|
||||
{
|
||||
if (!z.string().nonempty().safeParse(values.certificate).success) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: t("workflow_node.upload.form.certificate_pem.placeholder"),
|
||||
path: ["certificate"],
|
||||
});
|
||||
}
|
||||
|
||||
if (!z.string().nonempty().safeParse(values.privateKey).success) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: t("workflow_node.upload.form.private_key_pem.placeholder"),
|
||||
path: ["privateKey"],
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case UPLOAD_SOURCE_LOCAL:
|
||||
{
|
||||
if (!z.string().nonempty().safeParse(values.certificate).success) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: t("workflow_node.upload.form.certificate_path.placeholder"),
|
||||
path: ["certificate"],
|
||||
});
|
||||
}
|
||||
|
||||
if (!z.string().nonempty().safeParse(values.privateKey).success) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: t("workflow_node.upload.form.certificate_path.placeholder"),
|
||||
path: ["privateKey"],
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case UPLOAD_SOURCE_URL:
|
||||
{
|
||||
if (!z.url().safeParse(values.certificate).success) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: t("workflow_node.upload.form.certificate_url.placeholder"),
|
||||
path: ["certificate"],
|
||||
});
|
||||
}
|
||||
|
||||
if (!z.url().safeParse(values.privateKey).success) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: t("workflow_node.upload.form.private_key_url.placeholder"),
|
||||
path: ["privateKey"],
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: t("workflow_node.upload.form.source.placeholder"),
|
||||
path: ["source"],
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(BizUploadNodeConfigForm, {
|
||||
|
||||
@ -43,7 +43,23 @@ export const BizUploadNodeRegistry: NodeRegistry = {
|
||||
|
||||
return (
|
||||
<BaseNode
|
||||
description={<Field<string> name="config.domains">{({ field: { value } }) => <>{value || t("workflow.detail.design.editor.placeholder")}</>}</Field>}
|
||||
description={
|
||||
<Field<string> name="config.source">
|
||||
{({ field: { value: fieldSource } }) => (
|
||||
<>
|
||||
{fieldSource == null || fieldSource === "" || fieldSource === "form" ? (
|
||||
<Field<string> name="config.domains">
|
||||
{({ field: { value: fieldDomains } }) => <>{fieldDomains || t("workflow.detail.design.editor.placeholder")}</>}
|
||||
</Field>
|
||||
) : (
|
||||
<Field<string> name="config.certificate">
|
||||
{({ field: { value: fieldCertificate } }) => <>{fieldCertificate || t("workflow.detail.design.editor.placeholder")}</>}
|
||||
</Field>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
@ -121,14 +121,16 @@ export const defaultNodeConfigForBizApply = (): Partial<WorkflowNodeConfigForBiz
|
||||
};
|
||||
|
||||
export type WorkflowNodeConfigForBizUpload = {
|
||||
certificateId: string;
|
||||
domains: string;
|
||||
source: string;
|
||||
domains?: string;
|
||||
certificate: string;
|
||||
privateKey: string;
|
||||
};
|
||||
|
||||
export const defaultNodeConfigForBizUpload = (): Partial<WorkflowNodeConfigForBizUpload> => {
|
||||
return {};
|
||||
return {
|
||||
source: "form" as const,
|
||||
};
|
||||
};
|
||||
|
||||
export type WorkflowNodeConfigForBizMonitor = {
|
||||
|
||||
@ -129,12 +129,26 @@
|
||||
"workflow_node.upload.help": "Upload the user's existing SSL certificate.",
|
||||
"workflow_node.upload.default_name": "Uploading",
|
||||
"workflow_node.upload.form_anchor.parameters.tab": "Parameters",
|
||||
"workflow_node.upload.form.guide": "The file content will be read again every time this node executes.",
|
||||
"workflow_node.upload.form.source.label": "Upload source",
|
||||
"workflow_node.upload.form.source.placeholder": "Please select upload source",
|
||||
"workflow_node.upload.form.source.option.form.label": "Form",
|
||||
"workflow_node.upload.form.source.option.local.label": "Local path",
|
||||
"workflow_node.upload.form.source.option.url.label": "URL",
|
||||
"workflow_node.upload.form.domains.label": "Domains",
|
||||
"workflow_node.upload.form.domains.placholder": "Please select certificate file",
|
||||
"workflow_node.upload.form.certificate.label": "Certificate (PEM format)",
|
||||
"workflow_node.upload.form.certificate.placeholder": "-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----",
|
||||
"workflow_node.upload.form.private_key.label": "Private key (PEM format)",
|
||||
"workflow_node.upload.form.private_key.placeholder": "-----BEGIN (RSA|EC) PRIVATE KEY-----...-----END(RSA|EC) PRIVATE KEY-----",
|
||||
"workflow_node.upload.form.certificate_pem.label": "Certificate (PEM format)",
|
||||
"workflow_node.upload.form.certificate_pem.placeholder": "-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----",
|
||||
"workflow_node.upload.form.certificate_path.label": "Certificate file path",
|
||||
"workflow_node.upload.form.certificate_path.placeholder": "Please enter the local path for certificate file",
|
||||
"workflow_node.upload.form.certificate_url.label": "Certificate file URL",
|
||||
"workflow_node.upload.form.certificate_url.placeholder": "Please enter the URL for downloading certificate file",
|
||||
"workflow_node.upload.form.private_key_pem.label": "Private key (PEM format)",
|
||||
"workflow_node.upload.form.private_key_pem.placeholder": "-----BEGIN (RSA|EC) PRIVATE KEY-----...-----END(RSA|EC) PRIVATE KEY-----",
|
||||
"workflow_node.upload.form.private_key_path.label": "Private key file path",
|
||||
"workflow_node.upload.form.private_key_path.placeholder": "Please enter the local path for private key file",
|
||||
"workflow_node.upload.form.private_key_url.label": "Private key file URL",
|
||||
"workflow_node.upload.form.private_key_url.placeholder": "Please enter the URL for downloading private key file",
|
||||
"workflow_node.monitor.label": "Monitor certificate",
|
||||
"workflow_node.monitor.help": "Obtain the SSL certificate of the website through HTTPS protocol.",
|
||||
"workflow_node.monitor.default_name": "Monitoring",
|
||||
|
||||
@ -128,12 +128,26 @@
|
||||
"workflow_node.upload.help": "上传用户已有的本地 SSL 证书。",
|
||||
"workflow_node.upload.default_name": "上传",
|
||||
"workflow_node.upload.form_anchor.parameters.tab": "参数设置",
|
||||
"workflow_node.upload.form.guide": "每次执行此节点时,都将重新读取文件内容。",
|
||||
"workflow_node.upload.form.source.label": "上传来源",
|
||||
"workflow_node.upload.form.source.placeholder": "请选择上传来源",
|
||||
"workflow_node.upload.form.source.option.form.label": "表单",
|
||||
"workflow_node.upload.form.source.option.local.label": "本地路径",
|
||||
"workflow_node.upload.form.source.option.url.label": "URL 路径",
|
||||
"workflow_node.upload.form.domains.label": "域名",
|
||||
"workflow_node.upload.form.domains.placeholder": "上传证书文件后显示",
|
||||
"workflow_node.upload.form.certificate.label": "证书文件(PEM 格式)",
|
||||
"workflow_node.upload.form.certificate.placeholder": "-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----",
|
||||
"workflow_node.upload.form.private_key.label": "私钥文件(PEM 格式)",
|
||||
"workflow_node.upload.form.private_key.placeholder": "-----BEGIN (RSA|EC) PRIVATE KEY-----...-----END(RSA|EC) PRIVATE KEY-----",
|
||||
"workflow_node.upload.form.certificate_pem.label": "证书文件(PEM 格式)",
|
||||
"workflow_node.upload.form.certificate_pem.placeholder": "-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----",
|
||||
"workflow_node.upload.form.certificate_path.label": "证书文件路径",
|
||||
"workflow_node.upload.form.certificate_path.placeholder": "请输入证书文件本地路径",
|
||||
"workflow_node.upload.form.certificate_url.label": "证书文件 URL",
|
||||
"workflow_node.upload.form.certificate_url.placeholder": "请输入证书文件下载 URL",
|
||||
"workflow_node.upload.form.private_key_pem.label": "私钥文件(PEM 格式)",
|
||||
"workflow_node.upload.form.private_key_pem.placeholder": "-----BEGIN (RSA|EC) PRIVATE KEY-----...-----END(RSA|EC) PRIVATE KEY-----",
|
||||
"workflow_node.upload.form.private_key_path.label": "私钥文件路径",
|
||||
"workflow_node.upload.form.private_key_path.placeholder": "请输入私钥文件本地路径",
|
||||
"workflow_node.upload.form.private_key_url.label": "私钥文件 URL",
|
||||
"workflow_node.upload.form.private_key_url.placeholder": "请输入私钥文件下载 URL",
|
||||
"workflow_node.monitor.label": "监控网站证书",
|
||||
"workflow_node.monitor.help": "通过 HTTPS 协议获取指定网站的 SSL 证书。",
|
||||
"workflow_node.monitor.default_name": "监控",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user