mirror of
https://github.com/certimate-go/certimate.git
synced 2026-09-24 23:10:13 +08:00
feat(provider): new deployment provider: zenlayer zga
This commit is contained in:
@@ -256,6 +256,7 @@ require (
|
||||
github.com/tinylib/msgp v1.6.4 // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
github.com/zeebo/xxh3 v1.1.0 // indirect
|
||||
github.com/zenlayer/zenlayercloud-sdk-go v0.2.33 // indirect
|
||||
golang.org/x/image v0.40.0 // indirect
|
||||
golang.org/x/mod v0.35.0 // indirect
|
||||
golang.org/x/net v0.54.0 // indirect
|
||||
|
||||
@@ -982,6 +982,8 @@ github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
|
||||
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
|
||||
github.com/zenlayer/zenlayercloud-sdk-go v0.2.33 h1:lfzoPBtKyCFktOk69IfvpMI3n3o6YHSV/NVp3qQQ1mU=
|
||||
github.com/zenlayer/zenlayercloud-sdk-go v0.2.33/go.mod h1:iOR3Pzb2t3Z0+UvXhdUqi7nhwZM5Ux2oH4ixKH+qe88=
|
||||
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package deployers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/certimate-go/certimate/internal/domain"
|
||||
"github.com/certimate-go/certimate/pkg/core/deployer"
|
||||
zenlayerga "github.com/certimate-go/certimate/pkg/core/deployer/providers/zenlayer-ga"
|
||||
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Registries.MustRegister(domain.DeploymentProviderTypeZenlayerGA, func(options *ProviderFactoryOptions) (deployer.Provider, error) {
|
||||
credentials := domain.AccessConfigForZenlayer{}
|
||||
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
|
||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||
}
|
||||
|
||||
provider, err := zenlayerga.NewDeployer(&zenlayerga.DeployerConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
AccessKeyPassword: credentials.AccessKeyPassword,
|
||||
ResourceGroupId: credentials.ResourceGroupId,
|
||||
ResourceType: xmaps.GetString(options.ProviderExtendedConfig, "resourceType"),
|
||||
AcceleratorId: xmaps.GetString(options.ProviderExtendedConfig, "acceleratorId"),
|
||||
CertificateId: xmaps.GetString(options.ProviderExtendedConfig, "certificateId"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
}
|
||||
@@ -621,6 +621,12 @@ type AccessConfigForXinnet struct {
|
||||
ApiPassword string `json:"apiPassword"`
|
||||
}
|
||||
|
||||
type AccessConfigForZenlayer struct {
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
AccessKeyPassword string `json:"accessKeyPassword"`
|
||||
ResourceGroupId string `json:"resourceGroupId,omitempty"`
|
||||
}
|
||||
|
||||
type AccessConfigForZeroSSL struct {
|
||||
AccessConfigForACMEExternalAccountBinding
|
||||
}
|
||||
|
||||
@@ -123,6 +123,7 @@ const (
|
||||
AccessProviderTypeWeComBot = AccessProviderType("wecombot")
|
||||
AccessProviderTypeWestcn = AccessProviderType("westcn")
|
||||
AccessProviderTypeXinnet = AccessProviderType("xinnet")
|
||||
AccessProviderTypeZenlayer = AccessProviderType("zenlayer")
|
||||
AccessProviderTypeZeroSSL = AccessProviderType("zerossl")
|
||||
)
|
||||
|
||||
@@ -391,6 +392,7 @@ const (
|
||||
DeploymentProviderTypeWangsuCDNPro = DeploymentProviderType(AccessProviderTypeWangsu + "-cdnpro")
|
||||
DeploymentProviderTypeWangsuCertificate = DeploymentProviderType(AccessProviderTypeWangsu + "-certificate")
|
||||
DeploymentProviderTypeWebhook = DeploymentProviderType(AccessProviderTypeWebhook)
|
||||
DeploymentProviderTypeZenlayerGA = DeploymentProviderType(AccessProviderTypeZenlayer + "-ga")
|
||||
)
|
||||
|
||||
type NotificationProviderType string
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"github.com/zenlayer/zenlayercloud-sdk-go/zenlayercloud/common"
|
||||
zga "github.com/zenlayer/zenlayercloud-sdk-go/zenlayercloud/zga20230706"
|
||||
)
|
||||
|
||||
// This is a partial copy of https://github.com/zenlayer/zenlayercloud-sdk-go/blob/main/zenlayercloud/zga20230706/client.go
|
||||
// to lightweight the vendor packages in the built binary.
|
||||
type ZgaClient struct {
|
||||
*common.Client
|
||||
}
|
||||
|
||||
func NewZgaClient(config *common.Config, secretKeyId, secretKeyPassword string) (client *ZgaClient, err error) {
|
||||
client = &ZgaClient{}
|
||||
|
||||
err = client.InitWithCredential(common.NewCredential(secretKeyId, secretKeyPassword))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = client.WithConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (c *ZgaClient) CreateCertificate(request *zga.CreateCertificateRequest) (response *zga.CreateCertificateResponse, err error) {
|
||||
response = zga.NewCreateCertificateResponse()
|
||||
err = c.ApiCall(request, response)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ZgaClient) DescribeCertificates(request *zga.DescribeCertificatesRequest) (response *zga.DescribeCertificatesResponse, err error) {
|
||||
response = zga.NewDescribeCertificatesResponse()
|
||||
err = c.ApiCall(request, response)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ZgaClient) ModifyCertificate(request *zga.ModifyCertificateRequest) (response *zga.ModifyCertificateResponse, err error) {
|
||||
response = zga.NewModifyCertificateResponse()
|
||||
err = c.ApiCall(request, response)
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
package zenlayerga
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
zcommon "github.com/zenlayer/zenlayercloud-sdk-go/zenlayercloud/common"
|
||||
zga "github.com/zenlayer/zenlayercloud-sdk-go/zenlayercloud/zga20230706"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core/certmgr"
|
||||
"github.com/certimate-go/certimate/pkg/core/certmgr/providers/zenlayer-ga/internal"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
)
|
||||
|
||||
type CertmgrConfig struct {
|
||||
// Zenlayer AccessKeyId。
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// Zenlayer AccessKeyPassword。
|
||||
AccessKeyPassword string `json:"accessKeyPassword"`
|
||||
// Zenlayer 资源组 ID。
|
||||
ResourceGroupId string `json:"resourceGroupId,omitempty"`
|
||||
}
|
||||
|
||||
type Certmgr struct {
|
||||
config *CertmgrConfig
|
||||
logger *slog.Logger
|
||||
sdkClient *internal.ZgaClient
|
||||
}
|
||||
|
||||
var _ certmgr.Provider = (*Certmgr)(nil)
|
||||
|
||||
func NewCertmgr(config *CertmgrConfig) (*Certmgr, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the certmgr provider is nil")
|
||||
}
|
||||
|
||||
client, err := createSDKClient(config.AccessKeyId, config.AccessKeyPassword)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create client: %w", err)
|
||||
}
|
||||
|
||||
return &Certmgr{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
sdkClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Certmgr) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
c.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
c.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*certmgr.UploadResult, error) {
|
||||
// 解析证书内容
|
||||
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 查询证书列表,避免重复上传
|
||||
// REF: https://docs.console.zenlayer.com/api-reference/cn/networking/zga/certificate/describecertificates
|
||||
describeCertificatesPageNum := 1
|
||||
describeCertificatesPageSize := 100
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
describeCertificatesReq := zga.NewDescribeCertificatesRequest()
|
||||
describeCertificatesReq.ResourceGroupId = c.config.ResourceGroupId
|
||||
describeCertificatesReq.PageNum = describeCertificatesPageNum
|
||||
describeCertificatesReq.PageSize = describeCertificatesPageSize
|
||||
describeCertificatesResp, err := c.sdkClient.DescribeCertificates(describeCertificatesReq)
|
||||
c.logger.Debug("sdk request 'zga.DescribeCertificates'", slog.Any("request", describeCertificatesReq), slog.Any("response", describeCertificatesResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'zga.DescribeCertificates': %w", err)
|
||||
}
|
||||
|
||||
for _, certItem := range describeCertificatesResp.Response.DataSet {
|
||||
// 对比证书通用名称
|
||||
if !strings.EqualFold(certX509.Subject.CommonName, certItem.Common) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 对比证书扩展名称
|
||||
if !slices.Equal(certX509.DNSNames, certItem.Sans) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 对比证书有效期
|
||||
oldCertNotBefore, _ := time.Parse("2006-01-02T15:04:05Z", certItem.StartTime)
|
||||
oldCertNotAfter, _ := time.Parse("2006-01-02T15:04:05Z", certItem.EndTime)
|
||||
if !certX509.NotBefore.Equal(oldCertNotBefore) {
|
||||
continue
|
||||
} else if !certX509.NotAfter.Equal(oldCertNotAfter) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 对比证书指纹
|
||||
//
|
||||
// 注意,虽然文档中描述为 MD5 摘要,但示例给出的是 SHA-1 摘要,因此这里都尝试对比一下
|
||||
fingerprintMd5 := md5.Sum(certX509.Raw)
|
||||
fingerprintMd5Hex := hex.EncodeToString(fingerprintMd5[:])
|
||||
fingerprintSha1 := sha1.Sum(certX509.Raw)
|
||||
fingerprintSha1Hex := hex.EncodeToString(fingerprintSha1[:])
|
||||
if !strings.EqualFold(fingerprintMd5Hex, certItem.Fingerprint) && !strings.EqualFold(fingerprintSha1Hex, certItem.Fingerprint) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 对比证书公钥算法
|
||||
switch certX509.PublicKeyAlgorithm {
|
||||
case x509.RSA:
|
||||
if !strings.EqualFold(certItem.Algorithm, "RSA") {
|
||||
continue
|
||||
}
|
||||
case x509.ECDSA:
|
||||
if !strings.EqualFold(certItem.Algorithm, "EC") && !strings.EqualFold(certItem.Algorithm, "ECDSA") {
|
||||
continue
|
||||
}
|
||||
default:
|
||||
// 未知算法,跳过
|
||||
continue
|
||||
}
|
||||
|
||||
// 如果以上信息都一致,则视为已存在相同证书,直接返回
|
||||
c.logger.Info("ssl certificate already exists")
|
||||
return &certmgr.UploadResult{
|
||||
CertId: certItem.CertificateId,
|
||||
CertName: certItem.CertificateLabel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if len(describeCertificatesResp.Response.DataSet) < describeCertificatesPageSize {
|
||||
break
|
||||
}
|
||||
|
||||
describeCertificatesPageNum++
|
||||
}
|
||||
|
||||
// 生成新证书名(需符合 Zenlayer 命名规则)
|
||||
certName := fmt.Sprintf("certimate_%d", time.Now().UnixMilli())
|
||||
|
||||
// 创建证书
|
||||
// REF: https://docs.console.zenlayer.com/api-reference/cn/networking/zga/certificate/createcertificate
|
||||
createCertificateReq := zga.NewCreateCertificateRequest()
|
||||
createCertificateReq.ResourceGroupId = c.config.ResourceGroupId
|
||||
createCertificateReq.CertificateLabel = certName
|
||||
createCertificateReq.CertificateContent = certPEM
|
||||
createCertificateReq.CertificateKey = privkeyPEM
|
||||
createCertificateResp, err := c.sdkClient.CreateCertificate(createCertificateReq)
|
||||
c.logger.Debug("sdk request 'zga.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'zga.CreateCertificate': %w", err)
|
||||
}
|
||||
|
||||
return &certmgr.UploadResult{
|
||||
CertId: createCertificateResp.Response.CertificateId,
|
||||
CertName: certName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Certmgr) Replace(ctx context.Context, certIdOrName string, certPEM, privkeyPEM string) (*certmgr.OperateResult, error) {
|
||||
// 更新证书
|
||||
// REF: https://docs.console.zenlayer.com/api-reference/cn/networking/zga/certificate/modifycertificate
|
||||
modifyCertificateReq := zga.NewModifyCertificateRequest()
|
||||
modifyCertificateReq.CertificateId = certIdOrName
|
||||
modifyCertificateReq.CertificateContent = certPEM
|
||||
modifyCertificateReq.CertificateKey = privkeyPEM
|
||||
modifyCertificateResp, err := c.sdkClient.ModifyCertificate(modifyCertificateReq)
|
||||
c.logger.Debug("sdk request 'zga.ModifyCertificate'", slog.Any("request", modifyCertificateReq), slog.Any("response", modifyCertificateResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'zga.ModifyCertificate': %w", err)
|
||||
}
|
||||
|
||||
return &certmgr.OperateResult{}, nil
|
||||
}
|
||||
|
||||
func createSDKClient(accessKeyId, accessKeyPassword string) (*internal.ZgaClient, error) {
|
||||
config := zcommon.NewConfig()
|
||||
|
||||
client, err := internal.NewZgaClient(config, accessKeyId, accessKeyPassword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package zenlayerga
|
||||
|
||||
const (
|
||||
// 资源类型:部署到指定全球网络加速器。
|
||||
RESOURCE_TYPE_ACCELERATOR = "accelerator"
|
||||
// 资源类型:替换指定证书。
|
||||
RESOURCE_TYPE_CERTIFICATE = "certificate"
|
||||
)
|
||||
@@ -0,0 +1,40 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"github.com/zenlayer/zenlayercloud-sdk-go/zenlayercloud/common"
|
||||
zga "github.com/zenlayer/zenlayercloud-sdk-go/zenlayercloud/zga20230706"
|
||||
)
|
||||
|
||||
// This is a partial copy of https://github.com/zenlayer/zenlayercloud-sdk-go/blob/main/zenlayercloud/zga20230706/client.go
|
||||
// to lightweight the vendor packages in the built binary.
|
||||
type ZgaClient struct {
|
||||
*common.Client
|
||||
}
|
||||
|
||||
func NewZgaClient(config *common.Config, secretKeyId, secretKeyPassword string) (client *ZgaClient, err error) {
|
||||
client = &ZgaClient{}
|
||||
|
||||
err = client.InitWithCredential(common.NewCredential(secretKeyId, secretKeyPassword))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = client.WithConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (c *ZgaClient) DescribeAccelerators(request *zga.DescribeAcceleratorsRequest) (response *zga.DescribeAcceleratorsResponse, err error) {
|
||||
response = zga.NewDescribeAcceleratorsResponse()
|
||||
err = c.ApiCall(request, response)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ZgaClient) ModifyAcceleratorCertificate(request *zga.ModifyAcceleratorCertificateRequest) (response *zga.ModifyAcceleratorCertificateResponse, err error) {
|
||||
response = zga.NewModifyAcceleratorCertificateResponse()
|
||||
err = c.ApiCall(request, response)
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package zenlayerga
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
zcommon "github.com/zenlayer/zenlayercloud-sdk-go/zenlayercloud/common"
|
||||
zga "github.com/zenlayer/zenlayercloud-sdk-go/zenlayercloud/zga20230706"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core/certmgr"
|
||||
mcertmgr "github.com/certimate-go/certimate/pkg/core/certmgr/providers/zenlayer-ga"
|
||||
"github.com/certimate-go/certimate/pkg/core/deployer"
|
||||
"github.com/certimate-go/certimate/pkg/core/deployer/providers/zenlayer-ga/internal"
|
||||
xwait "github.com/certimate-go/certimate/pkg/utils/wait"
|
||||
)
|
||||
|
||||
type DeployerConfig struct {
|
||||
// Zenlayer AccessKeyId。
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// Zenlayer AccessKeyPassword。
|
||||
AccessKeyPassword string `json:"accessKeyPassword"`
|
||||
// Zenlayer 资源组 ID。
|
||||
ResourceGroupId string `json:"resourceGroupId,omitempty"`
|
||||
// 部署资源类型。
|
||||
ResourceType string `json:"resourceType"`
|
||||
// 加速器 ID。
|
||||
// 部署资源类型为 [RESOURCE_TYPE_ACCELERATOR] 时必填。
|
||||
AcceleratorId string `json:"acceleratorId,omitempty"`
|
||||
// 证书 ID。
|
||||
// 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时必填。
|
||||
CertificateId string `json:"certificateId,omitempty"`
|
||||
}
|
||||
|
||||
type Deployer struct {
|
||||
config *DeployerConfig
|
||||
logger *slog.Logger
|
||||
sdkClient *internal.ZgaClient
|
||||
sdkCertmgr certmgr.Provider
|
||||
}
|
||||
|
||||
var _ deployer.Provider = (*Deployer)(nil)
|
||||
|
||||
func NewDeployer(config *DeployerConfig) (*Deployer, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the deployer provider is nil")
|
||||
}
|
||||
|
||||
client, err := createSDKClient(config.AccessKeyId, config.AccessKeyPassword)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create client: %w", err)
|
||||
}
|
||||
|
||||
pcertmgr, err := mcertmgr.NewCertmgr(&mcertmgr.CertmgrConfig{
|
||||
AccessKeyId: config.AccessKeyId,
|
||||
AccessKeyPassword: config.AccessKeyPassword,
|
||||
ResourceGroupId: config.ResourceGroupId,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create certmgr: %w", err)
|
||||
}
|
||||
|
||||
return &Deployer{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
sdkClient: client,
|
||||
sdkCertmgr: pcertmgr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Deployer) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
d.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
d.logger = logger
|
||||
}
|
||||
|
||||
d.sdkCertmgr.SetLogger(logger)
|
||||
}
|
||||
|
||||
func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*deployer.DeployResult, error) {
|
||||
// 根据部署资源类型决定部署方式
|
||||
switch d.config.ResourceType {
|
||||
case RESOURCE_TYPE_ACCELERATOR:
|
||||
if err := d.deployToAccelerator(ctx, certPEM, privkeyPEM); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case RESOURCE_TYPE_CERTIFICATE:
|
||||
if err := d.deployToCertificate(ctx, certPEM, privkeyPEM); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType)
|
||||
}
|
||||
|
||||
return &deployer.DeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *Deployer) deployToAccelerator(ctx context.Context, certPEM, privkeyPEM string) error {
|
||||
if d.config.AcceleratorId == "" {
|
||||
return errors.New("config `acceleratorId` is required")
|
||||
}
|
||||
|
||||
// 上传证书
|
||||
upres, err := d.sdkCertmgr.Upload(ctx, certPEM, privkeyPEM)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to upload certificate file: %w", err)
|
||||
} else {
|
||||
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||
}
|
||||
|
||||
// 查询加速器信息
|
||||
// REF: https://docs.console.zenlayer.com/api-reference/cn/networking/zga/accelerator/describeaccelerators
|
||||
describeAcceleratorsReq := zga.NewDescribeAcceleratorsRequest()
|
||||
describeAcceleratorsReq.AcceleratorIds = []string{d.config.AcceleratorId}
|
||||
describeAcceleratorsReq.PageNum = 1
|
||||
describeAcceleratorsReq.PageSize = 1
|
||||
describeAcceleratorsResp, err := d.sdkClient.DescribeAccelerators(describeAcceleratorsReq)
|
||||
d.logger.Debug("sdk request 'zga.DescribeAccelerators'", slog.Any("request", describeAcceleratorsReq), slog.Any("response", describeAcceleratorsResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'zga.DescribeAccelerators': %w", err)
|
||||
} else if len(describeAcceleratorsResp.Response.DataSet) == 0 {
|
||||
return fmt.Errorf("could not found accelerator '%s'", d.config.AcceleratorId)
|
||||
}
|
||||
|
||||
// 修改加速器证书
|
||||
// REF: https://docs.console.zenlayer.com/api-reference/cn/networking/zga/accelerator/modifyacceleratorcertificate
|
||||
acceleratorInfo := describeAcceleratorsResp.Response.DataSet[0]
|
||||
if acceleratorInfo.Certificate == nil || acceleratorInfo.Certificate.CertificateId != upres.CertId {
|
||||
modifyAcceleratorCertificateReq := zga.NewModifyAcceleratorCertificateRequest()
|
||||
modifyAcceleratorCertificateReq.AcceleratorId = acceleratorInfo.AcceleratorId
|
||||
modifyAcceleratorCertificateReq.CertificateId = upres.CertId
|
||||
modifyAcceleratorCertificateResp, err := d.sdkClient.ModifyAcceleratorCertificate(modifyAcceleratorCertificateReq)
|
||||
d.logger.Debug("sdk request 'zga.ModifyAcceleratorCertificate'", slog.Any("request", modifyAcceleratorCertificateReq), slog.Any("response", modifyAcceleratorCertificateResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request 'zga.ModifyAcceleratorCertificate': %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 查询加速器状态,等待部署状态变更
|
||||
// REF: https://docs.console.zenlayer.com/api-reference/cn/networking/zga/accelerator/describeaccelerators
|
||||
if _, err := xwait.UntilWithContext(ctx, func(_ context.Context, _ int) (bool, error) {
|
||||
describeAcceleratorsReq := zga.NewDescribeAcceleratorsRequest()
|
||||
describeAcceleratorsReq.AcceleratorIds = []string{acceleratorInfo.AcceleratorId}
|
||||
describeAcceleratorsReq.PageNum = 1
|
||||
describeAcceleratorsReq.PageSize = 1
|
||||
describeAcceleratorsResp, err := d.sdkClient.DescribeAccelerators(describeAcceleratorsReq)
|
||||
d.logger.Debug("sdk request 'zga.DescribeAccelerators'", slog.Any("request", describeAcceleratorsReq), slog.Any("response", describeAcceleratorsResp))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to execute sdk request 'zga.DescribeAccelerators': %w", err)
|
||||
} else if len(describeAcceleratorsResp.Response.DataSet) == 0 {
|
||||
return false, fmt.Errorf("could not found accelerator '%s'", d.config.AcceleratorId)
|
||||
}
|
||||
|
||||
switch describeAcceleratorsResp.Response.DataSet[0].AcceleratorStatus {
|
||||
case "Accelerating":
|
||||
return true, nil
|
||||
case "NotAccelerate", "StopAccelerate", "AccelerateFailure":
|
||||
return false, fmt.Errorf("unexpected zenlayer accelerator status")
|
||||
}
|
||||
|
||||
d.logger.Info("waiting for zenlayer accelerator deploying completion ...")
|
||||
return false, nil
|
||||
}, time.Second*5); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Deployer) deployToCertificate(ctx context.Context, certPEM, privkeyPEM string) error {
|
||||
if d.config.CertificateId == "" {
|
||||
return errors.New("config `certificateId` is required")
|
||||
}
|
||||
|
||||
// 替换证书
|
||||
upres, err := d.sdkCertmgr.Replace(ctx, d.config.CertificateId, certPEM, privkeyPEM)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to replace certificate file: %w", err)
|
||||
} else {
|
||||
d.logger.Info("ssl certificate replaced", slog.Any("result", upres))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSDKClient(accessKeyId, accessKeyPassword string) (*internal.ZgaClient, error) {
|
||||
config := zcommon.NewConfig()
|
||||
|
||||
client, err := internal.NewZgaClient(config, accessKeyId, accessKeyPassword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package zenlayerga_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
provider "github.com/certimate-go/certimate/pkg/core/deployer/providers/zenlayer-ga"
|
||||
)
|
||||
|
||||
var (
|
||||
fInputCertPath string
|
||||
fInputKeyPath string
|
||||
fAccessKeyId string
|
||||
fAccessKeyPassword string
|
||||
fAcceleratorId string
|
||||
fCertificateId string
|
||||
)
|
||||
|
||||
func init() {
|
||||
argsPrefix := "ZENLAYERGA_"
|
||||
|
||||
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
|
||||
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
|
||||
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
|
||||
flag.StringVar(&fAccessKeyPassword, argsPrefix+"ACCESSKEYPASSWORD", "", "")
|
||||
flag.StringVar(&fAcceleratorId, argsPrefix+"ACCELERATORID", "", "")
|
||||
flag.StringVar(&fCertificateId, argsPrefix+"CERTIFICATEID", "", "")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./zenlayer_ga_test.go -args \
|
||||
--ZENLAYERGA_INPUTCERTPATH="/path/to/your-input-cert.pem" \
|
||||
--ZENLAYERGA_INPUTKEYPATH="/path/to/your-input-key.pem" \
|
||||
--ZENLAYERGA_ACCESSKEYID="your-access-key-id" \
|
||||
--ZENLAYERGA_ACCESSKEYPASSWORD="your-access-key-password" \
|
||||
--ZENLAYERGA_ACCELERATORID="your-ga-accelerator-id" \
|
||||
--ZENLAYERGA_CERTIFICATEID="your-ga-certificate-id"
|
||||
*/
|
||||
func TestDeploy(t *testing.T) {
|
||||
flag.Parse()
|
||||
|
||||
t.Run("Deploy_ToAccelerator", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
||||
fmt.Sprintf("ACCESSKEYPASSWORD: %v", fAccessKeyPassword),
|
||||
fmt.Sprintf("ACCELERATORID: %v", fAcceleratorId),
|
||||
}, "\n"))
|
||||
|
||||
provider, err := provider.NewDeployer(&provider.DeployerConfig{
|
||||
AccessKeyId: fAccessKeyId,
|
||||
AccessKeyPassword: fAccessKeyPassword,
|
||||
ResourceType: provider.RESOURCE_TYPE_ACCELERATOR,
|
||||
AcceleratorId: fAcceleratorId,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||
res, err := provider.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
|
||||
t.Run("Deploy_ToCertificate", func(t *testing.T) {
|
||||
t.Log(strings.Join([]string{
|
||||
"args:",
|
||||
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
|
||||
fmt.Sprintf("ACCESSKEYPASSWORD: %v", fAccessKeyPassword),
|
||||
fmt.Sprintf("CERTIFICATEID: %v", fCertificateId),
|
||||
}, "\n"))
|
||||
|
||||
provider, err := provider.NewDeployer(&provider.DeployerConfig{
|
||||
AccessKeyId: fAccessKeyId,
|
||||
AccessKeyPassword: fAccessKeyPassword,
|
||||
ResourceType: provider.RESOURCE_TYPE_CERTIFICATE,
|
||||
CertificateId: fCertificateId,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fInputCertData, _ := os.ReadFile(fInputCertPath)
|
||||
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
|
||||
res, err := provider.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("ok: %v", res)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 33.9 31.9" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><linearGradient id="a" gradientTransform="matrix(1 0 0 -1 0 32)" gradientUnits="userSpaceOnUse" x1="1.67" x2="28.86" y1="19.912" y2="8.982"><stop offset="0" stop-color="#1dbfff"/><stop offset="1" stop-color="#1ea0f3"/></linearGradient><linearGradient id="b" gradientTransform="matrix(1 0 0 -1 0 32)" gradientUnits="userSpaceOnUse" x1="1.025" x2="23.988" y1="21.133" y2="12.316"><stop offset="0" stop-color="#2bcdfd"/><stop offset="1" stop-color="#87e6ff"/></linearGradient><linearGradient id="c" gradientTransform="matrix(1 0 0 -1 0 32)" gradientUnits="userSpaceOnUse" x1="5.873" x2="32.043" y1="27.229" y2="9.249"><stop offset="0" stop-color="#32b4f8"/><stop offset="1" stop-color="#1073d8"/></linearGradient><path d="M19.3 3.9c-1-.4-2.1-.7-3.3-.7H15.4C9.8 3.2 5.2 7.8 5.2 13.4c0 1.2.2 2.5.7 3.6v-.5c0-5.2 4.2-9.4 9.4-9.4s9.4 4.2 9.4 9.4c0 .5 0 1.1-.1 1.6 0 .3-.1.6-.2.9-.3 1-.7 2-1.3 2.9-3.6 5.8-11.3 7.6-17.1 4-3.7-2.2-5.9-6.1-6-10.4v.4c0 8.8 7.2 16 16 16 2 0 3.9-.4 5.8-1.1 7.1-4 9.6-13 5.6-20.1-1.8-3.2-4.7-5.6-8.1-6.8z" fill="url(#a)"/><path d="M15.3 25.9c-5 0-9.1-3.9-9.4-8.9-.4-1.1-.7-2.4-.7-3.6 0-5.6 4.6-10.2 10.2-10.2H16h-1.7C7.9 3.2 2.7 6.8.4 12.4c0 0-.1.2-.1.4-.2.8-.3 1.6-.3 2.4v.3c.1 6.9 5.8 12.4 12.7 12.3 4.3-.1 8.2-2.3 10.4-5.9-1.7 2.5-4.8 4-7.8 4z" fill="url(#b)"/><path d="M16 0C9.3 0 3.3 4.1 1 10.4c-.3.8-.6 1.6-.7 2.4 0-.1.1-.4.1-.4 2.3-5.6 7.8-9.3 13.9-9.2H16c1.1.1 2.2.3 3.2.7 7.7 2.6 11.9 11 9.3 18.7-1.2 3.4-3.6 6.3-6.7 8.1C30 27.5 33.9 18.2 30.6 10 28.2 4 22.4.1 16 0z" fill="url(#c)"/></svg>
|
||||
@@ -111,6 +111,7 @@ import AccessConfigFieldsProviderWebhook from "./AccessConfigFieldsProviderWebho
|
||||
import AccessConfigFieldsProviderWeComBot from "./AccessConfigFieldsProviderWeComBot";
|
||||
import AccessConfigFieldsProviderWestcn from "./AccessConfigFieldsProviderWestcn";
|
||||
import AccessConfigFieldsProviderXinnet from "./AccessConfigFieldsProviderXinnet";
|
||||
import AccessConfigFieldsProviderZenlayer from "./AccessConfigFieldsProviderZenlayer";
|
||||
import AccessConfigFieldsProviderZeroSSL from "./AccessConfigFieldsProviderZeroSSL";
|
||||
|
||||
const providerComponentMap: Partial<Record<AccessProviderType, React.ComponentType<any>>> = {
|
||||
@@ -227,6 +228,7 @@ const providerComponentMap: Partial<Record<AccessProviderType, React.ComponentTy
|
||||
[ACCESS_PROVIDERS.WECOMBOT]: AccessConfigFieldsProviderWeComBot,
|
||||
[ACCESS_PROVIDERS.WESTCN]: AccessConfigFieldsProviderWestcn,
|
||||
[ACCESS_PROVIDERS.XINNET]: AccessConfigFieldsProviderXinnet,
|
||||
[ACCESS_PROVIDERS.ZENLAYER]: AccessConfigFieldsProviderZenlayer,
|
||||
[ACCESS_PROVIDERS.ZEROSSL]: AccessConfigFieldsProviderZeroSSL,
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
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 AccessConfigFormFieldsProviderZenlayer = () => {
|
||||
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, "accessKeyId"]}
|
||||
initialValue={initialValues.accessKeyId}
|
||||
label={t("access.form.zenlayer_access_key_id.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.zenlayer_access_key_id.tooltip") }}></span>}
|
||||
>
|
||||
<Input autoComplete="new-password" placeholder={t("access.form.zenlayer_access_key_id.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "accessKeyPassword"]}
|
||||
initialValue={initialValues.accessKeyPassword}
|
||||
label={t("access.form.zenlayer_access_key_password.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.zenlayer_access_key_password.tooltip") }}></span>}
|
||||
>
|
||||
<Input.Password autoComplete="new-password" placeholder={t("access.form.zenlayer_access_key_password.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "resourceGroupId"]}
|
||||
initialValue={initialValues.resourceGroupId}
|
||||
label={t("access.form.zenlayer_resource_group_id.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.zenlayer_resource_group_id.tooltip") }}></span>}
|
||||
>
|
||||
<Input allowClear autoComplete="new-password" placeholder={t("access.form.zenlayer_resource_group_id.placeholder")} />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
accessKeyId: "",
|
||||
accessKeyPassword: "",
|
||||
};
|
||||
};
|
||||
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
|
||||
const { t } = i18n;
|
||||
|
||||
return z.object({
|
||||
accessKeyId: z.string().nonempty(t("access.form.zenlayer_access_key_id.placeholder")),
|
||||
accessKeyPassword: z.string().nonempty(t("access.form.zenlayer_access_key_password.placeholder")),
|
||||
resourceGroupId: z.string().nullish(),
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(AccessConfigFormFieldsProviderZenlayer, {
|
||||
getInitialValues,
|
||||
getSchema,
|
||||
});
|
||||
|
||||
export default _default;
|
||||
@@ -122,6 +122,7 @@ import BizDeployNodeConfigFieldsProviderWangsuCDN from "./BizDeployNodeConfigFie
|
||||
import BizDeployNodeConfigFieldsProviderWangsuCDNPro from "./BizDeployNodeConfigFieldsProviderWangsuCDNPro";
|
||||
import BizDeployNodeConfigFieldsProviderWangsuCertificate from "./BizDeployNodeConfigFieldsProviderWangsuCertificate";
|
||||
import BizDeployNodeConfigFieldsProviderWebhook from "./BizDeployNodeConfigFieldsProviderWebhook";
|
||||
import BizDeployNodeConfigFieldsProviderZenlayerGA from "./BizDeployNodeConfigFieldsProviderZenlayerGA";
|
||||
|
||||
const providerComponentMap: Partial<Record<DeploymentProviderType, React.ComponentType<any>>> = {
|
||||
/*
|
||||
@@ -248,6 +249,7 @@ const providerComponentMap: Partial<Record<DeploymentProviderType, React.Compone
|
||||
[DEPLOYMENT_PROVIDERS.WANGSU_CDNPRO]: BizDeployNodeConfigFieldsProviderWangsuCDNPro,
|
||||
[DEPLOYMENT_PROVIDERS.WANGSU_CERTIFICATE]: BizDeployNodeConfigFieldsProviderWangsuCertificate,
|
||||
[DEPLOYMENT_PROVIDERS.WEBHOOK]: BizDeployNodeConfigFieldsProviderWebhook,
|
||||
[DEPLOYMENT_PROVIDERS.ZENLAYER_GA]: BizDeployNodeConfigFieldsProviderZenlayerGA,
|
||||
};
|
||||
|
||||
const useComponent = (provider: string, { initProps, deps = [] }: { initProps?: (provider: string) => any; deps?: unknown[] }) => {
|
||||
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input, Select } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import Show from "@/components/Show";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const RESOURCE_TYPE_ACCELERATOR = "accelerator" as const;
|
||||
const RESOURCE_TYPE_CERTIFICATE = "certificate" as const;
|
||||
|
||||
const BizDeployNodeConfigFieldsProviderZenlayerGA = () => {
|
||||
const { i18n, t } = useTranslation();
|
||||
|
||||
const { parentNamePath } = useFormNestedFieldsContext();
|
||||
const formSchema = z.object({
|
||||
[parentNamePath]: getSchema({ i18n }),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
const formInst = Form.useFormInstance();
|
||||
const initialValues = getInitialValues();
|
||||
|
||||
const fieldResourceType = Form.useWatch([parentNamePath, "resourceType"], formInst);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
name={[parentNamePath, "resourceType"]}
|
||||
initialValue={initialValues.resourceType}
|
||||
label={t("workflow_node.deploy.form.shared_resource_type.label")}
|
||||
rules={[formRule]}
|
||||
>
|
||||
<Select
|
||||
options={[RESOURCE_TYPE_ACCELERATOR, RESOURCE_TYPE_CERTIFICATE].map((s) => ({
|
||||
value: s,
|
||||
label: t(`workflow_node.deploy.form.zenlayer_ga_resource_type.option.${s}.label`),
|
||||
}))}
|
||||
placeholder={t("workflow_node.deploy.form.shared_resource_type.placeholder")}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Show when={fieldResourceType === RESOURCE_TYPE_ACCELERATOR}>
|
||||
<Form.Item
|
||||
name={[parentNamePath, "acceleratorId"]}
|
||||
initialValue={initialValues.acceleratorId}
|
||||
label={t("workflow_node.deploy.form.zenlayer_ga_accelerator_id.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.zenlayer_ga_accelerator_id.tooltip") }}></span>}
|
||||
>
|
||||
<Input placeholder={t("workflow_node.deploy.form.zenlayer_ga_accelerator_id.placeholder")} />
|
||||
</Form.Item>
|
||||
</Show>
|
||||
|
||||
<Show when={fieldResourceType === RESOURCE_TYPE_CERTIFICATE}>
|
||||
<Form.Item
|
||||
name={[parentNamePath, "certificateId"]}
|
||||
initialValue={initialValues.certificateId}
|
||||
label={t("workflow_node.deploy.form.zenlayer_ga_certificate_id.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.zenlayer_ga_certificate_id.tooltip") }}></span>}
|
||||
>
|
||||
<Input placeholder={t("workflow_node.deploy.form.zenlayer_ga_certificate_id.placeholder")} />
|
||||
</Form.Item>
|
||||
</Show>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
resourceType: RESOURCE_TYPE_ACCELERATOR,
|
||||
acceleratorId: "",
|
||||
};
|
||||
};
|
||||
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
|
||||
const { t } = i18n;
|
||||
|
||||
return z
|
||||
.object({
|
||||
resourceType: z.literal([RESOURCE_TYPE_ACCELERATOR, RESOURCE_TYPE_CERTIFICATE], t("workflow_node.deploy.form.shared_resource_type.placeholder")),
|
||||
acceleratorId: z.string().nullish(),
|
||||
certificateId: z.string().nullish(),
|
||||
})
|
||||
.superRefine((values, ctx) => {
|
||||
switch (values.resourceType) {
|
||||
case RESOURCE_TYPE_ACCELERATOR:
|
||||
{
|
||||
if (!values.acceleratorId?.trim()) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: t("workflow_node.deploy.form.zenlayer_ga_accelerator_id.placeholder"),
|
||||
path: ["acceleratorId"],
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RESOURCE_TYPE_CERTIFICATE:
|
||||
{
|
||||
if (!values.certificateId?.trim()) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: t("workflow_node.deploy.form.zenlayer_ga_certificate_id.placeholder"),
|
||||
path: ["certificateId"],
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(BizDeployNodeConfigFieldsProviderZenlayerGA, {
|
||||
getInitialValues,
|
||||
getSchema,
|
||||
});
|
||||
|
||||
export default _default;
|
||||
@@ -127,6 +127,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
|
||||
WECOMBOT: "wecombot",
|
||||
WESTCN: "westcn",
|
||||
XINNET: "xinnet",
|
||||
ZENLAYER: "zenlayer",
|
||||
ZEROSSL: "zerossl",
|
||||
} as const);
|
||||
|
||||
@@ -186,6 +187,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
|
||||
[ACCESS_PROVIDERS.CACHEFLY, "provider.cachefly", "/imgs/providers/cachefly.png", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.MOHUA, "provider.mohua", "/imgs/providers/mohua.png", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.UNICLOUD, "provider.unicloud", "/imgs/providers/unicloud.png", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.ZENLAYER, "provider.zenlayer", "/imgs/providers/zenlayer.svg", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS["1PANEL"], "provider.1panel", "/imgs/providers/1panel.svg", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.APISIX, "provider.apisix", "/imgs/providers/apisix.svg", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.BAOTAPANEL, "provider.baotapanel", "/imgs/providers/baotapanel.svg", [ACCESS_USAGES.HOSTING]],
|
||||
@@ -689,6 +691,7 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
|
||||
WANGSU_CDNPRO: `${ACCESS_PROVIDERS.WANGSU}-cdnpro`,
|
||||
WANGSU_CERTIFICATE: `${ACCESS_PROVIDERS.WANGSU}-certificate`,
|
||||
WEBHOOK: `${ACCESS_PROVIDERS.WEBHOOK}`,
|
||||
ZENLAYER_GA: `${ACCESS_PROVIDERS.ZENLAYER}-ga`,
|
||||
} as const);
|
||||
|
||||
export type DeploymentProviderType = (typeof DEPLOYMENT_PROVIDERS)[keyof typeof DEPLOYMENT_PROVIDERS];
|
||||
@@ -826,6 +829,7 @@ export const deploymentProvidersMap: Map<DeploymentProvider["type"] | string, De
|
||||
[DEPLOYMENT_PROVIDERS.FLYIO, "provider.flyio", DEPLOYMENT_CATEGORIES.WEBSITE],
|
||||
[DEPLOYMENT_PROVIDERS.NETLIFY, "provider.netlify", DEPLOYMENT_CATEGORIES.WEBSITE],
|
||||
[DEPLOYMENT_PROVIDERS.VERCEL, "provider.vercel", DEPLOYMENT_CATEGORIES.WEBSITE],
|
||||
[DEPLOYMENT_PROVIDERS.ZENLAYER_GA, "provider.zenlayer_ga", DEPLOYMENT_CATEGORIES.ACCELERATOR],
|
||||
[DEPLOYMENT_PROVIDERS.CDNFLY, "provider.cdnfly", DEPLOYMENT_CATEGORIES.CDN],
|
||||
[DEPLOYMENT_PROVIDERS.FLEXCDN, "provider.flexcdn", DEPLOYMENT_CATEGORIES.CDN],
|
||||
[DEPLOYMENT_PROVIDERS.GOEDGE, "provider.goedge", DEPLOYMENT_CATEGORIES.CDN],
|
||||
|
||||
@@ -748,5 +748,14 @@
|
||||
"access.form.xinnet_api_password.label": "Xinnet.com agent API password",
|
||||
"access.form.xinnet_api_password.placeholder": "Please enter Xinnet.com agent API password",
|
||||
"access.form.xinnet_agent.guide": "Xinnet.com API only supports calls from agents. Learn more about this: <br><a href=\"https://apidoc.xin.cn/doc-7283837\" target=\"_blank\">https://apidoc.xin.cn/doc-7283837</a>",
|
||||
"access.form.zenlayer_access_key_id.label": "Zenlayer AccessKeyID",
|
||||
"access.form.zenlayer_access_key_id.placeholder": "Please enter Zenlayer AccessKeyID",
|
||||
"access.form.zenlayer_access_key_id.tooltip": "For more information, see <a href=\"https://docs.console.zenlayer.com/welcome/platform/team-management/generate-an-api-access-key\" target=\"_blank\">https://docs.console.zenlayer.com/welcome/platform/team-management/generate-an-api-access-key</a>",
|
||||
"access.form.zenlayer_access_key_password.label": "Zenlayer AccessKeyPassword",
|
||||
"access.form.zenlayer_access_key_password.placeholder": "Please enter Zenlayer AccessKeyPassword",
|
||||
"access.form.zenlayer_access_key_password.tooltip": "For more information, see <a href=\"https://docs.console.zenlayer.com/welcome/platform/team-management/generate-an-api-access-key\" target=\"_blank\">https://docs.console.zenlayer.com/welcome/platform/team-management/generate-an-api-access-key</a>",
|
||||
"access.form.zenlayer_resource_group_id.label": "Zenlayer resource group ID (Optional)",
|
||||
"access.form.zenlayer_resource_group_id.placeholder": "Please enter Zenlayer resource group ID",
|
||||
"access.form.zenlayer_resource_group_id.tooltip": "For more information, see <a href=\"https://docs.console.zenlayer.com/welcome/platform/team-management/create-a-resource-group\" target=\"_blank\">https://docs.console.zenlayer.com/welcome/platform/team-management/create-a-resource-group</a>",
|
||||
"access.form.zerossl_eab.guide": "Learn more about using EAB key in ZeroSSL: <br><a href=\"https://zerossl.com/documentation/acme/\" target=\"_blank\">https://zerossl.com/documentation/acme/</a>"
|
||||
}
|
||||
|
||||
@@ -235,6 +235,8 @@
|
||||
"provider.westcn": "West.cn",
|
||||
"provider.xinnet": "Xinnet.com",
|
||||
"provider.zerossl": "ZeroSSL",
|
||||
"provider.zenlayer": "Zenlayer",
|
||||
"provider.zenlayer_ga": "Zenlayer - ZGA (Global Accelerator)",
|
||||
|
||||
"provider.category.all": "All",
|
||||
"provider.category.cdn": "CDN",
|
||||
|
||||
@@ -1265,6 +1265,14 @@
|
||||
"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.zenlayer_ga_resource_type.option.accelerator.label": "Accelerator",
|
||||
"workflow_node.deploy.form.zenlayer_ga_resource_type.option.certificate.label": "Certificate",
|
||||
"workflow_node.deploy.form.zenlayer_ga_accelerator_id.label": "Zenlayer ZGA accelerator ID",
|
||||
"workflow_node.deploy.form.zenlayer_ga_accelerator_id.placeholder": "Please enter Zenlayer ZGA accelerator ID",
|
||||
"workflow_node.deploy.form.zenlayer_ga_accelerator_id.tooltip": "For more information, <a href=\"https://console.zenlayer.com/gia/\" target=\"_blank\">https://console.zenlayer.com/gia/</a>",
|
||||
"workflow_node.deploy.form.zenlayer_ga_certificate_id.label": "Zenlayer ZGA certificate ID",
|
||||
"workflow_node.deploy.form.zenlayer_ga_certificate_id.placeholder": "Please enter Zenlayer ZGA certificate ID",
|
||||
"workflow_node.deploy.form.zenlayer_ga_certificate_id.tooltip": "For more information, <a href=\"https://console.zenlayer.com/gia/\" target=\"_blank\">https://console.zenlayer.com/gia/</a>",
|
||||
"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": " re-deployment.",
|
||||
|
||||
@@ -747,5 +747,14 @@
|
||||
"access.form.xinnet_api_password.label": "新网数码代理商 API 密码",
|
||||
"access.form.xinnet_api_password.placeholder": "请输入新网数码代理商 API 密码",
|
||||
"access.form.xinnet_agent.guide": "新网数码 API 仅支持代理商调用。点击下方链接了解更多:<br><a href=\"https://apidoc.xin.cn/doc-7283837\" target=\"_blank\">https://apidoc.xin.cn/doc-7283837</a>",
|
||||
"access.form.zenlayer_access_key_id.label": "Zenlayer AccessKeyID",
|
||||
"access.form.zenlayer_access_key_id.placeholder": "请输入 Zenlayer AccessKeyID",
|
||||
"access.form.zenlayer_access_key_id.tooltip": "这是什么?请参阅 <a href=\"https://docs.console.zenlayer.com/welcome/cn/platform/team-management/generate-an-api-access-key\" target=\"_blank\">https://docs.console.zenlayer.com/welcome/cn/platform/team-management/generate-an-api-access-key</a>",
|
||||
"access.form.zenlayer_access_key_password.label": "Zenlayer AccessKeyPassword",
|
||||
"access.form.zenlayer_access_key_password.placeholder": "请输入 Zenlayer AccessKeyPassword",
|
||||
"access.form.zenlayer_access_key_password.tooltip": "这是什么?请参阅 <a href=\"https://docs.console.zenlayer.com/welcome/cn/platform/team-management/generate-an-api-access-key\" target=\"_blank\">https://docs.console.zenlayer.com/welcome/cn/platform/team-management/generate-an-api-access-key</a>",
|
||||
"access.form.zenlayer_resource_group_id.label": "Zenlayer 资源组 ID(可选)",
|
||||
"access.form.zenlayer_resource_group_id.placeholder": "请输入 Zenlayer 资源组 ID",
|
||||
"access.form.zenlayer_resource_group_id.tooltip": "这是什么?请参阅 <a href=\"https://docs.console.zenlayer.com/welcome/cn/platform/team-management/create-a-resource-group\" target=\"_blank\">https://docs.console.zenlayer.com/welcome/cn/platform/team-management/create-a-resource-group</a>",
|
||||
"access.form.zerossl_eab.guide": "点击下方链接了解如何获取 ZeroSSL EAB:<br><a href=\"https://zerossl.com/documentation/acme/\" target=\"_blank\">https://zerossl.com/documentation/acme/</a>"
|
||||
}
|
||||
|
||||
@@ -235,6 +235,8 @@
|
||||
"provider.westcn": "西部数码",
|
||||
"provider.xinnet": "新网数码",
|
||||
"provider.zerossl": "ZeroSSL",
|
||||
"provider.zenlayer": "Zenlayer",
|
||||
"provider.zenlayer_ga": "Zenlayer - 全球网络加速器 ZGA",
|
||||
|
||||
"provider.category.all": "全部",
|
||||
"provider.category.cdn": "CDN",
|
||||
|
||||
@@ -1263,6 +1263,14 @@
|
||||
"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.zenlayer_ga_resource_type.option.accelerator.label": "部署到指定全球网络加速器",
|
||||
"workflow_node.deploy.form.zenlayer_ga_resource_type.option.certificate.label": "替换指定证书",
|
||||
"workflow_node.deploy.form.zenlayer_ga_accelerator_id.label": "Zenlayer ZGA 加速器 ID",
|
||||
"workflow_node.deploy.form.zenlayer_ga_accelerator_id.placeholder": "请输入 Zenlayer ZGA 加速器 ID",
|
||||
"workflow_node.deploy.form.zenlayer_ga_accelerator_id.tooltip": "这是什么?请参阅 <a href=\"https://console.zenlayer.com/gia/\" target=\"_blank\">https://console.zenlayer.com/gia/</a>",
|
||||
"workflow_node.deploy.form.zenlayer_ga_certificate_id.label": "Zenlayer ZGA 证书 ID",
|
||||
"workflow_node.deploy.form.zenlayer_ga_certificate_id.placeholder": "请输入 Zenlayer ZGA 证书 ID",
|
||||
"workflow_node.deploy.form.zenlayer_ga_certificate_id.tooltip": "这是什么?请参阅 <a href=\"https://console.zenlayer.com/gia/\" target=\"_blank\">https://console.zenlayer.com/gia/</a>",
|
||||
"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": "此部署节点。",
|
||||
|
||||
Reference in New Issue
Block a user