mirror of
https://github.com/certimate-go/certimate.git
synced 2026-09-24 23:10:13 +08:00
feat: standardize domain match patterns on deployment
This commit is contained in:
@@ -17,10 +17,11 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := tencentcloudcdn.NewSSLDeployerProvider(&tencentcloudcdn.SSLDeployerProviderConfig{
|
||||
SecretId: credentials.SecretId,
|
||||
SecretKey: credentials.SecretKey,
|
||||
Endpoint: xmaps.GetString(options.ProviderExtendedConfig, "endpoint"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
SecretId: credentials.SecretId,
|
||||
SecretKey: credentials.SecretKey,
|
||||
Endpoint: xmaps.GetString(options.ProviderExtendedConfig, "endpoint"),
|
||||
MatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "matchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
}); err != nil {
|
||||
|
||||
@@ -17,10 +17,11 @@ func init() {
|
||||
}
|
||||
|
||||
provider, err := tencentcloudecdn.NewSSLDeployerProvider(&tencentcloudecdn.SSLDeployerProviderConfig{
|
||||
SecretId: credentials.SecretId,
|
||||
SecretKey: credentials.SecretKey,
|
||||
Endpoint: xmaps.GetString(options.ProviderExtendedConfig, "endpoint"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
SecretId: credentials.SecretId,
|
||||
SecretKey: credentials.SecretKey,
|
||||
Endpoint: xmaps.GetString(options.ProviderExtendedConfig, "endpoint"),
|
||||
MatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "matchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
}); err != nil {
|
||||
|
||||
@@ -19,6 +19,7 @@ func init() {
|
||||
provider, err := volcenginecdn.NewSSLDeployerProvider(&volcenginecdn.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.SecretAccessKey,
|
||||
MatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "matchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
|
||||
@@ -19,6 +19,7 @@ func init() {
|
||||
provider, err := volcenginelive.NewSSLDeployerProvider(&volcenginelive.SSLDeployerProviderConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.SecretAccessKey,
|
||||
MatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "matchPattern"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
|
||||
@@ -85,10 +85,7 @@ func (ne *bizMonitorNodeExecutor) Execute(execCtx *NodeExecutionContext) (*NodeE
|
||||
|
||||
now := time.Now()
|
||||
isCertPeriodValid := now.Before(cert.NotAfter) && now.After(cert.NotBefore)
|
||||
isCertHostMatched := true
|
||||
if err := cert.VerifyHostname(targetDomain); err != nil {
|
||||
isCertHostMatched = false
|
||||
}
|
||||
isCertHostMatched := cert.VerifyHostname(targetDomain) == nil
|
||||
|
||||
validated := isCertPeriodValid && isCertHostMatched
|
||||
daysLeft := int(math.Floor(time.Until(cert.NotAfter).Hours() / 24))
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package tencentcloudcdn
|
||||
|
||||
const (
|
||||
// 匹配模式:精确匹配。
|
||||
MatchPatternExact = "exact"
|
||||
// 匹配模式:通配符匹配。
|
||||
MatchPatternWildcard = "wildcard"
|
||||
// 匹配模式:证书 SAN 匹配。
|
||||
MatchPatternCertSAN = "certsan"
|
||||
)
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/tencentcloud-ssl"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
@@ -24,6 +25,9 @@ type SSLDeployerProviderConfig struct {
|
||||
SecretKey string `json:"secretKey"`
|
||||
// 腾讯云接口端点。
|
||||
Endpoint string `json:"endpoint,omitempty"`
|
||||
// 域名匹配模式。
|
||||
// 零值时默认值 [MatchPatternExact]。
|
||||
MatchPattern string `json:"matchPattern,omitempty"`
|
||||
// 加速域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
@@ -95,17 +99,39 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
}
|
||||
|
||||
// 获取待部署的 CDN 实例
|
||||
// 如果是泛域名,根据证书匹配 CDN 实例
|
||||
domains := make([]string, 0)
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
temp, err := d.getDomainsByCertId(ctx, upres.CertId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
switch d.config.MatchPattern {
|
||||
case "", MatchPatternExact:
|
||||
{
|
||||
domains = append(domains, d.config.Domain)
|
||||
}
|
||||
|
||||
domains = temp
|
||||
} else {
|
||||
domains = append(domains, d.config.Domain)
|
||||
case MatchPatternWildcard:
|
||||
{
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
temp, err := d.getMatchedDomainsByWildcard(ctx, d.config.Domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = temp
|
||||
} else {
|
||||
domains = append(domains, d.config.Domain)
|
||||
}
|
||||
}
|
||||
|
||||
case MatchPatternCertSAN:
|
||||
{
|
||||
temp, err := d.getMatchedDomainsByCertId(ctx, upres.CertId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = temp
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported match pattern: '%s'", d.config.MatchPattern)
|
||||
}
|
||||
|
||||
// 遍历更新域名证书
|
||||
@@ -134,7 +160,55 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getDomainsByCertId(ctx context.Context, cloudCertId string) ([]string, error) {
|
||||
func (d *SSLDeployerProvider) getMatchedDomainsByWildcard(ctx context.Context, wildcardDomain string) ([]string, error) {
|
||||
domains := make([]string, 0)
|
||||
|
||||
// 遍历查询域名基本信息,获取匹配的域名
|
||||
// REF: https://cloud.tencent.com/document/api/228/41118
|
||||
describeDomainsOffset := int64(0)
|
||||
describeDomainsLimit := int64(100)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
describeDomainsReq := tccdn.NewDescribeDomainsRequest()
|
||||
describeDomainsReq.Filters = []*tccdn.DomainFilter{
|
||||
{
|
||||
Name: common.StringPtr("domain"),
|
||||
Value: common.StringPtrs([]string{strings.TrimPrefix(wildcardDomain, "*.")}),
|
||||
Fuzzy: common.BoolPtr(true),
|
||||
},
|
||||
}
|
||||
describeDomainsReq.Offset = common.Int64Ptr(describeDomainsOffset)
|
||||
describeDomainsReq.Limit = common.Int64Ptr(describeDomainsLimit)
|
||||
describeDomainsResp, err := d.sdkClient.DescribeDomains(describeDomainsReq)
|
||||
d.logger.Debug("sdk request 'cdn.DescribeDomains'", slog.Any("request", describeDomainsReq), slog.Any("response", describeDomainsResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.DescribeDomains': %w", err)
|
||||
}
|
||||
|
||||
if describeDomainsResp.Response.Domains != nil {
|
||||
for _, domain := range describeDomainsResp.Response.Domains {
|
||||
if lo.FromPtr(domain.Product) == "cdn" && xcert.MatchHostname(wildcardDomain, lo.FromPtr(domain.Domain)) {
|
||||
domains = append(domains, *domain.Domain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(describeDomainsResp.Response.Domains) < int(describeDomainsLimit) {
|
||||
break
|
||||
} else {
|
||||
describeDomainsOffset += describeDomainsLimit
|
||||
}
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getMatchedDomainsByCertId(ctx context.Context, cloudCertId string) ([]string, error) {
|
||||
// 获取证书中的可用域名
|
||||
// REF: https://cloud.tencent.com/document/api/228/42491
|
||||
describeCertDomainsReq := tccdn.NewDescribeCertDomainsRequest()
|
||||
@@ -177,7 +251,10 @@ func (d *SSLDeployerProvider) updateDomainHttpsServerCert(ctx context.Context, d
|
||||
}
|
||||
|
||||
domainConfig := describeDomainsConfigResp.Response.Domains[0]
|
||||
if domainConfig.Https != nil && domainConfig.Https.CertInfo != nil && domainConfig.Https.CertInfo.CertId != nil && *domainConfig.Https.CertInfo.CertId == cloudCertId {
|
||||
if domainConfig.Https != nil &&
|
||||
domainConfig.Https.CertInfo != nil &&
|
||||
domainConfig.Https.CertInfo.CertId != nil &&
|
||||
*domainConfig.Https.CertInfo.CertId == cloudCertId {
|
||||
// 已部署过此域名,跳过
|
||||
return nil
|
||||
}
|
||||
@@ -188,9 +265,7 @@ func (d *SSLDeployerProvider) updateDomainHttpsServerCert(ctx context.Context, d
|
||||
updateDomainConfigReq.Domain = common.StringPtr(domain)
|
||||
updateDomainConfigReq.Https = domainConfig.Https
|
||||
if updateDomainConfigReq.Https == nil {
|
||||
updateDomainConfigReq.Https = &tccdn.Https{
|
||||
Switch: common.StringPtr("on"),
|
||||
}
|
||||
updateDomainConfigReq.Https = &tccdn.Https{Switch: common.StringPtr("on")}
|
||||
} else {
|
||||
updateDomainConfigReq.Https.SslStatus = nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package tencentcloudecdn
|
||||
|
||||
const (
|
||||
// 匹配模式:精确匹配。
|
||||
MatchPatternExact = "exact"
|
||||
// 匹配模式:通配符匹配。
|
||||
MatchPatternWildcard = "wildcard"
|
||||
// 匹配模式:证书 SAN 匹配。
|
||||
MatchPatternCertSAN = "certsan"
|
||||
)
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/tencentcloud-ssl"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
@@ -24,6 +25,9 @@ type SSLDeployerProviderConfig struct {
|
||||
SecretKey string `json:"secretKey"`
|
||||
// 腾讯云接口端点。
|
||||
Endpoint string `json:"endpoint,omitempty"`
|
||||
// 域名匹配模式。
|
||||
// 零值时默认值 [MatchPatternExact]。
|
||||
MatchPattern string `json:"matchPattern,omitempty"`
|
||||
// 加速域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
@@ -94,18 +98,40 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||
}
|
||||
|
||||
// 获取待部署的 CDN 实例
|
||||
// 如果是泛域名,根据证书匹配 CDN 实例
|
||||
// 获取待部署的 ECDN 实例
|
||||
domains := make([]string, 0)
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
temp, err := d.getDomainsByCertId(upres.CertId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
switch d.config.MatchPattern {
|
||||
case "", MatchPatternExact:
|
||||
{
|
||||
domains = append(domains, d.config.Domain)
|
||||
}
|
||||
|
||||
domains = temp
|
||||
} else {
|
||||
domains = append(domains, d.config.Domain)
|
||||
case MatchPatternWildcard:
|
||||
{
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
temp, err := d.getMatchedDomainsByWildcard(ctx, d.config.Domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = temp
|
||||
} else {
|
||||
domains = append(domains, d.config.Domain)
|
||||
}
|
||||
}
|
||||
|
||||
case MatchPatternCertSAN:
|
||||
{
|
||||
temp, err := d.getMatchedDomainsByCertId(ctx, upres.CertId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = temp
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported match pattern: '%s'", d.config.MatchPattern)
|
||||
}
|
||||
|
||||
// 遍历更新域名证书
|
||||
@@ -134,7 +160,55 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getDomainsByCertId(cloudCertId string) ([]string, error) {
|
||||
func (d *SSLDeployerProvider) getMatchedDomainsByWildcard(ctx context.Context, wildcardDomain string) ([]string, error) {
|
||||
domains := make([]string, 0)
|
||||
|
||||
// 遍历查询域名基本信息,获取匹配的域名
|
||||
// REF: https://cloud.tencent.com/document/api/228/41118
|
||||
describeDomainsOffset := int64(0)
|
||||
describeDomainsLimit := int64(100)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
describeDomainsReq := tccdn.NewDescribeDomainsRequest()
|
||||
describeDomainsReq.Filters = []*tccdn.DomainFilter{
|
||||
{
|
||||
Name: common.StringPtr("domain"),
|
||||
Value: common.StringPtrs([]string{strings.TrimPrefix(wildcardDomain, "*.")}),
|
||||
Fuzzy: common.BoolPtr(true),
|
||||
},
|
||||
}
|
||||
describeDomainsReq.Offset = common.Int64Ptr(describeDomainsOffset)
|
||||
describeDomainsReq.Limit = common.Int64Ptr(describeDomainsLimit)
|
||||
describeDomainsResp, err := d.sdkClient.DescribeDomains(describeDomainsReq)
|
||||
d.logger.Debug("sdk request 'cdn.DescribeDomains'", slog.Any("request", describeDomainsReq), slog.Any("response", describeDomainsResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.DescribeDomains': %w", err)
|
||||
}
|
||||
|
||||
if describeDomainsResp.Response.Domains != nil {
|
||||
for _, domain := range describeDomainsResp.Response.Domains {
|
||||
if lo.FromPtr(domain.Product) == "ecdn" && xcert.MatchHostname(wildcardDomain, lo.FromPtr(domain.Domain)) {
|
||||
domains = append(domains, *domain.Domain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(describeDomainsResp.Response.Domains) < int(describeDomainsLimit) {
|
||||
break
|
||||
} else {
|
||||
describeDomainsOffset += describeDomainsLimit
|
||||
}
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getMatchedDomainsByCertId(ctx context.Context, cloudCertId string) ([]string, error) {
|
||||
// 获取证书中的可用域名
|
||||
// REF: https://cloud.tencent.com/document/api/228/42491
|
||||
describeCertDomainsReq := tccdn.NewDescribeCertDomainsRequest()
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package volcenginecdn
|
||||
|
||||
const (
|
||||
// 匹配模式:精确匹配。
|
||||
MatchPatternExact = "exact"
|
||||
// 匹配模式:通配符匹配。
|
||||
MatchPatternWildcard = "wildcard"
|
||||
// 匹配模式:证书 SAN 匹配。
|
||||
MatchPatternCertSAN = "certsan"
|
||||
)
|
||||
@@ -8,9 +8,11 @@ import (
|
||||
"strings"
|
||||
|
||||
vecdn "github.com/volcengine/volc-sdk-golang/service/cdn"
|
||||
ve "github.com/volcengine/volcengine-go-sdk/volcengine"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/volcengine-cdn"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
@@ -18,6 +20,9 @@ type SSLDeployerProviderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 火山引擎 AccessKeySecret。
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
// 域名匹配模式。
|
||||
// 零值时默认值 [MatchPatternExact]。
|
||||
MatchPattern string `json:"matchPattern,omitempty"`
|
||||
// 加速域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
@@ -79,44 +84,47 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||
}
|
||||
|
||||
// 获取待部署的 CDN 实例
|
||||
domains := make([]string, 0)
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
// 获取指定证书可关联的域名
|
||||
// REF: https://www.volcengine.com/docs/6454/125711
|
||||
describeCertConfigReq := &vecdn.DescribeCertConfigRequest{
|
||||
CertId: upres.CertId,
|
||||
}
|
||||
describeCertConfigResp, err := d.sdkClient.DescribeCertConfig(describeCertConfigReq)
|
||||
d.logger.Debug("sdk request 'cdn.DescribeCertConfig'", slog.Any("request", describeCertConfigReq), slog.Any("response", describeCertConfigResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.DescribeCertConfig': %w", err)
|
||||
switch d.config.MatchPattern {
|
||||
case "", MatchPatternExact:
|
||||
{
|
||||
domains = append(domains, d.config.Domain)
|
||||
}
|
||||
|
||||
if describeCertConfigResp.Result.CertNotConfig != nil {
|
||||
for i := range describeCertConfigResp.Result.CertNotConfig {
|
||||
domains = append(domains, describeCertConfigResp.Result.CertNotConfig[i].Domain)
|
||||
}
|
||||
}
|
||||
case MatchPatternWildcard:
|
||||
{
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
temp, err := d.getMatchedDomainsByWildcard(ctx, d.config.Domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if describeCertConfigResp.Result.OtherCertConfig != nil {
|
||||
for i := range describeCertConfigResp.Result.OtherCertConfig {
|
||||
domains = append(domains, describeCertConfigResp.Result.OtherCertConfig[i].Domain)
|
||||
}
|
||||
}
|
||||
|
||||
if len(domains) == 0 {
|
||||
if len(describeCertConfigResp.Result.SpecifiedCertConfig) > 0 {
|
||||
// 所有可关联的域名都配置了该证书,跳过部署
|
||||
d.logger.Info("no domains to deploy")
|
||||
domains = temp
|
||||
} else {
|
||||
return nil, errors.New("domain not found")
|
||||
domains = append(domains, d.config.Domain)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
domains = append(domains, d.config.Domain)
|
||||
|
||||
case MatchPatternCertSAN:
|
||||
{
|
||||
temp, err := d.getMatchedDomainsByCertId(ctx, upres.CertId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domains = temp
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported match pattern: '%s'", d.config.MatchPattern)
|
||||
}
|
||||
|
||||
if len(domains) > 0 {
|
||||
// 遍历绑定证书
|
||||
if len(domains) == 0 {
|
||||
d.logger.Info("no cdn domains to deploy")
|
||||
} else {
|
||||
d.logger.Info("found cdn domains to deploy", slog.Any("domains", domains))
|
||||
var errs []error
|
||||
|
||||
for _, domain := range domains {
|
||||
@@ -124,15 +132,7 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
// 关联证书与加速域名
|
||||
// REF: https://www.volcengine.com/docs/6454/125712
|
||||
batchDeployCertReq := &vecdn.BatchDeployCertRequest{
|
||||
CertId: upres.CertId,
|
||||
Domain: domain,
|
||||
}
|
||||
batchDeployCertResp, err := d.sdkClient.BatchDeployCert(batchDeployCertReq)
|
||||
d.logger.Debug("sdk request 'cdn.BatchDeployCert'", slog.Any("request", batchDeployCertReq), slog.Any("response", batchDeployCertResp))
|
||||
if err != nil {
|
||||
if err := d.bindCert(ctx, domain, upres.CertId); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
@@ -145,3 +145,98 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getMatchedDomainsByWildcard(ctx context.Context, wildcardDomain string) ([]string, error) {
|
||||
domains := make([]string, 0)
|
||||
|
||||
// 遍历获取加速域名列表,获取匹配的域名
|
||||
// REF: https://www.volcengine.com/docs/6454/75269
|
||||
listCdnDomainsPageNum := int64(1)
|
||||
listCdnDomainsPageSize := int64(100)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
listCdnDomainsReq := &vecdn.ListCdnDomainsRequest{
|
||||
Domain: ve.String(strings.TrimPrefix(wildcardDomain, "*.")),
|
||||
Status: ve.String("online"),
|
||||
PageNum: ve.Int64(listCdnDomainsPageNum),
|
||||
PageSize: ve.Int64(listCdnDomainsPageSize),
|
||||
}
|
||||
listCdnDomainsResp, err := d.sdkClient.ListCdnDomains(listCdnDomainsReq)
|
||||
d.logger.Debug("sdk request 'cdn.ListCdnDomains'", slog.Any("request", listCdnDomainsReq), slog.Any("response", listCdnDomainsResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.ListCdnDomains': %w", err)
|
||||
}
|
||||
|
||||
if listCdnDomainsResp.Result.Data != nil {
|
||||
for _, domain := range listCdnDomainsResp.Result.Data {
|
||||
if xcert.MatchHostname(wildcardDomain, domain.Domain) {
|
||||
domains = append(domains, domain.Domain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(listCdnDomainsResp.Result.Data) < int(listCdnDomainsPageSize) {
|
||||
break
|
||||
} else {
|
||||
listCdnDomainsPageSize++
|
||||
}
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getMatchedDomainsByCertId(ctx context.Context, cloudCertId string) ([]string, error) {
|
||||
domains := make([]string, 0)
|
||||
|
||||
// 获取指定证书可关联的域名
|
||||
// REF: https://www.volcengine.com/docs/6454/125711
|
||||
describeCertConfigReq := &vecdn.DescribeCertConfigRequest{
|
||||
CertId: cloudCertId,
|
||||
}
|
||||
describeCertConfigResp, err := d.sdkClient.DescribeCertConfig(describeCertConfigReq)
|
||||
d.logger.Debug("sdk request 'cdn.DescribeCertConfig'", slog.Any("request", describeCertConfigReq), slog.Any("response", describeCertConfigResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'cdn.DescribeCertConfig': %w", err)
|
||||
}
|
||||
|
||||
if describeCertConfigResp.Result.CertNotConfig != nil {
|
||||
for i := range describeCertConfigResp.Result.CertNotConfig {
|
||||
domains = append(domains, describeCertConfigResp.Result.CertNotConfig[i].Domain)
|
||||
}
|
||||
}
|
||||
|
||||
if describeCertConfigResp.Result.OtherCertConfig != nil {
|
||||
for i := range describeCertConfigResp.Result.OtherCertConfig {
|
||||
domains = append(domains, describeCertConfigResp.Result.OtherCertConfig[i].Domain)
|
||||
}
|
||||
}
|
||||
|
||||
if len(domains) == 0 {
|
||||
if len(describeCertConfigResp.Result.SpecifiedCertConfig) == 0 {
|
||||
return nil, errors.New("domains not found")
|
||||
}
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) bindCert(ctx context.Context, domain string, cloudCertId string) error {
|
||||
// 关联证书与加速域名
|
||||
// REF: https://www.volcengine.com/docs/6454/125712
|
||||
batchDeployCertReq := &vecdn.BatchDeployCertRequest{
|
||||
CertId: cloudCertId,
|
||||
Domain: domain,
|
||||
}
|
||||
batchDeployCertResp, err := d.sdkClient.BatchDeployCert(batchDeployCertReq)
|
||||
d.logger.Debug("sdk request 'cdn.BatchDeployCert'", slog.Any("request", batchDeployCertReq), slog.Any("response", batchDeployCertResp))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package volcenginelive
|
||||
|
||||
const (
|
||||
// 匹配模式:精确匹配。
|
||||
MatchPatternExact = "exact"
|
||||
// 匹配模式:通配符匹配。
|
||||
MatchPatternWildcard = "wildcard"
|
||||
)
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/volcengine-live"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
@@ -19,6 +20,9 @@ type SSLDeployerProviderConfig struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 火山引擎 AccessKeySecret。
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
// 域名匹配模式。
|
||||
// 零值时默认值 [MatchPatternExact]。
|
||||
MatchPattern string `json:"matchPattern,omitempty"`
|
||||
// 直播流域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
@@ -80,51 +84,37 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||
}
|
||||
|
||||
// 获取待部署的直播实例
|
||||
domains := make([]string, 0)
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
listDomainDetailPageNum := int32(1)
|
||||
listDomainDetailPageSize := int32(1000)
|
||||
listDomainDetailTotal := 0
|
||||
for {
|
||||
// 查询域名列表
|
||||
// REF: https://www.volcengine.com/docs/6469/1186277#%E6%9F%A5%E8%AF%A2%E5%9F%9F%E5%90%8D%E5%88%97%E8%A1%A8
|
||||
listDomainDetailReq := &velive.ListDomainDetailBody{
|
||||
PageNum: listDomainDetailPageNum,
|
||||
PageSize: listDomainDetailPageSize,
|
||||
}
|
||||
listDomainDetailResp, err := d.sdkClient.ListDomainDetail(ctx, listDomainDetailReq)
|
||||
d.logger.Debug("sdk request 'live.ListDomainDetail'", slog.Any("request", listDomainDetailReq), slog.Any("response", listDomainDetailResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'live.ListDomainDetail': %w", err)
|
||||
}
|
||||
switch d.config.MatchPattern {
|
||||
case "", MatchPatternExact:
|
||||
{
|
||||
domains = append(domains, d.config.Domain)
|
||||
}
|
||||
|
||||
if listDomainDetailResp.Result.DomainList != nil {
|
||||
for _, item := range listDomainDetailResp.Result.DomainList {
|
||||
// 仅匹配泛域名的下一级子域名
|
||||
wildcardDomain := strings.TrimPrefix(d.config.Domain, "*")
|
||||
if strings.HasSuffix(item.Domain, wildcardDomain) && !strings.Contains(strings.TrimSuffix(item.Domain, wildcardDomain), ".") {
|
||||
domains = append(domains, item.Domain)
|
||||
}
|
||||
case MatchPatternWildcard:
|
||||
{
|
||||
if strings.HasPrefix(d.config.Domain, "*.") {
|
||||
temp, err := d.getMatchedDomainsByWildcard(ctx, d.config.Domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
listDomainDetailLen := len(listDomainDetailResp.Result.DomainList)
|
||||
if listDomainDetailLen < int(listDomainDetailPageSize) || int(listDomainDetailResp.Result.Total) <= listDomainDetailTotal+listDomainDetailLen {
|
||||
break
|
||||
domains = temp
|
||||
} else {
|
||||
listDomainDetailPageNum++
|
||||
listDomainDetailTotal += listDomainDetailLen
|
||||
domains = append(domains, d.config.Domain)
|
||||
}
|
||||
}
|
||||
|
||||
if len(domains) == 0 {
|
||||
return nil, errors.New("domain not found")
|
||||
}
|
||||
} else {
|
||||
domains = append(domains, d.config.Domain)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported match pattern: '%s'", d.config.MatchPattern)
|
||||
}
|
||||
|
||||
if len(domains) > 0 {
|
||||
// 遍历绑定证书
|
||||
if len(domains) == 0 {
|
||||
d.logger.Info("no live domains to deploy")
|
||||
} else {
|
||||
d.logger.Info("found live domains to deploy", slog.Any("domains", domains))
|
||||
var errs []error
|
||||
|
||||
for _, domain := range domains {
|
||||
@@ -132,16 +122,7 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
// 绑定证书
|
||||
// REF: https://www.volcengine.com/docs/6469/1186278#%E7%BB%91%E5%AE%9A%E8%AF%81%E4%B9%A6
|
||||
bindCertReq := &velive.BindCertBody{
|
||||
ChainID: upres.CertId,
|
||||
Domain: domain,
|
||||
HTTPS: ve.Bool(true),
|
||||
}
|
||||
bindCertResp, err := d.sdkClient.BindCert(ctx, bindCertReq)
|
||||
d.logger.Debug("sdk request 'live.BindCert'", slog.Any("request", bindCertReq), slog.Any("response", bindCertResp))
|
||||
if err != nil {
|
||||
if err := d.bindCert(ctx, domain, upres.CertId); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
@@ -154,3 +135,67 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
|
||||
return &core.SSLDeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) getMatchedDomainsByWildcard(ctx context.Context, wildcardDomain string) ([]string, error) {
|
||||
domains := make([]string, 0)
|
||||
|
||||
// 遍历查询域名列表,获取匹配的域名
|
||||
// REF: https://www.volcengine.com/docs/6469/1186277#%E6%9F%A5%E8%AF%A2%E5%9F%9F%E5%90%8D%E5%88%97%E8%A1%A8
|
||||
listDomainDetailPageNum := int32(1)
|
||||
listDomainDetailPageSize := int32(1000)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
listDomainDetailReq := &velive.ListDomainDetailBody{
|
||||
DomainStatusList: ve.Int32Slice([]int32{0}),
|
||||
PageNum: listDomainDetailPageNum,
|
||||
PageSize: listDomainDetailPageSize,
|
||||
}
|
||||
listDomainDetailResp, err := d.sdkClient.ListDomainDetail(ctx, listDomainDetailReq)
|
||||
d.logger.Debug("sdk request 'live.ListDomainDetail'", slog.Any("request", listDomainDetailReq), slog.Any("response", listDomainDetailResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'live.ListDomainDetail': %w", err)
|
||||
}
|
||||
|
||||
if listDomainDetailResp.Result.DomainList != nil {
|
||||
for _, domain := range listDomainDetailResp.Result.DomainList {
|
||||
if xcert.MatchHostname(wildcardDomain, domain.Domain) {
|
||||
domains = append(domains, domain.Domain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(listDomainDetailResp.Result.DomainList) < int(listDomainDetailPageSize) {
|
||||
break
|
||||
} else {
|
||||
listDomainDetailPageNum++
|
||||
}
|
||||
}
|
||||
|
||||
if len(domains) == 0 {
|
||||
return nil, errors.New("domain not found")
|
||||
}
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) bindCert(ctx context.Context, domain string, cloudCertId string) error {
|
||||
// 绑定证书
|
||||
// REF: https://www.volcengine.com/docs/6469/1186278#%E7%BB%91%E5%AE%9A%E8%AF%81%E4%B9%A6
|
||||
bindCertReq := &velive.BindCertBody{
|
||||
ChainID: cloudCertId,
|
||||
Domain: domain,
|
||||
HTTPS: ve.Bool(true),
|
||||
}
|
||||
bindCertResp, err := d.sdkClient.BindCert(ctx, bindCertReq)
|
||||
d.logger.Debug("sdk request 'live.BindCert'", slog.Any("request", bindCertReq), slog.Any("response", bindCertResp))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package cert
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 检查目标主机名是否匹配待匹配主机名。
|
||||
//
|
||||
// 入参:
|
||||
// - match: 待匹配主机名。可以是泛域名,如 "*.example.com"。
|
||||
// - candidate: 目标主机名。如 "sub.example.com"。
|
||||
//
|
||||
// 出参:
|
||||
// - 是否匹配。
|
||||
func MatchHostname(match, candidate string) bool {
|
||||
if match == "" || candidate == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if !strings.Contains(match, "*") {
|
||||
return strings.EqualFold(match, candidate)
|
||||
}
|
||||
|
||||
mockCert := &x509.Certificate{}
|
||||
if ip := net.ParseIP(candidate); ip != nil {
|
||||
mockCert.IPAddresses = []net.IP{ip}
|
||||
} else {
|
||||
mockCert.DNSNames = []string{match}
|
||||
}
|
||||
return mockCert.VerifyHostname(candidate) == nil
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package cert_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
)
|
||||
|
||||
func TestCertUtil_Hostname(t *testing.T) {
|
||||
t.Run("MatchHostname", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
wildcard string
|
||||
target string
|
||||
expected bool
|
||||
}{
|
||||
{"*.example.com", "sub.example.com", true},
|
||||
{"*.example.com", "sub.sub.example.com", false},
|
||||
{"*.example.com", "example.com", false},
|
||||
|
||||
{"*.*.example.com", "a.b.example.com", false},
|
||||
{"*.*.example.com", "a.example.com", false},
|
||||
{"*.*.example.com", "a.b.c.example.com", false},
|
||||
|
||||
{"example.com", "example.com", true},
|
||||
{"example.com", "wrong.com", false},
|
||||
|
||||
{"", "example.com", false},
|
||||
{"*.example.com", "", false},
|
||||
|
||||
{"*.sub.example.com", "a.sub.example.com", true},
|
||||
{"*.sub.example.com", "a.b.sub.example.com", false},
|
||||
{"*.sub.example.com", "sub.example.com", false},
|
||||
|
||||
{"*.Example.COM", "sub.example.com", true},
|
||||
{"*.EXAMPLE.COM", "SUB.EXAMPLE.COM", true},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
result := xcert.MatchHostname(tc.wildcard, tc.target)
|
||||
status := "✓"
|
||||
pf := t.Logf
|
||||
if result != tc.expected {
|
||||
status = "✗"
|
||||
pf = t.Errorf
|
||||
}
|
||||
|
||||
pf("%s Wildcard: %-20s Target: %-20s Expected: %-5v Got: %-5v\n", status, tc.wildcard, tc.target, tc.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -49,12 +49,12 @@ const AccessConfigFormFieldsProvider1Panel = () => {
|
||||
<Form.Item
|
||||
name={[parentNamePath, "allowInsecureConnections"]}
|
||||
initialValue={initialValues.allowInsecureConnections}
|
||||
label={t("access.form.common_allow_insecure_conns.label")}
|
||||
label={t("access.form.shared_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")}
|
||||
checkedChildren={t("access.form.shared_allow_insecure_conns.switch.on")}
|
||||
unCheckedChildren={t("access.form.shared_allow_insecure_conns.switch.off")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
|
||||
@@ -39,12 +39,12 @@ const AccessConfigFormFieldsProviderAPISIX = () => {
|
||||
<Form.Item
|
||||
name={[parentNamePath, "allowInsecureConnections"]}
|
||||
initialValue={initialValues.allowInsecureConnections}
|
||||
label={t("access.form.common_allow_insecure_conns.label")}
|
||||
label={t("access.form.shared_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")}
|
||||
checkedChildren={t("access.form.shared_allow_insecure_conns.switch.on")}
|
||||
unCheckedChildren={t("access.form.shared_allow_insecure_conns.switch.off")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
|
||||
@@ -40,12 +40,12 @@ const AccessConfigFormFieldsProviderBaotaWAF = () => {
|
||||
<Form.Item
|
||||
name={[parentNamePath, "allowInsecureConnections"]}
|
||||
initialValue={initialValues.allowInsecureConnections}
|
||||
label={t("access.form.common_allow_insecure_conns.label")}
|
||||
label={t("access.form.shared_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")}
|
||||
checkedChildren={t("access.form.shared_allow_insecure_conns.switch.on")}
|
||||
unCheckedChildren={t("access.form.shared_allow_insecure_conns.switch.off")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
|
||||
@@ -49,12 +49,12 @@ const AccessConfigFormFieldsProviderCdnfly = () => {
|
||||
<Form.Item
|
||||
name={[parentNamePath, "allowInsecureConnections"]}
|
||||
initialValue={initialValues.allowInsecureConnections}
|
||||
label={t("access.form.common_allow_insecure_conns.label")}
|
||||
label={t("access.form.shared_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")}
|
||||
checkedChildren={t("access.form.shared_allow_insecure_conns.switch.on")}
|
||||
unCheckedChildren={t("access.form.shared_allow_insecure_conns.switch.off")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
|
||||
@@ -53,12 +53,12 @@ const AccessConfigFormFieldsProviderFlexCDN = () => {
|
||||
<Form.Item
|
||||
name={[parentNamePath, "allowInsecureConnections"]}
|
||||
initialValue={initialValues.allowInsecureConnections}
|
||||
label={t("access.form.common_allow_insecure_conns.label")}
|
||||
label={t("access.form.shared_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")}
|
||||
checkedChildren={t("access.form.shared_allow_insecure_conns.switch.on")}
|
||||
unCheckedChildren={t("access.form.shared_allow_insecure_conns.switch.off")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
|
||||
@@ -53,12 +53,12 @@ const AccessConfigFormFieldsProviderGoEdge = () => {
|
||||
<Form.Item
|
||||
name={[parentNamePath, "allowInsecureConnections"]}
|
||||
initialValue={initialValues.allowInsecureConnections}
|
||||
label={t("access.form.common_allow_insecure_conns.label")}
|
||||
label={t("access.form.shared_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")}
|
||||
checkedChildren={t("access.form.shared_allow_insecure_conns.switch.on")}
|
||||
unCheckedChildren={t("access.form.shared_allow_insecure_conns.switch.off")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
|
||||
@@ -34,12 +34,12 @@ const AccessConfigFormFieldsProviderKong = () => {
|
||||
<Form.Item
|
||||
name={[parentNamePath, "allowInsecureConnections"]}
|
||||
initialValue={initialValues.allowInsecureConnections}
|
||||
label={t("access.form.common_allow_insecure_conns.label")}
|
||||
label={t("access.form.shared_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")}
|
||||
checkedChildren={t("access.form.shared_allow_insecure_conns.switch.on")}
|
||||
unCheckedChildren={t("access.form.shared_allow_insecure_conns.switch.off")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
|
||||
@@ -45,12 +45,12 @@ const AccessConfigFormFieldsProviderLeCDN = () => {
|
||||
<Form.Item
|
||||
name={[parentNamePath, "allowInsecureConnections"]}
|
||||
initialValue={initialValues.allowInsecureConnections}
|
||||
label={t("access.form.common_allow_insecure_conns.label")}
|
||||
label={t("access.form.shared_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")}
|
||||
checkedChildren={t("access.form.shared_allow_insecure_conns.switch.on")}
|
||||
unCheckedChildren={t("access.form.shared_allow_insecure_conns.switch.off")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
|
||||
@@ -49,12 +49,12 @@ const AccessConfigFormFieldsProviderProxmoxVE = () => {
|
||||
<Form.Item
|
||||
name={[parentNamePath, "allowInsecureConnections"]}
|
||||
initialValue={initialValues.allowInsecureConnections}
|
||||
label={t("access.form.common_allow_insecure_conns.label")}
|
||||
label={t("access.form.shared_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")}
|
||||
checkedChildren={t("access.form.shared_allow_insecure_conns.switch.on")}
|
||||
unCheckedChildren={t("access.form.shared_allow_insecure_conns.switch.off")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
|
||||
+60
-10
@@ -1,12 +1,17 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input } from "antd";
|
||||
import { Form, Input, Radio } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import Show from "@/components/Show";
|
||||
import { validDomainName } from "@/utils/validators";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const MATCH_PATTERN_EXACT = "exact" as const;
|
||||
const MATCH_PATTERN_WILDCARD = "wildcard" as const;
|
||||
const MATCH_PATTERN_CERTSAN = "certsan" as const;
|
||||
|
||||
const BizDeployNodeConfigFieldsProviderTencentCloudCDN = () => {
|
||||
const { i18n, t } = useTranslation();
|
||||
|
||||
@@ -15,8 +20,11 @@ const BizDeployNodeConfigFieldsProviderTencentCloudCDN = () => {
|
||||
[parentNamePath]: getSchema({ i18n }),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
const formInst = Form.useFormInstance();
|
||||
const initialValues = getInitialValues();
|
||||
|
||||
const fieldDomainPattern = Form.useWatch([parentNamePath, "matchPattern"], { form: formInst, preserve: true });
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
@@ -30,20 +38,39 @@ const BizDeployNodeConfigFieldsProviderTencentCloudCDN = () => {
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "domain"]}
|
||||
initialValue={initialValues.domain}
|
||||
label={t("workflow_node.deploy.form.tencentcloud_cdn_domain.label")}
|
||||
name={[parentNamePath, "matchPattern"]}
|
||||
initialValue={initialValues.matchPattern}
|
||||
label={t("workflow_node.deploy.form.shared_domain_match_pattern.label")}
|
||||
extra={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.shared_domain_match_pattern.help_wildcard") }}></span>}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_cdn_domain.tooltip") }}></span>}
|
||||
>
|
||||
<Input placeholder={t("workflow_node.deploy.form.tencentcloud_cdn_domain.placeholder")} />
|
||||
<Radio.Group
|
||||
options={[MATCH_PATTERN_EXACT, MATCH_PATTERN_WILDCARD, MATCH_PATTERN_CERTSAN].map((s) => ({
|
||||
key: s,
|
||||
label: t(`workflow_node.deploy.form.shared_domain_match_pattern.option.${s}.label`),
|
||||
value: s,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Show when={fieldDomainPattern !== MATCH_PATTERN_CERTSAN}>
|
||||
<Form.Item
|
||||
name={[parentNamePath, "domain"]}
|
||||
initialValue={initialValues.domain}
|
||||
label={t("workflow_node.deploy.form.tencentcloud_cdn_domain.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_cdn_domain.tooltip") }}></span>}
|
||||
>
|
||||
<Input placeholder={t("workflow_node.deploy.form.tencentcloud_cdn_domain.placeholder")} />
|
||||
</Form.Item>
|
||||
</Show>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
matchPattern: MATCH_PATTERN_EXACT,
|
||||
domain: "",
|
||||
};
|
||||
};
|
||||
@@ -51,10 +78,33 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
|
||||
const { t } = i18n;
|
||||
|
||||
return z.object({
|
||||
endpoint: z.string().nullish(),
|
||||
domain: z.string().refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
|
||||
});
|
||||
return z
|
||||
.object({
|
||||
endpoint: z.string().nullish(),
|
||||
matchPattern: z.enum(
|
||||
[MATCH_PATTERN_EXACT, MATCH_PATTERN_WILDCARD, MATCH_PATTERN_CERTSAN],
|
||||
t("workflow_node.deploy.form.shared_domain_match_pattern.placeholder")
|
||||
),
|
||||
domain: z.string().nullish(),
|
||||
})
|
||||
.superRefine((values, ctx) => {
|
||||
if (values.matchPattern) {
|
||||
switch (values.matchPattern) {
|
||||
case MATCH_PATTERN_EXACT:
|
||||
case MATCH_PATTERN_WILDCARD:
|
||||
{
|
||||
if (!validDomainName(values.domain!, { allowWildcard: true })) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: t("common.errmsg.domain_invalid"),
|
||||
path: ["domain"],
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(BizDeployNodeConfigFieldsProviderTencentCloudCDN, {
|
||||
|
||||
+60
-10
@@ -1,12 +1,17 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input } from "antd";
|
||||
import { Form, Input, Radio } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import Show from "@/components/Show";
|
||||
import { validDomainName } from "@/utils/validators";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const MATCH_PATTERN_EXACT = "exact" as const;
|
||||
const MATCH_PATTERN_WILDCARD = "wildcard" as const;
|
||||
const MATCH_PATTERN_CERTSAN = "certsan" as const;
|
||||
|
||||
const BizDeployNodeConfigFieldsProviderTencentCloudECDN = () => {
|
||||
const { i18n, t } = useTranslation();
|
||||
|
||||
@@ -15,8 +20,11 @@ const BizDeployNodeConfigFieldsProviderTencentCloudECDN = () => {
|
||||
[parentNamePath]: getSchema({ i18n }),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
const formInst = Form.useFormInstance();
|
||||
const initialValues = getInitialValues();
|
||||
|
||||
const fieldDomainPattern = Form.useWatch([parentNamePath, "matchPattern"], { form: formInst, preserve: true });
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
@@ -30,20 +38,39 @@ const BizDeployNodeConfigFieldsProviderTencentCloudECDN = () => {
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "domain"]}
|
||||
initialValue={initialValues.domain}
|
||||
label={t("workflow_node.deploy.form.tencentcloud_ecdn_domain.label")}
|
||||
name={[parentNamePath, "matchPattern"]}
|
||||
initialValue={initialValues.matchPattern}
|
||||
label={t("workflow_node.deploy.form.shared_domain_match_pattern.label")}
|
||||
extra={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.shared_domain_match_pattern.help_wildcard") }}></span>}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_ecdn_domain.tooltip") }}></span>}
|
||||
>
|
||||
<Input placeholder={t("workflow_node.deploy.form.tencentcloud_ecdn_domain.placeholder")} />
|
||||
<Radio.Group
|
||||
options={[MATCH_PATTERN_EXACT, MATCH_PATTERN_WILDCARD, MATCH_PATTERN_CERTSAN].map((s) => ({
|
||||
key: s,
|
||||
label: t(`workflow_node.deploy.form.shared_domain_match_pattern.option.${s}.label`),
|
||||
value: s,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Show when={fieldDomainPattern !== MATCH_PATTERN_CERTSAN}>
|
||||
<Form.Item
|
||||
name={[parentNamePath, "domain"]}
|
||||
initialValue={initialValues.domain}
|
||||
label={t("workflow_node.deploy.form.tencentcloud_ecdn_domain.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.tencentcloud_ecdn_domain.tooltip") }}></span>}
|
||||
>
|
||||
<Input placeholder={t("workflow_node.deploy.form.tencentcloud_ecdn_domain.placeholder")} />
|
||||
</Form.Item>
|
||||
</Show>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
matchPattern: MATCH_PATTERN_EXACT,
|
||||
domain: "",
|
||||
};
|
||||
};
|
||||
@@ -51,10 +78,33 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
|
||||
const { t } = i18n;
|
||||
|
||||
return z.object({
|
||||
endpoint: z.string().nullish(),
|
||||
domain: z.string().refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
|
||||
});
|
||||
return z
|
||||
.object({
|
||||
endpoint: z.string().nullish(),
|
||||
matchPattern: z.enum(
|
||||
[MATCH_PATTERN_EXACT, MATCH_PATTERN_WILDCARD, MATCH_PATTERN_CERTSAN],
|
||||
t("workflow_node.deploy.form.shared_domain_match_pattern.placeholder")
|
||||
),
|
||||
domain: z.string().nullish(),
|
||||
})
|
||||
.superRefine((values, ctx) => {
|
||||
if (values.matchPattern) {
|
||||
switch (values.matchPattern) {
|
||||
case MATCH_PATTERN_EXACT:
|
||||
case MATCH_PATTERN_WILDCARD:
|
||||
{
|
||||
if (!validDomainName(values.domain!, { allowWildcard: true })) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: t("common.errmsg.domain_invalid"),
|
||||
path: ["domain"],
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(BizDeployNodeConfigFieldsProviderTencentCloudECDN, {
|
||||
|
||||
+59
-9
@@ -1,12 +1,17 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input } from "antd";
|
||||
import { Form, Input, Radio } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import Show from "@/components/Show";
|
||||
import { validDomainName } from "@/utils/validators";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const MATCH_PATTERN_EXACT = "exact" as const;
|
||||
const MATCH_PATTERN_WILDCARD = "wildcard" as const;
|
||||
const MATCH_PATTERN_CERTSAN = "certsan" as const;
|
||||
|
||||
const BizDeployNodeConfigFieldsProviderVolcEngineCDN = () => {
|
||||
const { i18n, t } = useTranslation();
|
||||
|
||||
@@ -15,25 +20,47 @@ const BizDeployNodeConfigFieldsProviderVolcEngineCDN = () => {
|
||||
[parentNamePath]: getSchema({ i18n }),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
const formInst = Form.useFormInstance();
|
||||
const initialValues = getInitialValues();
|
||||
|
||||
const fieldDomainPattern = Form.useWatch([parentNamePath, "matchPattern"], { form: formInst, preserve: true });
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
name={[parentNamePath, "domain"]}
|
||||
initialValue={initialValues.domain}
|
||||
label={t("workflow_node.deploy.form.volcengine_cdn_domain.label")}
|
||||
name={[parentNamePath, "matchPattern"]}
|
||||
initialValue={initialValues.matchPattern}
|
||||
label={t("workflow_node.deploy.form.shared_domain_match_pattern.label")}
|
||||
extra={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.shared_domain_match_pattern.help_wildcard") }}></span>}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.volcengine_cdn_domain.tooltip") }}></span>}
|
||||
>
|
||||
<Input placeholder={t("workflow_node.deploy.form.volcengine_cdn_domain.placeholder")} />
|
||||
<Radio.Group
|
||||
options={[MATCH_PATTERN_EXACT, MATCH_PATTERN_WILDCARD, MATCH_PATTERN_CERTSAN].map((s) => ({
|
||||
key: s,
|
||||
label: t(`workflow_node.deploy.form.shared_domain_match_pattern.option.${s}.label`),
|
||||
value: s,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Show when={fieldDomainPattern !== MATCH_PATTERN_CERTSAN}>
|
||||
<Form.Item
|
||||
name={[parentNamePath, "domain"]}
|
||||
initialValue={initialValues.domain}
|
||||
label={t("workflow_node.deploy.form.volcengine_cdn_domain.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.volcengine_cdn_domain.tooltip") }}></span>}
|
||||
>
|
||||
<Input placeholder={t("workflow_node.deploy.form.volcengine_cdn_domain.placeholder")} />
|
||||
</Form.Item>
|
||||
</Show>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
matchPattern: MATCH_PATTERN_EXACT,
|
||||
domain: "",
|
||||
};
|
||||
};
|
||||
@@ -41,9 +68,32 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
|
||||
const { t } = i18n;
|
||||
|
||||
return z.object({
|
||||
domain: z.string().refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
|
||||
});
|
||||
return z
|
||||
.object({
|
||||
matchPattern: z.enum(
|
||||
[MATCH_PATTERN_EXACT, MATCH_PATTERN_WILDCARD, MATCH_PATTERN_CERTSAN],
|
||||
t("workflow_node.deploy.form.shared_domain_match_pattern.placeholder")
|
||||
),
|
||||
domain: z.string().nullish(),
|
||||
})
|
||||
.superRefine((values, ctx) => {
|
||||
if (values.matchPattern) {
|
||||
switch (values.matchPattern) {
|
||||
case MATCH_PATTERN_EXACT:
|
||||
case MATCH_PATTERN_WILDCARD:
|
||||
{
|
||||
if (!validDomainName(values.domain!, { allowWildcard: true })) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: t("common.errmsg.domain_invalid"),
|
||||
path: ["domain"],
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(BizDeployNodeConfigFieldsProviderVolcEngineCDN, {
|
||||
|
||||
+43
-4
@@ -1,5 +1,5 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input } from "antd";
|
||||
import { Form, Input, Radio } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -7,6 +7,9 @@ import { validDomainName } from "@/utils/validators";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const MATCH_PATTERN_EXACT = "exact" as const;
|
||||
const MATCH_PATTERN_WILDCARD = "wildcard" as const;
|
||||
|
||||
const BizDeployNodeConfigFieldsProviderVolcEngineLive = () => {
|
||||
const { i18n, t } = useTranslation();
|
||||
|
||||
@@ -19,6 +22,21 @@ const BizDeployNodeConfigFieldsProviderVolcEngineLive = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
name={[parentNamePath, "matchPattern"]}
|
||||
initialValue={initialValues.matchPattern}
|
||||
label={t("workflow_node.deploy.form.shared_domain_match_pattern.label")}
|
||||
rules={[formRule]}
|
||||
>
|
||||
<Radio.Group
|
||||
options={[MATCH_PATTERN_EXACT, MATCH_PATTERN_WILDCARD].map((s) => ({
|
||||
key: s,
|
||||
label: t(`workflow_node.deploy.form.shared_domain_match_pattern.option.${s}.label`),
|
||||
value: s,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "domain"]}
|
||||
initialValue={initialValues.domain}
|
||||
@@ -34,6 +52,7 @@ const BizDeployNodeConfigFieldsProviderVolcEngineLive = () => {
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
matchPattern: MATCH_PATTERN_EXACT,
|
||||
domain: "",
|
||||
};
|
||||
};
|
||||
@@ -41,9 +60,29 @@ const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
|
||||
const { t } = i18n;
|
||||
|
||||
return z.object({
|
||||
domain: z.string().refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
|
||||
});
|
||||
return z
|
||||
.object({
|
||||
matchPattern: z.enum([MATCH_PATTERN_EXACT, MATCH_PATTERN_WILDCARD], t("workflow_node.deploy.form.shared_domain_match_pattern.placeholder")),
|
||||
domain: z.string().nullish(),
|
||||
})
|
||||
.superRefine((values, ctx) => {
|
||||
if (values.matchPattern) {
|
||||
switch (values.matchPattern) {
|
||||
case MATCH_PATTERN_EXACT:
|
||||
case MATCH_PATTERN_WILDCARD:
|
||||
{
|
||||
if (!validDomainName(values.domain!, { allowWildcard: true })) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: t("common.errmsg.domain_invalid"),
|
||||
path: ["domain"],
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(BizDeployNodeConfigFieldsProviderVolcEngineLive, {
|
||||
|
||||
@@ -42,6 +42,9 @@
|
||||
"access.form.provider.placeholder": "Please select a provider",
|
||||
"access.form.provider.help": "DNS provider: The provider that hosts your domain names and manages your DNS records. <br>Hosting provider: The provider that hosts your servers or cloud services for deploying certificates.",
|
||||
"access.form.provider.search.placeholder": "Search provider ...",
|
||||
"access.form.shared_allow_insecure_conns.label": "Insecure SSL/TLS connections",
|
||||
"access.form.shared_allow_insecure_conns.switch.on": "Allow",
|
||||
"access.form.shared_allow_insecure_conns.switch.off": "Disallow",
|
||||
"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.",
|
||||
@@ -50,9 +53,6 @@
|
||||
"access.form.1panel_api_key.label": "1Panel API key",
|
||||
"access.form.1panel_api_key.placeholder": "Please enter 1Panel API key",
|
||||
"access.form.1panel_api_key.tooltip": "For more information, see <a href=\"https://docs.1panel.pro/dev_manual/api_manual/\" target=\"_blank\">https://docs.1panel.pro/dev_manual/api_manual/</a>",
|
||||
"access.form.common_allow_insecure_conns.label": "Insecure SSL/TLS connections",
|
||||
"access.form.common_allow_insecure_conns.switch.on": "Allow",
|
||||
"access.form.common_allow_insecure_conns.switch.off": "Disallow",
|
||||
"access.form.acmeca_endpoint.label": "Endpoint",
|
||||
"access.form.acmeca_endpoint.placeholder": "Please enter endpoint",
|
||||
"access.form.acmeca_endpoint.tooltip": "For more information, see <a href=\"https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.1\" target=\"_blank\">https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.1</a>",
|
||||
|
||||
@@ -166,6 +166,12 @@
|
||||
"workflow_node.deploy.form.provider_access.label": "Hosting provider credential",
|
||||
"workflow_node.deploy.form.provider_access.placeholder": "Please select an credential of hosting provider",
|
||||
"workflow_node.deploy.form.provider_access.button": "Create",
|
||||
"workflow_node.deploy.form.shared_domain_match_pattern.label": "Domain match pattern",
|
||||
"workflow_node.deploy.form.shared_domain_match_pattern.placeholder": "Please select domain match pattern",
|
||||
"workflow_node.deploy.form.shared_domain_match_pattern.option.exact.label": "Exact matches",
|
||||
"workflow_node.deploy.form.shared_domain_match_pattern.option.wildcard.label": "Wildcard matches",
|
||||
"workflow_node.deploy.form.shared_domain_match_pattern.option.certsan.label": "via Certificate",
|
||||
"workflow_node.deploy.form.shared_domain_match_pattern.help_wildcard": "Notes: For sites that support wildcards, <strong>exact matches</strong> will only apply to the site itself and will not include its subdomains.",
|
||||
"workflow_node.deploy.form.1panel_console_auto_restart.label": "Auto restart 1Panel after deployment",
|
||||
"workflow_node.deploy.form.1panel_site_node_name.label": "1Panel node name (Optional)",
|
||||
"workflow_node.deploy.form.1panel_site_node_name.placeholder": "Please enter 1Panel node name",
|
||||
|
||||
@@ -41,9 +41,9 @@
|
||||
"access.form.provider.placeholder": "请选择提供商",
|
||||
"access.form.provider.help": "提供商分为两种类型:<br>【DNS 提供商】你的 DNS 托管方,通常等同于域名注册商,用于在申请证书时管理域名解析记录。<br>【主机提供商】你的服务器或云服务的托管方,用于部署签发的证书。",
|
||||
"access.form.provider.search.placeholder": "搜索提供商……",
|
||||
"access.form.common_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误",
|
||||
"access.form.common_allow_insecure_conns.switch.on": "允许",
|
||||
"access.form.common_allow_insecure_conns.switch.off": "不允许",
|
||||
"access.form.shared_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误",
|
||||
"access.form.shared_allow_insecure_conns.switch.on": "允许",
|
||||
"access.form.shared_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": "提示:请勿包含安全入口后缀。",
|
||||
|
||||
@@ -165,6 +165,12 @@
|
||||
"workflow_node.deploy.form.provider_access.label": "主机提供商授权",
|
||||
"workflow_node.deploy.form.provider_access.placeholder": "请选择主机提供商授权",
|
||||
"workflow_node.deploy.form.provider_access.button": "新建",
|
||||
"workflow_node.deploy.form.shared_domain_match_pattern.label": "域名匹配模式",
|
||||
"workflow_node.deploy.form.shared_domain_match_pattern.placeholder": "请选择域名匹配模式",
|
||||
"workflow_node.deploy.form.shared_domain_match_pattern.option.exact.label": "精确匹配",
|
||||
"workflow_node.deploy.form.shared_domain_match_pattern.option.wildcard.label": "通配符匹配(泛域名)",
|
||||
"workflow_node.deploy.form.shared_domain_match_pattern.option.certsan.label": "通过证书自动匹配",
|
||||
"workflow_node.deploy.form.shared_domain_match_pattern.help_wildcard": "注意:对于支持泛解析的站点,<strong>精确匹配</strong>仅适用于该泛解析站点本身,而不包括符合条件的子域名。",
|
||||
"workflow_node.deploy.form.1panel_console_auto_restart.label": "部署后自动重启 1Panel 服务",
|
||||
"workflow_node.deploy.form.1panel_site_node_name.label": "1Panel 子节点名称(可选)",
|
||||
"workflow_node.deploy.form.1panel_site_node_name.placeholder": "请输入 1Panel 子节点名称",
|
||||
|
||||
Reference in New Issue
Block a user