mirror of
https://github.com/certimate-go/certimate.git
synced 2026-06-22 21:05:48 +08:00
Merge pull request #948 from fudiwei/dev
This commit is contained in:
commit
6e5ee19829
8
go.mod
8
go.mod
@ -35,6 +35,7 @@ require (
|
||||
github.com/blinkbean/dingtalk v1.1.3
|
||||
github.com/byteplus-sdk/byteplus-sdk-golang v1.0.56
|
||||
github.com/go-acme/lego/v4 v4.25.2
|
||||
github.com/go-cmd/cmd v1.4.3
|
||||
github.com/go-lark/lark v1.16.0
|
||||
github.com/go-resty/resty/v2 v2.16.5
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0
|
||||
@ -65,6 +66,7 @@ require (
|
||||
github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.21
|
||||
github.com/volcengine/volc-sdk-golang v1.0.219
|
||||
github.com/volcengine/volcengine-go-sdk v1.1.30
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0
|
||||
gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1
|
||||
gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.0
|
||||
golang.org/x/crypto v0.41.0
|
||||
@ -97,7 +99,6 @@ require (
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/go-acme/alidns-20150109/v4 v4.5.10 // indirect
|
||||
github.com/go-acme/tencentclouddnspod v1.0.1208 // indirect
|
||||
github.com/go-cmd/cmd v1.4.3 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
@ -127,6 +128,7 @@ require (
|
||||
github.com/namedotcom/go/v4 v4.0.2 // indirect
|
||||
github.com/nrdcg/bunny-go v0.0.0-20250327222614-988a091fc7ea // indirect
|
||||
github.com/nrdcg/desec v0.11.0 // indirect
|
||||
github.com/nrdcg/goacmedns v0.2.0 // indirect
|
||||
github.com/nrdcg/mailinabox v0.2.0 // indirect
|
||||
github.com/nrdcg/porkbun v0.4.0 // indirect
|
||||
github.com/peterhellberg/link v1.2.0 // indirect
|
||||
@ -206,7 +208,7 @@ require (
|
||||
github.com/nrdcg/namesilo v0.2.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/spf13/cast v1.9.2 // indirect
|
||||
github.com/spf13/cobra v1.9.1 // indirect
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/pflag v1.0.9
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
golang.org/x/image v0.29.0 // indirect
|
||||
@ -217,7 +219,7 @@ require (
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
golang.org/x/term v0.34.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
golang.org/x/time v0.12.0
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
|
||||
7
go.sum
7
go.sum
@ -404,9 +404,8 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8Wd
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
|
||||
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
|
||||
@ -691,6 +690,8 @@ github.com/nrdcg/bunny-go v0.0.0-20250327222614-988a091fc7ea h1:OSgRS4kqOs/WuxuF
|
||||
github.com/nrdcg/bunny-go v0.0.0-20250327222614-988a091fc7ea/go.mod h1:IDRRngAngb2eTEaWgpO0hukQFI/vJId46fT1KErMytA=
|
||||
github.com/nrdcg/desec v0.11.0 h1:XZVHy07sg12y8FozMp+l7XvzPsdzog0AYXuQMaHBsfs=
|
||||
github.com/nrdcg/desec v0.11.0/go.mod h1:5+4vyhMRTs49V9CNoODF/HwT8Mwxv9DJ6j+7NekUnBs=
|
||||
github.com/nrdcg/goacmedns v0.2.0 h1:ADMbThobzEMnr6kg2ohs4KGa3LFqmgiBA22/6jUWJR0=
|
||||
github.com/nrdcg/goacmedns v0.2.0/go.mod h1:T5o6+xvSLrQpugmwHvrSNkzWht0UGAwj2ACBMhh73Cg=
|
||||
github.com/nrdcg/mailinabox v0.2.0 h1:IKq8mfKiVwNW2hQii/ng1dJ4yYMMv3HAP3fMFIq2CFk=
|
||||
github.com/nrdcg/mailinabox v0.2.0/go.mod h1:0yxqeYOiGyxAu7Sb94eMxHPIOsPYXAjTeA9ZhePhGnc=
|
||||
github.com/nrdcg/namesilo v0.2.1 h1:kLjCjsufdW/IlC+iSfAqj0iQGgKjlbUUeDJio5Y6eMg=
|
||||
@ -893,6 +894,8 @@ github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+
|
||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
||||
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
|
||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
|
||||
28
internal/certapply/applicators/sp_acmedns.go
Normal file
28
internal/certapply/applicators/sp_acmedns.go
Normal file
@ -0,0 +1,28 @@
|
||||
package applicators
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
|
||||
"github.com/certimate-go/certimate/internal/domain"
|
||||
"github.com/certimate-go/certimate/pkg/core/ssl-applicator/acme-dns01/providers/acmedns"
|
||||
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := ACMEDns01Registries.Register(domain.ACMEDns01ProviderTypeACMEDNS, func(options *ProviderFactoryOptions) (challenge.Provider, error) {
|
||||
credentials := domain.AccessConfigForACMEDNS{}
|
||||
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
|
||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||
}
|
||||
|
||||
provider, err := acmedns.NewChallengeProvider(&acmedns.ChallengeProviderConfig{
|
||||
ServerUrl: credentials.ServerUrl,
|
||||
Credentials: credentials.Credentials,
|
||||
})
|
||||
return provider, err
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@ -22,8 +22,9 @@ import (
|
||||
)
|
||||
|
||||
type ObtainCertificateRequest struct {
|
||||
Domains []string
|
||||
KeyType certcrypto.KeyType
|
||||
Domains []string
|
||||
KeyType certcrypto.KeyType
|
||||
ValidityTo time.Time
|
||||
|
||||
// 提供商相关
|
||||
ChallengeType string
|
||||
@ -153,6 +154,7 @@ func (c *ACMEClient) ObtainCertificate(request *ObtainCertificateRequest) (*Obta
|
||||
Domains: request.Domains,
|
||||
Bundle: true,
|
||||
Profile: request.ACMEProfile,
|
||||
NotAfter: request.ValidityTo,
|
||||
ReplacesCertID: lo.If(request.ARIReplacesAcctUrl == c.account.ACMEAcctUrl, request.ARIReplacesCertId).Else(""),
|
||||
}
|
||||
resp, err := c.client.Certificate.Obtain(req)
|
||||
|
||||
@ -32,6 +32,11 @@ type AccessConfigForACMECA struct {
|
||||
Endpoint string `json:"endpoint"`
|
||||
}
|
||||
|
||||
type AccessConfigForACMEDNS struct {
|
||||
ServerUrl string `json:"serverUrl"`
|
||||
Credentials string `json:"credentials"`
|
||||
}
|
||||
|
||||
type AccessConfigForACMEHttpReq struct {
|
||||
Endpoint string `json:"endpoint"`
|
||||
Mode string `json:"mode,omitempty"`
|
||||
|
||||
@ -11,6 +11,7 @@ NOTICE: If you add new constant, please keep ASCII order.
|
||||
const (
|
||||
AccessProviderType1Panel = AccessProviderType("1panel")
|
||||
AccessProviderTypeACMECA = AccessProviderType("acmeca")
|
||||
AccessProviderTypeACMEDNS = AccessProviderType("acmedns")
|
||||
AccessProviderTypeACMEHttpReq = AccessProviderType("acmehttpreq")
|
||||
AccessProviderTypeAkamai = AccessProviderType("akamai") // Akamai(预留)
|
||||
AccessProviderTypeAliyun = AccessProviderType("aliyun")
|
||||
@ -121,6 +122,7 @@ ACME DNS-01 提供商常量值。
|
||||
NOTICE: If you add new constant, please keep ASCII order.
|
||||
*/
|
||||
const (
|
||||
ACMEDns01ProviderTypeACMEDNS = ACMEDns01ProviderType(AccessProviderTypeACMEDNS)
|
||||
ACMEDns01ProviderTypeACMEHttpReq = ACMEDns01ProviderType(AccessProviderTypeACMEHttpReq)
|
||||
ACMEDns01ProviderTypeAliyun = ACMEDns01ProviderType(AccessProviderTypeAliyun) // 兼容旧值,等同于 [ACMEDns01ProviderTypeAliyunDNS]
|
||||
ACMEDns01ProviderTypeAliyunDNS = ACMEDns01ProviderType(AccessProviderTypeAliyun + "-dns")
|
||||
|
||||
@ -150,6 +150,7 @@ func (c WorkflowNodeConfig) AsBizApply() WorkflowNodeConfigForBizApply {
|
||||
CAProvider: xmaps.GetString(c, "caProvider"),
|
||||
CAProviderAccessId: xmaps.GetString(c, "caProviderAccessId"),
|
||||
CAProviderConfig: xmaps.GetKVMapAny(c, "caProviderConfig"),
|
||||
ValidityLifetime: xmaps.GetString(c, "validityLifetime"),
|
||||
ACMEProfile: xmaps.GetString(c, "acmeProfile"),
|
||||
Nameservers: nameservers,
|
||||
DnsPropagationWait: xmaps.GetInt32(c, "dnsPropagationWait"),
|
||||
@ -216,10 +217,11 @@ type WorkflowNodeConfigForBizApply struct {
|
||||
Provider string `json:"provider"` // 质询提供商
|
||||
ProviderAccessId string `json:"providerAccessId"` // 质询提供商授权记录 ID
|
||||
ProviderConfig map[string]any `json:"providerConfig,omitempty"` // 质询提供商额外配置
|
||||
KeyAlgorithm string `json:"keyAlgorithm,omitempty"` // 证书算法
|
||||
CAProvider string `json:"caProvider,omitempty"` // CA 提供商(零值时使用全局配置)
|
||||
CAProviderAccessId string `json:"caProviderAccessId,omitempty"` // CA 提供商授权记录 ID
|
||||
CAProviderConfig map[string]any `json:"caProviderConfig,omitempty"` // CA 提供商额外配置
|
||||
KeyAlgorithm string `json:"keyAlgorithm,omitempty"` // 证书算法
|
||||
ValidityLifetime string `json:"validityLifetime,omitempty"` // 证书有效期,形如 "30d"、"6h"
|
||||
ACMEProfile string `json:"acmeProfile,omitempty"` // ACME Profiles Extension
|
||||
Nameservers []string `json:"nameservers,omitempty"` // DNS 服务器列表,以半角分号分隔
|
||||
DnsPropagationWait int32 `json:"dnsPropagationWait,omitempty"` // DNS 传播等待时间,等同于 lego 的 `--dns-propagation-wait` 参数
|
||||
|
||||
@ -41,7 +41,7 @@ func (s *sender[TIn, TOut]) SendWithContext(ctx context.Context, params *TIn) (*
|
||||
aesCryptor := xcrypto.NewAESCryptor(aesKey)
|
||||
|
||||
// 准备临时输入文件
|
||||
tempIn, err := os.CreateTemp("", "certimate_mprocin_*.tmp")
|
||||
tempIn, err := os.CreateTemp("", "certimate.mprocin_*.tmp")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create temp input file: %w", err)
|
||||
} else {
|
||||
@ -64,7 +64,7 @@ func (s *sender[TIn, TOut]) SendWithContext(ctx context.Context, params *TIn) (*
|
||||
defer os.Remove(tempIn.Name())
|
||||
|
||||
// 准备临时输出文件
|
||||
tempOut, err := os.CreateTemp("", "certimate_mprocout_*.tmp")
|
||||
tempOut, err := os.CreateTemp("", "certimate.mprocout_*.tmp")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create temp output file: %w", err)
|
||||
} else {
|
||||
@ -73,7 +73,7 @@ func (s *sender[TIn, TOut]) SendWithContext(ctx context.Context, params *TIn) (*
|
||||
defer os.Remove(tempOut.Name())
|
||||
|
||||
// 准备临时错误文件
|
||||
tempErr, err := os.CreateTemp("", "certimate_mprocerr_*.tmp")
|
||||
tempErr, err := os.CreateTemp("", "certimate.mprocerr_*.tmp")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create temp error file: %w", err)
|
||||
} else {
|
||||
|
||||
@ -15,6 +15,7 @@ import (
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
legolog "github.com/go-acme/lego/v4/log"
|
||||
"github.com/samber/lo"
|
||||
"github.com/xhit/go-str2duration/v2"
|
||||
|
||||
"github.com/certimate-go/certimate/internal/app"
|
||||
"github.com/certimate-go/certimate/internal/certapply"
|
||||
@ -261,7 +262,15 @@ func (ne *bizApplyNodeExecutor) executeObtain(execCtx *NodeExecutionContext, nod
|
||||
DnsPropagationTimeout: nodeCfg.DnsPropagationTimeout,
|
||||
DnsTTL: nodeCfg.DnsTTL,
|
||||
HttpDelayWait: nodeCfg.HttpDelayWait,
|
||||
ACMEProfile: nodeCfg.ACMEProfile,
|
||||
ValidityTo: lo.If(nodeCfg.ValidityLifetime == "", time.Time{}).
|
||||
ElseF(func() time.Time {
|
||||
duration, err := str2duration.ParseDuration(nodeCfg.ValidityLifetime)
|
||||
if err != nil {
|
||||
return time.Time{}
|
||||
}
|
||||
return time.Now().Add(duration)
|
||||
}),
|
||||
ACMEProfile: nodeCfg.ACMEProfile,
|
||||
ARIReplacesAcctUrl: lo.If(lastCertificate == nil, "").
|
||||
ElseF(func() string {
|
||||
if lastCertificate.ACMERenewed {
|
||||
|
||||
@ -0,0 +1,44 @@
|
||||
package acmedns
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/acmedns"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
ServerUrl string `json:"serverUrl"`
|
||||
Credentials string `json:"credentials"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
tempfile, err := os.CreateTemp("", "certimate.acmedns_*.tmp")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create temp credentials file: %w", err)
|
||||
} else {
|
||||
if _, err := tempfile.Write([]byte(config.Credentials)); err != nil {
|
||||
return nil, fmt.Errorf("failed to write temp credentials file: %w", err)
|
||||
}
|
||||
|
||||
tempfile.Close()
|
||||
}
|
||||
|
||||
providerConfig := acmedns.NewDefaultConfig()
|
||||
providerConfig.APIBase = config.ServerUrl
|
||||
providerConfig.StoragePath = tempfile.Name()
|
||||
|
||||
provider, err := acmedns.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
BIN
ui/public/imgs/providers/acmedns.png
Normal file
BIN
ui/public/imgs/providers/acmedns.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
@ -13,6 +13,7 @@ import { FormNestedFieldsContextProvider } from "./forms/_context";
|
||||
import { useProviderFilterByUsage } from "./forms/_hooks";
|
||||
import AccessConfigFieldsProvider1Panel from "./forms/AccessConfigFieldsProvider1Panel";
|
||||
import AccessConfigFieldsProviderACMECA from "./forms/AccessConfigFieldsProviderACMECA";
|
||||
import AccessConfigFieldsProviderACMEDNS from "./forms/AccessConfigFieldsProviderACMEDNS";
|
||||
import AccessConfigFieldsProviderACMEHttpReq from "./forms/AccessConfigFieldsProviderACMEHttpReq";
|
||||
import AccessConfigFieldsProviderAliyun from "./forms/AccessConfigFieldsProviderAliyun";
|
||||
import AccessConfigFieldsProviderAPISIX from "./forms/AccessConfigFieldsProviderAPISIX";
|
||||
@ -133,6 +134,9 @@ const AccessForm = ({ className, style, disabled, initialValues, mode, usage, ..
|
||||
case ACCESS_PROVIDERS.ACMECA: {
|
||||
return <AccessConfigFieldsProviderACMECA />;
|
||||
}
|
||||
case ACCESS_PROVIDERS.ACMEDNS: {
|
||||
return <AccessConfigFieldsProviderACMEDNS />;
|
||||
}
|
||||
case ACCESS_PROVIDERS.ACMEHTTPREQ: {
|
||||
return <AccessConfigFieldsProviderACMEHttpReq />;
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ const AccessConfigFormFieldsProvider1Panel = () => {
|
||||
name={[parentNamePath, "serverUrl"]}
|
||||
initialValue={initialValues.serverUrl}
|
||||
label={t("access.form.1panel_server_url.label")}
|
||||
extra={t("access.form.1panel_server_url.help")}
|
||||
rules={[formRule]}
|
||||
>
|
||||
<Input placeholder={t("access.form.1panel_server_url.placeholder")} />
|
||||
|
||||
@ -0,0 +1,77 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import TextFileInput from "@/components/TextFileInput";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const AccessConfigFieldsProviderACMEDNS = () => {
|
||||
const { i18n, t } = useTranslation();
|
||||
|
||||
const { parentNamePath } = useFormNestedFieldsContext();
|
||||
const formSchema = z.object({
|
||||
[parentNamePath]: getSchema({ i18n }),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
const initialValues = getInitialValues();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
name={[parentNamePath, "serverUrl"]}
|
||||
initialValue={initialValues.serverUrl}
|
||||
label={t("access.form.acmedns_server_url.label")}
|
||||
rules={[formRule]}
|
||||
>
|
||||
<Input placeholder={t("access.form.acmedns_server_url.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "credentials"]}
|
||||
initialValue={initialValues.credentials}
|
||||
label={t("access.form.acmedns_credentials.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.acmedns_credentials.tooltip") }}></span>}
|
||||
>
|
||||
<TextFileInput autoSize={{ minRows: 3, maxRows: 10 }} placeholder={t("access.form.acmedns_credentials.placeholder")} />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
serverUrl: "https://auth.acme-dns.io/",
|
||||
credentials: "",
|
||||
};
|
||||
};
|
||||
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
|
||||
const { t } = i18n;
|
||||
|
||||
return z.object({
|
||||
serverUrl: z.url(t("common.errmsg.url_invalid")),
|
||||
credentials: z
|
||||
.string()
|
||||
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
||||
.refine((v) => {
|
||||
if (!v) return false;
|
||||
|
||||
try {
|
||||
const obj = JSON.parse(v);
|
||||
return typeof obj === "object" && !Array.isArray(obj);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}, t("access.form.acmedns_credentials.errmsg.json_invalid")),
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(AccessConfigFieldsProviderACMEDNS, {
|
||||
getInitialValues,
|
||||
getSchema,
|
||||
});
|
||||
|
||||
export default _default;
|
||||
@ -21,6 +21,7 @@ const AccessConfigFormFieldsProviderBaotaPanel = () => {
|
||||
name={[parentNamePath, "serverUrl"]}
|
||||
initialValue={initialValues.serverUrl}
|
||||
label={t("access.form.baotapanel_server_url.label")}
|
||||
extra={t("access.form.baotapanel_server_url.help")}
|
||||
rules={[formRule]}
|
||||
>
|
||||
<Input placeholder={t("access.form.baotapanel_server_url.placeholder")} />
|
||||
|
||||
@ -21,6 +21,7 @@ const AccessConfigFormFieldsProviderBaotaWAF = () => {
|
||||
name={[parentNamePath, "serverUrl"]}
|
||||
initialValue={initialValues.serverUrl}
|
||||
label={t("access.form.baotawaf_server_url.label")}
|
||||
extra={t("access.form.baotawaf_server_url.help")}
|
||||
rules={[formRule]}
|
||||
>
|
||||
<Input placeholder={t("access.form.baotawaf_server_url.placeholder")} />
|
||||
|
||||
@ -21,6 +21,7 @@ const AccessConfigFormFieldsProviderRatPanel = () => {
|
||||
name={[parentNamePath, "serverUrl"]}
|
||||
initialValue={initialValues.serverUrl}
|
||||
label={t("access.form.ratpanel_server_url.label")}
|
||||
extra={t("access.form.ratpanel_server_url.help")}
|
||||
rules={[formRule]}
|
||||
>
|
||||
<Input placeholder={t("access.form.ratpanel_server_url.placeholder")} />
|
||||
|
||||
@ -1,90 +0,0 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input, Select, Switch } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const AccessConfigFormFieldsProvider1Panel = () => {
|
||||
const { i18n, t } = useTranslation();
|
||||
|
||||
const { parentNamePath } = useFormNestedFieldsContext();
|
||||
const formSchema = z.object({
|
||||
[parentNamePath]: getSchema({ i18n }),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
const initialValues = getInitialValues();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
name={[parentNamePath, "serverUrl"]}
|
||||
initialValue={initialValues.serverUrl}
|
||||
label={t("access.form.1panel_server_url.label")}
|
||||
rules={[formRule]}
|
||||
>
|
||||
<Input placeholder={t("access.form.1panel_server_url.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "apiVersion"]}
|
||||
initialValue={initialValues.apiVersion}
|
||||
label={t("access.form.1panel_api_version.label")}
|
||||
rules={[formRule]}
|
||||
>
|
||||
<Select options={["v1", "v2"].map((s) => ({ label: s, value: s }))} placeholder={t("access.form.1panel_api_version.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "apiKey"]}
|
||||
initialValue={initialValues.apiKey}
|
||||
label={t("access.form.1panel_api_key.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.1panel_api_key.tooltip") }}></span>}
|
||||
>
|
||||
<Input.Password autoComplete="new-password" placeholder={t("access.form.1panel_api_key.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "allowInsecureConnections"]}
|
||||
initialValue={initialValues.allowInsecureConnections}
|
||||
label={t("access.form.common_allow_insecure_conns.label")}
|
||||
rules={[formRule]}
|
||||
>
|
||||
<Switch
|
||||
checkedChildren={t("access.form.common_allow_insecure_conns.switch.on")}
|
||||
unCheckedChildren={t("access.form.common_allow_insecure_conns.switch.off")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
serverUrl: "http://<your-host-addr>:20410/",
|
||||
apiVersion: "v1",
|
||||
apiKey: "",
|
||||
};
|
||||
};
|
||||
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
|
||||
const { t } = i18n;
|
||||
|
||||
return z.object({
|
||||
serverUrl: z.url(t("common.errmsg.url_invalid")),
|
||||
apiVersion: z.string().nonempty(t("access.form.1panel_api_version.placeholder")),
|
||||
apiKey: z
|
||||
.string()
|
||||
.min(1, t("access.form.1panel_api_key.placeholder"))
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||
allowInsecureConnections: z.boolean().nullish(),
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(AccessConfigFormFieldsProvider1Panel, {
|
||||
getInitialValues,
|
||||
getSchema,
|
||||
});
|
||||
|
||||
export default _default;
|
||||
@ -4,7 +4,7 @@ import { Link } from "react-router";
|
||||
import { type FlowNodeEntity, getNodeForm } from "@flowgram.ai/fixed-layout-editor";
|
||||
import { IconChevronRight, IconCircleMinus, IconPlus } from "@tabler/icons-react";
|
||||
import { useControllableValue, useMount } from "ahooks";
|
||||
import { type AnchorProps, AutoComplete, Button, Divider, Flex, Form, type FormInstance, Input, InputNumber, Select, Switch, Typography } from "antd";
|
||||
import { type AnchorProps, AutoComplete, Button, Divider, Flex, Form, type FormInstance, Input, InputNumber, Select, Space, Switch, Typography } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
@ -317,9 +317,20 @@ const BizApplyNodeConfigForm = ({ node, ...props }: BizApplyNodeConfigFormProps)
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="validityLifetime"
|
||||
label={t("workflow_node.apply.form.validity_lifetime.label")}
|
||||
extra={t("workflow_node.apply.form.validity_lifetime.help")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.validity_lifetime.tooltip") }}></span>}
|
||||
>
|
||||
<InternalValidityLifetimeInput placeholder={t("workflow_node.apply.form.validity_lifetime.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="acmeProfile"
|
||||
label={t("workflow_node.apply.form.acme_profile.label")}
|
||||
extra={t("workflow_node.apply.form.acme_profile.help")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.apply.form.acme_profile.tooltip") }}></span>}
|
||||
>
|
||||
@ -526,6 +537,82 @@ const InternalEmailInput = memo(
|
||||
}
|
||||
);
|
||||
|
||||
const InternalValidityLifetimeInput = memo(
|
||||
({ disabled, placeholder, ...props }: { disabled?: boolean; placeholder?: string; value?: string; onChange?: (value: string) => void }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [value, setValue] = useControllableValue<string>(props, {
|
||||
valuePropName: "value",
|
||||
defaultValuePropName: "defaultValue",
|
||||
trigger: "onChange",
|
||||
});
|
||||
|
||||
const parseCombinedValue = (val: string): [string | undefined, string | undefined] => {
|
||||
const match = String(val).match(/^(\d+)([a-zA-Z]+)$/);
|
||||
if (match) {
|
||||
return [match[1], match[2]];
|
||||
}
|
||||
|
||||
return [undefined, undefined];
|
||||
};
|
||||
|
||||
const [inputValue, setInputValue] = useState(parseCombinedValue(value)[0]);
|
||||
const [selectValue, setSelectValue] = useState(parseCombinedValue(value)[1] || "d");
|
||||
useEffect(() => {
|
||||
const [v, u] = parseCombinedValue(value);
|
||||
setInputValue(v);
|
||||
setSelectValue(u || "d");
|
||||
}, [value]);
|
||||
|
||||
const handleInputClear = () => {
|
||||
setValue("");
|
||||
};
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setInputValue(e.currentTarget.value);
|
||||
|
||||
if (e.currentTarget.value) {
|
||||
setValue(`${e.currentTarget.value}${selectValue}`);
|
||||
} else {
|
||||
setValue("");
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectChange = (value: string) => {
|
||||
setSelectValue(value);
|
||||
|
||||
if (inputValue) {
|
||||
setValue(`${inputValue}${value}`);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Space.Compact className="w-full">
|
||||
<Input
|
||||
allowClear
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
type="number"
|
||||
value={inputValue}
|
||||
onChange={handleInputChange}
|
||||
onClear={handleInputClear}
|
||||
/>
|
||||
<div className="w-24">
|
||||
<Select
|
||||
options={["h", "d"].map((s) => ({
|
||||
key: s,
|
||||
label: t(`workflow_node.apply.form.validity_lifetime.units.${s}`),
|
||||
value: s,
|
||||
}))}
|
||||
value={selectValue}
|
||||
onChange={handleSelectChange}
|
||||
/>
|
||||
</div>
|
||||
</Space.Compact>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const getAnchorItems = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }): Required<AnchorProps>["items"] => {
|
||||
const { t } = i18n;
|
||||
|
||||
@ -595,6 +682,13 @@ const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> })
|
||||
(v) => (v == null || v === "" ? void 0 : Number(v)),
|
||||
z.number().int(t("workflow_node.apply.form.dns_ttl.placeholder")).gte(1, t("workflow_node.apply.form.dns_ttl.placeholder")).nullish()
|
||||
),
|
||||
validityLifetime: z
|
||||
.string()
|
||||
.nullish()
|
||||
.refine((v) => {
|
||||
if (!v) return true;
|
||||
return /^\d+[d|h]$/.test(v) && parseInt(v) > 0;
|
||||
}, t("workflow_node.apply.form.validity_lifetime.placeholder")),
|
||||
acmeProfile: z.string().nullish(),
|
||||
disableFollowCNAME: z.boolean().nullish(),
|
||||
disableARI: z.boolean().nullish(),
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
export const ACCESS_PROVIDERS = Object.freeze({
|
||||
["1PANEL"]: "1panel",
|
||||
ACMECA: "acmeca",
|
||||
ACMEDNS: "acmedns",
|
||||
ACMEHTTPREQ: "acmehttpreq",
|
||||
ALIYUN: "aliyun",
|
||||
APISIX: "apisix",
|
||||
@ -173,6 +174,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
|
||||
[ACCESS_PROVIDERS.CMCCCLOUD, "provider.cmcccloud", "/imgs/providers/cmcccloud.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.WESTCN, "provider.westcn", "/imgs/providers/westcn.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.POWERDNS, "provider.powerdns", "/imgs/providers/powerdns.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.ACMEDNS, "provider.acmedns", "/imgs/providers/acmedns.png", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.ACMEHTTPREQ, "provider.acmehttpreq", "/imgs/providers/acmehttpreq.svg", [ACCESS_USAGES.DNS]],
|
||||
|
||||
[ACCESS_PROVIDERS.LETSENCRYPT, "provider.letsencrypt", "/imgs/providers/letsencrypt.svg", [ACCESS_USAGES.CA], "builtin"],
|
||||
@ -264,6 +266,7 @@ export const caProvidersMap: Map<CAProvider["type"] | string, CAProvider> = new
|
||||
NOTICE: If you add new constant, please keep ASCII order.
|
||||
*/
|
||||
export const ACME_DNS01_PROVIDERS = Object.freeze({
|
||||
ACMEDNS: `${ACCESS_PROVIDERS.ACMEDNS}`,
|
||||
ACMEHTTPREQ: `${ACCESS_PROVIDERS.ACMEHTTPREQ}`,
|
||||
ALIYUN: `${ACCESS_PROVIDERS.ALIYUN}`, // 兼容旧值,等同于 `ALIYUN_DNS`
|
||||
ALIYUN_DNS: `${ACCESS_PROVIDERS.ALIYUN}-dns`,
|
||||
@ -369,6 +372,7 @@ export const acmeDns01ProvidersMap: Map<ACMEDns01Provider["type"] | string, ACME
|
||||
[ACME_DNS01_PROVIDERS.UCLOUD_UDNR, "provider.ucloud.udnr"],
|
||||
[ACME_DNS01_PROVIDERS.WESTCN, "provider.westcn"],
|
||||
[ACME_DNS01_PROVIDERS.POWERDNS, "provider.powerdns"],
|
||||
[ACME_DNS01_PROVIDERS.ACMEDNS, "provider.acmedns"],
|
||||
[ACME_DNS01_PROVIDERS.ACMEHTTPREQ, "provider.acmehttpreq"],
|
||||
] satisfies Array<[ACMEDns01ProviderType, string]>
|
||||
).map(([type, name]) => [
|
||||
|
||||
@ -102,6 +102,7 @@ export type WorkflowNodeConfigForBizApply = {
|
||||
caProviderAccessId?: string;
|
||||
caProviderConfig?: Record<string, unknown>;
|
||||
keyAlgorithm: string;
|
||||
validityLifetime?: string;
|
||||
acmeProfile?: string;
|
||||
nameservers?: string;
|
||||
dnsPropagationTimeout?: number;
|
||||
|
||||
@ -44,6 +44,7 @@
|
||||
"access.form.provider.search.placeholder": "Search provider ...",
|
||||
"access.form.1panel_server_url.label": "1Panel server URL",
|
||||
"access.form.1panel_server_url.placeholder": "Please enter 1Panel server URL",
|
||||
"access.form.1panel_server_url.help": "Notes: DO NOT include the security entrance suffix.",
|
||||
"access.form.1panel_api_version.label": "1Panel version",
|
||||
"access.form.1panel_api_version.placeholder": "Please select 1Panel version",
|
||||
"access.form.1panel_api_key.label": "1Panel API key",
|
||||
@ -59,6 +60,12 @@
|
||||
"access.form.acmeca_eab_kid.placeholder": "Please enter ACME EAB KID",
|
||||
"access.form.acmeca_eab_hmac_key.label": "ACME EAB HMAC key (Optional)",
|
||||
"access.form.acmeca_eab_hmac_key.placeholder": "Please enter ACME EAB HMAC key",
|
||||
"access.form.acmedns_server_url.label": "ACME-DNS server URL",
|
||||
"access.form.acmedns_server_url.placeholder": "Please enter ACME-DNS server URL",
|
||||
"access.form.acmedns_credentials.label": "ACME-DNS credentials",
|
||||
"access.form.acmedns_credentials.placeholder": "Please enter ACME-DNS credentials",
|
||||
"access.form.acmedns_credentials.tooltip": "For more information, see <a href=\"https://github.com/joohoi/acme-dns\" target=\"_blank\">https://github.com/joohoi/acme-dns</a>",
|
||||
"access.form.acmedns_credentials.errmsg.json_invalid": "Please enter a valiod JSON string",
|
||||
"access.form.acmehttpreq_endpoint.label": "Endpoint",
|
||||
"access.form.acmehttpreq_endpoint.placeholder": "Please enter endpoint",
|
||||
"access.form.acmehttpreq_endpoint.tooltip": "For more information, see <a href=\"https://go-acme.github.io/lego/dns/httpreq/\" target=\"_blank\">https://go-acme.github.io/lego/dns/httpreq/</a>",
|
||||
@ -116,11 +123,13 @@
|
||||
"access.form.baishan_api_token.placeholder": "Please enter Baishan Cloud API token",
|
||||
"access.form.baotapanel_server_url.label": "aaPanel server URL",
|
||||
"access.form.baotapanel_server_url.placeholder": "Please enter aaPanel server URL",
|
||||
"access.form.baotapanel_server_url.help": "Notes: DO NOT include the security entrance suffix.",
|
||||
"access.form.baotapanel_api_key.label": "aaPanel API key",
|
||||
"access.form.baotapanel_api_key.placeholder": "Please enter aaPanel API key",
|
||||
"access.form.baotapanel_api_key.tooltip": "For more information, see <a href=\"https://www.bt.cn/bbs/thread-20376-1-1.html\" target=\"_blank\">https://www.bt.cn/bbs/thread-20376-1-1.html</a>",
|
||||
"access.form.baotawaf_server_url.label": "aaWAF server URL",
|
||||
"access.form.baotawaf_server_url.placeholder": "Please enter aaWAF server URL",
|
||||
"access.form.baotawaf_server_url.help": "Notes: DO NOT include the security entrance suffix.",
|
||||
"access.form.baotawaf_api_key.label": "aaWAF API key",
|
||||
"access.form.baotawaf_api_key.placeholder": "Please enter aaWAF API key",
|
||||
"access.form.baotawaf_api_key.tooltip": "For more information, see <a href=\"https://github.com/aaPanel/aaWAF/blob/main/API.md\" target=\"_blank\">https://github.com/aaPanel/aaWAF/blob/main/API.md</a>",
|
||||
@ -387,6 +396,7 @@
|
||||
"access.form.rainyun_api_key.tooltip": "For more information, see <a href=\"https://app.rainyun.com/account/settings/api-key\" target=\"_blank\">https://app.rainyun.com/account/settings/api-key</a>",
|
||||
"access.form.ratpanel_server_url.label": "RatPanel server URL",
|
||||
"access.form.ratpanel_server_url.placeholder": "Please enter RatPanel server URL",
|
||||
"access.form.ratpanel_server_url.help": "Notes: DO NOT include the security entrance suffix.",
|
||||
"access.form.ratpanel_access_token_id.label": "RatPanel access token ID",
|
||||
"access.form.ratpanel_access_token_id.placeholder": "Please enter RatPanel access token ID",
|
||||
"access.form.ratpanel_access_token_id.tooltip": "For more information, see <a href=\"https://ratpanel.github.io/advanced/api.html\" target=\"_blank\">https://ratpanel.github.io/advanced/api.html</a>",
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
"provider.1panel.console": "1Panel - Console itself",
|
||||
"provider.1panel.site": "1Panel - Website",
|
||||
"provider.acmeca": "ACME Custom CA Endpoint",
|
||||
"provider.acmedns": "ACME-DNS",
|
||||
"provider.acmehttpreq": "ACME Custom HTTP Endpoint",
|
||||
"provider.aliyun": "Alibaba Cloud",
|
||||
"provider.aliyun.alb": "Alibaba Cloud - ALB (Application Load Balancer)",
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
"workflow_node.apply.help": "Apply for SSL certificate issuance from the certificate authority.",
|
||||
"workflow_node.apply.default_name": "Application",
|
||||
"workflow_node.apply.form_anchor.parameters.tab": "Parameters",
|
||||
"workflow_node.apply.form_anchor.certificate.tab": "CA Config",
|
||||
"workflow_node.apply.form_anchor.certificate.tab": "Certificate",
|
||||
"workflow_node.apply.form_anchor.certificate.title": "Certificate settings",
|
||||
"workflow_node.apply.form_anchor.advanced.tab": "Advanced",
|
||||
"workflow_node.apply.form_anchor.advanced.title": "Advanced settings",
|
||||
@ -71,9 +71,16 @@
|
||||
"workflow_node.apply.form.ca_provider_access.label": "Certificate authority credential",
|
||||
"workflow_node.apply.form.ca_provider_access.placeholder": "Please select an credential of the certificate authority",
|
||||
"workflow_node.apply.form.ca_provider_access.button": "Create",
|
||||
"workflow_node.apply.form.acme_profile.label": "ACME certificate profile (Optional)",
|
||||
"workflow_node.apply.form.acme_profile.placeholder": "Please enter ACME certificate profile",
|
||||
"workflow_node.apply.form.acme_profile.tooltip": "It determines the ACME profile which will be used to affect issuance of the certificate requested. If you don't understand this option, just keep it by default. <br><a href=\"https://letsencrypt.org/docs/profiles/\" target=\"_blank\">Click here to learn more</a>.",
|
||||
"workflow_node.apply.form.validity_lifetime.label": "Certificate validity lifetime (Optional)",
|
||||
"workflow_node.apply.form.validity_lifetime.placeholder": "Please enter certificate's validity lifetime",
|
||||
"workflow_node.apply.form.validity_lifetime.help": "Notes: Not all CAs support this feature.",
|
||||
"workflow_node.apply.form.validity_lifetime.tooltip": "It determines the <em>NotAfter</em> field of the certificate in the ACME protocol. If you don't understand this option, just keep it by default.",
|
||||
"workflow_node.apply.form.validity_lifetime.units.h": "Hour(s)",
|
||||
"workflow_node.apply.form.validity_lifetime.units.d": "Day(s)",
|
||||
"workflow_node.apply.form.acme_profile.label": "Certificate ACME profile (Optional)",
|
||||
"workflow_node.apply.form.acme_profile.placeholder": "Please enter certificate's ACME profile",
|
||||
"workflow_node.apply.form.acme_profile.help": "Notes: Not all CAs support this feature.",
|
||||
"workflow_node.apply.form.acme_profile.tooltip": "It determines the <em>Profile</em> field of the certificate in the ACME protocol. If you don't understand this option, just keep it by default. <br><a href=\"https://letsencrypt.org/docs/profiles/\" target=\"_blank\">Click here to learn more</a>.",
|
||||
"workflow_node.apply.form.nameservers.label": "DNS recursive nameservers (Optional)",
|
||||
"workflow_node.apply.form.nameservers.placeholder": "Please enter DNS recursive nameservers (separated by semicolons)",
|
||||
"workflow_node.apply.form.nameservers.tooltip": "It determines whether to custom DNS recursive nameservers during ACME DNS-01 challenge. If you don't understand this option, just keep it by default. <br><a href=\"https://go-acme.github.io/lego/usage/cli/options/index.html#dns-resolvers-and-challenge-verification\" target=\"_blank\">Click here to learn more</a>.",
|
||||
|
||||
@ -46,6 +46,7 @@
|
||||
"access.form.common_allow_insecure_conns.switch.off": "不允许",
|
||||
"access.form.1panel_server_url.label": "1Panel 服务地址",
|
||||
"access.form.1panel_server_url.placeholder": "请输入 1Panel 服务地址",
|
||||
"access.form.1panel_server_url.help": "提示:请勿包含安全入口后缀。",
|
||||
"access.form.1panel_api_version.label": "1Panel 版本",
|
||||
"access.form.1panel_api_version.placeholder": "请选择 1Panel 版本",
|
||||
"access.form.1panel_api_key.label": "1Panel 接口密钥",
|
||||
@ -58,6 +59,12 @@
|
||||
"access.form.acmeca_eab_kid.placeholder": "请输入 ACME EAB KID",
|
||||
"access.form.acmeca_eab_hmac_key.label": "ACME EAB HMAC Key(可选)",
|
||||
"access.form.acmeca_eab_hmac_key.placeholder": "请输入 ACME EAB HMAC Key",
|
||||
"access.form.acmedns_server_url.label": "ACME-DNS 服务地址",
|
||||
"access.form.acmedns_server_url.placeholder": "请输入 ACME-DNS 服务地址",
|
||||
"access.form.acmedns_credentials.label": "ACME-DNS 凭证文件",
|
||||
"access.form.acmedns_credentials.placeholder": "请输入 ACME-DNS 凭证文件",
|
||||
"access.form.acmedns_credentials.tooltip": "这是什么?请参阅 <a href=\"https://github.com/joohoi/acme-dns\" target=\"_blank\">https://github.com/joohoi/acme-dns</a>",
|
||||
"access.form.acmedns_credentials.errmsg.json_invalid": "请输入有效的 JSON 格式字符串",
|
||||
"access.form.acmehttpreq_endpoint.label": "服务端点",
|
||||
"access.form.acmehttpreq_endpoint.placeholder": "请输入服务端点",
|
||||
"access.form.acmehttpreq_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://go-acme.github.io/lego/dns/httpreq/\" target=\"_blank\">https://go-acme.github.io/lego/dns/httpreq/</a>",
|
||||
@ -112,11 +119,13 @@
|
||||
"access.form.baishan_api_token.placeholder": "请输入白山云 API Token",
|
||||
"access.form.baotapanel_server_url.label": "宝塔面板服务地址",
|
||||
"access.form.baotapanel_server_url.placeholder": "请输入宝塔面板服务地址",
|
||||
"access.form.baotapanel_server_url.help": "提示:请勿包含安全入口后缀。",
|
||||
"access.form.baotapanel_api_key.label": "宝塔面板接口密钥",
|
||||
"access.form.baotapanel_api_key.placeholder": "请输入宝塔面板接口密钥",
|
||||
"access.form.baotapanel_api_key.tooltip": "这是什么?请参阅 <a href=\"https://www.bt.cn/bbs/thread-113890-1-1.html\" target=\"_blank\">https://www.bt.cn/bbs/thread-113890-1-1.html</a>",
|
||||
"access.form.baotawaf_server_url.label": "堡塔云 WAF 服务地址",
|
||||
"access.form.baotawaf_server_url.placeholder": "请输入堡塔云 WAF 服务地址",
|
||||
"access.form.baotawaf_server_url.help": "提示:请勿包含安全入口后缀。",
|
||||
"access.form.baotawaf_api_key.label": "堡塔云 WAF 接口密钥",
|
||||
"access.form.baotawaf_api_key.placeholder": "请输入 堡塔云 WAF 接口密钥",
|
||||
"access.form.baotawaf_api_key.tooltip": "这是什么?请参阅 <a href=\"https://github.com/aaPanel/aaWAF/blob/main/API.md\" target=\"_blank\">https://github.com/aaPanel/aaWAF/blob/main/API.md</a>",
|
||||
@ -386,6 +395,7 @@
|
||||
"access.form.rainyun_api_key.tooltip": "这是什么?请参阅 <a href=\"https://app.rainyun.com/account/settings/api-key\" target=\"_blank\">https://app.rainyun.com/account/settings/api-key</a>",
|
||||
"access.form.ratpanel_server_url.label": "耗子面板服务地址",
|
||||
"access.form.ratpanel_server_url.placeholder": "请输入耗子面板服务地址",
|
||||
"access.form.ratpanel_server_url.help": "提示:请勿包含安全入口后缀。",
|
||||
"access.form.ratpanel_access_token_id.label": "耗子面板 AccessToken ID",
|
||||
"access.form.ratpanel_access_token_id.placeholder": "请输入耗子面板 AccessToken ID",
|
||||
"access.form.ratpanel_access_token_id.tooltip": "这是什么?请参阅 <a href=\"https://ratpanel.github.io/advanced/api.html\" target=\"_blank\">https://ratpanel.github.io/advanced/api.html</a>",
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
"provider.1panel.console": "1Panel - 面板自身",
|
||||
"provider.1panel.site": "1Panel - 网站",
|
||||
"provider.acmeca": "ACME 自定义 CA 端点",
|
||||
"provider.acmedns": "ACME-DNS",
|
||||
"provider.acmehttpreq": "ACME 自定义 HTTP 端点",
|
||||
"provider.aliyun": "阿里云",
|
||||
"provider.aliyun.alb": "阿里云 - 应用型负载均衡 ALB",
|
||||
|
||||
@ -63,15 +63,22 @@
|
||||
"workflow_node.apply.form.tencentcloud_eo_zone_id.placeholder": "请输入腾讯云 EdgeOne 站点 ID",
|
||||
"workflow_node.apply.form.tencentcloud_eo_zone_id.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/edgeone\" target=\"_blank\">https://console.cloud.tencent.com/edgeone</a>",
|
||||
"workflow_node.apply.form.key_algorithm.label": "证书算法",
|
||||
"workflow_node.apply.form.key_algorithm.placeholder": "请选择证书算法",
|
||||
"workflow_node.apply.form.key_algorithm.placeholder": "请选择证书的算法",
|
||||
"workflow_node.apply.form.ca_provider.label": "证书颁发机构(可选)",
|
||||
"workflow_node.apply.form.ca_provider.placeholder": "请选择证书颁发机构",
|
||||
"workflow_node.apply.form.ca_provider.button": "设置",
|
||||
"workflow_node.apply.form.ca_provider_access.label": "证书颁发机构授权",
|
||||
"workflow_node.apply.form.ca_provider_access.placeholder": "请选择证书颁发机构授权",
|
||||
"workflow_node.apply.form.ca_provider_access.button": "新建",
|
||||
"workflow_node.apply.form.acme_profile.label": "ACME 证书配置(可选)",
|
||||
"workflow_node.apply.form.acme_profile.placeholder": "请输入 ACME 证书配置",
|
||||
"workflow_node.apply.form.validity_lifetime.label": "证书有效期(可选)",
|
||||
"workflow_node.apply.form.validity_lifetime.placeholder": "请输入证书的有效期",
|
||||
"workflow_node.apply.form.validity_lifetime.help": "注意:并非所有证书颁发机构都支持此特性。",
|
||||
"workflow_node.apply.form.validity_lifetime.tooltip": "表示证书的有效期。如果你不了解该选项的用途,保持默认即可。",
|
||||
"workflow_node.apply.form.validity_lifetime.units.h": "小时",
|
||||
"workflow_node.apply.form.validity_lifetime.units.d": "天",
|
||||
"workflow_node.apply.form.acme_profile.label": "证书 ACME 配置(可选)",
|
||||
"workflow_node.apply.form.acme_profile.placeholder": "请输入证书的 ACME 配置",
|
||||
"workflow_node.apply.form.acme_profile.help": "注意:并非所有证书颁发机构都支持此特性。",
|
||||
"workflow_node.apply.form.acme_profile.tooltip": "表示证书颁发时使用的 ACME 证书配置。如果你不了解该选项的用途,保持默认即可。<br><a href=\"https://letsencrypt.org/zh-cn/docs/profiles/\" target=\"_blank\">点此了解更多</a>。",
|
||||
"workflow_node.apply.form.nameservers.label": "DNS 递归服务器(可选)",
|
||||
"workflow_node.apply.form.nameservers.placeholder": "请输入 DNS 递归服务器(多个值请用半角分号隔开)",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user