mirror of
https://github.com/certimate-go/certimate.git
synced 2026-06-22 21:05:48 +08:00
Merge pull request #954 from fudiwei/dev
This commit is contained in:
commit
44596652d6
1
go.mod
1
go.mod
@ -140,6 +140,7 @@ require (
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/vultr/govultr/v3 v3.21.1 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.mongodb.org/mongo-driver v1.17.2 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
|
||||
2
go.sum
2
go.sum
@ -887,6 +887,8 @@ github.com/volcengine/volc-sdk-golang v1.0.219 h1:IqMCdpJ6uuqS2ZZQYUVHKVd+2H1au0
|
||||
github.com/volcengine/volc-sdk-golang v1.0.219/go.mod h1:zHJlaqiMbIB+0mcrsZPTwOb3FB7S/0MCfqlnO8R7hlM=
|
||||
github.com/volcengine/volcengine-go-sdk v1.1.30 h1:D85LL8euXgxwsZzwQ0azT/hannEsiKxKY4h/7/HU764=
|
||||
github.com/volcengine/volcengine-go-sdk v1.1.30/go.mod h1:EyKoi6t6eZxoPNGr2GdFCZti2Skd7MO3eUzx7TtSvNo=
|
||||
github.com/vultr/govultr/v3 v3.21.1 h1:0cnA8fXiqayPGbAlNHaW+5oCQjpDNkkAm3Nt3LOHplM=
|
||||
github.com/vultr/govultr/v3 v3.21.1/go.mod h1:9WwnWGCKnwDlNjHjtt+j+nP+0QWq6hQXzaHgddqrLWY=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
|
||||
29
internal/certapply/applicators/sp_vultr.go
Normal file
29
internal/certapply/applicators/sp_vultr.go
Normal file
@ -0,0 +1,29 @@
|
||||
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/vultr"
|
||||
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := ACMEDns01Registries.Register(domain.ACMEDns01ProviderTypeVultr, func(options *ProviderFactoryOptions) (challenge.Provider, error) {
|
||||
credentials := domain.AccessConfigForVultr{}
|
||||
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
|
||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||
}
|
||||
|
||||
provider, err := vultr.NewChallengeProvider(&vultr.ChallengeProviderConfig{
|
||||
ApiKey: credentials.ApiKey,
|
||||
DnsPropagationTimeout: options.DnsPropagationTimeout,
|
||||
DnsTTL: options.DnsTTL,
|
||||
})
|
||||
return provider, err
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@ -15,6 +15,7 @@ import (
|
||||
var acmeDirUrls = map[string]string{
|
||||
string(domain.CAProviderTypeLetsEncrypt): "https://acme-v02.api.letsencrypt.org/directory",
|
||||
string(domain.CAProviderTypeLetsEncryptStaging): "https://acme-staging-v02.api.letsencrypt.org/directory",
|
||||
string(domain.CAProviderTypeActalisSSL): "https://acme-api.actalis.com/acme/directory",
|
||||
string(domain.CAProviderTypeBuypass): "https://api.buypass.com/acme/directory",
|
||||
string(domain.CAProviderTypeGoogleTrustServices): "https://dv.acme-v02.api.pki.goog/directory",
|
||||
string(domain.CAProviderTypeSSLCom): "https://acme.ssl.com/sslcom-dv-rsa",
|
||||
|
||||
@ -20,6 +20,7 @@ func init() {
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
ResourceGroupId: credentials.ResourceGroupId,
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
|
||||
@ -20,6 +20,7 @@ func init() {
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeySecret: credentials.AccessKeySecret,
|
||||
ResourceGroupId: credentials.ResourceGroupId,
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
|
||||
})
|
||||
return provider, err
|
||||
@ -2,6 +2,7 @@ package deployers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/certimate-go/certimate/internal/domain"
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
@ -16,6 +17,50 @@ func init() {
|
||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||
}
|
||||
|
||||
parseKeyValueMap := func(s string) (map[string]string, error) {
|
||||
result := make(map[string]string)
|
||||
|
||||
lines := strings.Split(s, "\n")
|
||||
for i, line := range lines {
|
||||
if strings.TrimSpace(line) == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
pos := strings.Index(line, ":")
|
||||
if pos == -1 {
|
||||
return nil, fmt.Errorf("invalid line format at line %d", i+1)
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(line[:pos])
|
||||
value := strings.TrimSpace(line[pos+1:])
|
||||
if key == "" {
|
||||
return nil, fmt.Errorf("invalid key at line %d", i+1)
|
||||
}
|
||||
|
||||
result[key] = value
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
secretAnnotations := make(map[string]string)
|
||||
if secretAnnotationsString := xmaps.GetString(options.ProviderExtendedConfig, "secretAnnotations"); secretAnnotationsString != "" {
|
||||
temp, err := parseKeyValueMap(secretAnnotationsString)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse kubernetes secret annotations: %w", err)
|
||||
}
|
||||
secretAnnotations = temp
|
||||
}
|
||||
|
||||
secretLabels := make(map[string]string)
|
||||
if secretLabelsString := xmaps.GetString(options.ProviderExtendedConfig, "secretLabels"); secretLabelsString != "" {
|
||||
temp, err := parseKeyValueMap(secretLabelsString)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse kubernetes secret labels: %w", err)
|
||||
}
|
||||
secretLabels = temp
|
||||
}
|
||||
|
||||
provider, err := k8ssecret.NewSSLDeployerProvider(&k8ssecret.SSLDeployerProviderConfig{
|
||||
KubeConfig: credentials.KubeConfig,
|
||||
Namespace: xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "namespace", "default"),
|
||||
@ -23,6 +68,8 @@ func init() {
|
||||
SecretType: xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "secretType", "kubernetes.io/tls"),
|
||||
SecretDataKeyForCrt: xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "secretDataKeyForCrt", "tls.crt"),
|
||||
SecretDataKeyForKey: xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "secretDataKeyForKey", "tls.key"),
|
||||
SecretAnnotations: secretAnnotations,
|
||||
SecretLabels: secretLabels,
|
||||
})
|
||||
return provider, err
|
||||
}); err != nil {
|
||||
|
||||
@ -43,6 +43,7 @@ func init() {
|
||||
WebhookData: xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "webhookData", credentials.DataString),
|
||||
Method: credentials.Method,
|
||||
Headers: mergedHeaders,
|
||||
Timeout: xmaps.GetInt32(options.ProviderExtendedConfig, "timeout"),
|
||||
AllowInsecureConnections: credentials.AllowInsecureConnections,
|
||||
})
|
||||
return provider, err
|
||||
|
||||
@ -44,6 +44,10 @@ type AccessConfigForACMEHttpReq struct {
|
||||
Password string `json:"password,omitempty"`
|
||||
}
|
||||
|
||||
type AccessConfigForActalisSSL struct {
|
||||
AccessConfigForACMEExternalAccountBinding
|
||||
}
|
||||
|
||||
type AccessConfigForAliyun struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
@ -402,6 +406,10 @@ type AccessConfigForVolcEngine struct {
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
}
|
||||
|
||||
type AccessConfigForVultr struct {
|
||||
ApiKey string `json:"apiKey"`
|
||||
}
|
||||
|
||||
type AccessConfigForWangsu struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
|
||||
@ -13,6 +13,7 @@ const (
|
||||
AccessProviderTypeACMECA = AccessProviderType("acmeca")
|
||||
AccessProviderTypeACMEDNS = AccessProviderType("acmedns")
|
||||
AccessProviderTypeACMEHttpReq = AccessProviderType("acmehttpreq")
|
||||
AccessProviderTypeActalisSSL = AccessProviderType("actalisssl")
|
||||
AccessProviderTypeAkamai = AccessProviderType("akamai") // Akamai(预留)
|
||||
AccessProviderTypeAliyun = AccessProviderType("aliyun")
|
||||
AccessProviderTypeAPISIX = AccessProviderType("apisix")
|
||||
@ -86,6 +87,7 @@ const (
|
||||
AccessProviderTypeUpyun = AccessProviderType("upyun")
|
||||
AccessProviderTypeVercel = AccessProviderType("vercel")
|
||||
AccessProviderTypeVolcEngine = AccessProviderType("volcengine")
|
||||
AccessProviderTypeVultr = AccessProviderType("vultr")
|
||||
AccessProviderTypeWangsu = AccessProviderType("wangsu")
|
||||
AccessProviderTypeWebhook = AccessProviderType("webhook")
|
||||
AccessProviderTypeWeComBot = AccessProviderType("wecombot")
|
||||
@ -104,6 +106,7 @@ NOTICE: If you add new constant, please keep ASCII order.
|
||||
*/
|
||||
const (
|
||||
CAProviderTypeACMECA = CAProviderType(AccessProviderTypeACMECA)
|
||||
CAProviderTypeActalisSSL = CAProviderType(AccessProviderTypeActalisSSL)
|
||||
CAProviderTypeBuypass = CAProviderType(AccessProviderTypeBuypass)
|
||||
CAProviderTypeGoogleTrustServices = CAProviderType(AccessProviderTypeGoogleTrustServices)
|
||||
CAProviderTypeLetsEncrypt = CAProviderType(AccessProviderTypeLetsEncrypt)
|
||||
@ -171,6 +174,7 @@ const (
|
||||
ACMEDns01ProviderTypeVercel = ACMEDns01ProviderType(AccessProviderTypeVercel)
|
||||
ACMEDns01ProviderTypeVolcEngine = ACMEDns01ProviderType(AccessProviderTypeVolcEngine) // 兼容旧值,等同于 [ACMEDns01ProviderTypeVolcEngineDNS]
|
||||
ACMEDns01ProviderTypeVolcEngineDNS = ACMEDns01ProviderType(AccessProviderTypeVolcEngine + "-dns")
|
||||
ACMEDns01ProviderTypeVultr = ACMEDns01ProviderType(AccessProviderTypeVultr)
|
||||
ACMEDns01ProviderTypeWestcn = ACMEDns01ProviderType(AccessProviderTypeWestcn)
|
||||
)
|
||||
|
||||
|
||||
@ -43,6 +43,7 @@ func init() {
|
||||
WebhookData: xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "webhookData", credentials.DataString),
|
||||
Method: credentials.Method,
|
||||
Headers: mergedHeaders,
|
||||
Timeout: xmaps.GetInt32(options.ProviderExtendedConfig, "timeout"),
|
||||
AllowInsecureConnections: credentials.AllowInsecureConnections,
|
||||
})
|
||||
return provider, err
|
||||
|
||||
@ -111,7 +111,9 @@ func (s *WorkflowService) StartRun(ctx context.Context, req *dtos.WorkflowStartR
|
||||
workflowRun = resp
|
||||
}
|
||||
|
||||
s.dispatcher.Start(ctx, workflowRun.Id)
|
||||
if err := s.dispatcher.Start(ctx, workflowRun.Id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dtos.WorkflowStartRunResp{RunId: workflowRun.Id}, nil
|
||||
}
|
||||
@ -131,7 +133,9 @@ func (s *WorkflowService) CancelRun(ctx context.Context, req *dtos.WorkflowCance
|
||||
return nil, errors.New("workflow run is not pending or processing")
|
||||
}
|
||||
|
||||
s.dispatcher.Cancel(ctx, workflowRun.Id)
|
||||
if err := s.dispatcher.Cancel(ctx, workflowRun.Id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dtos.WorkflowCancelRunResp{}, nil
|
||||
}
|
||||
|
||||
@ -27,6 +27,9 @@ type NotifierProviderConfig struct {
|
||||
Method string `json:"method,omitempty"`
|
||||
// 请求标头。
|
||||
Headers map[string]string `json:"headers,omitempty"`
|
||||
// 请求超时(单位:秒)。
|
||||
// 零值时默认值 30。
|
||||
Timeout int32 `json:"timeout,omitempty"`
|
||||
// 是否允许不安全的连接。
|
||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||
}
|
||||
@ -48,6 +51,9 @@ func NewNotifierProvider(config *NotifierProviderConfig) (*NotifierProvider, err
|
||||
SetTimeout(30 * time.Second).
|
||||
SetRetryCount(3).
|
||||
SetRetryWaitTime(5 * time.Second)
|
||||
if config.Timeout > 0 {
|
||||
client.SetTimeout(time.Duration(config.Timeout) * time.Second)
|
||||
}
|
||||
if config.AllowInsecureConnections {
|
||||
client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
|
||||
}
|
||||
|
||||
38
pkg/core/ssl-applicator/acme-dns01/providers/vultr/vultr.go
Normal file
38
pkg/core/ssl-applicator/acme-dns01/providers/vultr/vultr.go
Normal file
@ -0,0 +1,38 @@
|
||||
package vultr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/providers/dns/vultr"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
)
|
||||
|
||||
type ChallengeProviderConfig struct {
|
||||
ApiKey string `json:"apiKey"`
|
||||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
|
||||
DnsTTL int32 `json:"dnsTTL,omitempty"`
|
||||
}
|
||||
|
||||
func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the acme challenge provider is nil")
|
||||
}
|
||||
|
||||
providerConfig := vultr.NewDefaultConfig()
|
||||
providerConfig.APIKey = config.ApiKey
|
||||
if config.DnsPropagationTimeout != 0 {
|
||||
providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
|
||||
}
|
||||
if config.DnsTTL != 0 {
|
||||
providerConfig.TTL = int(config.DnsTTL)
|
||||
}
|
||||
|
||||
provider, err := vultr.NewDNSProviderConfig(providerConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
@ -5,13 +5,16 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
alicdn "github.com/alibabacloud-go/cdn-20180510/v5/client"
|
||||
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
"github.com/samber/lo"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/aliyun-cas"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
@ -21,14 +24,17 @@ type SSLDeployerProviderConfig struct {
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
// 阿里云资源组 ID。
|
||||
ResourceGroupId string `json:"resourceGroupId,omitempty"`
|
||||
// 阿里云地域。
|
||||
Region string `json:"region"`
|
||||
// 加速域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
|
||||
type SSLDeployerProvider struct {
|
||||
config *SSLDeployerProviderConfig
|
||||
logger *slog.Logger
|
||||
sdkClient *alicdn.Client
|
||||
config *SSLDeployerProviderConfig
|
||||
logger *slog.Logger
|
||||
sdkClient *alicdn.Client
|
||||
sslManager core.SSLManager
|
||||
}
|
||||
|
||||
var _ core.SSLDeployer = (*SSLDeployerProvider)(nil)
|
||||
@ -43,10 +49,23 @@ func NewSSLDeployerProvider(config *SSLDeployerProviderConfig) (*SSLDeployerProv
|
||||
return nil, fmt.Errorf("could not create sdk client: %w", err)
|
||||
}
|
||||
|
||||
sslmgr, err := sslmgrsp.NewSSLManagerProvider(&sslmgrsp.SSLManagerProviderConfig{
|
||||
AccessKeyId: config.AccessKeyId,
|
||||
AccessKeySecret: config.AccessKeySecret,
|
||||
ResourceGroupId: config.ResourceGroupId,
|
||||
Region: lo.
|
||||
If(config.Region == "" || strings.HasPrefix(config.Region, "cn-"), "cn-hangzhou").
|
||||
Else("ap-southeast-1"),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create ssl manager: %w", err)
|
||||
}
|
||||
|
||||
return &SSLDeployerProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
sdkClient: client,
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
sdkClient: client,
|
||||
sslManager: sslmgr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -59,18 +78,32 @@ func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) {
|
||||
}
|
||||
|
||||
func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) {
|
||||
if d.config.Domain == "" {
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
// 上传证书
|
||||
upres, err := d.sslManager.Upload(ctx, certPEM, privkeyPEM)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
|
||||
} else {
|
||||
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||
}
|
||||
|
||||
// "*.example.com" → ".example.com",适配阿里云 CDN 要求的泛域名格式
|
||||
domain := strings.TrimPrefix(d.config.Domain, "*")
|
||||
|
||||
// 设置 CDN 域名域名证书
|
||||
// REF: https://help.aliyun.com/zh/cdn/developer-reference/api-cdn-2018-05-10-setcdndomainsslcertificate
|
||||
certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
|
||||
setCdnDomainSSLCertificateReq := &alicdn.SetCdnDomainSSLCertificateRequest{
|
||||
DomainName: tea.String(domain),
|
||||
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
|
||||
CertType: tea.String("upload"),
|
||||
DomainName: tea.String(domain),
|
||||
CertType: tea.String("cas"),
|
||||
CertId: tea.Int64(int64(certId)),
|
||||
CertRegion: lo.
|
||||
If(d.config.Region == "" || strings.HasPrefix(d.config.Region, "cn-"), tea.String("cn-hangzhou")).
|
||||
Else(tea.String("ap-southeast-1")),
|
||||
SSLProtocol: tea.String("on"),
|
||||
SSLPub: tea.String(certPEM),
|
||||
SSLPri: tea.String(privkeyPEM),
|
||||
}
|
||||
setCdnDomainSSLCertificateResp, err := d.sdkClient.SetCdnDomainSSLCertificate(setCdnDomainSSLCertificateReq)
|
||||
d.logger.Debug("sdk request 'cdn.SetCdnDomainSSLCertificate'", slog.Any("request", setCdnDomainSSLCertificateReq), slog.Any("response", setCdnDomainSSLCertificateResp))
|
||||
|
||||
@ -5,13 +5,16 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||
alidcdn "github.com/alibabacloud-go/dcdn-20180115/v3/client"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
"github.com/samber/lo"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/aliyun-cas"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
@ -21,14 +24,17 @@ type SSLDeployerProviderConfig struct {
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
// 阿里云资源组 ID。
|
||||
ResourceGroupId string `json:"resourceGroupId,omitempty"`
|
||||
// 阿里云地域。
|
||||
Region string `json:"region"`
|
||||
// 加速域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
|
||||
type SSLDeployerProvider struct {
|
||||
config *SSLDeployerProviderConfig
|
||||
logger *slog.Logger
|
||||
sdkClient *alidcdn.Client
|
||||
config *SSLDeployerProviderConfig
|
||||
logger *slog.Logger
|
||||
sdkClient *alidcdn.Client
|
||||
sslManager core.SSLManager
|
||||
}
|
||||
|
||||
var _ core.SSLDeployer = (*SSLDeployerProvider)(nil)
|
||||
@ -43,10 +49,23 @@ func NewSSLDeployerProvider(config *SSLDeployerProviderConfig) (*SSLDeployerProv
|
||||
return nil, fmt.Errorf("could not create sdk client: %w", err)
|
||||
}
|
||||
|
||||
sslmgr, err := sslmgrsp.NewSSLManagerProvider(&sslmgrsp.SSLManagerProviderConfig{
|
||||
AccessKeyId: config.AccessKeyId,
|
||||
AccessKeySecret: config.AccessKeySecret,
|
||||
ResourceGroupId: config.ResourceGroupId,
|
||||
Region: lo.
|
||||
If(config.Region == "" || strings.HasPrefix(config.Region, "cn-"), "cn-hangzhou").
|
||||
Else("ap-southeast-1"),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create ssl manager: %w", err)
|
||||
}
|
||||
|
||||
return &SSLDeployerProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
sdkClient: client,
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
sdkClient: client,
|
||||
sslManager: sslmgr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -63,18 +82,28 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
// 上传证书
|
||||
upres, err := d.sslManager.Upload(ctx, certPEM, privkeyPEM)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
|
||||
} else {
|
||||
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||
}
|
||||
|
||||
// "*.example.com" → ".example.com",适配阿里云 DCDN 要求的泛域名格式
|
||||
domain := strings.TrimPrefix(d.config.Domain, "*")
|
||||
|
||||
// 配置域名证书
|
||||
// REF: https://help.aliyun.com/zh/edge-security-acceleration/dcdn/developer-reference/api-dcdn-2018-01-15-setdcdndomainsslcertificate
|
||||
certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
|
||||
setDcdnDomainSSLCertificateReq := &alidcdn.SetDcdnDomainSSLCertificateRequest{
|
||||
DomainName: tea.String(domain),
|
||||
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
|
||||
CertType: tea.String("upload"),
|
||||
DomainName: tea.String(domain),
|
||||
CertType: tea.String("cas"),
|
||||
CertId: tea.Int64(int64(certId)),
|
||||
CertRegion: lo.
|
||||
If(d.config.Region == "" || strings.HasPrefix(d.config.Region, "cn-"), tea.String("cn-hangzhou")).
|
||||
Else(tea.String("ap-southeast-1")),
|
||||
SSLProtocol: tea.String("on"),
|
||||
SSLPub: tea.String(certPEM),
|
||||
SSLPri: tea.String(privkeyPEM),
|
||||
}
|
||||
setDcdnDomainSSLCertificateResp, err := d.sdkClient.SetDcdnDomainSSLCertificate(setDcdnDomainSSLCertificateReq)
|
||||
d.logger.Debug("sdk request 'dcdn.SetDcdnDomainSSLCertificate'", slog.Any("request", setDcdnDomainSSLCertificateReq), slog.Any("response", setDcdnDomainSSLCertificateResp))
|
||||
|
||||
@ -94,7 +94,7 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
|
||||
// 为网站业务转发规则关联 SSL 证书
|
||||
// REF: https://help.aliyun.com/zh/anti-ddos/anti-ddos-pro-and-premium/developer-reference/api-ddoscoo-2020-01-01-associatewebcert
|
||||
certId, _ := strconv.Atoi(upres.CertId)
|
||||
certId, _ := strconv.ParseInt(upres.CertId, 10, 32)
|
||||
associateWebCertReq := &aliddos.AssociateWebCertRequest{
|
||||
Domain: tea.String(d.config.Domain),
|
||||
CertId: tea.Int32(int32(certId)),
|
||||
|
||||
@ -5,12 +5,16 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
alivod "github.com/alibabacloud-go/vod-20170321/v4/client"
|
||||
"github.com/samber/lo"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/aliyun-cas"
|
||||
)
|
||||
|
||||
type SSLDeployerProviderConfig struct {
|
||||
@ -27,9 +31,10 @@ type SSLDeployerProviderConfig struct {
|
||||
}
|
||||
|
||||
type SSLDeployerProvider struct {
|
||||
config *SSLDeployerProviderConfig
|
||||
logger *slog.Logger
|
||||
sdkClient *alivod.Client
|
||||
config *SSLDeployerProviderConfig
|
||||
logger *slog.Logger
|
||||
sdkClient *alivod.Client
|
||||
sslManager core.SSLManager
|
||||
}
|
||||
|
||||
var _ core.SSLDeployer = (*SSLDeployerProvider)(nil)
|
||||
@ -44,10 +49,23 @@ func NewSSLDeployerProvider(config *SSLDeployerProviderConfig) (*SSLDeployerProv
|
||||
return nil, fmt.Errorf("could not create sdk client: %w", err)
|
||||
}
|
||||
|
||||
sslmgr, err := sslmgrsp.NewSSLManagerProvider(&sslmgrsp.SSLManagerProviderConfig{
|
||||
AccessKeyId: config.AccessKeyId,
|
||||
AccessKeySecret: config.AccessKeySecret,
|
||||
ResourceGroupId: config.ResourceGroupId,
|
||||
Region: lo.
|
||||
If(config.Region == "" || strings.HasPrefix(config.Region, "cn-"), "cn-hangzhou").
|
||||
Else("ap-southeast-1"),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create ssl manager: %w", err)
|
||||
}
|
||||
|
||||
return &SSLDeployerProvider{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
sdkClient: client,
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
sdkClient: client,
|
||||
sslManager: sslmgr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -64,15 +82,25 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
return nil, errors.New("config `domain` is required")
|
||||
}
|
||||
|
||||
// 上传证书
|
||||
upres, err := d.sslManager.Upload(ctx, certPEM, privkeyPEM)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
|
||||
} else {
|
||||
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||
}
|
||||
|
||||
// 设置域名证书
|
||||
// REF: https://help.aliyun.com/zh/vod/developer-reference/api-vod-2017-03-21-setvoddomainsslcertificate
|
||||
certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
|
||||
setVodDomainSSLCertificateReq := &alivod.SetVodDomainSSLCertificateRequest{
|
||||
DomainName: tea.String(d.config.Domain),
|
||||
CertName: tea.String(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
|
||||
CertType: tea.String("upload"),
|
||||
DomainName: tea.String(d.config.Domain),
|
||||
CertType: tea.String("cas"),
|
||||
CertId: tea.Int64(int64(certId)),
|
||||
CertRegion: lo.
|
||||
If(d.config.Region == "" || strings.HasPrefix(d.config.Region, "cn-"), tea.String("cn-hangzhou")).
|
||||
Else(tea.String("ap-southeast-1")),
|
||||
SSLProtocol: tea.String("on"),
|
||||
SSLPub: tea.String(certPEM),
|
||||
SSLPri: tea.String(privkeyPEM),
|
||||
}
|
||||
setVodDomainSSLCertificateResp, err := d.sdkClient.SetVodDomainSSLCertificate(setVodDomainSSLCertificateReq)
|
||||
d.logger.Debug("sdk request 'live.SetVodDomainSSLCertificate'", slog.Any("request", setVodDomainSSLCertificateReq), slog.Any("response", setVodDomainSSLCertificateResp))
|
||||
|
||||
@ -114,8 +114,8 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
modifySiteReq := &btwafsdk.ModifySiteRequest{
|
||||
SiteId: lo.ToPtr(siteId),
|
||||
Type: lo.ToPtr("openCert"),
|
||||
Server: &btwafsdk.SiteServerInfo{
|
||||
ListenSSLPorts: lo.ToPtr([]int32{d.config.SitePort}),
|
||||
Server: &btwafsdk.SiteServerInfoMod{
|
||||
ListenSSLPorts: lo.ToPtr([]string{fmt.Sprintf("%d", d.config.SitePort)}),
|
||||
SSL: &btwafsdk.SiteServerSSLInfo{
|
||||
IsSSL: lo.ToPtr(int32(1)),
|
||||
FullChain: lo.ToPtr(certPEM),
|
||||
|
||||
@ -30,6 +30,10 @@ type SSLDeployerProviderConfig struct {
|
||||
SecretDataKeyForCrt string `json:"secretDataKeyForCrt,omitempty"`
|
||||
// Kubernetes Secret 中用于存放私钥的 Key。
|
||||
SecretDataKeyForKey string `json:"secretDataKeyForKey,omitempty"`
|
||||
// Kubernetes Secret 注解。
|
||||
SecretAnnotations map[string]string `json:"secretAnnotations,omitempty"`
|
||||
// Kubernetes Secret 标签。
|
||||
SecretLabels map[string]string `json:"secretLabels,omitempty"`
|
||||
}
|
||||
|
||||
type SSLDeployerProvider struct {
|
||||
@ -94,6 +98,17 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
"certimate/issuer-sn": certX509.Issuer.SerialNumber,
|
||||
"certimate/issuer-org": strings.Join(certX509.Issuer.Organization, ","),
|
||||
}
|
||||
secretLabels := map[string]string{}
|
||||
if d.config.SecretAnnotations != nil {
|
||||
for k, v := range d.config.SecretAnnotations {
|
||||
secretAnnotations[k] = v
|
||||
}
|
||||
}
|
||||
if d.config.SecretLabels != nil {
|
||||
for k, v := range d.config.SecretLabels {
|
||||
secretLabels[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// 获取 Secret 实例,如果不存在则创建
|
||||
secretPayload, err = client.CoreV1().Secrets(d.config.Namespace).Get(context.TODO(), d.config.SecretName, k8smeta.GetOptions{})
|
||||
@ -106,6 +121,7 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
ObjectMeta: k8smeta.ObjectMeta{
|
||||
Name: d.config.SecretName,
|
||||
Annotations: secretAnnotations,
|
||||
Labels: secretLabels,
|
||||
},
|
||||
Type: k8score.SecretType(d.config.SecretType),
|
||||
}
|
||||
@ -131,6 +147,13 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
secretPayload.ObjectMeta.Annotations[k] = v
|
||||
}
|
||||
}
|
||||
if secretPayload.ObjectMeta.Labels == nil {
|
||||
secretPayload.ObjectMeta.Labels = secretLabels
|
||||
} else {
|
||||
for k, v := range secretLabels {
|
||||
secretPayload.ObjectMeta.Labels[k] = v
|
||||
}
|
||||
}
|
||||
if secretPayload.Data == nil {
|
||||
secretPayload.Data = make(map[string][]byte)
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privke
|
||||
|
||||
// RCDN SSL 绑定域名
|
||||
// REF: https://apifox.com/apidoc/shared/a4595cc8-44c5-4678-a2a3-eed7738dab03/api-184214120
|
||||
certId, _ := strconv.Atoi(upres.CertId)
|
||||
certId, _ := strconv.ParseInt(upres.CertId, 10, 32)
|
||||
rcdnInstanceSslBindReq := &rainyunsdk.RcdnInstanceSslBindRequest{
|
||||
CertId: int32(certId),
|
||||
Domains: []string{d.config.Domain},
|
||||
|
||||
@ -28,6 +28,9 @@ type SSLDeployerProviderConfig struct {
|
||||
Method string `json:"method,omitempty"`
|
||||
// 请求标头。
|
||||
Headers map[string]string `json:"headers,omitempty"`
|
||||
// 请求超时(单位:秒)。
|
||||
// 零值时默认值 30。
|
||||
Timeout int32 `json:"timeout,omitempty"`
|
||||
// 是否允许不安全的连接。
|
||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||
}
|
||||
@ -49,6 +52,9 @@ func NewSSLDeployerProvider(config *SSLDeployerProviderConfig) (*SSLDeployerProv
|
||||
SetTimeout(30 * time.Second).
|
||||
SetRetryCount(3).
|
||||
SetRetryWaitTime(5 * time.Second)
|
||||
if config.Timeout > 0 {
|
||||
client.SetTimeout(time.Duration(config.Timeout) * time.Second)
|
||||
}
|
||||
if config.AllowInsecureConnections {
|
||||
client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
|
||||
}
|
||||
|
||||
@ -6,9 +6,9 @@ import (
|
||||
)
|
||||
|
||||
type ModifySiteRequest struct {
|
||||
SiteId *string `json:"site_id,omitempty"`
|
||||
Type *string `json:"types,omitempty"`
|
||||
Server *SiteServerInfo `json:"server,omitempty"`
|
||||
SiteId *string `json:"site_id,omitempty"`
|
||||
Type *string `json:"types,omitempty"`
|
||||
Server *SiteServerInfoMod `json:"server,omitempty"`
|
||||
}
|
||||
|
||||
type ModifySiteResponse struct {
|
||||
|
||||
@ -32,6 +32,11 @@ type SiteServerInfo struct {
|
||||
SSL *SiteServerSSLInfo `json:"ssl,omitempty"`
|
||||
}
|
||||
|
||||
type SiteServerInfoMod struct {
|
||||
ListenSSLPorts *[]string `json:"listen_ssl_port,omitempty"`
|
||||
SSL *SiteServerSSLInfo `json:"ssl,omitempty"`
|
||||
}
|
||||
|
||||
type SiteServerSSLInfo struct {
|
||||
IsSSL *int32 `json:"is_ssl,omitempty"`
|
||||
FullChain *string `json:"full_chain,omitempty"`
|
||||
|
||||
BIN
ui/public/imgs/providers/actalisssl.png
Normal file
BIN
ui/public/imgs/providers/actalisssl.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
1
ui/public/imgs/providers/vultr.svg
Normal file
1
ui/public/imgs/providers/vultr.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0" viewBox="0 0 54 54" style="enable-background:new 0 0 54 54;" xml:space="preserve"><g><rect class="st0" width="54" height="54" fill="#102147"/></g><g><path class="st1" d="M20.6,9.8c-0.4-0.6-1-1-1.8-1H8.1C6.9,8.8,6,9.7,6,10.9l0,0c0,0.4,0.1,0.8,0.3,1.1l2.2,3.5l14.3-2.2L20.6,9.8z" fill="#007BFC"/><path class="st2" d="M22.8,13.3c-0.4-0.6-1.1-1-1.8-1H10.3c-1.2,0-2.1,0.9-2.1,2.1l0,0c0,0.4,0.1,0.8,0.3,1.1l3.1,4.9l14.3-2.2 L22.8,13.3z" fill="#51B9FF"/><path class="st3" d="M11.6,20.4c-0.2-0.3-0.3-0.7-0.3-1.1c0-1.2,0.9-2.1,2.1-2.1l0,0h10.8c0.7,0,1.4,0.4,1.8,1l9.6,15.3 c0.2,0.3,0.3,0.7,0.3,1.1c0,0.4-0.1,0.8-0.3,1.1l-5.4,8.5c-0.6,1-1.9,1.3-2.9,0.6c-0.3-0.2-0.5-0.4-0.6-0.6L11.6,20.4z" fill="#FFFFFF"/><path class="st3" d="M38.7,25c0.6,1,1.9,1.3,2.9,0.6c0.3-0.2,0.5-0.4,0.6-0.6l1.8-2.9l3.5-5.6c0.2-0.3,0.3-0.7,0.3-1.1 c0-0.4-0.1-0.8-0.3-1.1l-2.8-4.4c-0.4-0.6-1.1-1-1.8-1H32.3c-1.2,0-2.1,0.9-2.1,2.1l0,0c0,0.4,0.1,0.8,0.3,1.1L38.7,25z" fill="#FFFFFF"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@ -15,6 +15,7 @@ import AccessConfigFieldsProvider1Panel from "./forms/AccessConfigFieldsProvider
|
||||
import AccessConfigFieldsProviderACMECA from "./forms/AccessConfigFieldsProviderACMECA";
|
||||
import AccessConfigFieldsProviderACMEDNS from "./forms/AccessConfigFieldsProviderACMEDNS";
|
||||
import AccessConfigFieldsProviderACMEHttpReq from "./forms/AccessConfigFieldsProviderACMEHttpReq";
|
||||
import AccessConfigFieldsProviderActalisSSL from "./forms/AccessConfigFieldsProviderActalisSSL";
|
||||
import AccessConfigFieldsProviderAliyun from "./forms/AccessConfigFieldsProviderAliyun";
|
||||
import AccessConfigFieldsProviderAPISIX from "./forms/AccessConfigFieldsProviderAPISIX";
|
||||
import AccessConfigFieldsProviderAWS from "./forms/AccessConfigFieldsProviderAWS";
|
||||
@ -80,6 +81,7 @@ import AccessConfigFieldsProviderUniCloud from "./forms/AccessConfigFieldsProvid
|
||||
import AccessConfigFieldsProviderUpyun from "./forms/AccessConfigFieldsProviderUpyun";
|
||||
import AccessConfigFieldsProviderVercel from "./forms/AccessConfigFieldsProviderVercel";
|
||||
import AccessConfigFieldsProviderVolcEngine from "./forms/AccessConfigFieldsProviderVolcEngine";
|
||||
import AccessConfigFieldsProviderVultr from "./forms/AccessConfigFieldsProviderVultr";
|
||||
import AccessConfigFieldsProviderWangsu from "./forms/AccessConfigFieldsProviderWangsu";
|
||||
import AccessConfigFieldsProviderWebhook from "./forms/AccessConfigFieldsProviderWebhook";
|
||||
import AccessConfigFieldsProviderWeComBot from "./forms/AccessConfigFieldsProviderWeComBot";
|
||||
@ -140,6 +142,9 @@ const AccessForm = ({ className, style, disabled, initialValues, mode, usage, ..
|
||||
case ACCESS_PROVIDERS.ACMEHTTPREQ: {
|
||||
return <AccessConfigFieldsProviderACMEHttpReq />;
|
||||
}
|
||||
case ACCESS_PROVIDERS.ACTALISSSL: {
|
||||
return <AccessConfigFieldsProviderActalisSSL />;
|
||||
}
|
||||
case ACCESS_PROVIDERS.ALIYUN: {
|
||||
return <AccessConfigFieldsProviderAliyun />;
|
||||
}
|
||||
@ -335,6 +340,9 @@ const AccessForm = ({ className, style, disabled, initialValues, mode, usage, ..
|
||||
case ACCESS_PROVIDERS.VOLCENGINE: {
|
||||
return <AccessConfigFieldsProviderVolcEngine />;
|
||||
}
|
||||
case ACCESS_PROVIDERS.VULTR: {
|
||||
return <AccessConfigFieldsProviderVultr />;
|
||||
}
|
||||
case ACCESS_PROVIDERS.WANGSU: {
|
||||
return <AccessConfigFieldsProviderWangsu />;
|
||||
}
|
||||
|
||||
@ -0,0 +1,70 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const AccessConfigFormFieldsProviderActalisSSL = () => {
|
||||
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, "eabKid"]}
|
||||
initialValue={initialValues.eabKid}
|
||||
label={t("access.form.actalisssl_eab_kid.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.actalisssl_eab_kid.tooltip") }}></span>}
|
||||
>
|
||||
<Input autoComplete="new-password" placeholder={t("access.form.actalisssl_eab_kid.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "eabHmacKey"]}
|
||||
initialValue={initialValues.eabHmacKey}
|
||||
label={t("access.form.actalisssl_eab_hmac_key.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.actalisssl_eab_hmac_key.tooltip") }}></span>}
|
||||
>
|
||||
<Input.Password autoComplete="new-password" placeholder={t("access.form.actalisssl_eab_hmac_key.placeholder")} />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
eabKid: "",
|
||||
eabHmacKey: "",
|
||||
};
|
||||
};
|
||||
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
|
||||
const { t } = i18n;
|
||||
|
||||
return z.object({
|
||||
eabKid: z
|
||||
.string()
|
||||
.min(1, t("access.form.actalisssl_eab_kid.placeholder"))
|
||||
.max(256, t("common.errmsg.string_max", { max: 256 })),
|
||||
eabHmacKey: z
|
||||
.string()
|
||||
.min(1, t("access.form.actalisssl_eab_hmac_key.placeholder"))
|
||||
.max(256, t("common.errmsg.string_max", { max: 256 })),
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(AccessConfigFormFieldsProviderActalisSSL, {
|
||||
getInitialValues,
|
||||
getSchema,
|
||||
});
|
||||
|
||||
export default _default;
|
||||
@ -0,0 +1,52 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const AccessConfigFormFieldsProviderVultr = () => {
|
||||
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, "apiKey"]}
|
||||
initialValue={initialValues.apiKey}
|
||||
label={t("access.form.vultr_api_key.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.vultr_api_key.tooltip") }}></span>}
|
||||
>
|
||||
<Input.Password autoComplete="new-password" placeholder={t("access.form.vultr_api_key.placeholder")} />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
apiKey: "",
|
||||
};
|
||||
};
|
||||
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
|
||||
const { t } = i18n;
|
||||
|
||||
return z.object({
|
||||
apiKey: z.string().nonempty(t("access.form.vultr_api_key.placeholder")),
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(AccessConfigFormFieldsProviderVultr, {
|
||||
getInitialValues,
|
||||
getSchema,
|
||||
});
|
||||
|
||||
export default _default;
|
||||
@ -509,7 +509,7 @@ const BizApplyNodeConfigForm = ({ node, ...props }: BizApplyNodeConfigFormProps)
|
||||
>
|
||||
<AutoComplete
|
||||
allowClear
|
||||
options={["classic", "tlsserver", "shortlived"].map((value) => ({ value }))}
|
||||
options={["classic", "tlsserver", "shortlived"].map((s) => ({ value: s }))}
|
||||
placeholder={t("workflow_node.apply.form.acme_profile.placeholder")}
|
||||
filterOption={(inputValue, option) => option!.value.toLowerCase().includes(inputValue.toLowerCase())}
|
||||
/>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input } from "antd";
|
||||
import { AutoComplete, Form, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
@ -19,6 +19,20 @@ const BizDeployNodeConfigFieldsProviderAliyunCDN = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
name={[parentNamePath, "region"]}
|
||||
initialValue={initialValues.region}
|
||||
label={t("workflow_node.deploy.form.aliyun_cdn_region.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_cdn_region.tooltip") }}></span>}
|
||||
>
|
||||
<AutoComplete
|
||||
allowClear
|
||||
options={["cn-hangzhou", "ap-southeast-1"].map((s) => ({ value: s }))}
|
||||
placeholder={t("workflow_node.deploy.form.aliyun_cdn_region.placeholder")}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "domain"]}
|
||||
initialValue={initialValues.domain}
|
||||
@ -34,6 +48,7 @@ const BizDeployNodeConfigFieldsProviderAliyunCDN = () => {
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
region: "",
|
||||
domain: "",
|
||||
};
|
||||
};
|
||||
@ -42,6 +57,7 @@ const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> })
|
||||
const { t } = i18n;
|
||||
|
||||
return z.object({
|
||||
region: z.string().nullish(),
|
||||
domain: z.string().refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
|
||||
});
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input } from "antd";
|
||||
import { AutoComplete, Form, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
@ -19,6 +19,20 @@ const BizDeployNodeConfigFieldsProviderAliyunDCDN = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
name={[parentNamePath, "region"]}
|
||||
initialValue={initialValues.region}
|
||||
label={t("workflow_node.deploy.form.aliyun_dcdn_region.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.aliyun_dcdn_region.tooltip") }}></span>}
|
||||
>
|
||||
<AutoComplete
|
||||
allowClear
|
||||
options={["cn-hangzhou", "ap-southeast-1"].map((s) => ({ value: s }))}
|
||||
placeholder={t("workflow_node.deploy.form.aliyun_dcdn_region.placeholder")}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "domain"]}
|
||||
initialValue={initialValues.domain}
|
||||
@ -34,6 +48,7 @@ const BizDeployNodeConfigFieldsProviderAliyunDCDN = () => {
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
region: "",
|
||||
domain: "",
|
||||
};
|
||||
};
|
||||
@ -42,6 +57,7 @@ const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> })
|
||||
const { t } = i18n;
|
||||
|
||||
return z.object({
|
||||
region: z.string().nullish(),
|
||||
domain: z.string().refine((v) => validDomainName(v, { allowWildcard: true }), t("common.errmsg.domain_invalid")),
|
||||
});
|
||||
};
|
||||
|
||||
@ -3,6 +3,8 @@ import { Form, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import CodeInput from "@/components/CodeInput";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const BizDeployNodeConfigFieldsProviderKubernetesSecret = () => {
|
||||
@ -13,8 +15,23 @@ const BizDeployNodeConfigFieldsProviderKubernetesSecret = () => {
|
||||
[parentNamePath]: getSchema({ i18n }),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
const formInst = Form.useFormInstance();
|
||||
const initialValues = getInitialValues();
|
||||
|
||||
const handleSecretAnnotationsBlur = () => {
|
||||
let value = formInst.getFieldValue([parentNamePath, "secretAnnotations"]);
|
||||
value = value.trim();
|
||||
value = value.replace(/(?<!\r)\n/g, "\r\n");
|
||||
formInst.setFieldValue([parentNamePath, "secretAnnotations"], value);
|
||||
};
|
||||
|
||||
const handleSecretLabelsBlur = () => {
|
||||
let value = formInst.getFieldValue([parentNamePath, "secretLabels"]);
|
||||
value = value.trim();
|
||||
value = value.replace(/(?<!\r)\n/g, "\r\n");
|
||||
formInst.setFieldValue([parentNamePath, "secretLabels"], value);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
@ -66,6 +83,40 @@ const BizDeployNodeConfigFieldsProviderKubernetesSecret = () => {
|
||||
>
|
||||
<Input placeholder={t("workflow_node.deploy.form.k8s_secret_data_key_for_key.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "secretAnnotations"]}
|
||||
initialValue={initialValues.secretAnnotations}
|
||||
label={t("workflow_node.deploy.form.k8s_secret_annotations.label")}
|
||||
extra={t("workflow_node.deploy.form.k8s_secret_annotations.help")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.k8s_secret_annotations.tooltip") }}></span>}
|
||||
>
|
||||
<CodeInput
|
||||
height="auto"
|
||||
minHeight="64px"
|
||||
maxHeight="256px"
|
||||
placeholder={t("workflow_node.deploy.form.k8s_secret_annotations.placeholder")}
|
||||
onBlur={handleSecretAnnotationsBlur}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "secretLabels"]}
|
||||
initialValue={initialValues.secretLabels}
|
||||
label={t("workflow_node.deploy.form.k8s_secret_labels.label")}
|
||||
extra={t("workflow_node.deploy.form.k8s_secret_labels.help")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.k8s_secret_labels.tooltip") }}></span>}
|
||||
>
|
||||
<CodeInput
|
||||
height="auto"
|
||||
minHeight="64px"
|
||||
maxHeight="256px"
|
||||
placeholder={t("workflow_node.deploy.form.k8s_secret_labels.placeholder")}
|
||||
onBlur={handleSecretLabelsBlur}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -88,6 +139,34 @@ const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> })
|
||||
secretType: z.string().nonempty(t("workflow_node.deploy.form.k8s_secret_type.placeholder")),
|
||||
secretDataKeyForCrt: z.string().nonempty(t("workflow_node.deploy.form.k8s_secret_data_key_for_crt.placeholder")),
|
||||
secretDataKeyForKey: z.string().nonempty(t("workflow_node.deploy.form.k8s_secret_data_key_for_key.placeholder")),
|
||||
secretAnnotations: z
|
||||
.string()
|
||||
.nullish()
|
||||
.refine((v) => {
|
||||
if (!v) return true;
|
||||
|
||||
const lines = v.split(/\r?\n/);
|
||||
for (const line of lines) {
|
||||
if (line.split(":").length < 2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}, t("workflow_node.deploy.form.k8s_secret_annotations.errmsg.invalid")),
|
||||
secretLabels: z
|
||||
.string()
|
||||
.nullish()
|
||||
.refine((v) => {
|
||||
if (!v) return true;
|
||||
|
||||
const lines = v.split(/\r?\n/);
|
||||
for (const line of lines) {
|
||||
if (line.split(":").length < 2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}, t("workflow_node.deploy.form.k8s_secret_labels.errmsg.invalid")),
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form } from "antd";
|
||||
import { Form, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
@ -51,6 +51,17 @@ const BizDeployNodeConfigFieldsProviderWebhook = () => {
|
||||
<Form.Item>
|
||||
<Tips message={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.webhook_data.guide") }}></span>} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name={[parentNamePath, "timeout"]} label={t("workflow_node.deploy.form.webhook_timeout.label")} rules={[formRule]}>
|
||||
<Input
|
||||
type="number"
|
||||
allowClear
|
||||
min={0}
|
||||
max={3600}
|
||||
placeholder={t("workflow_node.deploy.form.webhook_timeout.placeholder")}
|
||||
addonAfter={t("workflow_node.deploy.form.webhook_timeout.unit")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -76,6 +87,10 @@ const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> })
|
||||
return false;
|
||||
}
|
||||
}, t("workflow_node.deploy.form.webhook_data.errmsg.json_invalid")),
|
||||
timeout: z.preprocess(
|
||||
(v) => (v == null || v === "" ? void 0 : Number(v)),
|
||||
z.number().int(t("workflow_node.deploy.form.webhook_timeout.placeholder")).gte(1, t("workflow_node.deploy.form.webhook_timeout.placeholder")).nullish()
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form } from "antd";
|
||||
import { Form, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
@ -51,6 +51,17 @@ const BizNotifyNodeConfigFieldsProviderWebhook = () => {
|
||||
<Form.Item>
|
||||
<Tips message={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.notify.form.webhook_data.guide") }}></span>} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name={[parentNamePath, "timeout"]} label={t("workflow_node.notify.form.webhook_timeout.label")} rules={[formRule]}>
|
||||
<Input
|
||||
type="number"
|
||||
allowClear
|
||||
min={0}
|
||||
max={3600}
|
||||
placeholder={t("workflow_node.notify.form.webhook_timeout.placeholder")}
|
||||
addonAfter={t("workflow_node.notify.form.webhook_timeout.unit")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -76,6 +87,10 @@ const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> })
|
||||
return false;
|
||||
}
|
||||
}, t("workflow_node.notify.form.webhook_data.errmsg.json_invalid")),
|
||||
timeout: z.preprocess(
|
||||
(v) => (v == null || v === "" ? void 0 : Number(v)),
|
||||
z.number().int(t("workflow_node.notify.form.webhook_timeout.placeholder")).gte(1, t("workflow_node.notify.form.webhook_timeout.placeholder")).nullish()
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -19,6 +19,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
|
||||
ACMECA: "acmeca",
|
||||
ACMEDNS: "acmedns",
|
||||
ACMEHTTPREQ: "acmehttpreq",
|
||||
ACTALISSSL: "actalisssl",
|
||||
ALIYUN: "aliyun",
|
||||
APISIX: "apisix",
|
||||
AWS: "aws",
|
||||
@ -88,6 +89,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
|
||||
UPYUN: "upyun",
|
||||
VERCEL: "vercel",
|
||||
VOLCENGINE: "volcengine",
|
||||
VULTR: "vultr",
|
||||
WANGSU: "wangsu",
|
||||
WEBHOOK: "webhook",
|
||||
WECOMBOT: "wecombot",
|
||||
@ -178,6 +180,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
|
||||
[ACCESS_PROVIDERS.PORKBUN, "provider.porkbun", "/imgs/providers/porkbun.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.SPACESHIP, "provider.spaceship", "/imgs/providers/spaceship.png", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.VERCEL, "provider.vercel", "/imgs/providers/vercel.svg", [ACCESS_USAGES.DNS]],
|
||||
[ACCESS_PROVIDERS.VULTR, "provider.vultr", "/imgs/providers/vultr.svg", [ACCESS_USAGES.DNS]],
|
||||
[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]],
|
||||
@ -186,6 +189,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
|
||||
|
||||
[ACCESS_PROVIDERS.LETSENCRYPT, "provider.letsencrypt", "/imgs/providers/letsencrypt.svg", [ACCESS_USAGES.CA], "builtin"],
|
||||
[ACCESS_PROVIDERS.LETSENCRYPTSTAGING, "provider.letsencryptstaging", "/imgs/providers/letsencrypt.svg", [ACCESS_USAGES.CA], "builtin"],
|
||||
[ACCESS_PROVIDERS.ACTALISSSL, "provider.actalisssl", "/imgs/providers/actalisssl.png", [ACCESS_USAGES.CA]],
|
||||
[ACCESS_PROVIDERS.BUYPASS, "provider.buypass", "/imgs/providers/buypass.png", [ACCESS_USAGES.CA]],
|
||||
[ACCESS_PROVIDERS.GOOGLETRUSTSERVICES, "provider.googletrustservices", "/imgs/providers/google.svg", [ACCESS_USAGES.CA]],
|
||||
[ACCESS_PROVIDERS.SSLCOM, "provider.sslcom", "/imgs/providers/sslcom.svg", [ACCESS_USAGES.CA]],
|
||||
@ -221,6 +225,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
|
||||
*/
|
||||
export const CA_PROVIDERS = Object.freeze({
|
||||
ACMECA: `${ACCESS_PROVIDERS.ACMECA}`,
|
||||
ACTALISSSL: `${ACCESS_PROVIDERS.ACTALISSSL}`,
|
||||
BUYPASS: `${ACCESS_PROVIDERS.BUYPASS}`,
|
||||
GOOGLETRUSTSERVICES: `${ACCESS_PROVIDERS.GOOGLETRUSTSERVICES}`,
|
||||
LETSENCRYPT: `${ACCESS_PROVIDERS.LETSENCRYPT}`,
|
||||
@ -242,6 +247,7 @@ export const caProvidersMap: Map<CAProvider["type"] | string, CAProvider> = new
|
||||
[
|
||||
[CA_PROVIDERS.LETSENCRYPT, "builtin"],
|
||||
[CA_PROVIDERS.LETSENCRYPTSTAGING, "builtin"],
|
||||
[CA_PROVIDERS.ACTALISSSL],
|
||||
[CA_PROVIDERS.BUYPASS],
|
||||
[CA_PROVIDERS.GOOGLETRUSTSERVICES],
|
||||
[CA_PROVIDERS.SSLCOM],
|
||||
@ -316,6 +322,7 @@ export const ACME_DNS01_PROVIDERS = Object.freeze({
|
||||
VERCEL: `${ACCESS_PROVIDERS.VERCEL}`,
|
||||
VOLCENGINE: `${ACCESS_PROVIDERS.VOLCENGINE}`, // 兼容旧值,等同于 `VOLCENGINE_DNS`
|
||||
VOLCENGINE_DNS: `${ACCESS_PROVIDERS.VOLCENGINE}-dns`,
|
||||
VULTR: `${ACCESS_PROVIDERS.VULTR}`,
|
||||
WESTCN: `${ACCESS_PROVIDERS.WESTCN}`,
|
||||
} as const);
|
||||
|
||||
@ -362,6 +369,7 @@ export const acmeDns01ProvidersMap: Map<ACMEDns01Provider["type"] | string, ACME
|
||||
[ACME_DNS01_PROVIDERS.PORKBUN, "provider.porkbun"],
|
||||
[ACME_DNS01_PROVIDERS.SPACESHIP, "provider.spaceship"],
|
||||
[ACME_DNS01_PROVIDERS.VERCEL, "provider.vercel"],
|
||||
[ACME_DNS01_PROVIDERS.VULTR, "provider.vultr"],
|
||||
[ACME_DNS01_PROVIDERS.CMCCCLOUD_DNS, "provider.cmcccloud.dns"],
|
||||
[ACME_DNS01_PROVIDERS.CTCCCLOUD_SMARTDNS, "provider.ctcccloud.smartdns"],
|
||||
[ACME_DNS01_PROVIDERS.RAINYUN, "provider.rainyun"],
|
||||
|
||||
@ -65,7 +65,7 @@
|
||||
"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.acmedns_credentials.errmsg.json_invalid": "Please enter a valid 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>",
|
||||
@ -78,6 +78,12 @@
|
||||
"access.form.acmehttpreq_password.label": "HTTP Basic Auth password (Optional)",
|
||||
"access.form.acmehttpreq_password.placeholder": "Please enter HTTP Basic Auth password",
|
||||
"access.form.acmehttpreq_password.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>",
|
||||
"access.form.actalisssl_eab_kid.label": "ACME EAB KID",
|
||||
"access.form.actalisssl_eab_kid.placeholder": "Please enter ACME EAB KID",
|
||||
"access.form.actalisssl_eab_kid.tooltip": "For more information, see <a href=\"https://www.actalis.com/manage-with-acme\" target=\"_blank\">https://www.actalis.com/manage-with-acme</a>",
|
||||
"access.form.actalisssl_eab_hmac_key.label": "ACME EAB HMAC key",
|
||||
"access.form.actalisssl_eab_hmac_key.placeholder": "Please enter ACME EAB HMAC key",
|
||||
"access.form.actalisssl_eab_hmac_key.tooltip": "For more information, see <a href=\"https://www.actalis.com/manage-with-acme\" target=\"_blank\">https://www.actalis.com/manage-with-acme</a>",
|
||||
"access.form.aliyun_access_key_id.label": "Aliyun AccessKeyId",
|
||||
"access.form.aliyun_access_key_id.placeholder": "Please enter Aliyun AccessKeyId",
|
||||
"access.form.aliyun_access_key_id.tooltip": "For more information, see <a href=\"https://www.alibabacloud.com/help/en/acr/create-and-obtain-an-accesskey-pair\" target=\"_blank\">https://www.alibabacloud.com/help/en/acr/create-and-obtain-an-accesskey-pair</a>",
|
||||
@ -492,6 +498,9 @@
|
||||
"access.form.volcengine_secret_access_key.label": "VolcEngine SecretAccessKey",
|
||||
"access.form.volcengine_secret_access_key.placeholder": "Please enter VolcEngine SecretAccessKey",
|
||||
"access.form.volcengine_secret_access_key.tooltip": "For more information, see <a href=\"https://www.volcengine.com/docs/6291/216571\" target=\"_blank\">https://www.volcengine.com/docs/6291/216571</a>",
|
||||
"access.form.vultr_api_key.label": "Vultr API key",
|
||||
"access.form.vultr_api_key.placeholder": "Please enter Vultr API key",
|
||||
"access.form.vultr_api_key.tooltip": "For more information, see <a href=\"https://docs.vultr.com/platform/other/users/manage-users/api-access/regenerate-user-api-key\" target=\"_blank\">https://docs.vultr.com/platform/other/users/manage-users/api-access/regenerate-user-api-key</a>",
|
||||
"access.form.wangsu_access_key_id.label": "Wangsu Cloud AccessKeyId",
|
||||
"access.form.wangsu_access_key_id.placeholder": "Please enter Wangsu Cloud AccessKeyId",
|
||||
"access.form.wangsu_access_key_id.tooltip": "For more information, see <a href=\"https://en.wangsu.com/document/account-manage/15775\" target=\"_blank\">https://en.wangsu.com/document/account-manage/15775</a>",
|
||||
@ -509,7 +518,7 @@
|
||||
"access.form.webhook_headers.placeholder": "Please enter Webhook request headers",
|
||||
"access.form.webhook_headers.errmsg.invalid": "Please enter a valid request headers",
|
||||
"access.form.webhook_headers.tooltip": "Example: <br><i>Content-Type: application/json<br>User-Agent: certimate</i>",
|
||||
"access.form.webhook_data.errmsg.json_invalid": "Please enter a valiod JSON string",
|
||||
"access.form.webhook_data.errmsg.json_invalid": "Please enter a valid JSON string",
|
||||
"access.form.webhook_data.label": "Webhook data (Optional)",
|
||||
"access.form.webhook_data.placeholder": "Please enter the default Webhook data",
|
||||
"access.form.webhook_data.help": "Notes: It can be overrided in the workflows.",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"provider.acmeca": "ACME Custom CA Endpoint",
|
||||
"provider.acmedns": "ACME-DNS",
|
||||
"provider.acmehttpreq": "ACME Custom HTTP Endpoint",
|
||||
"provider.actalisssl": "Actalis SSL",
|
||||
"provider.aliyun": "Alibaba Cloud",
|
||||
"provider.aliyun.alb": "Alibaba Cloud - ALB (Application Load Balancer)",
|
||||
"provider.aliyun.apigw": "Alibaba Cloud - API Gateway",
|
||||
@ -171,6 +172,7 @@
|
||||
"provider.volcengine.imagex": "Volcengine - ImageX",
|
||||
"provider.volcengine.live": "Volcengine - Live",
|
||||
"provider.volcengine.tos": "Volcengine - TOS (Tinder Object Storage)",
|
||||
"provider.vultr": "Vultr",
|
||||
"provider.wangsu": "Wangsu Cloud",
|
||||
"provider.wangsu.cdn": "Wangsu Cloud - CDN (Content Delivery Network)",
|
||||
"provider.wangsu.cdnpro": "Wangsu Cloud - CDN Pro (CDN 360)",
|
||||
|
||||
@ -92,8 +92,8 @@
|
||||
"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.validity_lifetime.units.h": "hours",
|
||||
"workflow_node.apply.form.validity_lifetime.units.d": "days",
|
||||
"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.",
|
||||
@ -256,9 +256,15 @@
|
||||
"workflow_node.deploy.form.aliyun_clb_snidomain.placeholder": "Please enter Alibaba Cloud CLB SNI domain name",
|
||||
"workflow_node.deploy.form.aliyun_clb_snidomain.help": "",
|
||||
"workflow_node.deploy.form.aliyun_clb_snidomain.tooltip": "For more information, see <a href=\"https://slb.console.aliyun.com/clb\" target=\"_blank\">https://slb.console.aliyun.com/clb</a>",
|
||||
"workflow_node.deploy.form.aliyun_cdn_region.label": "Alibaba Cloud CDN region",
|
||||
"workflow_node.deploy.form.aliyun_cdn_region.placeholder": "Please enter Alibaba Cloud CDN region (e.g. cn-hangzhou)",
|
||||
"workflow_node.deploy.form.aliyun_cdn_region.tooltip": "<ul style=\"list-style: disc;\"><li><strong>ap-southeast-1</strong> for Alibaba Cloud International</li><li><strong>cn-hangzhou</strong> for Alibaba Cloud in China</li></ul>",
|
||||
"workflow_node.deploy.form.aliyun_cdn_domain.label": "Alibaba Cloud CDN domain",
|
||||
"workflow_node.deploy.form.aliyun_cdn_domain.placeholder": "Please enter Alibaba Cloud CDN domain name",
|
||||
"workflow_node.deploy.form.aliyun_cdn_domain.tooltip": "For more information, see <a href=\"https://cdn.console.aliyun.com\" target=\"_blank\">https://cdn.console.aliyun.com</a>",
|
||||
"workflow_node.deploy.form.aliyun_dcdn_region.label": "Alibaba Cloud DCDN region",
|
||||
"workflow_node.deploy.form.aliyun_dcdn_region.placeholder": "Please enter Alibaba Cloud DCDN region (e.g. cn-hangzhou)",
|
||||
"workflow_node.deploy.form.aliyun_dcdn_region.tooltip": "<ul style=\"list-style: disc;\"><li><strong>ap-southeast-1</strong> for Alibaba Cloud International</li><li><strong>cn-hangzhou</strong> for Alibaba Cloud in China</li></ul>",
|
||||
"workflow_node.deploy.form.aliyun_dcdn_domain.label": "Alibaba Cloud DCDN domain",
|
||||
"workflow_node.deploy.form.aliyun_dcdn_domain.placeholder": "Please enter Alibaba Cloud DCDN domain name",
|
||||
"workflow_node.deploy.form.aliyun_dcdn_domain.tooltip": "For more information, see <a href=\"https://dcdn.console.aliyun.com\" target=\"_blank\">https://dcdn.console.aliyun.com</a>",
|
||||
@ -588,6 +594,16 @@
|
||||
"workflow_node.deploy.form.k8s_secret_data_key_for_key.label": "Kubernetes Secret data key for private key",
|
||||
"workflow_node.deploy.form.k8s_secret_data_key_for_key.placeholder": "Please enter Kubernetes Secret data key for private key",
|
||||
"workflow_node.deploy.form.k8s_secret_data_key_for_key.tooltip": "For more information, see <a href=\"https://kubernetes.io/docs/concepts/configuration/secret/\" target=\"_blank\">https://kubernetes.io/docs/concepts/configuration/secret/</a>",
|
||||
"workflow_node.deploy.form.k8s_secret_annotations.label": "Kubernetes Secret annotations (Optional)",
|
||||
"workflow_node.deploy.form.k8s_secret_annotations.placeholder": "Please enter Kubernetes Secret annotations",
|
||||
"workflow_node.deploy.form.k8s_secret_annotations.help": "Notes: One key value pair per line, separated by colon.",
|
||||
"workflow_node.deploy.form.k8s_secret_annotations.errmsg.invalid": "Please enter a valid annotations",
|
||||
"workflow_node.deploy.form.k8s_secret_annotations.tooltip": "Example: <br><i>environment: production<br>app: nginx</i>",
|
||||
"workflow_node.deploy.form.k8s_secret_labels.label": "Kubernetes Secret labels (Optional)",
|
||||
"workflow_node.deploy.form.k8s_secret_labels.placeholder": "Please enter Kubernetes Secret labels",
|
||||
"workflow_node.deploy.form.k8s_secret_labels.help": "Notes: One key value pair per line, separated by colon.",
|
||||
"workflow_node.deploy.form.k8s_secret_labels.errmsg.invalid": "Please enter a valid labels",
|
||||
"workflow_node.deploy.form.k8s_secret_labels.tooltip": "Example: <br><i>environment: production<br>app: nginx</i>",
|
||||
"workflow_node.deploy.form.kong_resource_type.label": "Resource type",
|
||||
"workflow_node.deploy.form.kong_resource_type.placeholder": "Please select resource type",
|
||||
"workflow_node.deploy.form.kong_resource_type.option.certificate.label": "SSL certificate",
|
||||
@ -996,7 +1012,10 @@
|
||||
"workflow_node.deploy.form.webhook_data.placeholder": "Please enter Webhook data",
|
||||
"workflow_node.deploy.form.webhook_data.help": "Notes: Leave it blank to use the default Webhook data provided by the credential.",
|
||||
"workflow_node.deploy.form.webhook_data.guide": "<details><summary>Supported variables: </summary><ol style=\"list-style: disc;\"><li><strong>${DOMAIN}</strong>: The primary domain of the certificate (<i>CommonName</i>).</li><li><strong>${DOMAINS}</strong>: The domain list of the certificate (<i>SubjectAltNames</i>).</li><li><strong>${CERTIFICATE}</strong>: The PEM format content of the certificate file.</li><li><strong>${SERVER_CERTIFICATE}</strong>: The PEM format content of the server certificate file.</li><li><strong>${INTERMEDIA_CERTIFICATE}</strong>: The PEM format content of the intermediate CA certificate file.</li><li><strong>${PRIVATE_KEY}</strong>: The PEM format content of the private key file.</li></ol></details><br>Please visit the credentials page for addtional notes.",
|
||||
"workflow_node.deploy.form.webhook_data.errmsg.json_invalid": "Please enter a valiod JSON string",
|
||||
"workflow_node.deploy.form.webhook_data.errmsg.json_invalid": "Please enter a valid JSON string",
|
||||
"workflow_node.deploy.form.webhook_timeout.label": "Webhook timeout (Optional)",
|
||||
"workflow_node.deploy.form.webhook_timeout.placeholder": "Please enter Webhook timeout",
|
||||
"workflow_node.deploy.form.webhook_timeout.unit": "seconds",
|
||||
"workflow_node.deploy.form.skip_on_last_succeeded.label": "Repeated deployment",
|
||||
"workflow_node.deploy.form.skip_on_last_succeeded.prefix": "If the last deployment was successful, ",
|
||||
"workflow_node.deploy.form.skip_on_last_succeeded.suffix": " to re-deploy.",
|
||||
@ -1041,7 +1060,10 @@
|
||||
"workflow_node.notify.form.webhook_data.placeholder": "Please enter Webhook data",
|
||||
"workflow_node.notify.form.webhook_data.help": "Notes: Leave it blank to use the default Webhook data provided by the credential.",
|
||||
"workflow_node.notify.form.webhook_data.guide": "<details><summary>Supported variables: </summary><ol style=\"list-style: disc;\"><li><strong>${SUBJECT}</strong>: The subject of notification.</li><li><strong>${MESSAGE}</strong>: The message of notification.</li></ol></details><br>Please visit the credentials page for addtional notes.",
|
||||
"workflow_node.notify.form.webhook_data.errmsg.json_invalid": "Please enter a valiod JSON string",
|
||||
"workflow_node.notify.form.webhook_data.errmsg.json_invalid": "Please enter a valid JSON string",
|
||||
"workflow_node.notify.form.webhook_timeout.label": "Webhook timeout (Optional)",
|
||||
"workflow_node.notify.form.webhook_timeout.placeholder": "Please enter Webhook timeout",
|
||||
"workflow_node.notify.form.webhook_timeout.unit": "seconds",
|
||||
"workflow_node.notify.form.skip_on_all_prev_skipped.label": "Silent behavior",
|
||||
"workflow_node.notify.form.skip_on_all_prev_skipped.prefix": "If all the previous nodes were skipped, ",
|
||||
"workflow_node.notify.form.skip_on_all_prev_skipped.suffix": " to notify.",
|
||||
|
||||
@ -77,6 +77,12 @@
|
||||
"access.form.acmehttpreq_password.label": "HTTP 基本认证密码(可选)",
|
||||
"access.form.acmehttpreq_password.placeholder": "请输入 HTTP 基本认证密码",
|
||||
"access.form.acmehttpreq_password.tooltip": "这是什么?请参阅 <a href=\"https://go-acme.github.io/lego/dns/httpreq/\" target=\"_blank\">https://go-acme.github.io/lego/dns/httpreq/</a>",
|
||||
"access.form.actalisssl_eab_kid.label": "ACME EAB KID",
|
||||
"access.form.actalisssl_eab_kid.placeholder": "请输入 ACME EAB KID",
|
||||
"access.form.actalisssl_eab_kid.tooltip": "这是什么?请参阅 <a href=\"https://www.actalis.com/manage-with-acme\" target=\"_blank\">https://www.actalis.com/manage-with-acme</a>",
|
||||
"access.form.actalisssl_eab_hmac_key.label": "ACME EAB HMAC Key",
|
||||
"access.form.actalisssl_eab_hmac_key.placeholder": "请输入 ACME EAB HMAC Key",
|
||||
"access.form.actalisssl_eab_hmac_key.tooltip": "这是什么?请参阅 <a href=\"https://www.actalis.com/manage-with-acme\" target=\"_blank\">https://www.actalis.com/manage-with-acme</a>",
|
||||
"access.form.aliyun_access_key_id.label": "阿里云 AccessKeyId",
|
||||
"access.form.aliyun_access_key_id.placeholder": "请输入阿里云 AccessKeyId",
|
||||
"access.form.aliyun_access_key_id.tooltip": "这是什么?请参阅 <a href=\"https://help.aliyun.com/zh/ram/user-guide/create-an-accesskey-pair\" target=\"_blank\">https://help.aliyun.com/zh/ram/user-guide/create-an-accesskey-pair</a>",
|
||||
@ -491,6 +497,9 @@
|
||||
"access.form.volcengine_secret_access_key.label": "火山引擎 SecretAccessKey",
|
||||
"access.form.volcengine_secret_access_key.placeholder": "请输入火山引擎 SecretAccessKey",
|
||||
"access.form.volcengine_secret_access_key.tooltip": "这是什么?请参阅 <a href=\"https://www.volcengine.com/docs/6291/216571\" target=\"_blank\">https://www.volcengine.com/docs/6291/216571</a>",
|
||||
"access.form.vultr_api_key.label": "Vultr API Key",
|
||||
"access.form.vultr_api_key.placeholder": "请输入 Vultr API Key",
|
||||
"access.form.vultr_api_key.tooltip": "这是什么?请参阅 <a href=\"https://docs.vultr.com/platform/other/users/manage-users/api-access/regenerate-user-api-key\" target=\"_blank\">https://docs.vultr.com/platform/other/users/manage-users/api-access/regenerate-user-api-key</a>",
|
||||
"access.form.wangsu_access_key_id.label": "网宿云 AccessKeyId",
|
||||
"access.form.wangsu_access_key_id.placeholder": "请输入网宿云 AccessKeyId",
|
||||
"access.form.wangsu_access_key_id.tooltip": "这是什么?请参阅 <a href=\"https://www.wangsu.com/document/account-manage/15775\" target=\"_blank\">https://www.wangsu.com/document/account-manage/15775</a>",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"provider.acmeca": "ACME 自定义 CA 端点",
|
||||
"provider.acmedns": "ACME-DNS",
|
||||
"provider.acmehttpreq": "ACME 自定义 HTTP 端点",
|
||||
"provider.actalisssl": "Actalis SSL",
|
||||
"provider.aliyun": "阿里云",
|
||||
"provider.aliyun.alb": "阿里云 - 应用型负载均衡 ALB",
|
||||
"provider.aliyun.apigw": "阿里云 - API 网关",
|
||||
@ -171,6 +172,7 @@
|
||||
"provider.volcengine.imagex": "火山引擎 - 图片服务 ImageX",
|
||||
"provider.volcengine.live": "火山引擎 - 视频直播 Live",
|
||||
"provider.volcengine.tos": "火山引擎 - 对象存储 TOS",
|
||||
"provider.vultr": "Vultr",
|
||||
"provider.wangsu": "网宿云",
|
||||
"provider.wangsu.cdn": "网宿云 - 内容分发网络 CDN",
|
||||
"provider.wangsu.cdnpro": "网宿云 - CDN Pro (CDN 360)",
|
||||
|
||||
@ -255,9 +255,15 @@
|
||||
"workflow_node.deploy.form.aliyun_clb_snidomain.placeholder": "请输入阿里云 CLB 扩展域名(支持泛域名)",
|
||||
"workflow_node.deploy.form.aliyun_clb_snidomain.help": "提示:不填写时,将替换监听器的默认证书;否则,将替换扩展域名证书。",
|
||||
"workflow_node.deploy.form.aliyun_clb_snidomain.tooltip": "这是什么?请参阅 <a href=\"https://slb.console.aliyun.com/clb\" target=\"_blank\">https://slb.console.aliyun.com/clb</a>",
|
||||
"workflow_node.deploy.form.aliyun_cdn_region.label": "阿里云 CDN 服务地域",
|
||||
"workflow_node.deploy.form.aliyun_cdn_region.placeholder": "请输入阿里云 CDN 服务地域(例如:cn-hangzhou)",
|
||||
"workflow_node.deploy.form.aliyun_cdn_region.tooltip": "中国站请填写 <strong>cn-hangzhou</strong>;<br>国际站请填写 <strong>ap-southeast-1</strong>。",
|
||||
"workflow_node.deploy.form.aliyun_cdn_domain.label": "阿里云 CDN 加速域名",
|
||||
"workflow_node.deploy.form.aliyun_cdn_domain.placeholder": "请输入阿里云 CDN 加速域名(支持泛域名)",
|
||||
"workflow_node.deploy.form.aliyun_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://cdn.console.aliyun.com\" target=\"_blank\">https://cdn.console.aliyun.com</a>",
|
||||
"workflow_node.deploy.form.aliyun_dcdn_region.label": "阿里云 DCDN 服务地域",
|
||||
"workflow_node.deploy.form.aliyun_dcdn_region.placeholder": "请输入阿里云 DCDN 服务地域(例如:cn-hangzhou)",
|
||||
"workflow_node.deploy.form.aliyun_dcdn_region.tooltip": "中国站请填写 <strong>cn-hangzhou</strong>;<br>国际站请填写 <strong>ap-southeast-1</strong>。",
|
||||
"workflow_node.deploy.form.aliyun_dcdn_domain.label": "阿里云 DCDN 加速域名",
|
||||
"workflow_node.deploy.form.aliyun_dcdn_domain.placeholder": "请输入阿里云 DCDN 加速域名(支持泛域名)",
|
||||
"workflow_node.deploy.form.aliyun_dcdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://dcdn.console.aliyun.com\" target=\"_blank\">https://dcdn.console.aliyun.com</a>",
|
||||
@ -586,6 +592,16 @@
|
||||
"workflow_node.deploy.form.k8s_secret_data_key_for_key.label": "Kubernetes Secret 数据键(用于存放私钥的字段)",
|
||||
"workflow_node.deploy.form.k8s_secret_data_key_for_key.placeholder": "请输入 Kubernetes Secret 中用于存放私钥的数据键",
|
||||
"workflow_node.deploy.form.k8s_secret_data_key_for_key.tooltip": "这是什么?请参阅 <a href=\"https://kubernetes.io/zh-cn/docs/concepts/configuration/secret/\" target=\"_blank\">https://kubernetes.io/zh-cn/docs/concepts/configuration/secret/</a>",
|
||||
"workflow_node.deploy.form.k8s_secret_annotations.label": "Kubernetes Secret 注解(可选)",
|
||||
"workflow_node.deploy.form.k8s_secret_annotations.placeholder": "请输入 Kubernetes Secret 注解",
|
||||
"workflow_node.deploy.form.k8s_secret_annotations.help": "提示:每行一个键值对,以分号分隔。",
|
||||
"workflow_node.deploy.form.k8s_secret_annotations.errmsg.invalid": "请输入有效的注解键值对",
|
||||
"workflow_node.deploy.form.k8s_secret_annotations.tooltip": "示例:<br><i>environment: production<br>app: nginx</i>",
|
||||
"workflow_node.deploy.form.k8s_secret_labels.label": "Kubernetes Secret 标签(可选)",
|
||||
"workflow_node.deploy.form.k8s_secret_labels.placeholder": "请输入 Kubernetes Secret 标签",
|
||||
"workflow_node.deploy.form.k8s_secret_labels.help": "提示:每行一个键值对,以分号分隔。",
|
||||
"workflow_node.deploy.form.k8s_secret_labels.errmsg.invalid": "请输入有效的标签键值对",
|
||||
"workflow_node.deploy.form.k8s_secret_labels.tooltip": "示例:<br><i>environment: production<br>app: nginx</i>",
|
||||
"workflow_node.deploy.form.kong_resource_type.label": "证书部署方式",
|
||||
"workflow_node.deploy.form.kong_resource_type.placeholder": "请选择证书部署方式",
|
||||
"workflow_node.deploy.form.kong_resource_type.option.certificate.label": "替换指定证书",
|
||||
@ -995,6 +1011,9 @@
|
||||
"workflow_node.deploy.form.webhook_data.help": "提示:不填写时,将使用所选部署目标授权的默认 Webhook 回调数据。",
|
||||
"workflow_node.deploy.form.webhook_data.guide": "<details><summary>支持的变量:</summary><ol style=\"list-style: disc;\"><li><strong>${DOMAIN}</strong>:证书的主域名(即 <i>CommonName</i>)。</li><li><strong>${DOMAINS}</strong>:证书的多域名列表(即 <i>SubjectAltNames</i>)。</li><li><strong>${CERTIFICATE}</strong>:证书文件 PEM 格式内容。</li><li><strong>${SERVER_CERTIFICATE}</strong>:证书文件(仅含服务器证书)PEM 格式内容。</li><li><strong>${INTERMEDIA_CERTIFICATE}</strong>:证书文件(仅含中间证书)PEM 格式内容。</li><li><strong>${PRIVATE_KEY}</strong>:私钥文件 PEM 格式内容。</li></ol></details><br>其他注意事项请前往授权凭据页面查看。",
|
||||
"workflow_node.deploy.form.webhook_data.errmsg.json_invalid": "请输入有效的 JSON 格式字符串",
|
||||
"workflow_node.deploy.form.webhook_timeout.label": "Webhook 超时时间(可选)",
|
||||
"workflow_node.deploy.form.webhook_timeout.placeholder": "请输入 Webhook 超时时间",
|
||||
"workflow_node.deploy.form.webhook_timeout.unit": "秒",
|
||||
"workflow_node.deploy.form.skip_on_last_succeeded.label": "重复部署",
|
||||
"workflow_node.deploy.form.skip_on_last_succeeded.prefix": "当上次部署相同证书成功时,再次运行工作流时",
|
||||
"workflow_node.deploy.form.skip_on_last_succeeded.suffix": "此部署节点。",
|
||||
@ -1040,6 +1059,9 @@
|
||||
"workflow_node.notify.form.webhook_data.help": "提示:不填写时,将使用所选部署目标授权的默认 Webhook 回调数据。",
|
||||
"workflow_node.notify.form.webhook_data.guide": "<details><summary>支持的变量:</summary><ol style=\"list-style: disc;\"><li><strong>${SUBJECT}</strong>:通知主题。</li><li><strong>${MESSAGE}</strong>:通知内容。</ol></details><br>其他注意事项请前往授权凭据页面查看。",
|
||||
"workflow_node.notify.form.webhook_data.errmsg.json_invalid": "请输入有效的 JSON 格式字符串",
|
||||
"workflow_node.notify.form.webhook_timeout.label": "Webhook 超时时间(可选)",
|
||||
"workflow_node.notify.form.webhook_timeout.placeholder": "请输入 Webhook 超时时间",
|
||||
"workflow_node.notify.form.webhook_timeout.unit": "秒",
|
||||
"workflow_node.notify.form.skip_on_all_prev_skipped.label": "静默行为",
|
||||
"workflow_node.notify.form.skip_on_all_prev_skipped.prefix": "当前序申请、上传、部署等节点均已跳过执行时,",
|
||||
"workflow_node.notify.form.skip_on_all_prev_skipped.suffix": "此通知节点。",
|
||||
|
||||
@ -108,6 +108,76 @@ const SSLProviderEditFormLetsEncryptStagingConfig = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const SSLProviderEditFormActalisSSLConfig = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { pending, settings, updateSettings } = useContext(SSLProviderContext);
|
||||
|
||||
const formSchema = z.object({
|
||||
eabKid: z
|
||||
.string(t("access.form.actalisssl_eab_kid.placeholder"))
|
||||
.min(1, t("access.form.actalisssl_eab_kid.placeholder"))
|
||||
.max(256, t("common.errmsg.string_max", { max: 256 })),
|
||||
eabHmacKey: z
|
||||
.string(t("access.form.actalisssl_eab_hmac_key.placeholder"))
|
||||
.min(1, t("access.form.actalisssl_eab_hmac_key.placeholder"))
|
||||
.max(256, t("common.errmsg.string_max", { max: 256 })),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
const { form: formInst, formProps } = useAntdForm<z.infer<typeof formSchema>>({
|
||||
initialValues: settings?.content?.config?.[CA_PROVIDERS.ACTALISSSL],
|
||||
onSubmit: async (values) => {
|
||||
const newSettings = produce(settings, (draft) => {
|
||||
draft.content ??= {} as SSLProviderSettingsContent;
|
||||
draft.content.provider = CA_PROVIDERS.ACTALISSSL;
|
||||
|
||||
draft.content.config ??= {} as SSLProviderSettingsContent["config"];
|
||||
draft.content.config[CA_PROVIDERS.ACTALISSSL] = values;
|
||||
});
|
||||
await updateSettings(newSettings);
|
||||
|
||||
setFormChanged(false);
|
||||
},
|
||||
});
|
||||
|
||||
const [formChanged, setFormChanged] = useState(false);
|
||||
useEffect(() => {
|
||||
setFormChanged(settings?.content?.provider !== CA_PROVIDERS.ACTALISSSL);
|
||||
}, [settings?.content?.provider]);
|
||||
|
||||
const handleFormChange = () => {
|
||||
setFormChanged(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form {...formProps} form={formInst} disabled={pending} layout="vertical" onValuesChange={handleFormChange}>
|
||||
<Form.Item
|
||||
name="eabKid"
|
||||
label={t("access.form.actalisssl_eab_kid.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.actalisssl_eab_kid.tooltip") }}></span>}
|
||||
>
|
||||
<Input autoComplete="new-password" placeholder={t("access.form.actalisssl_eab_kid.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="eabHmacKey"
|
||||
label={t("access.form.actalisssl_eab_hmac_key.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.actalisssl_eab_hmac_key.tooltip") }}></span>}
|
||||
>
|
||||
<Input.Password autoComplete="new-password" placeholder={t("access.form.actalisssl_eab_hmac_key.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit" disabled={!formChanged} loading={pending}>
|
||||
{t("common.button.save")}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
const SSLProviderEditFormBuypassConfig = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@ -466,6 +536,7 @@ const SettingsSSLProvider = () => {
|
||||
const providers = [
|
||||
[CA_PROVIDERS.LETSENCRYPT, "provider.letsencrypt", "letsencrypt.org", "/imgs/providers/letsencrypt.svg"],
|
||||
[CA_PROVIDERS.LETSENCRYPTSTAGING, "provider.letsencryptstaging", "letsencrypt.org", "/imgs/providers/letsencrypt.svg"],
|
||||
[CA_PROVIDERS.ACTALISSSL, "provider.actalisssl", "actalis.com", "/imgs/providers/actalisssl.png"],
|
||||
[CA_PROVIDERS.BUYPASS, "provider.buypass", "buypass.com", "/imgs/providers/buypass.png"],
|
||||
[CA_PROVIDERS.GOOGLETRUSTSERVICES, "provider.googletrustservices", "pki.goog", "/imgs/providers/google.svg"],
|
||||
[CA_PROVIDERS.SSLCOM, "provider.sslcom", "ssl.com", "/imgs/providers/sslcom.svg"],
|
||||
@ -486,6 +557,8 @@ const SettingsSSLProvider = () => {
|
||||
return <SSLProviderEditFormLetsEncryptConfig />;
|
||||
case CA_PROVIDERS.LETSENCRYPTSTAGING:
|
||||
return <SSLProviderEditFormLetsEncryptStagingConfig />;
|
||||
case CA_PROVIDERS.ACTALISSSL:
|
||||
return <SSLProviderEditFormActalisSSLConfig />;
|
||||
case CA_PROVIDERS.BUYPASS:
|
||||
return <SSLProviderEditFormBuypassConfig />;
|
||||
case CA_PROVIDERS.GOOGLETRUSTSERVICES:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user