feat: plugin migration

This commit is contained in:
Yoan.liu
2026-08-30 23:28:19 +08:00
parent 4cbc5d5f14
commit 6611182d50
106 changed files with 1 additions and 6104 deletions
@@ -1,28 +0,0 @@
package deployers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
dplyimpl "github.com/certimate-go/certimate/pkg/core/deployer/providers/baishan-cdn"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
Registries.MustRegister(domain.DeploymentProviderTypeBaishanCDN, func(options *ProviderFactoryOptions) (core.Deployer, error) {
credentials := domain.AccessConfigForBaishan{}
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := dplyimpl.NewDeployer(&dplyimpl.DeployerConfig{
ApiToken: credentials.ApiToken,
DeployTarget: xmaps.GetString(options.ProviderExtendedConfig, "deployTarget"),
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
CertificateId: xmaps.GetString(options.ProviderExtendedConfig, "certificateId"),
})
return provider, err
})
}
@@ -1,29 +0,0 @@
package deployers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
dplyimpl "github.com/certimate-go/certimate/pkg/core/deployer/providers/ksyun-cdn"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
Registries.MustRegister(domain.DeploymentProviderTypeKsyunCDN, func(options *ProviderFactoryOptions) (core.Deployer, error) {
credentials := domain.AccessConfigForKsyun{}
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := dplyimpl.NewDeployer(&dplyimpl.DeployerConfig{
AccessKeyId: credentials.AccessKeyId,
SecretAccessKey: credentials.SecretAccessKey,
DeployTarget: xmaps.GetString(options.ProviderExtendedConfig, "deployTarget"),
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
CertificateId: xmaps.GetString(options.ProviderExtendedConfig, "certificateId"),
})
return provider, err
})
}
@@ -1,25 +0,0 @@
package deployers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
dplyimpl "github.com/certimate-go/certimate/pkg/core/deployer/providers/ksyun-kcm"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
Registries.MustRegister(domain.DeploymentProviderTypeKsyunKCM, func(options *ProviderFactoryOptions) (core.Deployer, error) {
credentials := domain.AccessConfigForKsyun{}
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := dplyimpl.NewDeployer(&dplyimpl.DeployerConfig{
AccessKeyId: credentials.AccessKeyId,
SecretAccessKey: credentials.SecretAccessKey,
})
return provider, err
})
}
@@ -1,28 +0,0 @@
package deployers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
dplyimpl "github.com/certimate-go/certimate/pkg/core/deployer/providers/ksyun-slb"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
Registries.MustRegister(domain.DeploymentProviderTypeKsyunSLB, func(options *ProviderFactoryOptions) (core.Deployer, error) {
credentials := domain.AccessConfigForKsyun{}
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := dplyimpl.NewDeployer(&dplyimpl.DeployerConfig{
AccessKeyId: credentials.AccessKeyId,
SecretAccessKey: credentials.SecretAccessKey,
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
DeployTarget: xmaps.GetString(options.ProviderExtendedConfig, "deployTarget"),
CertificateId: xmaps.GetString(options.ProviderExtendedConfig, "certificateId"),
})
return provider, err
})
}
@@ -1,27 +0,0 @@
package deployers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
dplyimpl "github.com/certimate-go/certimate/pkg/core/deployer/providers/wangsu-cdn"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
Registries.MustRegister(domain.DeploymentProviderTypeWangsuCDN, func(options *ProviderFactoryOptions) (core.Deployer, error) {
credentials := domain.AccessConfigForWangsu{}
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := dplyimpl.NewDeployer(&dplyimpl.DeployerConfig{
AccessKeyId: credentials.AccessKeyId,
AccessKeySecret: credentials.AccessKeySecret,
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
Domains: xmaps.GetStringsBySplit(options.ProviderExtendedConfig, "domains", ";"),
})
return provider, err
})
}
@@ -1,31 +0,0 @@
package deployers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
dplyimpl "github.com/certimate-go/certimate/pkg/core/deployer/providers/wangsu-cdnpro"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
Registries.MustRegister(domain.DeploymentProviderTypeWangsuCDNPro, func(options *ProviderFactoryOptions) (core.Deployer, error) {
credentials := domain.AccessConfigForWangsu{}
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := dplyimpl.NewDeployer(&dplyimpl.DeployerConfig{
AccessKeyId: credentials.AccessKeyId,
AccessKeySecret: credentials.AccessKeySecret,
ApiKey: credentials.ApiKey,
Environment: xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "environment", "production"),
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
CertificateId: xmaps.GetString(options.ProviderExtendedConfig, "certificateId"),
WebhookId: xmaps.GetString(options.ProviderExtendedConfig, "webhookId"),
})
return provider, err
})
}
@@ -1,26 +0,0 @@
package deployers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
dplyimpl "github.com/certimate-go/certimate/pkg/core/deployer/providers/wangsu-certificate"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
Registries.MustRegister(domain.DeploymentProviderTypeWangsuCertificate, func(options *ProviderFactoryOptions) (core.Deployer, error) {
credentials := domain.AccessConfigForWangsu{}
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := dplyimpl.NewDeployer(&dplyimpl.DeployerConfig{
AccessKeyId: credentials.AccessKeyId,
AccessKeySecret: credentials.AccessKeySecret,
CertificateId: xmaps.GetString(options.ProviderExtendedConfig, "certificateId"),
})
return provider, err
})
}
-15
View File
@@ -105,10 +105,6 @@ type AccessConfigForBaiduCloud struct {
SecretAccessKey string `json:"secretAccessKey"`
}
type AccessConfigForBaishan struct {
ApiToken string `json:"apiToken"`
}
type AccessConfigForBaotaPanel struct {
ServerUrl string `json:"serverUrl"`
ApiKey string `json:"apiKey"`
@@ -375,11 +371,6 @@ type AccessConfigForKong struct {
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
}
type AccessConfigForKsyun struct {
AccessKeyId string `json:"accessKeyId"`
SecretAccessKey string `json:"secretAccessKey"`
}
type AccessConfigForLarkBot struct {
WebhookUrl string `json:"webhookUrl"`
Secret string `json:"secret,omitempty"`
@@ -659,12 +650,6 @@ type AccessConfigForVultr struct {
ApiKey string `json:"apiKey"`
}
type AccessConfigForWangsu struct {
AccessKeyId string `json:"accessKeyId"`
AccessKeySecret string `json:"accessKeySecret"`
ApiKey string `json:"apiKey"`
}
type AccessConfigForWebhook struct {
Url string `json:"url"`
Method string `json:"method,omitempty"`
-10
View File
@@ -28,7 +28,6 @@ const (
AccessProviderTypeAxisNow = AccessProviderType("axisnow")
AccessProviderTypeAzure = AccessProviderType("azure")
AccessProviderTypeBaiduCloud = AccessProviderType("baiducloud")
AccessProviderTypeBaishan = AccessProviderType("baishan")
AccessProviderTypeBaotaPanel = AccessProviderType("baotapanel")
AccessProviderTypeBaotaPanelGo = AccessProviderType("baotapanelgo")
AccessProviderTypeBaotaWAF = AccessProviderType("baotawaf")
@@ -81,7 +80,6 @@ const (
AccessProviderTypeIONOS = AccessProviderType("ionos")
AccessProviderTypeJDCloud = AccessProviderType("jdcloud")
AccessProviderTypeKong = AccessProviderType("kong")
AccessProviderTypeKsyun = AccessProviderType("ksyun")
AccessProviderTypeLarkBot = AccessProviderType("larkbot")
AccessProviderTypeLeCDN = AccessProviderType("lecdn")
AccessProviderTypeLetsEncrypt = AccessProviderType("letsencrypt")
@@ -130,7 +128,6 @@ const (
AccessProviderTypeVercel = AccessProviderType("vercel")
AccessProviderTypeVolcEngine = AccessProviderType("volcengine")
AccessProviderTypeVultr = AccessProviderType("vultr")
AccessProviderTypeWangsu = AccessProviderType("wangsu")
AccessProviderTypeWebhook = AccessProviderType("webhook")
AccessProviderTypeWeComBot = AccessProviderType("wecombot")
AccessProviderTypeWestcn = AccessProviderType("westcn")
@@ -346,7 +343,6 @@ const (
DeploymentProviderTypeBaiduCloudBLB = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-blb")
DeploymentProviderTypeBaiduCloudCDN = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-cdn")
DeploymentProviderTypeBaiduCloudCert = DeploymentProviderType(AccessProviderTypeBaiduCloud + "-cert")
DeploymentProviderTypeBaishanCDN = DeploymentProviderType(AccessProviderTypeBaishan + "-cdn")
DeploymentProviderTypeBaotaPanel = DeploymentProviderType(AccessProviderTypeBaotaPanel)
DeploymentProviderTypeBaotaPanelConsole = DeploymentProviderType(AccessProviderTypeBaotaPanel + "-console")
DeploymentProviderTypeBaotaPanelGo = DeploymentProviderType(AccessProviderTypeBaotaPanelGo)
@@ -399,9 +395,6 @@ const (
DeploymentProviderTypeJDCloudSSL = DeploymentProviderType(AccessProviderTypeJDCloud + "-ssl")
DeploymentProviderTypeJDCloudWAF = DeploymentProviderType(AccessProviderTypeJDCloud + "-waf")
DeploymentProviderTypeKong = DeploymentProviderType(AccessProviderTypeKong)
DeploymentProviderTypeKsyunCDN = DeploymentProviderType(AccessProviderTypeKsyun + "-cdn")
DeploymentProviderTypeKsyunKCM = DeploymentProviderType(AccessProviderTypeKsyun + "-kcm")
DeploymentProviderTypeKsyunSLB = DeploymentProviderType(AccessProviderTypeKsyun + "-slb")
DeploymentProviderTypeLeCDN = DeploymentProviderType(AccessProviderTypeLeCDN)
DeploymentProviderTypeLinodeLOS = DeploymentProviderType(AccessProviderTypeLinode + "-los")
DeploymentProviderTypeLocal = DeploymentProviderType(AccessProviderTypeLocal)
@@ -457,9 +450,6 @@ const (
DeploymentProviderTypeVolcEngineTOS = DeploymentProviderType(AccessProviderTypeVolcEngine + "-tos")
DeploymentProviderTypeVolcEngineVOD = DeploymentProviderType(AccessProviderTypeVolcEngine + "-vod")
DeploymentProviderTypeVolcEngineWAF = DeploymentProviderType(AccessProviderTypeVolcEngine + "-waf")
DeploymentProviderTypeWangsuCDN = DeploymentProviderType(AccessProviderTypeWangsu + "-cdn")
DeploymentProviderTypeWangsuCDNPro = DeploymentProviderType(AccessProviderTypeWangsu + "-cdnpro")
DeploymentProviderTypeWangsuCertificate = DeploymentProviderType(AccessProviderTypeWangsu + "-certificate")
DeploymentProviderTypeWebhook = DeploymentProviderType(AccessProviderTypeWebhook)
DeploymentProviderTypeYandexCloudCertificateManager = DeploymentProviderType(AccessProviderTypeYandexCloud + "-certificatemanager")
)
@@ -1,46 +0,0 @@
//go:build tester
package baishancdn_test
import (
"testing"
"github.com/stretchr/testify/require"
tester "github.com/certimate-go/certimate/pkg/core/certmgr/providers-tester"
impl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/baishan-cdn"
)
var (
fp = tester.InitArgs("BAISHANCDN_")
fTestCertPath string
fTestKeyPath string
fApiToken string
)
func init() {
fp.DefineString(&fTestCertPath, "TESTCERTPATH")
fp.DefineString(&fTestKeyPath, "TESTKEYPATH")
fp.DefineString(&fApiToken, "APITOKEN")
}
/*
Shell command to run this test:
go test -tags=tester -v ./baishan_cdn_test.go -args \
--BAISHANCDN_TESTCERTPATH="/path/to/your-test-cert.pem" \
--BAISHANCDN_TESTKEYPATH="/path/to/your-test-key.pem" \
--BAISHANCDN_APITOKEN="your-api-token"
*/
func TestProvider(t *testing.T) {
fp.Parse()
t.Run("Upload", func(t *testing.T) {
provider, err := impl.NewCertmgr(&impl.CertmgrConfig{
ApiToken: fApiToken,
})
require.NoError(t, err)
tester.Upload(t, provider, tester.UploadInput{CertPath: fTestCertPath, KeyPath: fTestKeyPath})
})
}
@@ -1,50 +0,0 @@
//go:build tester
package ksyunkcm_test
import (
"testing"
"github.com/stretchr/testify/require"
tester "github.com/certimate-go/certimate/pkg/core/certmgr/providers-tester"
impl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/ksyun-kcm"
)
var (
fp = tester.InitArgs("KSYUNKCM_")
fTestCertPath string
fTestKeyPath string
fAccessKeyId string
fSecretAccessKey string
)
func init() {
fp.DefineString(&fTestCertPath, "TESTCERTPATH")
fp.DefineString(&fTestKeyPath, "TESTKEYPATH")
fp.DefineString(&fAccessKeyId, "ACCESSKEYID")
fp.DefineString(&fSecretAccessKey, "SECRETACCESSKEY")
}
/*
Shell command to run this test:
go test -tags=tester -v ./ksyun_kcm_test.go -args \
--KSYUNKCM_TESTCERTPATH="/path/to/your-test-cert.pem" \
--KSYUNKCM_TESTKEYPATH="/path/to/your-test-key.pem" \
--KSYUNKCM_ACCESSKEYID="your-access-key-id" \
--KSYUNKCM_SECRETACCESSKEY="your-secret-access-key"
*/
func TestProvider(t *testing.T) {
fp.Parse()
t.Run("Upload", func(t *testing.T) {
provider, err := impl.NewCertmgr(&impl.CertmgrConfig{
AccessKeyId: fAccessKeyId,
SecretAccessKey: fSecretAccessKey,
})
require.NoError(t, err)
tester.Upload(t, provider, tester.UploadInput{CertPath: fTestCertPath, KeyPath: fTestKeyPath})
})
}
@@ -1,54 +0,0 @@
//go:build tester
package ksyunslb_test
import (
"testing"
"github.com/stretchr/testify/require"
tester "github.com/certimate-go/certimate/pkg/core/certmgr/providers-tester"
impl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/ksyun-slb"
)
var (
fp = tester.InitArgs("KSYUNSLB_")
fTestCertPath string
fTestKeyPath string
fAccessKeyId string
fSecretAccessKey string
fRegion string
)
func init() {
fp.DefineString(&fTestCertPath, "TESTCERTPATH")
fp.DefineString(&fTestKeyPath, "TESTKEYPATH")
fp.DefineString(&fAccessKeyId, "ACCESSKEYID")
fp.DefineString(&fSecretAccessKey, "SECRETACCESSKEY")
fp.DefineString(&fRegion, "REGION")
}
/*
Shell command to run this test:
go test -tags=tester -v ./ksyun_slb_test.go -args \
--KSYUNSLB_TESTCERTPATH="/path/to/your-test-cert.pem" \
--KSYUNSLB_TESTKEYPATH="/path/to/your-test-key.pem" \
--KSYUNSLB_ACCESSKEYID="your-access-key-id" \
--KSYUNSLB_SECRETACCESSKEY="your-secret-access-key" \
--KSYUNSLB_REGION="cn-beijing-6"
*/
func TestProvider(t *testing.T) {
fp.Parse()
t.Run("Upload", func(t *testing.T) {
provider, err := impl.NewCertmgr(&impl.CertmgrConfig{
AccessKeyId: fAccessKeyId,
SecretAccessKey: fSecretAccessKey,
Region: fRegion,
})
require.NoError(t, err)
tester.Upload(t, provider, tester.UploadInput{CertPath: fTestCertPath, KeyPath: fTestKeyPath})
})
}
@@ -1,50 +0,0 @@
//go:build tester
package wangsucertificate_test
import (
"testing"
"github.com/stretchr/testify/require"
tester "github.com/certimate-go/certimate/pkg/core/certmgr/providers-tester"
impl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/wangsu-certificate"
)
var (
fp = tester.InitArgs("WANGSUCERTIFICATE_")
fTestCertPath string
fTestKeyPath string
fAccessKeyId string
fAccessKeySecret string
)
func init() {
fp.DefineString(&fTestCertPath, "TESTCERTPATH")
fp.DefineString(&fTestKeyPath, "TESTKEYPATH")
fp.DefineString(&fAccessKeyId, "ACCESSKEYID")
fp.DefineString(&fAccessKeySecret, "ACCESSKEYSECRET")
}
/*
Shell command to run this test:
go test -tags=tester -v ./wangsu_certificate_test.go -args \
--WANGSUCERTIFICATE_TESTCERTPATH="/path/to/your-test-cert.pem" \
--WANGSUCERTIFICATE_TESTKEYPATH="/path/to/your-test-key.pem" \
--WANGSUCERTIFICATE_ACCESSKEYID="your-access-key-id" \
--WANGSUCERTIFICATE_ACCESSKEYSECRET="your-access-key-secret"
*/
func TestProvider(t *testing.T) {
fp.Parse()
t.Run("Upload", func(t *testing.T) {
provider, err := impl.NewCertmgr(&impl.CertmgrConfig{
AccessKeyId: fAccessKeyId,
AccessKeySecret: fAccessKeySecret,
})
require.NoError(t, err)
tester.Upload(t, provider, tester.UploadInput{CertPath: fTestCertPath, KeyPath: fTestKeyPath})
})
}
@@ -1,124 +0,0 @@
package baishancdn
import (
"context"
"fmt"
"log/slog"
"regexp"
"strings"
"time"
"github.com/samber/lo"
"github.com/certimate-go/certimate/pkg/core"
baishansdk "github.com/certimate-go/certimate/pkg/sdk3rd/baishan"
)
type (
Provider = core.Certmgr
UploadResult = core.CertmgrUploadResult
ReplaceResult = core.CertmgrReplaceResult
)
type CertmgrConfig struct {
// 白山云 API Token。
ApiToken string `json:"apiToken"`
}
type Certmgr struct {
config *CertmgrConfig
logger *slog.Logger
sdkClient *baishansdk.Client
}
var _ Provider = (*Certmgr)(nil)
func NewCertmgr(config *CertmgrConfig) (*Certmgr, error) {
if config == nil {
return nil, fmt.Errorf("the configuration of the certmgr provider is nil")
}
client, err := createSDKClient(config.ApiToken)
if err != nil {
return nil, fmt.Errorf("could not create client: %w", err)
}
return &Certmgr{
config: config,
logger: slog.Default(),
sdkClient: client,
}, nil
}
func (d *Certmgr) SetLogger(logger *slog.Logger) {
if logger == nil {
d.logger = slog.New(slog.DiscardHandler)
} else {
d.logger = logger
}
}
func (d *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*UploadResult, error) {
// 生成新证书名(需符合白山云命名规则)
certName := fmt.Sprintf("certimate_%d", time.Now().UnixMilli())
// 新增证书
// REF: https://portal.baishancloud.com/track/document/downloadPdf/1441
certId := ""
uploadDomainCertificateReq := &baishansdk.UploadDomainCertificateRequest{
Name: lo.ToPtr(certName),
Certificate: lo.ToPtr(certPEM),
Key: lo.ToPtr(privkeyPEM),
}
uploadDomainCertificateResp, err := d.sdkClient.UploadDomainCertificateWithContext(ctx, uploadDomainCertificateReq)
d.logger.Debug("sdk request 'cdn.UploadDomainCertificate'", slog.Any("request", uploadDomainCertificateReq), slog.Any("response", uploadDomainCertificateResp))
if err != nil {
if uploadDomainCertificateResp != nil {
if uploadDomainCertificateResp.GetCode() == 400699 && strings.Contains(uploadDomainCertificateResp.GetMessage(), "this certificate is exists") {
// 证书已存在,忽略新增证书接口错误
re := regexp.MustCompile(`\d+`)
certId = re.FindString(uploadDomainCertificateResp.GetMessage())
}
}
if certId == "" {
return nil, fmt.Errorf("failed to execute sdk request 'cdn.SetDomainCertificate': %w", err)
}
} else {
certId = uploadDomainCertificateResp.Data.CertId.String()
}
return &UploadResult{
CertId: certId,
CertName: certName,
}, nil
}
func (d *Certmgr) Replace(ctx context.Context, certIdOrName string, certPEM, privkeyPEM string) (*ReplaceResult, error) {
// 替换证书
// REF: https://portal.baishancloud.com/track/document/downloadPdf/1441
uploadDomainCertificateReq := &baishansdk.UploadDomainCertificateRequest{
CertificateId: lo.ToPtr(certIdOrName),
Name: lo.ToPtr(fmt.Sprintf("certimate_%d", time.Now().UnixMilli())),
Certificate: lo.ToPtr(certPEM),
Key: lo.ToPtr(privkeyPEM),
}
uploadDomainCertificateResp, err := d.sdkClient.UploadDomainCertificateWithContext(ctx, uploadDomainCertificateReq)
d.logger.Debug("sdk request 'cdn.UploadDomainCertificate'", slog.Any("request", uploadDomainCertificateReq), slog.Any("response", uploadDomainCertificateResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'cdn.UploadDomainCertificate': %w", err)
}
return &ReplaceResult{}, nil
}
func createSDKClient(apiToken string) (*baishansdk.Client, error) {
client, err := baishansdk.NewClient(
baishansdk.WithApiToken(apiToken),
)
if err != nil {
return nil, err
}
return client, nil
}
@@ -1,184 +0,0 @@
package ksyunkcm
import (
"context"
"crypto/sha1"
"encoding/hex"
"fmt"
"log/slog"
"strings"
"time"
"github.com/samber/lo"
"github.com/certimate-go/certimate/pkg/core"
ksyunkcmsdk "github.com/certimate-go/certimate/pkg/sdk3rd/ksyun/kcm"
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
)
type (
Provider = core.Certmgr
UploadResult = core.CertmgrUploadResult
ReplaceResult = core.CertmgrReplaceResult
)
type CertmgrConfig struct {
// 金山云 AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// 金山云 SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
// 金山云项目 ID。
ProjectId int64 `json:"projectId,omitempty"`
}
type Certmgr struct {
config *CertmgrConfig
logger *slog.Logger
sdkClient *ksyunkcmsdk.Client
}
var _ Provider = (*Certmgr)(nil)
func NewCertmgr(config *CertmgrConfig) (*Certmgr, error) {
if config == nil {
return nil, fmt.Errorf("the configuration of the certmgr provider is nil")
}
client, err := createSDKClient(config.AccessKeyId, config.SecretAccessKey)
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) (*UploadResult, error) {
// 避免重复上传
if upres, upok, err := c.tryGetResultIfCertExists(ctx, certPEM); err != nil {
return nil, err
} else if upok {
c.logger.Info("ssl certificate already exists")
return upres, nil
}
// 上传证书
uploadCertificateReq := &ksyunkcmsdk.UploadCertificateRequest{
ProjectId: lo.ToPtr(c.config.ProjectId),
CertName: lo.ToPtr(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
CertFile: lo.ToPtr(certPEM),
CertKey: lo.ToPtr(privkeyPEM),
}
uploadCertificateResp, err := c.sdkClient.UploadCertificateWithContext(ctx, uploadCertificateReq)
c.logger.Debug("sdk request 'kcm.UploadCertificate'", slog.Any("request", uploadCertificateReq), slog.Any("response", uploadCertificateResp))
if err != nil {
if uploadCertificateResp != nil &&
uploadCertificateResp.Error != nil && uploadCertificateResp.Error.Message == "重复的证书文件" {
if upres, upok, err := c.tryGetResultIfCertExists(ctx, certPEM); err != nil {
return nil, err
} else if !upok {
return nil, fmt.Errorf("could not find ssl certificate, may be upload failed")
} else {
c.logger.Info("ssl certificate already exists")
return upres, nil
}
}
return nil, fmt.Errorf("failed to execute sdk request 'kcm.UploadCertificate': %w", err)
}
return &UploadResult{
CertId: uploadCertificateResp.Ret.CertId,
CertName: uploadCertificateResp.Ret.CertName,
}, nil
}
func (c *Certmgr) Replace(ctx context.Context, certIdOrName string, certPEM, privkeyPEM string) (*ReplaceResult, error) {
return nil, core.ErrUnsupported
}
func (c *Certmgr) tryGetResultIfCertExists(ctx context.Context, certPEM string) (*UploadResult, bool, error) {
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
if err != nil {
return nil, false, err
}
listUserCertificatesPage := 1
listUserCertificatesPageSize := 100
for {
select {
case <-ctx.Done():
return nil, false, ctx.Err()
default:
}
listUserCertificatesReq := &ksyunkcmsdk.ListUserCertificatesRequest{
Page: lo.ToPtr(int32(listUserCertificatesPage)),
PageSize: lo.ToPtr(int32(listUserCertificatesPageSize)),
}
listUserCertificatesResp, err := c.sdkClient.ListUserCertificatesWithContext(ctx, listUserCertificatesReq)
c.logger.Debug("sdk request 'kcm.ListUserCertificates'", slog.Any("request", listUserCertificatesReq), slog.Any("response", listUserCertificatesResp))
if err != nil {
return nil, false, fmt.Errorf("failed to execute sdk request 'kcm.ListUserCertificates': %w", err)
}
if listUserCertificatesResp.Ret == nil || listUserCertificatesResp.Ret.Certs == nil {
break
}
fingerprintSha1 := sha1.Sum(certX509.Raw)
fingerprintSha1Hex := hex.EncodeToString(fingerprintSha1[:])
for _, certItem := range listUserCertificatesResp.Ret.Certs {
// 对比证书备用名称
if !strings.EqualFold(strings.Join(certX509.DNSNames, ","), strings.Join(certItem.Domains, ",")) {
continue
}
// 对比证书颁发者
if certX509.Issuer.CommonName != certItem.CA {
continue
}
// 对比证书指纹
if !strings.EqualFold(fingerprintSha1Hex, certItem.FingerPrint) {
continue
}
// 如果以上信息都一致,则视为已存在相同证书,直接返回
return &UploadResult{
CertId: certItem.CertId,
CertName: certItem.CertName,
}, true, nil
}
if len(listUserCertificatesResp.Ret.Certs) < listUserCertificatesPageSize {
break
}
listUserCertificatesPage++
}
return nil, false, nil
}
func createSDKClient(accessKeyId, secretAccessKey string) (*ksyunkcmsdk.Client, error) {
client, err := ksyunkcmsdk.NewClient(
ksyunkcmsdk.WithAkSk(accessKeyId, secretAccessKey),
)
if err != nil {
return nil, err
}
return client, nil
}
@@ -1,183 +0,0 @@
package ksyunslb
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/samber/lo"
"github.com/certimate-go/certimate/pkg/core"
cmgrimpl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/ksyun-kcm"
ksyunkcmsdk "github.com/certimate-go/certimate/pkg/sdk3rd/ksyun/kcm"
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
)
type (
Provider = core.Certmgr
UploadResult = core.CertmgrUploadResult
ReplaceResult = core.CertmgrReplaceResult
)
type CertmgrConfig struct {
// 金山云 AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// 金山云 SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
// 金山云项目 ID。
ProjectId int64 `json:"projectId,omitempty"`
// 金山云地域。
Region string `json:"region"`
}
type Certmgr struct {
config *CertmgrConfig
logger *slog.Logger
sdkClient *ksyunkcmsdk.Client
sdkCertmgr core.Certmgr
}
var _ Provider = (*Certmgr)(nil)
func NewCertmgr(config *CertmgrConfig) (*Certmgr, error) {
if config == nil {
return nil, fmt.Errorf("the configuration of the certmgr provider is nil")
}
client, err := createSDKClient(config.AccessKeyId, config.SecretAccessKey)
if err != nil {
return nil, fmt.Errorf("could not create client: %w", err)
}
pcertmgr, err := cmgrimpl.NewCertmgr(&cmgrimpl.CertmgrConfig{
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
ProjectId: config.ProjectId,
})
if err != nil {
return nil, fmt.Errorf("could not create certmgr: %w", err)
}
return &Certmgr{
config: config,
logger: slog.Default(),
sdkClient: client,
sdkCertmgr: pcertmgr,
}, nil
}
func (c *Certmgr) SetLogger(logger *slog.Logger) {
if logger == nil {
c.logger = slog.New(slog.DiscardHandler)
} else {
c.logger = logger
}
c.sdkCertmgr.SetLogger(logger)
}
func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*UploadResult, error) {
// 描述证书,避免重复上传
describeCertificatesPage := 1
describeCertificatesPageSize := 100
for {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
describeCertificatesReq := &ksyunkcmsdk.DescribeCertificatesRequest{
Region: lo.ToPtr(c.config.Region),
Page: lo.ToPtr(int32(describeCertificatesPage)),
PageSize: lo.ToPtr(int32(describeCertificatesPageSize)),
}
describeCertificatesResp, err := c.sdkClient.DescribeCertificatesWithContext(ctx, describeCertificatesReq)
c.logger.Debug("sdk request 'kcm.DescribeCertificates'", slog.Any("request", describeCertificatesReq), slog.Any("response", describeCertificatesResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'kcm.DescribeCertificates': %w", err)
}
if describeCertificatesResp.CertificateSet == nil {
break
}
for _, certItem := range describeCertificatesResp.CertificateSet {
// 如果已存在相同证书,直接返回
if xcert.EqualCertificatesFromPEM(certPEM, certItem.PublicKey) {
c.logger.Info("ssl certificate already exists")
return &UploadResult{
CertId: certItem.CertificateId,
CertName: certItem.CertificateName,
}, nil
}
}
if len(describeCertificatesResp.CertificateSet) < describeCertificatesPageSize {
break
}
describeCertificatesPage++
}
// 托管证书到 KCM
upres, err := c.sdkCertmgr.Upload(ctx, certPEM, privkeyPEM)
if err != nil {
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
}
// 创建证书
// REF: https://apiexplorer.ksyun.com/#/api/96/CreateCertificate/2016-03-04/1013
createCertificateReq := &ksyunkcmsdk.CreateCertificateRequest{
Region: lo.ToPtr(c.config.Region),
CertificateName: lo.ToPtr(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
Description: lo.ToPtr("upload from Certimate"),
Source: lo.ToPtr("kcm"),
SSLCertificateId: lo.ToPtr(upres.CertId),
}
createCertificateResp, err := c.sdkClient.CreateCertificateWithContext(ctx, createCertificateReq)
c.logger.Debug("sdk request 'kcm.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'kcm.CreateCertificate': %w", err)
}
return &UploadResult{
CertId: createCertificateResp.Certificate.CertificateId,
CertName: createCertificateResp.Certificate.CertificateName,
}, nil
}
func (c *Certmgr) Replace(ctx context.Context, certIdOrName string, certPEM, privkeyPEM string) (*ReplaceResult, error) {
// 托管证书到 KCM
upres, err := c.sdkCertmgr.Upload(ctx, certPEM, privkeyPEM)
if err != nil {
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
}
// 更新证书
// REF: https://apiexplorer.ksyun.com/#/api/96/ModifyCertificate/2016-03-04/1013
modifyCertificateReq := &ksyunkcmsdk.ModifyCertificateRequest{
Region: lo.ToPtr(c.config.Region),
Description: lo.ToPtr("upload from Certimate"),
SSLCertificateId: lo.ToPtr(upres.CertId),
}
modifyCertificateResp, err := c.sdkClient.ModifyCertificateWithContext(ctx, modifyCertificateReq)
c.logger.Debug("sdk request 'kcm.ModifyCertificate'", slog.Any("request", modifyCertificateReq), slog.Any("response", modifyCertificateResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'kcm.ModifyCertificate': %w", err)
}
return &ReplaceResult{}, nil
}
func createSDKClient(accessKeyId, secretAccessKey string) (*ksyunkcmsdk.Client, error) {
client, err := ksyunkcmsdk.NewClient(
ksyunkcmsdk.WithAkSk(accessKeyId, secretAccessKey),
)
if err != nil {
return nil, err
}
return client, nil
}
@@ -1,166 +0,0 @@
package wangsucertificate
import (
"context"
"fmt"
"log/slog"
"regexp"
"strings"
"time"
"github.com/samber/lo"
"github.com/certimate-go/certimate/pkg/core"
wangsucertificate "github.com/certimate-go/certimate/pkg/sdk3rd/wangsu/certificate"
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
)
type (
Provider = core.Certmgr
UploadResult = core.CertmgrUploadResult
ReplaceResult = core.CertmgrReplaceResult
)
type CertmgrConfig struct {
// 网宿云 AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// 网宿云 AccessKeySecret。
AccessKeySecret string `json:"accessKeySecret"`
}
type Certmgr struct {
config *CertmgrConfig
logger *slog.Logger
sdkClient *wangsucertificate.Client
}
var _ Provider = (*Certmgr)(nil)
func NewCertmgr(config *CertmgrConfig) (*Certmgr, error) {
if config == nil {
return nil, fmt.Errorf("the configuration of the certmgr provider is nil")
}
client, err := createSDKClient(config.AccessKeyId, config.AccessKeySecret)
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) (*UploadResult, error) {
// 解析证书内容
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
if err != nil {
return nil, err
}
// 查询证书列表,避免重复上传
// REF: https://www.wangsu.com/document/api-doc/22675?productCode=certificatemanagement
listCertificatesResp, err := c.sdkClient.ListCertificatesWithContext(ctx)
c.logger.Debug("sdk request 'certificatemanagement.ListCertificates'", slog.Any("response", listCertificatesResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'certificatemanagement.ListCertificates': %w", err)
}
if listCertificatesResp.Certificates != nil {
for _, certItem := range listCertificatesResp.Certificates {
// 对比证书序列号
if !strings.EqualFold(certX509.SerialNumber.Text(16), certItem.Serial) {
continue
}
// 对比证书有效期
timezoneOfCST := time.FixedZone("CST", 8*60*60)
newCertNotBefore := certX509.NotBefore
newCertNotAfter := certX509.NotAfter
oldCertNotBefore, _ := time.ParseInLocation(time.DateTime, certItem.ValidityFrom, timezoneOfCST)
oldCertNotAfter, _ := time.ParseInLocation(time.DateTime, certItem.ValidityTo, timezoneOfCST)
if !newCertNotBefore.Equal(oldCertNotBefore) || !newCertNotAfter.Equal(oldCertNotAfter) {
continue
}
// 如果以上信息都一致,则视为已存在相同证书,直接返回
c.logger.Info("ssl certificate already exists")
return &UploadResult{
CertId: certItem.Id,
CertName: certItem.Name,
}, nil
}
}
// 生成新证书名(需符合网宿云命名规则)
certName := fmt.Sprintf("certimate_%d", time.Now().UnixMilli())
// 新增证书
// REF: https://www.wangsu.com/document/api-doc/25199?productCode=certificatemanagement
createCertificateReq := &wangsucertificate.CreateCertificateRequest{
Name: lo.ToPtr(certName),
Certificate: lo.ToPtr(certPEM),
PrivateKey: lo.ToPtr(privkeyPEM),
Comment: lo.ToPtr("upload from Certimate"),
}
createCertificateResp, err := c.sdkClient.CreateCertificateWithContext(ctx, createCertificateReq)
c.logger.Debug("sdk request 'certificatemanagement.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'certificatemanagement.CreateCertificate': %w", err)
}
// 网宿云证书 URL 中包含证书 ID
// 格式:
// https://open.chinanetcenter.com/api/certificate/100001
wangsuCertIdMatches := regexp.MustCompile(`/certificate/([0-9]+)`).FindStringSubmatch(createCertificateResp.CertificateLocation)
if len(wangsuCertIdMatches) == 0 {
return nil, fmt.Errorf("received empty certificate id")
}
return &UploadResult{
CertId: wangsuCertIdMatches[1],
CertName: certName,
}, nil
}
func (c *Certmgr) Replace(ctx context.Context, certIdOrName string, certPEM, privkeyPEM string) (*ReplaceResult, error) {
certId := certIdOrName
certName := fmt.Sprintf("certimate_%d", time.Now().UnixMilli())
// 修改证书
// REF: https://www.wangsu.com/document/api-doc/25568?productCode=certificatemanagement
updateCertificateReq := &wangsucertificate.UpdateCertificateRequest{
Name: lo.ToPtr(certName),
Certificate: lo.ToPtr(certPEM),
PrivateKey: lo.ToPtr(privkeyPEM),
Comment: lo.ToPtr("upload from Certimate"),
}
updateCertificateResp, err := c.sdkClient.UpdateCertificateWithContext(ctx, certId, updateCertificateReq)
c.logger.Debug("sdk request 'certificatemanagement.UpdateCertificate'", slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'certificatemanagement.UpdateCertificate': %w", err)
}
return &ReplaceResult{}, nil
}
func createSDKClient(accessKeyId, accessKeySecret string) (*wangsucertificate.Client, error) {
client, err := wangsucertificate.NewClient(
wangsucertificate.WithAkSk(accessKeyId, accessKeySecret),
)
if err != nil {
return nil, err
}
return client, nil
}
@@ -1,51 +0,0 @@
//go:build tester
package baishancdn_test
import (
"testing"
"github.com/stretchr/testify/require"
tester "github.com/certimate-go/certimate/pkg/core/deployer/providers-tester"
impl "github.com/certimate-go/certimate/pkg/core/deployer/providers/baishan-cdn"
)
var (
fp = tester.InitArgs("BAISHANCDN_")
fTestCertPath string
fTestKeyPath string
fApiToken string
fDomain string
)
func init() {
fp.DefineString(&fTestCertPath, "TESTCERTPATH")
fp.DefineString(&fTestKeyPath, "TESTKEYPATH")
fp.DefineString(&fApiToken, "APITOKEN")
fp.DefineString(&fDomain, "DOMAIN")
}
/*
Shell command to run this test:
go test -tags=tester -v ./baishan_cdn_test.go -args \
--BAISHANCDN_TESTCERTPATH="/path/to/your-test-cert.pem" \
--BAISHANCDN_TESTKEYPATH="/path/to/your-test-key.pem" \
--BAISHANCDN_APITOKEN="your-api-token" \
--BAISHANCDN_DOMAIN="example.com"
*/
func TestProvider(t *testing.T) {
fp.Parse()
t.Run("Deploy", func(t *testing.T) {
provider, err := impl.NewDeployer(&impl.DeployerConfig{
ApiToken: fApiToken,
DomainMatchPattern: impl.DOMAIN_MATCH_PATTERN_EXACT,
Domain: fDomain,
})
require.NoError(t, err)
tester.Deploy(t, provider, tester.DeployInput{CertPath: fTestCertPath, KeyPath: fTestKeyPath})
})
}
@@ -1,71 +0,0 @@
//go:build tester
package ksyuncdn_test
import (
"testing"
"github.com/stretchr/testify/require"
tester "github.com/certimate-go/certimate/pkg/core/deployer/providers-tester"
impl "github.com/certimate-go/certimate/pkg/core/deployer/providers/ksyun-cdn"
)
var (
fp = tester.InitArgs("KSYUNCDN_")
fTestCertPath string
fTestKeyPath string
fAccessKeyId string
fSecretAccessKey string
fDomain string
fCertificateId string
)
func init() {
fp.DefineString(&fTestCertPath, "TESTCERTPATH")
fp.DefineString(&fTestKeyPath, "TESTKEYPATH")
fp.DefineString(&fAccessKeyId, "ACCESSKEYID")
fp.DefineString(&fSecretAccessKey, "SECRETACCESSKEY")
fp.DefineString(&fDomain, "DOMAIN")
fp.DefineString(&fCertificateId, "CERTIFICATEID")
}
/*
Shell command to run this test:
go test -tags=tester -v ./ksyun_cdn_test.go -args \
--KSYUNCDN_TESTCERTPATH="/path/to/your-test-cert.pem" \
--KSYUNCDN_TESTKEYPATH="/path/to/your-test-key.pem" \
--KSYUNCDN_ACCESSKEYID="your-access-key-id" \
--KSYUNCDN_SECRETACCESSKEY="your-secret-access-key" \
--KSYUNCDN_DOMAIN="example.com" \
--KSYUNCDN_CERTIFICATEID="your-certificate-id"
*/
func TestProvider(t *testing.T) {
fp.Parse()
t.Run("Deploy_ToDomain", func(t *testing.T) {
provider, err := impl.NewDeployer(&impl.DeployerConfig{
AccessKeyId: fAccessKeyId,
SecretAccessKey: fSecretAccessKey,
DeployTarget: impl.DEPLOY_TARGET_DOMAIN,
DomainMatchPattern: impl.DOMAIN_MATCH_PATTERN_EXACT,
Domain: fDomain,
})
require.NoError(t, err)
tester.Deploy(t, provider, tester.DeployInput{CertPath: fTestCertPath, KeyPath: fTestKeyPath})
})
t.Run("Deploy_ToCertificate", func(t *testing.T) {
provider, err := impl.NewDeployer(&impl.DeployerConfig{
AccessKeyId: fAccessKeyId,
SecretAccessKey: fSecretAccessKey,
DeployTarget: impl.DEPLOY_TARGET_CERTIFICATE,
CertificateId: fCertificateId,
})
require.NoError(t, err)
tester.Deploy(t, provider, tester.DeployInput{CertPath: fTestCertPath, KeyPath: fTestKeyPath})
})
}
@@ -1,59 +0,0 @@
//go:build tester
package ksyunslb_test
import (
"testing"
"github.com/stretchr/testify/require"
tester "github.com/certimate-go/certimate/pkg/core/deployer/providers-tester"
impl "github.com/certimate-go/certimate/pkg/core/deployer/providers/ksyun-slb"
)
var (
fp = tester.InitArgs("KSYUNSLB_")
fTestCertPath string
fTestKeyPath string
fAccessKeyId string
fSecretAccessKey string
fRegion string
fCertificateId string
)
func init() {
fp.DefineString(&fTestCertPath, "TESTCERTPATH")
fp.DefineString(&fTestKeyPath, "TESTKEYPATH")
fp.DefineString(&fAccessKeyId, "ACCESSKEYID")
fp.DefineString(&fSecretAccessKey, "SECRETACCESSKEY")
fp.DefineString(&fRegion, "REGION")
fp.DefineString(&fCertificateId, "CERTIFICATEID")
}
/*
Shell command to run this test:
go test -tags=tester -v ./ksyun_slb_test.go -args \
--KSYUNSLB_TESTCERTPATH="/path/to/your-test-cert.pem" \
--KSYUNSLB_TESTKEYPATH="/path/to/your-test-key.pem" \
--KSYUNSLB_ACCESSKEYID="your-access-key-id" \
--KSYUNSLB_SECRETACCESSKEY="your-secret-access-key" \
--KSYUNSLB_REGION="cn-beijing-6" \
--KSYUNSLB_CERTIFICATEID="your-certificate-id"
*/
func TestProvider(t *testing.T) {
fp.Parse()
t.Run("Deploy_ToCertificate", func(t *testing.T) {
provider, err := impl.NewDeployer(&impl.DeployerConfig{
AccessKeyId: fAccessKeyId,
SecretAccessKey: fSecretAccessKey,
Region: fRegion,
DeployTarget: impl.DEPLOY_TARGET_CERTIFICATE,
CertificateId: fCertificateId,
})
require.NoError(t, err)
tester.Deploy(t, provider, tester.DeployInput{CertPath: fTestCertPath, KeyPath: fTestKeyPath})
})
}
@@ -1,54 +0,0 @@
//go:build tester
package wangsucdn_test
import (
"testing"
"github.com/stretchr/testify/require"
tester "github.com/certimate-go/certimate/pkg/core/deployer/providers-tester"
impl "github.com/certimate-go/certimate/pkg/core/deployer/providers/wangsu-cdn"
)
var (
fp = tester.InitArgs("WANGSUCDN_")
fTestCertPath string
fTestKeyPath string
fAccessKeyId string
fAccessKeySecret string
fDomain string
)
func init() {
fp.DefineString(&fTestCertPath, "TESTCERTPATH")
fp.DefineString(&fTestKeyPath, "TESTKEYPATH")
fp.DefineString(&fAccessKeyId, "ACCESSKEYID")
fp.DefineString(&fAccessKeySecret, "ACCESSKEYSECRET")
fp.DefineString(&fDomain, "DOMAIN")
}
/*
Shell command to run this test:
go test -tags=tester -v ./wangsu_cdn_test.go -args \
--WANGSUCDN_TESTCERTPATH="/path/to/your-test-cert.pem" \
--WANGSUCDN_TESTKEYPATH="/path/to/your-test-key.pem" \
--WANGSUCDN_ACCESSKEYID="your-access-key-id" \
--WANGSUCDN_ACCESSKEYSECRET="your-access-key-secret" \
--WANGSUCDN_DOMAIN="example.com"
*/
func TestProvider(t *testing.T) {
fp.Parse()
t.Run("Deploy", func(t *testing.T) {
provider, err := impl.NewDeployer(&impl.DeployerConfig{
AccessKeyId: fAccessKeyId,
AccessKeySecret: fAccessKeySecret,
Domains: []string{fDomain},
})
require.NoError(t, err)
tester.Deploy(t, provider, tester.DeployInput{CertPath: fTestCertPath, KeyPath: fTestKeyPath})
})
}
@@ -1,70 +0,0 @@
//go:build tester
package wangsucdnpro_test
import (
"testing"
"github.com/stretchr/testify/require"
tester "github.com/certimate-go/certimate/pkg/core/deployer/providers-tester"
impl "github.com/certimate-go/certimate/pkg/core/deployer/providers/wangsu-cdnpro"
)
var (
fp = tester.InitArgs("WANGSUCDNPRO_")
fTestCertPath string
fTestKeyPath string
fAccessKeyId string
fAccessKeySecret string
fApiKey string
fEnvironment string
fDomain string
fCertificateId string
fWebhookId string
)
func init() {
fp.DefineString(&fTestCertPath, "TESTCERTPATH")
fp.DefineString(&fTestKeyPath, "TESTKEYPATH")
fp.DefineString(&fAccessKeyId, "ACCESSKEYID")
fp.DefineString(&fAccessKeySecret, "ACCESSKEYSECRET")
fp.DefineString(&fApiKey, "APIKEY")
fp.DefineString(&fEnvironment, "ENVIRONMENT", "production")
fp.DefineString(&fDomain, "DOMAIN")
fp.DefineString(&fCertificateId, "CERTIFICATEID")
fp.DefineString(&fWebhookId, "WEBHOOKID")
}
/*
Shell command to run this test:
go test -tags=tester -v ./wangsu_cdnpro_test.go -args \
--WANGSUCDNPRO_TESTCERTPATH="/path/to/your-test-cert.pem" \
--WANGSUCDNPRO_TESTKEYPATH="/path/to/your-test-key.pem" \
--WANGSUCDNPRO_ACCESSKEYID="your-access-key-id" \
--WANGSUCDNPRO_ACCESSKEYSECRET="your-access-key-secret" \
--WANGSUCDNPRO_APIKEY="your-api-key" \
--WANGSUCDNPRO_ENVIRONMENT="production" \
--WANGSUCDNPRO_DOMAIN="example.com" \
--WANGSUCDNPRO_CERTIFICATEID="your-certificate-id" \
--WANGSUCDNPRO_WEBHOOKID="your-webhook-id"
*/
func TestProvider(t *testing.T) {
fp.Parse()
t.Run("Deploy", func(t *testing.T) {
provider, err := impl.NewDeployer(&impl.DeployerConfig{
AccessKeyId: fAccessKeyId,
AccessKeySecret: fAccessKeySecret,
ApiKey: fApiKey,
Environment: fEnvironment,
Domain: fDomain,
CertificateId: fCertificateId,
WebhookId: fWebhookId,
})
require.NoError(t, err)
tester.Deploy(t, provider, tester.DeployInput{CertPath: fTestCertPath, KeyPath: fTestKeyPath})
})
}
@@ -1,54 +0,0 @@
//go:build tester
package wangsucertificate_test
import (
"testing"
"github.com/stretchr/testify/require"
tester "github.com/certimate-go/certimate/pkg/core/deployer/providers-tester"
impl "github.com/certimate-go/certimate/pkg/core/deployer/providers/wangsu-certificate"
)
var (
fp = tester.InitArgs("WANGSUCERTIFICATE_")
fTestCertPath string
fTestKeyPath string
fAccessKeyId string
fAccessKeySecret string
fCertificateId string
)
func init() {
fp.DefineString(&fTestCertPath, "TESTCERTPATH")
fp.DefineString(&fTestKeyPath, "TESTKEYPATH")
fp.DefineString(&fAccessKeyId, "ACCESSKEYID")
fp.DefineString(&fAccessKeySecret, "ACCESSKEYSECRET")
fp.DefineString(&fCertificateId, "CERTIFICATEID")
}
/*
Shell command to run this test:
go test -tags=tester -v ./wangsu_certificate_test.go -args \
--WANGSUCERTIFICATE_TESTCERTPATH="/path/to/your-test-cert.pem" \
--WANGSUCERTIFICATE_TESTKEYPATH="/path/to/your-test-key.pem" \
--WANGSUCERTIFICATE_ACCESSKEYID="your-access-key-id" \
--WANGSUCERTIFICATE_ACCESSKEYSECRET="your-access-key-secret" \
--WANGSUCERTIFICATE_CERTIFICATEID="your-certificate-id"
*/
func TestProvider(t *testing.T) {
fp.Parse()
t.Run("Deploy", func(t *testing.T) {
provider, err := impl.NewDeployer(&impl.DeployerConfig{
AccessKeyId: fAccessKeyId,
AccessKeySecret: fAccessKeySecret,
CertificateId: fCertificateId,
})
require.NoError(t, err)
tester.Deploy(t, provider, tester.DeployInput{CertPath: fTestCertPath, KeyPath: fTestKeyPath})
})
}
@@ -1,184 +0,0 @@
package baishancdn
import (
"context"
"encoding/json"
"fmt"
"log/slog"
"strings"
"github.com/samber/lo"
"github.com/certimate-go/certimate/pkg/core"
cmgrimpl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/baishan-cdn"
baishansdk "github.com/certimate-go/certimate/pkg/sdk3rd/baishan"
)
type (
Provider = core.Deployer
DeployResult = core.DeployerDeployResult
)
type DeployerConfig struct {
// 白山云 API Token。
ApiToken string `json:"apiToken"`
// 部署目标。
DeployTarget string `json:"deployTarget"`
// 域名匹配模式。暂时只支持精确匹配。
// 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。
DomainMatchPattern string `json:"domainMatchPattern,omitempty"`
// 加速域名(支持泛域名)。
Domain string `json:"domain"`
// 证书 ID。
// 部署目标为 [DEPLOY_TARGET_CERTIFICATE] 时必填。
CertificateId string `json:"certificateId,omitempty"`
}
type Deployer struct {
config *DeployerConfig
logger *slog.Logger
sdkClient *baishansdk.Client
sdkCertmgr core.Certmgr
}
var _ Provider = (*Deployer)(nil)
func NewDeployer(config *DeployerConfig) (*Deployer, error) {
if config == nil {
return nil, fmt.Errorf("the configuration of the deployer provider is nil")
}
client, err := createSDKClient(config.ApiToken)
if err != nil {
return nil, fmt.Errorf("could not create client: %w", err)
}
pcertmgr, err := cmgrimpl.NewCertmgr(&cmgrimpl.CertmgrConfig{
ApiToken: config.ApiToken,
})
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) (*DeployResult, error) {
// 根据部署目标决定业务流程
switch d.config.DeployTarget {
case DEPLOY_TARGET_DOMAIN:
if err := d.deployToDomain(ctx, certPEM, privkeyPEM); err != nil {
return nil, err
}
case DEPLOY_TARGET_CERTIFICATE:
if err := d.deployToCertificate(ctx, certPEM, privkeyPEM); err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("unsupported deploy target '%s'", d.config.DeployTarget)
}
return &DeployResult{}, nil
}
func (d *Deployer) deployToDomain(ctx context.Context, certPEM, privkeyPEM string) error {
domain := normalizeDomain(d.config.Domain)
if domain == "" {
return fmt.Errorf("config `domain` 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://portal.baishancloud.com/track/document/api/1/1065
getDomainConfigReq := &baishansdk.GetDomainConfigRequest{
Domains: lo.ToPtr(domain),
Config: []*string{lo.ToPtr("https")},
}
getDomainConfigResp, err := d.sdkClient.GetDomainConfigWithContext(ctx, getDomainConfigReq)
d.logger.Debug("sdk request 'cdn.GetDomainConfig'", slog.Any("request", getDomainConfigReq), slog.Any("response", getDomainConfigResp))
if err != nil {
return fmt.Errorf("failed to execute sdk request 'cdn.GetDomainConfig': %w", err)
} else if len(getDomainConfigResp.Data) == 0 {
return fmt.Errorf("could not find domain '%s'", domain)
}
// 设置域名配置
// REF: https://portal.baishancloud.com/track/document/api/1/1045
setDomainConfigReq := &baishansdk.SetDomainConfigRequest{
Domains: lo.ToPtr(domain),
Config: &baishansdk.DomainConfig{
Https: &baishansdk.DomainConfigHttps{
CertId: json.Number(upres.CertId),
ForceHttps: getDomainConfigResp.Data[0].Config.Https.ForceHttps,
EnableHttp2: getDomainConfigResp.Data[0].Config.Https.EnableHttp2,
EnableOcsp: getDomainConfigResp.Data[0].Config.Https.EnableOcsp,
},
},
}
setDomainConfigResp, err := d.sdkClient.SetDomainConfigWithContext(ctx, setDomainConfigReq)
d.logger.Debug("sdk request 'cdn.SetDomainConfig'", slog.Any("request", setDomainConfigReq), slog.Any("response", setDomainConfigResp))
if err != nil {
return fmt.Errorf("failed to execute sdk request 'cdn.SetDomainConfig': %w", err)
}
return nil
}
func (d *Deployer) deployToCertificate(ctx context.Context, certPEM, privkeyPEM string) error {
if d.config.CertificateId == "" {
return fmt.Errorf("config `certificateId` is required")
}
// 替换证书
rplres, 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", rplres))
}
return nil
}
func createSDKClient(apiToken string) (*baishansdk.Client, error) {
client, err := baishansdk.NewClient(
baishansdk.WithApiToken(apiToken),
)
if err != nil {
return nil, err
}
return client, nil
}
func normalizeDomain(domain string) string {
// "*.example.com" → ".example.com",适配白山云 CDN 的泛域名参数要求
if strings.HasPrefix(domain, "*.") {
return strings.TrimPrefix(domain, "*")
}
return domain
}
@@ -1,13 +0,0 @@
package baishancdn
const (
// 部署目标:替换指定域名的证书。
DEPLOY_TARGET_DOMAIN = "domain"
// 部署目标:替换指定证书。
DEPLOY_TARGET_CERTIFICATE = "certificate"
)
const (
// 匹配模式:精确匹配。
DOMAIN_MATCH_PATTERN_EXACT = "exact"
)
@@ -1,17 +0,0 @@
package ksyuncdn
const (
// 部署目标:替换指定域名的证书。
DEPLOY_TARGET_DOMAIN = "domain"
// 部署目标:替换指定证书。
DEPLOY_TARGET_CERTIFICATE = "certificate"
)
const (
// 匹配模式:精确匹配。
DOMAIN_MATCH_PATTERN_EXACT = "exact"
// 匹配模式:通配符匹配。
DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard"
// 匹配模式:证书 SAN 匹配。
DOMAIN_MATCH_PATTERN_CERTSAN = "certsan"
)
@@ -1,285 +0,0 @@
package ksyuncdn
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/samber/lo"
"github.com/certimate-go/certimate/pkg/core"
ksyuncdnsdk "github.com/certimate-go/certimate/pkg/sdk3rd/ksyun/cdn"
xcerthostname "github.com/certimate-go/certimate/pkg/utils/cert/hostname"
xloop "github.com/certimate-go/certimate/pkg/utils/loop"
)
type (
Provider = core.Deployer
DeployResult = core.DeployerDeployResult
)
type DeployerConfig struct {
// 金山云 AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// 金山云 SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
// 金山云项目 ID。
ProjectId int64 `json:"projectId,omitempty"`
// 部署目标。
DeployTarget string `json:"deployTarget"`
// 域名匹配模式。
// 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。
DomainMatchPattern string `json:"domainMatchPattern,omitempty"`
// 加速域名(支持泛域名)。
Domain string `json:"domain"`
// 证书 ID。
// 部署目标为 [DEPLOY_TARGET_CERTIFICATE] 时必填。
CertificateId string `json:"certificateId,omitempty"`
}
type Deployer struct {
config *DeployerConfig
logger *slog.Logger
sdkClient *ksyuncdnsdk.Client
}
var _ Provider = (*Deployer)(nil)
func NewDeployer(config *DeployerConfig) (*Deployer, error) {
if config == nil {
return nil, fmt.Errorf("the configuration of the deployer provider is nil")
}
client, err := createSDKClient(config.AccessKeyId, config.SecretAccessKey)
if err != nil {
return nil, fmt.Errorf("could not create client: %w", err)
}
return &Deployer{
config: config,
logger: slog.Default(),
sdkClient: client,
}, nil
}
func (d *Deployer) SetLogger(logger *slog.Logger) {
if logger == nil {
d.logger = slog.New(slog.DiscardHandler)
} else {
d.logger = logger
}
}
func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*DeployResult, error) {
// 根据部署目标决定业务流程
switch d.config.DeployTarget {
case DEPLOY_TARGET_DOMAIN:
if err := d.deployToDomain(ctx, certPEM, privkeyPEM); err != nil {
return nil, err
}
case DEPLOY_TARGET_CERTIFICATE:
if err := d.deployToCertificate(ctx, certPEM, privkeyPEM); err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("unsupported deploy target '%s'", d.config.DeployTarget)
}
return &DeployResult{}, nil
}
func (d *Deployer) deployToDomain(ctx context.Context, certPEM, privkeyPEM string) error {
_, err := d.getAllDomains(ctx)
if err != nil {
return err
}
// 获取待部署的域名列表
var domainIds []string
switch d.config.DomainMatchPattern {
case "", DOMAIN_MATCH_PATTERN_EXACT:
{
if d.config.Domain == "" {
return fmt.Errorf("config `domain` is required")
}
domainCandidates, err := d.getAllDomains(ctx)
if err != nil {
return err
}
domains := lo.Filter(domainCandidates, func(domainItem *ksyuncdnsdk.CDNDomain, _ int) bool {
return d.config.Domain == domainItem.DomainName
})
if len(domains) == 0 {
return fmt.Errorf("could not find domain")
}
domainIds = lo.Map(domains, func(domainItem *ksyuncdnsdk.CDNDomain, _ int) string {
return domainItem.DomainId
})
}
case DOMAIN_MATCH_PATTERN_WILDCARD:
{
if d.config.Domain == "" {
return fmt.Errorf("config `domain` is required")
}
domainCandidates, err := d.getAllDomains(ctx)
if err != nil {
return err
}
domains := lo.Filter(domainCandidates, func(domainItem *ksyuncdnsdk.CDNDomain, _ int) bool {
return xcerthostname.IsMatch(d.config.Domain, domainItem.DomainName)
})
if len(domains) == 0 {
return fmt.Errorf("could not find any domains matched by wildcard")
}
domainIds = lo.Map(domains, func(domainItem *ksyuncdnsdk.CDNDomain, _ int) string {
return domainItem.DomainId
})
}
case DOMAIN_MATCH_PATTERN_CERTSAN:
{
domainCandidates, err := d.getAllDomains(ctx)
if err != nil {
return err
}
domains := lo.Filter(domainCandidates, func(domainItem *ksyuncdnsdk.CDNDomain, _ int) bool {
return xcerthostname.IsMatchByCertificatePEM(certPEM, domainItem.DomainName)
})
if len(domains) == 0 {
return fmt.Errorf("could not find any domains matched by certificate")
}
domainIds = lo.Map(domains, func(domainItem *ksyuncdnsdk.CDNDomain, _ int) string {
return domainItem.DomainId
})
}
default:
return fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern)
}
// 批量更新域名证书
if len(domainIds) == 0 {
d.logger.Info("no cdn domains to deploy")
} else {
d.logger.Info("found cdn domains to deploy", slog.Any("domainIds", domainIds))
if err := xloop.ForRangeAllWithContext(ctx, domainIds, func(ctx context.Context, domainId string, _ int) error {
return d.updateDomainCertificate(ctx, domainId, certPEM, privkeyPEM)
}); err != nil {
return err
}
}
return nil
}
func (d *Deployer) deployToCertificate(ctx context.Context, certPEM, privkeyPEM string) error {
if d.config.CertificateId == "" {
return fmt.Errorf("config `certificateId` is required")
}
// 更新证书
// REF: https://docs.ksyun.com/documents/259
setCertificateReq := &ksyuncdnsdk.SetCertificateRequest{
CertificateId: lo.ToPtr(d.config.CertificateId),
CertificateName: lo.ToPtr(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
ServerCertificate: lo.ToPtr(certPEM),
PrivateKey: lo.ToPtr(privkeyPEM),
}
setCertificateResp, err := d.sdkClient.SetCertificateWithContext(ctx, setCertificateReq)
d.logger.Debug("sdk request 'cdn.SetCertificate'", slog.Any("request", setCertificateReq), slog.Any("response", setCertificateResp))
if err != nil {
return fmt.Errorf("failed to execute sdk request 'cdn.SetCertificate': %w", err)
}
return nil
}
func (d *Deployer) getAllDomains(ctx context.Context) ([]*ksyuncdnsdk.CDNDomain, error) {
domains := make([]*ksyuncdnsdk.CDNDomain, 0)
// 查询域名列表
// REF: https://docs.ksyun.com/documents/198
getCdnDomainsPageNumber := 1
getCdnDomainsPageSize := 100
for {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
getCdnDomainsReq := &ksyuncdnsdk.GetCDNDomainsRequest{
ProjectId: lo.IfF(d.config.ProjectId != 0, func() *int64 { return lo.ToPtr(d.config.ProjectId) }).Else(nil),
PageNumber: lo.ToPtr(int32(getCdnDomainsPageNumber)),
PageSize: lo.ToPtr(int32(getCdnDomainsPageSize)),
}
getCdnDomainsResp, err := d.sdkClient.GetCDNDomainsWithContext(ctx, getCdnDomainsReq)
d.logger.Debug("sdk request 'cdn.GetCdnDomains'", slog.Any("request", getCdnDomainsReq), slog.Any("response", getCdnDomainsResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'cdn.GetCdnDomains': %w", err)
}
if getCdnDomainsResp.Domains == nil {
break
}
ignoredStatuses := []string{"offline", "icp_checking", "icp_check_failed", "locking", "locked"}
for _, domainItem := range getCdnDomainsResp.Domains {
if lo.Contains(ignoredStatuses, domainItem.DomainStatus) {
continue
}
domains = append(domains, domainItem)
}
if len(getCdnDomainsResp.Domains) < getCdnDomainsPageSize {
break
}
getCdnDomainsPageNumber++
}
return domains, nil
}
func (d *Deployer) updateDomainCertificate(ctx context.Context, cloudDomainId string, certPEM, privkeyPEM string) error {
// 为加速域名配置证书接口
// REF: https://docs.ksyun.com/documents/261
configCertificateReq := &ksyuncdnsdk.ConfigCertificateRequest{
Enable: lo.ToPtr("on"),
DomainIds: lo.ToPtr(cloudDomainId),
CertificateName: lo.ToPtr(fmt.Sprintf("certimate-%d", time.Now().UnixMilli())),
ServerCertificate: lo.ToPtr(certPEM),
PrivateKey: lo.ToPtr(privkeyPEM),
}
configCertificateResp, err := d.sdkClient.ConfigCertificateWithContext(ctx, configCertificateReq)
d.logger.Debug("sdk request 'cdn.ConfigCertificate'", slog.Any("request", configCertificateReq), slog.Any("response", configCertificateResp))
if err != nil {
return fmt.Errorf("failed to execute sdk request 'cdn.ConfigCertificate': %w", err)
}
return nil
}
func createSDKClient(accessKeyId, secretAccessKey string) (*ksyuncdnsdk.Client, error) {
client, err := ksyuncdnsdk.NewClient(
ksyuncdnsdk.WithAkSk(accessKeyId, secretAccessKey),
)
if err != nil {
return nil, err
}
return client, nil
}
@@ -1,72 +0,0 @@
package ksyunkcm
import (
"context"
"fmt"
"log/slog"
"github.com/certimate-go/certimate/pkg/core"
cmgrimpl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/ksyun-kcm"
)
type (
Provider = core.Deployer
DeployResult = core.DeployerDeployResult
)
type DeployerConfig struct {
// 金山云 AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// 金山云 SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
}
type Deployer struct {
config *DeployerConfig
logger *slog.Logger
sdkCertmgr core.Certmgr
}
var _ Provider = (*Deployer)(nil)
func NewDeployer(config *DeployerConfig) (*Deployer, error) {
if config == nil {
return nil, fmt.Errorf("the configuration of the deployer provider is nil")
}
pcertmgr, err := cmgrimpl.NewCertmgr(&cmgrimpl.CertmgrConfig{
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
})
if err != nil {
return nil, fmt.Errorf("could not create certmgr: %w", err)
}
return &Deployer{
config: config,
logger: slog.Default(),
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) (*DeployResult, error) {
// 上传证书
upres, err := d.sdkCertmgr.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))
}
return &DeployResult{}, nil
}
@@ -1,6 +0,0 @@
package ksyunslb
const (
// 部署目标:替换指定证书。
DEPLOY_TARGET_CERTIFICATE = "certificate"
)
@@ -1,102 +0,0 @@
package ksyunslb
import (
"context"
"fmt"
"log/slog"
"github.com/certimate-go/certimate/pkg/core"
cmgrimpl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/ksyun-slb"
)
type (
Provider = core.Deployer
DeployResult = core.DeployerDeployResult
)
type DeployerConfig struct {
// 金山云 AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// 金山云 SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
// 金山云项目 ID。
ProjectId int64 `json:"projectId,omitempty"`
// 金山云地域。
Region string `json:"region"`
// 部署目标。
DeployTarget string `json:"deployTarget"`
// 证书 ID。
// 部署目标为 [DEPLOY_TARGET_CERTIFICATE] 时必填。
CertificateId string `json:"certificateId,omitempty"`
}
type Deployer struct {
config *DeployerConfig
logger *slog.Logger
sdkCertmgr core.Certmgr
}
var _ Provider = (*Deployer)(nil)
func NewDeployer(config *DeployerConfig) (*Deployer, error) {
if config == nil {
return nil, fmt.Errorf("the configuration of the deployer provider is nil")
}
pcertmgr, err := cmgrimpl.NewCertmgr(&cmgrimpl.CertmgrConfig{
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
ProjectId: config.ProjectId,
Region: config.Region,
})
if err != nil {
return nil, fmt.Errorf("could not create certmgr: %w", err)
}
return &Deployer{
config: config,
logger: slog.Default(),
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) (*DeployResult, error) {
// 根据部署目标决定业务流程
switch d.config.DeployTarget {
case DEPLOY_TARGET_CERTIFICATE:
if err := d.deployToCertificate(ctx, certPEM, privkeyPEM); err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("unsupported deploy target '%s'", d.config.DeployTarget)
}
return &DeployResult{}, nil
}
func (d *Deployer) deployToCertificate(ctx context.Context, certPEM, privkeyPEM string) error {
if d.config.CertificateId == "" {
return fmt.Errorf("config `certificateId` is required")
}
// 替换证书
rplres, 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", rplres))
}
return nil
}
@@ -1,6 +0,0 @@
package wangsucdn
const (
// 匹配模式:精确匹配。
DOMAIN_MATCH_PATTERN_EXACT = "exact"
)
@@ -1,139 +0,0 @@
package wangsucdn
import (
"context"
"fmt"
"log/slog"
"strconv"
"strings"
"github.com/samber/lo"
"github.com/certimate-go/certimate/pkg/core"
cmgrimpl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/wangsu-certificate"
wangsucdn "github.com/certimate-go/certimate/pkg/sdk3rd/wangsu/cdn"
)
type (
Provider = core.Deployer
DeployResult = core.DeployerDeployResult
)
type DeployerConfig struct {
// 网宿云 AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// 网宿云 AccessKeySecret。
AccessKeySecret string `json:"accessKeySecret"`
// 域名匹配模式。暂时只支持精确匹配。
// 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。
DomainMatchPattern string `json:"domainMatchPattern,omitempty"`
// 加速域名数组(支持泛域名)。
Domains []string `json:"domains"`
}
type Deployer struct {
config *DeployerConfig
logger *slog.Logger
sdkClient *wangsucdn.Client
sdkCertmgr core.Certmgr
}
var _ Provider = (*Deployer)(nil)
func NewDeployer(config *DeployerConfig) (*Deployer, error) {
if config == nil {
return nil, fmt.Errorf("the configuration of the deployer provider is nil")
}
client, err := createSDKClient(config.AccessKeyId, config.AccessKeySecret)
if err != nil {
return nil, fmt.Errorf("could not create client: %w", err)
}
pcertmgr, err := cmgrimpl.NewCertmgr(&cmgrimpl.CertmgrConfig{
AccessKeyId: config.AccessKeyId,
AccessKeySecret: config.AccessKeySecret,
})
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) (*DeployResult, error) {
// 上传证书
upres, err := d.sdkCertmgr.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))
}
// 获取待部署的域名列表
domains := make([]string, 0)
switch d.config.DomainMatchPattern {
case "", DOMAIN_MATCH_PATTERN_EXACT:
{
if len(d.config.Domains) == 0 {
return nil, fmt.Errorf("config `domains` is required")
}
domains = lo.Map(d.config.Domains, func(domain string, _ int) string {
return normalizeDomain(domain)
})
}
default:
return nil, fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern)
}
// 批量修改域名证书配置
// REF: https://www.wangsu.com/document/api-doc/37447
certIdAsInt, _ := strconv.ParseInt(upres.CertId, 10, 64)
batchUpdateCertificateConfigReq := &wangsucdn.BatchUpdateCertificateConfigRequest{
CertificateId: certIdAsInt,
DomainNames: domains,
}
batchUpdateCertificateConfigResp, err := d.sdkClient.BatchUpdateCertificateConfigWithContext(ctx, batchUpdateCertificateConfigReq)
d.logger.Debug("sdk request 'cdn.BatchUpdateCertificateConfig'", slog.Any("request", batchUpdateCertificateConfigReq), slog.Any("response", batchUpdateCertificateConfigResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'cdn.BatchUpdateCertificateConfig': %w", err)
}
return &DeployResult{}, nil
}
func createSDKClient(accessKeyId, accessKeySecret string) (*wangsucdn.Client, error) {
client, err := wangsucdn.NewClient(
wangsucdn.WithAkSk(accessKeyId, accessKeySecret),
)
if err != nil {
return nil, err
}
return client, nil
}
func normalizeDomain(domain string) string {
// "*.example.com" → ".example.com",适配网宿云 CDN 的泛域名参数要求
if strings.HasPrefix(domain, "*.") {
return strings.TrimPrefix(domain, "*")
}
return domain
}
@@ -1,6 +0,0 @@
package wangsucdnpro
const (
// 匹配模式:精确匹配。
DOMAIN_MATCH_PATTERN_EXACT = "exact"
)
@@ -1,273 +0,0 @@
package wangsucdnpro
import (
"bytes"
"context"
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"fmt"
"log/slog"
"net/http"
"regexp"
"strconv"
"time"
"github.com/samber/lo"
"github.com/certimate-go/certimate/pkg/core"
wangsucdnpro "github.com/certimate-go/certimate/pkg/sdk3rd/wangsu/cdnpro"
xwait "github.com/certimate-go/certimate/pkg/utils/wait"
)
type (
Provider = core.Deployer
DeployResult = core.DeployerDeployResult
)
type DeployerConfig struct {
// 网宿云 AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// 网宿云 AccessKeySecret。
AccessKeySecret string `json:"accessKeySecret"`
// 网宿云 API Key。
ApiKey string `json:"apiKey"`
// 网宿云环境。
Environment string `json:"environment"`
// 域名匹配模式。暂时只支持精确匹配。
// 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。
DomainMatchPattern string `json:"domainMatchPattern,omitempty"`
// 加速域名(支持泛域名)。
Domain string `json:"domain"`
// 证书 ID。
// 选填。零值时表示新建证书;否则表示更新证书。
CertificateId string `json:"certificateId,omitempty"`
// Webhook ID。
// 选填。
WebhookId string `json:"webhookId,omitempty"`
}
type Deployer struct {
config *DeployerConfig
logger *slog.Logger
sdkClient *wangsucdnpro.Client
}
var _ Provider = (*Deployer)(nil)
func NewDeployer(config *DeployerConfig) (*Deployer, error) {
if config == nil {
return nil, fmt.Errorf("the configuration of the deployer provider is nil")
}
client, err := createSDKClient(config.AccessKeyId, config.AccessKeySecret)
if err != nil {
return nil, fmt.Errorf("could not create client: %w", err)
}
return &Deployer{
config: config,
logger: slog.Default(),
sdkClient: client,
}, nil
}
func (d *Deployer) SetLogger(logger *slog.Logger) {
if logger == nil {
d.logger = slog.New(slog.DiscardHandler)
} else {
d.logger = logger
}
}
func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*DeployResult, error) {
if d.config.Domain == "" {
return nil, fmt.Errorf("config `domain` is required")
}
// 查询已部署加速域名的详情
getHostnameDetailResp, err := d.sdkClient.GetHostnameDetailWithContext(ctx, d.config.Domain)
d.logger.Debug("sdk request 'cdnpro.GetHostnameDetail'", slog.String("params.hostname", d.config.Domain), slog.Any("response", getHostnameDetailResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'cdnpro.GetHostnameDetail': %w", err)
}
// 生成网宿云证书参数
encryptedPrivateKey, err := encryptPrivateKey(privkeyPEM, d.config.ApiKey, time.Now().Unix())
if err != nil {
return nil, fmt.Errorf("failed to encrypt private key: %w", err)
}
certificateNewVersionInfo := &wangsucdnpro.CertificateVersion{
PrivateKey: lo.ToPtr(encryptedPrivateKey),
Certificate: lo.ToPtr(certPEM),
}
// 网宿云证书 URL 中包含证书 ID 及版本号
// 格式:
// http://open.chinanetcenter.com/cdn/certificates/5dca2205f9e9cc0001df7b33
// http://open.chinanetcenter.com/cdn/certificates/329f12c1fe6708c23c31e91f/versions/5
var wangsuCertUrl string
var wangsuCertId string
var wangsuCertVer int32
// 如果原证书 ID 为空,则创建证书;否则更新证书。
timestamp := time.Now().Unix()
if d.config.CertificateId == "" {
// 创建证书
createCertificateReq := &wangsucdnpro.CreateCertificateRequest{
Timestamp: timestamp,
Name: lo.ToPtr(fmt.Sprintf("certimate_%d", time.Now().UnixMilli())),
AutoRenew: lo.ToPtr("Off"),
NewVersion: certificateNewVersionInfo,
}
createCertificateResp, err := d.sdkClient.CreateCertificateWithContext(ctx, createCertificateReq)
d.logger.Debug("sdk request 'cdnpro.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'cdnpro.CreateCertificate': %w", err)
}
wangsuCertUrl = createCertificateResp.CertificateLocation
d.logger.Info("ssl certificate uploaded", slog.Any("certUrl", wangsuCertUrl))
wangsuCertIdMatches := regexp.MustCompile(`/certificates/([a-zA-Z0-9-]+)`).FindStringSubmatch(wangsuCertUrl)
if len(wangsuCertIdMatches) > 1 {
wangsuCertId = wangsuCertIdMatches[1]
}
wangsuCertVer = 1
} else {
// 更新证书
updateCertificateReq := &wangsucdnpro.UpdateCertificateRequest{
Timestamp: timestamp,
Name: lo.ToPtr(fmt.Sprintf("certimate_%d", time.Now().UnixMilli())),
AutoRenew: lo.ToPtr("Off"),
NewVersion: certificateNewVersionInfo,
}
updateCertificateResp, err := d.sdkClient.UpdateCertificateWithContext(ctx, d.config.CertificateId, updateCertificateReq)
d.logger.Debug("sdk request 'cdnpro.UpdateCertificate'", slog.String("params.certificateId", d.config.CertificateId), slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'cdnpro.UpdateCertificate': %w", err)
}
wangsuCertUrl = updateCertificateResp.CertificateLocation
d.logger.Info("ssl certificate uploaded", slog.Any("certUrl", wangsuCertUrl))
wangsuCertIdMatches := regexp.MustCompile(`/certificates/([a-zA-Z0-9-]+)`).FindStringSubmatch(wangsuCertUrl)
if len(wangsuCertIdMatches) > 1 {
wangsuCertId = wangsuCertIdMatches[1]
}
wangsuCertVerMatches := regexp.MustCompile(`/versions/(\d+)`).FindStringSubmatch(wangsuCertUrl)
if len(wangsuCertVerMatches) > 1 {
n, _ := strconv.ParseInt(wangsuCertVerMatches[1], 10, 32)
wangsuCertVer = int32(n)
}
}
// 创建部署任务
// REF: https://www.wangsu.com/document/api-doc/27034
var wangsuTaskId string
createDeploymentTaskReq := &wangsucdnpro.CreateDeploymentTaskRequest{
Name: lo.ToPtr(fmt.Sprintf("certimate_%d", time.Now().UnixMilli())),
Target: lo.ToPtr(d.config.Environment),
Actions: &[]wangsucdnpro.DeploymentTaskAction{
{
Action: lo.ToPtr("deploy_cert"),
CertificateId: lo.ToPtr(wangsuCertId),
Version: lo.ToPtr(wangsuCertVer),
},
},
Webhook: lo.EmptyableToPtr(d.config.WebhookId),
}
createDeploymentTaskResp, err := d.sdkClient.CreateDeploymentTaskWithContext(ctx, createDeploymentTaskReq)
d.logger.Debug("sdk request 'cdnpro.CreateCertificate'", slog.Any("request", createDeploymentTaskReq), slog.Any("response", createDeploymentTaskResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'cdnpro.CreateDeploymentTask': %w", err)
} else {
wangsuTaskMatches := regexp.MustCompile(`/deploymentTasks/([a-zA-Z0-9-]+)`).FindStringSubmatch(createDeploymentTaskResp.DeploymentTaskLocation)
if len(wangsuTaskMatches) > 1 {
wangsuTaskId = wangsuTaskMatches[1]
}
}
// 获取部署任务详细信息,等待任务状态变更
// REF: https://www.wangsu.com/document/api-doc/27038
if _, err := xwait.UntilWithContext(ctx, func(_ context.Context, _ int) (bool, error) {
getDeploymentTaskDetailResp, err := d.sdkClient.GetDeploymentTaskDetailWithContext(ctx, wangsuTaskId)
d.logger.Info("sdk request 'cdnpro.GetDeploymentTaskDetail'", slog.String("params.taskId", wangsuTaskId), slog.Any("response", getDeploymentTaskDetailResp))
if err != nil {
return false, fmt.Errorf("failed to execute sdk request 'cdnpro.GetDeploymentTaskDetail': %w", err)
}
if getDeploymentTaskDetailResp.Status == "failed" {
return false, fmt.Errorf("unexpected deployment task status")
} else if getDeploymentTaskDetailResp.Status == "succeeded" || getDeploymentTaskDetailResp.FinishTime != "" {
return true, nil
}
d.logger.Info(fmt.Sprintf("waiting for deployment task completion (current status: %s) ...", getDeploymentTaskDetailResp.Status))
return false, nil
}, 10*time.Second); err != nil {
return nil, err
}
return &DeployResult{}, nil
}
func createSDKClient(accessKeyId, accessKeySecret string) (*wangsucdnpro.Client, error) {
client, err := wangsucdnpro.NewClient(
wangsucdnpro.WithAkSk(accessKeyId, accessKeySecret),
)
if err != nil {
return nil, err
}
return client, nil
}
func encryptPrivateKey(privkeyPEM string, apiKey string, timestamp int64) (string, error) {
date := time.Unix(timestamp, 0).UTC()
dateStr := date.Format(http.TimeFormat)
h := hmac.New(sha256.New, []byte(apiKey))
h.Write([]byte(dateStr))
aesivkey := h.Sum(nil)
aesivkeyHex := hex.EncodeToString(aesivkey)
if len(aesivkeyHex) != 64 {
return "", fmt.Errorf("invalid hmac length: %d", len(aesivkeyHex))
}
ivHex := aesivkeyHex[:32]
keyHex := aesivkeyHex[32:64]
iv, err := hex.DecodeString(ivHex)
if err != nil {
return "", fmt.Errorf("failed to decode iv: %w", err)
}
key, err := hex.DecodeString(keyHex)
if err != nil {
return "", fmt.Errorf("failed to decode key: %w", err)
}
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
plainBytes := []byte(privkeyPEM)
padlen := aes.BlockSize - len(plainBytes)%aes.BlockSize
if padlen > 0 {
paddata := bytes.Repeat([]byte{byte(padlen)}, padlen)
plainBytes = append(plainBytes, paddata...)
}
encBytes := make([]byte, len(plainBytes))
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(encBytes, plainBytes)
return base64.StdEncoding.EncodeToString(encBytes), nil
}
@@ -1,104 +0,0 @@
package wangsucertificate
import (
"context"
"fmt"
"log/slog"
"github.com/certimate-go/certimate/pkg/core"
cmgrimpl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/wangsu-certificate"
wangsucertificate "github.com/certimate-go/certimate/pkg/sdk3rd/wangsu/certificate"
)
type (
Provider = core.Deployer
DeployResult = core.DeployerDeployResult
)
type DeployerConfig struct {
// 网宿云 AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// 网宿云 AccessKeySecret。
AccessKeySecret string `json:"accessKeySecret"`
// 证书 ID。
// 选填。零值时表示新建证书;否则表示更新证书。
CertificateId string `json:"certificateId,omitempty"`
}
type Deployer struct {
config *DeployerConfig
logger *slog.Logger
sdkClient *wangsucertificate.Client
sdkCertmgr core.Certmgr
}
var _ Provider = (*Deployer)(nil)
func NewDeployer(config *DeployerConfig) (*Deployer, error) {
if config == nil {
return nil, fmt.Errorf("the configuration of the deployer provider is nil")
}
client, err := createSDKClient(config.AccessKeyId, config.AccessKeySecret)
if err != nil {
return nil, fmt.Errorf("could not create client: %w", err)
}
pcertmgr, err := cmgrimpl.NewCertmgr(&cmgrimpl.CertmgrConfig{
AccessKeyId: config.AccessKeyId,
AccessKeySecret: config.AccessKeySecret,
})
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) (*DeployResult, error) {
if d.config.CertificateId == "" {
// 上传证书
upres, err := d.sdkCertmgr.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))
}
} else {
// 替换证书
rplres, err := d.sdkCertmgr.Replace(ctx, d.config.CertificateId, certPEM, privkeyPEM)
if err != nil {
return nil, fmt.Errorf("failed to replace certificate file: %w", err)
} else {
d.logger.Info("ssl certificate replaced", slog.Any("result", rplres))
}
}
return &DeployResult{}, nil
}
func createSDKClient(accessKeyId, accessKeySecret string) (*wangsucertificate.Client, error) {
client, err := wangsucertificate.NewClient(
wangsucertificate.WithAkSk(accessKeyId, accessKeySecret),
)
if err != nil {
return nil, err
}
return client, nil
}
@@ -1,51 +0,0 @@
package baishan
import (
"context"
"net/http"
)
type GetDomainConfigRequest struct {
Domains *string `json:"domains,omitempty" url:"domains,omitempty"`
Config []*string `json:"config,omitempty" url:"config,omitempty"`
}
type GetDomainConfigResponse struct {
sdkResponseBase
Data []*struct {
Domain string `json:"domain"`
Config *DomainConfig `json:"config"`
} `json:"data,omitempty"`
}
func (c *Client) GetDomainConfig(req *GetDomainConfigRequest) (*GetDomainConfigResponse, error) {
return c.GetDomainConfigWithContext(context.Background(), req)
}
func (c *Client) GetDomainConfigWithContext(ctx context.Context, req *GetDomainConfigRequest) (*GetDomainConfigResponse, error) {
httpreq, err := c.newRequest(http.MethodGet, "/v2/domain/config")
if err != nil {
return nil, err
} else {
if req.Domains != nil {
httpreq.SetQueryParam("domains", *req.Domains)
}
if req.Config != nil {
for _, config := range req.Config {
if config != nil {
httpreq.QueryParam.Add("config[]", *config)
}
}
}
httpreq.SetContext(ctx)
}
result := &GetDomainConfigResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}
-52
View File
@@ -1,52 +0,0 @@
package baishan
import (
"context"
"encoding/json"
"net/http"
qs "github.com/google/go-querystring/query"
)
type GetDomainListRequest struct {
PageNumber *int32 `json:"page_number,omitempty" url:"page_number,omitempty"`
PageSize *int32 `json:"page_size,omitempty" url:"page_size,omitempty"`
DomainStatus *string `json:"domain_status,omitempty" url:"domain_status,omitempty"`
}
type GetDomainListResponse struct {
sdkResponseBase
Data []*struct {
List []*Domain `json:"list"`
PageNumber json.Number `json:"page_number"`
PageSize json.Number `json:"page_size"`
TotalNumber json.Number `json:"total_number"`
} `json:"data,omitempty"`
}
func (c *Client) GetDomainList(req *GetDomainListRequest) (*GetDomainListResponse, error) {
return c.GetDomainListWithContext(context.Background(), req)
}
func (c *Client) GetDomainListWithContext(ctx context.Context, req *GetDomainListRequest) (*GetDomainListResponse, error) {
httpreq, err := c.newRequest(http.MethodGet, "/v2/domain/list")
if err != nil {
return nil, err
} else {
values, err := qs.Values(req)
if err != nil {
return nil, err
}
httpreq.SetQueryParamsFromValues(values)
httpreq.SetContext(ctx)
}
result := &GetDomainListResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}
@@ -1,40 +0,0 @@
package baishan
import (
"context"
"net/http"
)
type SetDomainConfigRequest struct {
Domains *string `json:"domains,omitempty"`
Config *DomainConfig `json:"config,omitempty"`
}
type SetDomainConfigResponse struct {
sdkResponseBase
Data *struct {
Config *DomainConfig `json:"config"`
} `json:"data,omitempty"`
}
func (c *Client) SetDomainConfig(req *SetDomainConfigRequest) (*SetDomainConfigResponse, error) {
return c.SetDomainConfigWithContext(context.Background(), req)
}
func (c *Client) SetDomainConfigWithContext(ctx context.Context, req *SetDomainConfigRequest) (*SetDomainConfigResponse, error) {
httpreq, err := c.newRequest(http.MethodPost, "/v2/domain/config")
if err != nil {
return nil, err
} else {
httpreq.SetBody(req)
httpreq.SetContext(ctx)
}
result := &SetDomainConfigResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}
@@ -1,40 +0,0 @@
package baishan
import (
"context"
"net/http"
)
type UploadDomainCertificateRequest struct {
CertificateId *string `json:"cert_id,omitempty"`
Certificate *string `json:"certificate,omitempty"`
Key *string `json:"key,omitempty"`
Name *string `json:"name,omitempty"`
}
type UploadDomainCertificateResponse struct {
sdkResponseBase
Data *DomainCertificate `json:"data,omitempty"`
}
func (c *Client) UploadDomainCertificate(req *UploadDomainCertificateRequest) (*UploadDomainCertificateResponse, error) {
return c.UploadDomainCertificateWithContext(context.Background(), req)
}
func (c *Client) UploadDomainCertificateWithContext(ctx context.Context, req *UploadDomainCertificateRequest) (*UploadDomainCertificateResponse, error) {
httpreq, err := c.newRequest(http.MethodPost, "/v2/domain/certificate")
if err != nil {
return nil, err
} else {
httpreq.SetBody(req)
httpreq.SetContext(ctx)
}
result := &UploadDomainCertificateResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}
-100
View File
@@ -1,100 +0,0 @@
// A simple SDK client for BaishanCloud.
// API documentation: https://portal.baishancloud.com/track/document/api/1/1249
package baishan
import (
"encoding/json"
"fmt"
"time"
"github.com/go-resty/resty/v2"
"github.com/certimate-go/certimate/internal/app"
)
type Client struct {
rc *resty.Client
}
func NewClient(optFns ...OptionsFunc) (*Client, error) {
opts := &Options{}
for _, fn := range optFns {
fn(opts)
}
if opts.ApiToken == "" {
return nil, fmt.Errorf("sdkerr: unset apiToken")
}
httper := resty.New().
SetBaseURL("https://cdn.api.baishan.com").
SetHeader("Accept", "application/json").
SetHeader("Content-Type", "application/json").
SetHeader("User-Agent", app.AppUserAgent).
SetQueryParam("token", opts.ApiToken)
return &Client{rc: httper}, nil
}
func (c *Client) SetTimeout(timeout time.Duration) *Client {
c.rc.SetTimeout(timeout)
return c
}
func (c *Client) newRequest(method string, path string) (*resty.Request, error) {
if method == "" {
return nil, fmt.Errorf("sdkerr: unset method")
}
if path == "" {
return nil, fmt.Errorf("sdkerr: unset path")
}
req := c.rc.R()
req.Method = method
req.URL = path
// WARN:
// DO NOT CALL `req.SetResult` or `req.SetError` AGAIN! USE `doRequestWithResult` INSTEAD.
return req, nil
}
func (c *Client) doRequest(req *resty.Request) (*resty.Response, error) {
if req == nil {
return nil, fmt.Errorf("sdkerr: nil request")
}
resp, err := req.Send()
if err != nil {
return resp, fmt.Errorf("sdkerr: failed to send request: %w", err)
} else if resp.IsError() {
return resp, fmt.Errorf("sdkerr: unexpected status code: %d (resp: %s)", resp.StatusCode(), resp.String())
}
return resp, nil
}
func (c *Client) doRequestWithResult(req *resty.Request, res sdkResponse) (*resty.Response, error) {
if req == nil {
return nil, fmt.Errorf("sdkerr: nil request")
}
resp, err := c.doRequest(req)
if err != nil {
if resp != nil {
json.Unmarshal(resp.Body(), &res)
}
return resp, err
}
if len(resp.Body()) != 0 {
if err := json.Unmarshal(resp.Body(), &res); err != nil {
return resp, fmt.Errorf("sdkerr: failed to unmarshal response: %w (resp: %s)", err, resp.String())
} else {
if rCode := res.GetCode(); rCode != 0 {
return resp, fmt.Errorf("sdkerr: api error: code='%d', message='%s'", rCode, res.GetMessage())
}
}
}
return resp, nil
}
-34
View File
@@ -1,34 +0,0 @@
package baishan
import (
"encoding/json"
)
type Domain struct {
Id string `json:"id"`
Domain string `json:"domain"`
Type string `json:"type"`
Status string `json:"status"`
Cname string `json:"cname"`
Area string `json:"area"`
CreateTime string `json:"create_time"`
UpdateTime string `json:"update_time"`
}
type DomainCertificate struct {
CertId json.Number `json:"cert_id"`
Name string `json:"name"`
CertStartTime string `json:"cert_start_time"`
CertExpireTime string `json:"cert_expire_time"`
}
type DomainConfig struct {
Https *DomainConfigHttps `json:"https"`
}
type DomainConfigHttps struct {
CertId json.Number `json:"cert_id"`
ForceHttps *string `json:"force_https,omitempty"`
EnableHttp2 *string `json:"http2,omitempty"`
EnableOcsp *string `json:"ocsp,omitempty"`
}
-13
View File
@@ -1,13 +0,0 @@
package baishan
type Options struct {
ApiToken string
}
type OptionsFunc func(*Options)
func WithApiToken(apiToken string) OptionsFunc {
return func(o *Options) {
o.ApiToken = apiToken
}
}
-29
View File
@@ -1,29 +0,0 @@
package baishan
type sdkResponse interface {
GetCode() int
GetMessage() string
}
type sdkResponseBase struct {
Code *int `json:"code,omitempty"`
Message *string `json:"message,omitempty"`
}
func (r *sdkResponseBase) GetCode() int {
if r.Code == nil {
return 0
}
return *r.Code
}
func (r *sdkResponseBase) GetMessage() string {
if r.Message == nil {
return ""
}
return *r.Message
}
var _ sdkResponse = (*sdkResponseBase)(nil)
-5
View File
@@ -1,5 +0,0 @@
本模块存在相应的官方 SDK:`github.com/KscSDK/ksc-sdk-go/service/cdnv1`。
但会使得依赖体积显著膨胀,故没有使用。
具体细节见 [PR #1341](https://github.com/certimate-go/certimate/pull/1341)。
@@ -1,51 +0,0 @@
package cdn
import (
"context"
"net/http"
)
type ConfigCertificateRequest struct {
Enable *string `json:"Enable,omitempty"`
DomainIds *string `json:"DomainIds,omitempty"`
CertificateId *string `json:"CertificateId,omitempty"`
CertificateName *string `json:"CertificateName,omitempty"`
ServerCertificate *string `json:"ServerCertificate,omitempty"`
PrivateKey *string `json:"PrivateKey,omitempty"`
}
type ConfigCertificateResponse struct {
sdkResponseBase
CertificateId string `json:"CertificateId,omitempty"`
}
func (c *Client) ConfigCertificate(req *ConfigCertificateRequest) (*ConfigCertificateResponse, error) {
return c.ConfigCertificateWithContext(context.Background(), req)
}
func (c *Client) ConfigCertificateWithContext(ctx context.Context, req *ConfigCertificateRequest) (*ConfigCertificateResponse, error) {
params := &struct {
ConfigCertificateRequest `json:",inline"`
Action string
Version string
}{
ConfigCertificateRequest: *req,
Action: "ConfigCertificate",
Version: "2016-09-01",
}
httpreq, err := c.newRequest(http.MethodPost, "/2016-09-01/cert/ConfigCertificate", params)
if err != nil {
return nil, err
} else {
httpreq.SetContext(ctx)
}
result := &ConfigCertificateResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}
@@ -1,55 +0,0 @@
package cdn
import (
"context"
"net/http"
)
type GetCDNDomainsRequest struct {
ProjectId *int64 `json:"ProjectId,omitempty"`
DomainName *string `json:"DomainName,omitempty"`
DomainStatus *string `json:"DomainStatus,omitempty"`
FuzzyMatch *bool `json:"FuzzyMatch,omitempty"`
CdnType *bool `json:"CdnType,omitempty"`
PageNumber *int32 `json:"PageNumber,omitempty"`
PageSize *int32 `json:"PageSize,omitempty"`
}
type GetCdnDomainsResponse struct {
sdkResponseBase
Domains []*CDNDomain `json:"Domains"`
PageNumber int32 `json:"PageNumber,omitempty"`
PageSize int32 `json:"PageSize,omitempty"`
TotalCount int32 `json:"TotalCount,omitempty"`
}
func (c *Client) GetCDNDomains(req *GetCDNDomainsRequest) (*GetCdnDomainsResponse, error) {
return c.GetCDNDomainsWithContext(context.Background(), req)
}
func (c *Client) GetCDNDomainsWithContext(ctx context.Context, req *GetCDNDomainsRequest) (*GetCdnDomainsResponse, error) {
params := &struct {
GetCDNDomainsRequest `json:",inline"`
Action string
Version string
}{
GetCDNDomainsRequest: *req,
Action: "GetCdnDomains",
Version: "2019-06-01",
}
httpreq, err := c.newRequest(http.MethodGet, "/2019-06-01/GetCdnDomains", params)
if err != nil {
return nil, err
} else {
httpreq.SetContext(ctx)
}
result := &GetCdnDomainsResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}
@@ -1,49 +0,0 @@
package cdn
import (
"context"
"net/http"
)
type SetCertificateRequest struct {
CertificateId *string `json:"CertificateId,omitempty"`
CertificateName *string `json:"CertificateName,omitempty"`
ServerCertificate *string `json:"ServerCertificate,omitempty"`
PrivateKey *string `json:"PrivateKey,omitempty"`
}
type SetCertificateResponse struct {
sdkResponseBase
CertificateId string `json:"CertificateId,omitempty"`
}
func (c *Client) SetCertificate(req *SetCertificateRequest) (*SetCertificateResponse, error) {
return c.SetCertificateWithContext(context.Background(), req)
}
func (c *Client) SetCertificateWithContext(ctx context.Context, req *SetCertificateRequest) (*SetCertificateResponse, error) {
params := &struct {
SetCertificateRequest `json:",inline"`
Action string
Version string
}{
SetCertificateRequest: *req,
Action: "SetCertificate",
Version: "2016-09-01",
}
httpreq, err := c.newRequest(http.MethodPost, "/2016-09-01/cert/SetCertificate", params)
if err != nil {
return nil, err
} else {
httpreq.SetContext(ctx)
}
result := &SetCertificateResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}
-54
View File
@@ -1,54 +0,0 @@
// A simple SDK client for KingsoftCloud CDN.
// API documentation: https://apiexplorer.ksyun.com/#/api/home
package cdn
import (
"fmt"
"time"
"github.com/go-resty/resty/v2"
common "github.com/certimate-go/certimate/pkg/sdk3rd/ksyun/zz-shared-common"
)
const (
service = "cdn"
endpoint = "https://" + service + ".api.ksyun.com"
)
type Client struct {
client *common.Client
}
func NewClient(optFns ...common.OptionsFunc) (*Client, error) {
client, err := common.NewClient(endpoint, service, optFns...)
if err != nil {
return nil, err
}
return &Client{client: client}, nil
}
func (c *Client) SetTimeout(timeout time.Duration) *Client {
c.client.SetTimeout(timeout)
return c
}
func (c *Client) newRequest(method string, path string, params any) (*resty.Request, error) {
return c.client.NewRequest(method, path, params)
}
func (c *Client) doRequest(req *resty.Request) (*resty.Response, error) {
return c.client.DoRequest(req)
}
func (c *Client) doRequestWithResult(req *resty.Request, res sdkResponse) (*resty.Response, error) {
resp, err := c.client.DoRequestWithResult(req, res)
if err == nil {
if err := res.GetAPIError(); err != nil {
return resp, fmt.Errorf("sdkerr: api error: %s", err.Error())
}
}
return resp, err
}
-14
View File
@@ -1,14 +0,0 @@
package cdn
type CDNDomain struct {
Region string `json:"Region"`
DomainId string `json:"DomainId"`
DomainName string `json:"DomainName"`
Description string `json:"Description"`
Cname string `json:"Cname"`
CdnType string `json:"CdnType"`
CdnSubType string `json:"CdnSubType"`
DomainStatus string `json:"DomainStatus"`
CreatedTime string `json:"CreatedTime"`
ModifiedTime string `json:"ModifiedTime"`
}
-9
View File
@@ -1,9 +0,0 @@
package cdn
import (
common "github.com/certimate-go/certimate/pkg/sdk3rd/ksyun/zz-shared-common"
)
func WithAkSk(ak, sk string) common.OptionsFunc {
return common.WithAkSk(ak, sk)
}
-36
View File
@@ -1,36 +0,0 @@
package cdn
import (
"fmt"
)
type sdkResponse interface {
GetAPIError() error
}
type sdkResponseBase struct {
RequestId *string `json:"RequestId,omitempty"`
Error *sdkAPIError `json:"Error,omitempty"`
}
type sdkAPIError struct {
Type string `json:"Type,omitempty"`
Code string `json:"Code"`
Message string `json:"Message"`
}
func (e sdkAPIError) Error() string {
if e.Type == "" {
return fmt.Sprintf("[%s] %s ", e.Code, e.Message)
}
return fmt.Sprintf("[%s_%s] %s ", e.Type, e.Code, e.Message)
}
func (r *sdkResponseBase) GetAPIError() error {
if r.Error != nil {
return r.Error
}
return nil
}
var _ sdkResponse = (*sdkResponseBase)(nil)
-5
View File
@@ -1,5 +0,0 @@
本模块存在相应的官方 SDK:`github.com/KscSDK/ksc-sdk-go/service/kcm`。
但会使得依赖体积显著膨胀,故没有使用。
具体细节见 [PR #1341](https://github.com/certimate-go/certimate/pull/1341)。
@@ -1,61 +0,0 @@
package kcm
import (
"context"
"net/http"
qs "github.com/google/go-querystring/query"
)
type CreateCertificateRequest struct {
Region *string `json:"Region,omitempty" url:"Region,omitempty"`
CertificateName *string `json:"CertificateName,omitempty" url:"CertificateName,omitempty"`
Description *string `json:"Description,omitempty" url:"Description,omitempty"`
PublicKey *string `json:"PublicKey,omitempty" url:"-"`
PrivateKey *string `json:"PrivateKey,omitempty" url:"-"`
CertificateType *string `json:"CertificateType,omitempty" url:"CertificateType,omitempty"`
Source *string `json:"Source,omitempty" url:"Source,omitempty"`
SSLCertificateId *string `json:"SslCertificateId,omitempty" url:"SslCertificateId,omitempty"`
}
type CreateCertificateResponse struct {
sdkResponseBase
Certificate *LBCertificate `json:"Certificate,omitempty"`
}
func (c *Client) CreateCertificate(req *CreateCertificateRequest) (*CreateCertificateResponse, error) {
return c.CreateCertificateWithContext(context.Background(), req)
}
func (c *Client) CreateCertificateWithContext(ctx context.Context, req *CreateCertificateRequest) (*CreateCertificateResponse, error) {
params := &struct {
CreateCertificateRequest `json:",inline"`
Action string
Version string
}{
CreateCertificateRequest: *req,
Action: "CreateCertificate",
Version: "2016-03-04",
}
httpreq, err := c.newRequest(http.MethodPost, "/", params)
if err != nil {
return nil, err
} else {
values, err := qs.Values(req)
if err != nil {
return nil, err
}
httpreq.SetQueryParamsFromValues(values)
httpreq.SetContext(ctx)
}
result := &CreateCertificateResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}
@@ -1,48 +0,0 @@
package kcm
import (
"context"
"net/http"
)
type DescribeCertificatesRequest struct {
Region *string `json:"Region,omitempty"`
Page *int32 `json:"Page,omitempty"`
PageSize *int32 `json:"PageSize,omitempty"`
}
type DescribeCertificatesResponse struct {
sdkResponseBase
CertificateSet []*LBCertificate `json:"CertificateSet"`
}
func (c *Client) DescribeCertificates(req *DescribeCertificatesRequest) (*DescribeCertificatesResponse, error) {
return c.DescribeCertificatesWithContext(context.Background(), req)
}
func (c *Client) DescribeCertificatesWithContext(ctx context.Context, req *DescribeCertificatesRequest) (*DescribeCertificatesResponse, error) {
params := &struct {
DescribeCertificatesRequest `json:",inline"`
Action string
Version string
}{
DescribeCertificatesRequest: *req,
Action: "DescribeCertificates",
Version: "2016-03-04",
}
httpreq, err := c.newRequest(http.MethodGet, "/", params)
if err != nil {
return nil, err
} else {
httpreq.SetContext(ctx)
}
result := &DescribeCertificatesResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}
@@ -1,50 +0,0 @@
package kcm
import (
"context"
"net/http"
)
type ListUserCertificatesRequest struct {
Page *int32 `json:"Page,omitempty"`
PageSize *int32 `json:"PageSize,omitempty"`
}
type ListUserCertificatesResponse struct {
sdkResponseBase
Success bool `json:"Success"`
Ret *struct {
Certs []*UserCertificate `json:"Certs"`
} `json:"Ret,omitempty"`
}
func (c *Client) ListUserCertificates(req *ListUserCertificatesRequest) (*ListUserCertificatesResponse, error) {
return c.ListUserCertificatesWithContext(context.Background(), req)
}
func (c *Client) ListUserCertificatesWithContext(ctx context.Context, req *ListUserCertificatesRequest) (*ListUserCertificatesResponse, error) {
params := &struct {
ListUserCertificatesRequest `json:",inline"`
Action string
Version string
}{
ListUserCertificatesRequest: *req,
Action: "ListUserCertificates",
Version: "2016-03-04",
}
httpreq, err := c.newRequest(http.MethodGet, "/", params)
if err != nil {
return nil, err
} else {
httpreq.SetContext(ctx)
}
result := &ListUserCertificatesResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}
@@ -1,60 +0,0 @@
package kcm
import (
"context"
"net/http"
qs "github.com/google/go-querystring/query"
)
type ModifyCertificateRequest struct {
Region *string `json:"Region,omitempty" url:"Region,omitempty"`
CertificateId *string `json:"CertificateId,omitempty" url:"CertificateId,omitempty"`
CertificateName *string `json:"CertificateName,omitempty" url:"CertificateName,omitempty"`
Description *string `json:"Description,omitempty" url:"Description,omitempty"`
PublicKey *string `json:"PublicKey,omitempty" url:"-"`
PrivateKey *string `json:"PrivateKey,omitempty" url:"-"`
SSLCertificateId *string `json:"SslCertificateId,omitempty" url:"SslCertificateId,omitempty"`
}
type ModifyCertificateResponse struct {
sdkResponseBase
Certificate *LBCertificate `json:"Certificate,omitempty"`
}
func (c *Client) ModifyCertificate(req *ModifyCertificateRequest) (*ModifyCertificateResponse, error) {
return c.ModifyCertificateWithContext(context.Background(), req)
}
func (c *Client) ModifyCertificateWithContext(ctx context.Context, req *ModifyCertificateRequest) (*ModifyCertificateResponse, error) {
params := &struct {
ModifyCertificateRequest `json:",inline"`
Action string
Version string
}{
ModifyCertificateRequest: *req,
Action: "ModifyCertificate",
Version: "2016-03-04",
}
httpreq, err := c.newRequest(http.MethodPost, "/", params)
if err != nil {
return nil, err
} else {
values, err := qs.Values(req)
if err != nil {
return nil, err
}
httpreq.SetQueryParamsFromValues(values)
httpreq.SetContext(ctx)
}
result := &ModifyCertificateResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}
@@ -1,50 +0,0 @@
package kcm
import (
"context"
"net/http"
)
type UploadCertificateRequest struct {
ProjectId *int64 `json:"ProjectId,omitempty"`
CertName *string `json:"CertName,omitempty"`
CertFile *string `json:"CertFile,omitempty"`
CertKey *string `json:"CertKey,omitempty"`
}
type UploadCertificateResponse struct {
sdkResponseBase
Success bool `json:"Success"`
Ret *UserCertificate `json:"Ret,omitempty"`
}
func (c *Client) UploadCertificate(req *UploadCertificateRequest) (*UploadCertificateResponse, error) {
return c.UploadCertificateWithContext(context.Background(), req)
}
func (c *Client) UploadCertificateWithContext(ctx context.Context, req *UploadCertificateRequest) (*UploadCertificateResponse, error) {
params := &struct {
UploadCertificateRequest `json:",inline"`
Action string
Version string
}{
UploadCertificateRequest: *req,
Action: "UploadCertificate",
Version: "2016-03-04",
}
httpreq, err := c.newRequest(http.MethodPost, "/", params)
if err != nil {
return nil, err
} else {
httpreq.SetContext(ctx)
}
result := &UploadCertificateResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}
-54
View File
@@ -1,54 +0,0 @@
// A simple SDK client for KingsoftCloud KCM.
// API documentation: https://apiexplorer.ksyun.com/#/api/home
package kcm
import (
"fmt"
"time"
"github.com/go-resty/resty/v2"
common "github.com/certimate-go/certimate/pkg/sdk3rd/ksyun/zz-shared-common"
)
const (
service = "kcm"
endpoint = "https://" + service + ".api.ksyun.com"
)
type Client struct {
client *common.Client
}
func NewClient(optFns ...common.OptionsFunc) (*Client, error) {
client, err := common.NewClient(endpoint, service, optFns...)
if err != nil {
return nil, err
}
return &Client{client: client}, nil
}
func (c *Client) SetTimeout(timeout time.Duration) *Client {
c.client.SetTimeout(timeout)
return c
}
func (c *Client) newRequest(method string, path string, params any) (*resty.Request, error) {
return c.client.NewRequest(method, path, params)
}
func (c *Client) doRequest(req *resty.Request) (*resty.Response, error) {
return c.client.DoRequest(req)
}
func (c *Client) doRequestWithResult(req *resty.Request, res sdkResponse) (*resty.Response, error) {
resp, err := c.client.DoRequestWithResult(req, res)
if err == nil {
if err := res.GetAPIError(); err != nil {
return resp, fmt.Errorf("sdkerr: api error: %s", err.Error())
}
}
return resp, err
}
-32
View File
@@ -1,32 +0,0 @@
package kcm
type UserCertificate struct {
CertId string `json:"CertID"`
CertName string `json:"CertName"`
MainDomain string `json:"MainDomain"`
Domains []string `json:"Domains"`
AdditionalDomains []string `json:"AdditionalDomains"`
Brand string `json:"Brand"`
CA string `json:"CA"`
Level string `json:"Level"`
FingerPrint string `json:"FingerPrint"`
DomainCount int32 `json:"DomainCount"`
WildcardCount int32 `json:"WildcardCount"`
IssueTime string `json:"IssueTime"`
ExpireTime string `json:"ExpireTime"`
Source string `json:"Source"`
}
type LBCertificate struct {
CertificateId string `json:"CertificateId"`
CertificateName string `json:"CertificateName"`
CommonName string `json:"CommonName"`
CertAuthority string `json:"CertAuthority"`
CertType string `json:"CertType"`
CertificateType string `json:"CertificateType"`
PublicKey string `json:"PublicKey"`
ExpireTime string `json:"ExpireTime"`
CreateTime string `json:"CreateTime"`
Source string `json:"Source"`
SSLCertificateId string `json:"SslCertificateId,omitempty"`
}
-9
View File
@@ -1,9 +0,0 @@
package kcm
import (
common "github.com/certimate-go/certimate/pkg/sdk3rd/ksyun/zz-shared-common"
)
func WithAkSk(ak, sk string) common.OptionsFunc {
return common.WithAkSk(ak, sk)
}
-32
View File
@@ -1,32 +0,0 @@
package kcm
import (
"fmt"
)
type sdkResponse interface {
GetAPIError() error
}
type sdkResponseBase struct {
RequestId *string `json:"RequestId,omitempty"`
Error *sdkAPIError `json:"Error,omitempty"`
}
type sdkAPIError struct {
Code string `json:"Code"`
Message string `json:"Message"`
}
func (e sdkAPIError) Error() string {
return fmt.Sprintf("[%s] %s ", e.Code, e.Message)
}
func (r *sdkResponseBase) GetAPIError() error {
if r.Error != nil {
return r.Error
}
return nil
}
var _ sdkResponse = (*sdkResponseBase)(nil)
-147
View File
@@ -1,147 +0,0 @@
package common
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"reflect"
"strings"
"time"
"github.com/go-resty/resty/v2"
"github.com/certimate-go/certimate/internal/app"
)
type Client struct {
rc *resty.Client
}
func NewClient(baseUrl string, service string, optFns ...OptionsFunc) (*Client, error) {
options := &Options{}
for _, fn := range optFns {
fn(options)
}
if baseUrl == "" {
return nil, fmt.Errorf("sdkerr: unset baseUrl")
}
if _, err := url.Parse(baseUrl); err != nil {
return nil, fmt.Errorf("sdkerr: invalid baseUrl: %w", err)
}
if service == "" {
return nil, fmt.Errorf("sdkerr: unset service")
}
if options.AccessKeyId == "" {
return nil, fmt.Errorf("sdkerr: unset accessKeyId")
}
if options.SecretAccessKey == "" {
return nil, fmt.Errorf("sdkerr: unset secretAccessKey")
}
signer := NewSigner(options.AccessKeyId, options.SecretAccessKey, service)
httper := resty.New().
SetBaseURL(baseUrl).
SetHeader("Accept", "application/json").
SetHeader("Content-Type", "application/json").
SetHeader("User-Agent", app.AppUserAgent).
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
if err := signer.Sign(req); err != nil {
return fmt.Errorf("sdkerr: sign error: %w", err)
}
return nil
})
return &Client{rc: httper}, nil
}
func (c *Client) SetTimeout(timeout time.Duration) *Client {
c.rc.SetTimeout(timeout)
return c
}
func (c *Client) NewRequest(method string, path string, params any) (*resty.Request, error) {
if method == "" {
return nil, fmt.Errorf("sdkerr: unset method")
}
if path == "" {
return nil, fmt.Errorf("sdkerr: unset path")
}
paramsMap := make(map[string]string)
if params != nil {
temp := make(map[string]any)
jsonb, _ := json.Marshal(params)
json.Unmarshal(jsonb, &temp)
for k, v := range temp {
if v == nil {
continue
}
switch reflect.Indirect(reflect.ValueOf(v)).Kind() {
case reflect.String:
paramsMap[k] = v.(string)
case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
paramsMap[k] = fmt.Sprintf("%v", v)
default:
jsonb, _ := json.Marshal(v)
paramsMap[k] = string(jsonb)
}
}
}
req := c.rc.R()
req.Method = method
req.URL = path
if strings.ToUpper(method) == http.MethodGet {
req.SetQueryParams(paramsMap)
} else {
req.SetBody(paramsMap)
}
// WARN:
// DO NOT CALL `req.SetBody` or `req.SetFormData` AGAIN! USE `newRequest` INSTEAD.
// DO NOT CALL `req.SetResult` or `req.SetError` AGAIN! USE `doRequestWithResult` INSTEAD.
return req, nil
}
func (c *Client) DoRequest(req *resty.Request) (*resty.Response, error) {
if req == nil {
return nil, fmt.Errorf("sdkerr: nil request")
}
resp, err := req.Send()
if err != nil {
return resp, fmt.Errorf("sdkerr: failed to send request: %w", err)
} else if resp.IsError() {
return resp, fmt.Errorf("sdkerr: unexpected status code: %d (resp: %s)", resp.StatusCode(), resp.String())
}
return resp, nil
}
func (c *Client) DoRequestWithResult(req *resty.Request, res any) (*resty.Response, error) {
if req == nil {
return nil, fmt.Errorf("sdkerr: nil request")
}
resp, err := c.DoRequest(req)
if err != nil {
if resp != nil {
json.Unmarshal(resp.Body(), &res)
}
return resp, err
}
if len(resp.Body()) != 0 {
if err := json.Unmarshal(resp.Body(), &res); err != nil {
return resp, fmt.Errorf("sdkerr: failed to unmarshal response: %w (resp: %s)", err, resp.String())
}
}
return resp, nil
}
@@ -1,15 +0,0 @@
package common
type Options struct {
AccessKeyId string
SecretAccessKey string
}
type OptionsFunc func(*Options)
func WithAkSk(ak, sk string) OptionsFunc {
return func(o *Options) {
o.AccessKeyId = ak
o.SecretAccessKey = sk
}
}
-133
View File
@@ -1,133 +0,0 @@
package common
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"io"
"net/http"
"net/url"
"sort"
"strings"
"time"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
type signer struct {
accessKeyId string
secretAccessKey string
service string
}
func NewSigner(ak, sk, service string) *signer {
return &signer{
accessKeyId: ak,
secretAccessKey: sk,
service: service,
}
}
func (s *signer) Sign(req *http.Request) error {
// API 签名机制:
// https://docs.ksyun.com/documents/40298
method := strings.ToUpper(req.Method)
query := url.Values{}
if req.URL != nil {
query = req.URL.Query()
}
payload := make(map[string]string)
if method != http.MethodGet && req.Body != nil {
payloadb, err := io.ReadAll(req.Body)
if err != nil {
return err
}
if err := json.Unmarshal(payloadb, &payload); err != nil {
return err
}
req.Body = io.NopCloser(bytes.NewReader(payloadb))
}
params := make(map[string]string)
for k := range query {
params[k] = query.Get(k)
}
for k, v := range payload {
params[k] = v
}
nowUtc := time.Now().UTC()
timestamp := nowUtc.Format("2006-01-02T15:04:05Z")
params["Accesskey"] = s.accessKeyId
params["Service"] = s.service
params["Timestamp"] = timestamp
params["SignatureVersion"] = "1.0"
params["SignatureMethod"] = "HMAC-SHA256"
paramsKeys := xmaps.Keys(params)
sort.Strings(paramsKeys)
stringToSign := ""
for i, k := range paramsKeys {
if i > 0 {
stringToSign += "&"
}
stringToSign += escapeQuery(k) + "=" + escapeQuery(params[k])
}
h := hmac.New(sha256.New, []byte(s.secretAccessKey))
h.Write([]byte(stringToSign))
signature := strings.ToLower(hex.EncodeToString(h.Sum(nil)))
if method == http.MethodGet {
query.Set("Accesskey", params["Accesskey"])
query.Set("Service", params["Service"])
query.Set("Timestamp", params["Timestamp"])
query.Set("SignatureVersion", params["SignatureVersion"])
query.Set("SignatureMethod", params["SignatureMethod"])
req.URL.RawQuery = query.Encode() + "&Signature=" + signature
} else {
if _, ok := payload["Action"]; ok {
query.Set("Action", payload["Action"])
delete(payload, "Action")
}
if _, ok := payload["Version"]; ok {
query.Set("Version", payload["Version"])
delete(payload, "Version")
}
payload["Accesskey"] = params["Accesskey"]
payload["Service"] = params["Service"]
payload["Timestamp"] = params["Timestamp"]
payload["SignatureVersion"] = params["SignatureVersion"]
payload["SignatureMethod"] = params["SignatureMethod"]
payload["Signature"] = signature
jsonb, err := json.Marshal(payload)
if err != nil {
return err
}
req.URL.RawQuery = query.Encode()
req.Body = io.NopCloser(bytes.NewReader(jsonb))
req.ContentLength = int64(len(jsonb))
}
return nil
}
func escapeQuery(str string) string {
res := url.QueryEscape(str)
res = strings.ReplaceAll(res, "+", "%20")
return res
}
@@ -1,36 +0,0 @@
package cdn
import (
"context"
"net/http"
)
type BatchUpdateCertificateConfigRequest struct {
CertificateId int64 `json:"certificateId"`
DomainNames []string `json:"domainNames"`
}
type BatchUpdateCertificateConfigResponse struct {
sdkResponseBase
}
func (c *Client) BatchUpdateCertificateConfig(req *BatchUpdateCertificateConfigRequest) (*BatchUpdateCertificateConfigResponse, error) {
return c.BatchUpdateCertificateConfigWithContext(context.Background(), req)
}
func (c *Client) BatchUpdateCertificateConfigWithContext(ctx context.Context, req *BatchUpdateCertificateConfigRequest) (*BatchUpdateCertificateConfigResponse, error) {
httpreq, err := c.newRequest(http.MethodPut, "/api/config/certificate/batch")
if err != nil {
return nil, err
} else {
httpreq.SetBody(req)
httpreq.SetContext(ctx)
}
result := &BatchUpdateCertificateConfigResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}
-49
View File
@@ -1,49 +0,0 @@
// A simple SDK client for WangsuCloud CDN.
// API documentation: https://www.wangsu.com/document/api-doc/product
package cdn
import (
"fmt"
"time"
"github.com/go-resty/resty/v2"
common "github.com/certimate-go/certimate/pkg/sdk3rd/wangsu/zz-shared-common"
)
type Client struct {
client *common.Client
}
func NewClient(optFns ...common.OptionsFunc) (*Client, error) {
client, err := common.NewClient(optFns...)
if err != nil {
return nil, err
}
return &Client{client: client}, nil
}
func (c *Client) SetTimeout(timeout time.Duration) *Client {
c.client.SetTimeout(timeout)
return c
}
func (c *Client) newRequest(method string, path string) (*resty.Request, error) {
return c.client.NewRequest(method, path)
}
func (c *Client) doRequest(req *resty.Request) (*resty.Response, error) {
return c.client.DoRequest(req)
}
func (c *Client) doRequestWithResult(req *resty.Request, res sdkResponse) (*resty.Response, error) {
resp, err := c.client.DoRequestWithResult(req, res)
if err == nil {
if rCode := res.GetCode(); rCode != "" && rCode != "0" {
return resp, fmt.Errorf("sdkerr: api error: code='%s', message='%s'", rCode, res.GetMessage())
}
}
return resp, err
}
-9
View File
@@ -1,9 +0,0 @@
package cdn
import (
common "github.com/certimate-go/certimate/pkg/sdk3rd/wangsu/zz-shared-common"
)
func WithAkSk(ak, sk string) common.OptionsFunc {
return common.WithAkSk(ak, sk)
}
-29
View File
@@ -1,29 +0,0 @@
package cdn
type sdkResponse interface {
GetCode() string
GetMessage() string
}
type sdkResponseBase struct {
Code *string `json:"code,omitempty"`
Message *string `json:"message,omitempty"`
}
var _ sdkResponse = (*sdkResponseBase)(nil)
func (r *sdkResponseBase) GetCode() string {
if r.Code == nil {
return ""
}
return *r.Code
}
func (r *sdkResponseBase) GetMessage() string {
if r.Message == nil {
return ""
}
return *r.Message
}
@@ -1,46 +0,0 @@
package cdnpro
import (
"context"
"fmt"
"net/http"
)
type CreateCertificateRequest struct {
Timestamp int64 `json:"-"`
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
AutoRenew *string `json:"autoRenew,omitempty"`
ForceRenew *bool `json:"forceRenew,omitempty"`
NewVersion *CertificateVersion `json:"newVersion,omitempty"`
}
type CreateCertificateResponse struct {
sdkResponseBase
CertificateLocation string `json:"location,omitempty"`
}
func (c *Client) CreateCertificate(req *CreateCertificateRequest) (*CreateCertificateResponse, error) {
return c.CreateCertificateWithContext(context.Background(), req)
}
func (c *Client) CreateCertificateWithContext(ctx context.Context, req *CreateCertificateRequest) (*CreateCertificateResponse, error) {
httpreq, err := c.newRequest(http.MethodPost, "/cdn/certificates")
if err != nil {
return nil, err
} else {
httpreq.SetBody(req)
httpreq.SetHeader("X-CNC-Timestamp", fmt.Sprintf("%d", req.Timestamp))
httpreq.SetContext(ctx)
}
result := &CreateCertificateResponse{}
if httpresp, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
} else {
result.CertificateLocation = httpresp.Header().Get("Location")
}
return result, nil
}
@@ -1,42 +0,0 @@
package cdnpro
import (
"context"
"net/http"
)
type CreateDeploymentTaskRequest struct {
Name *string `json:"name,omitempty"`
Target *string `json:"target,omitempty"`
Actions *[]DeploymentTaskAction `json:"actions,omitempty"`
Webhook *string `json:"webhook,omitempty"`
}
type CreateDeploymentTaskResponse struct {
sdkResponseBase
DeploymentTaskLocation string `json:"location,omitempty"`
}
func (c *Client) CreateDeploymentTask(req *CreateDeploymentTaskRequest) (*CreateDeploymentTaskResponse, error) {
return c.CreateDeploymentTaskWithContext(context.Background(), req)
}
func (c *Client) CreateDeploymentTaskWithContext(ctx context.Context, req *CreateDeploymentTaskRequest) (*CreateDeploymentTaskResponse, error) {
httpreq, err := c.newRequest(http.MethodPost, "/cdn/deploymentTasks")
if err != nil {
return nil, err
} else {
httpreq.SetBody(req)
httpreq.SetContext(ctx)
}
result := &CreateDeploymentTaskResponse{}
if httpresp, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
} else {
result.DeploymentTaskLocation = httpresp.Header().Get("Location")
}
return result, nil
}
@@ -1,45 +0,0 @@
package cdnpro
import (
"context"
"fmt"
"net/http"
"net/url"
)
type GetDeploymentTaskDetailResponse struct {
sdkResponseBase
Name string `json:"name"`
Target string `json:"target"`
Actions []DeploymentTaskAction `json:"actions"`
Status string `json:"status"`
StatusDetails string `json:"statusDetails"`
SubmissionTime string `json:"submissionTime"`
FinishTime string `json:"finishTime"`
ApiRequestId string `json:"apiRequestId"`
}
func (c *Client) GetDeploymentTaskDetail(deploymentTaskId string) (*GetDeploymentTaskDetailResponse, error) {
return c.GetDeploymentTaskDetailWithContext(context.Background(), deploymentTaskId)
}
func (c *Client) GetDeploymentTaskDetailWithContext(ctx context.Context, deploymentTaskId string) (*GetDeploymentTaskDetailResponse, error) {
if deploymentTaskId == "" {
return nil, fmt.Errorf("sdkerr: bad request: unset deploymentTaskId")
}
httpreq, err := c.newRequest(http.MethodGet, fmt.Sprintf("/cdn/deploymentTasks/%s", url.PathEscape(deploymentTaskId)))
if err != nil {
return nil, err
} else {
httpreq.SetContext(ctx)
}
result := &GetDeploymentTaskDetailResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}
@@ -1,40 +0,0 @@
package cdnpro
import (
"context"
"fmt"
"net/http"
"net/url"
)
type GetHostnameDetailResponse struct {
sdkResponseBase
Hostname string `json:"hostname"`
PropertyInProduction *HostnameProperty `json:"propertyInProduction,omitempty"`
PropertyInStaging *HostnameProperty `json:"propertyInStaging,omitempty"`
}
func (c *Client) GetHostnameDetail(hostname string) (*GetHostnameDetailResponse, error) {
return c.GetHostnameDetailWithContext(context.Background(), hostname)
}
func (c *Client) GetHostnameDetailWithContext(ctx context.Context, hostname string) (*GetHostnameDetailResponse, error) {
if hostname == "" {
return nil, fmt.Errorf("sdkerr: bad request: unset hostname")
}
httpreq, err := c.newRequest(http.MethodGet, fmt.Sprintf("/cdn/hostnames/%s", url.PathEscape(hostname)))
if err != nil {
return nil, err
} else {
httpreq.SetContext(ctx)
}
result := &GetHostnameDetailResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}
@@ -1,51 +0,0 @@
package cdnpro
import (
"context"
"fmt"
"net/http"
"net/url"
)
type UpdateCertificateRequest struct {
Timestamp int64 `json:"-"`
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
AutoRenew *string `json:"autoRenew,omitempty"`
ForceRenew *bool `json:"forceRenew,omitempty"`
NewVersion *CertificateVersion `json:"newVersion,omitempty"`
}
type UpdateCertificateResponse struct {
sdkResponseBase
CertificateLocation string `json:"location,omitempty"`
}
func (c *Client) UpdateCertificate(certificateId string, req *UpdateCertificateRequest) (*UpdateCertificateResponse, error) {
return c.UpdateCertificateWithContext(context.Background(), certificateId, req)
}
func (c *Client) UpdateCertificateWithContext(ctx context.Context, certificateId string, req *UpdateCertificateRequest) (*UpdateCertificateResponse, error) {
if certificateId == "" {
return nil, fmt.Errorf("sdkerr: bad request: unset certificateId")
}
httpreq, err := c.newRequest(http.MethodPatch, fmt.Sprintf("/cdn/certificates/%s", url.PathEscape(certificateId)))
if err != nil {
return nil, err
} else {
httpreq.SetBody(req)
httpreq.SetHeader("X-CNC-Timestamp", fmt.Sprintf("%d", req.Timestamp))
httpreq.SetContext(ctx)
}
result := &UpdateCertificateResponse{}
if httpresp, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
} else {
result.CertificateLocation = httpresp.Header().Get("Location")
}
return result, nil
}
-41
View File
@@ -1,41 +0,0 @@
// A simple SDK client for WangsuCloud CDN.
// API documentation: https://www.wangsu.com/document/api-doc/product
package cdnpro
import (
"time"
"github.com/go-resty/resty/v2"
common "github.com/certimate-go/certimate/pkg/sdk3rd/wangsu/zz-shared-common"
)
type Client struct {
client *common.Client
}
func NewClient(optFns ...common.OptionsFunc) (*Client, error) {
client, err := common.NewClient(optFns...)
if err != nil {
return nil, err
}
return &Client{client: client}, nil
}
func (c *Client) SetTimeout(timeout time.Duration) *Client {
c.client.SetTimeout(timeout)
return c
}
func (c *Client) newRequest(method string, path string) (*resty.Request, error) {
return c.client.NewRequest(method, path)
}
func (c *Client) doRequest(req *resty.Request) (*resty.Response, error) {
return c.client.DoRequest(req)
}
func (c *Client) doRequestWithResult(req *resty.Request, res sdkResponse) (*resty.Response, error) {
return c.client.DoRequestWithResult(req, res)
}
-33
View File
@@ -1,33 +0,0 @@
package cdnpro
type CertificateVersion struct {
Comments *string `json:"comments,omitempty"`
PrivateKey *string `json:"privateKey,omitempty"`
Certificate *string `json:"certificate,omitempty"`
ChainCert *string `json:"chainCert,omitempty"`
IdentificationInfo *CertificateVersionIdentificationInfo `json:"identificationInfo,omitempty"`
}
type CertificateVersionIdentificationInfo struct {
Country *string `json:"country,omitempty"`
State *string `json:"state,omitempty"`
City *string `json:"city,omitempty"`
Company *string `json:"company,omitempty"`
Department *string `json:"department,omitempty"`
CommonName *string `json:"commonName,omitempty"`
Email *string `json:"email,omitempty"`
SubjectAlternativeNames []*string `json:"subjectAlternativeNames,omitempty"`
}
type HostnameProperty struct {
PropertyId string `json:"propertyId"`
Version int32 `json:"version"`
CertificateId *string `json:"certificateId,omitempty"`
}
type DeploymentTaskAction struct {
Action *string `json:"action,omitempty"`
PropertyId *string `json:"propertyId,omitempty"`
CertificateId *string `json:"certificateId,omitempty"`
Version *int32 `json:"version,omitempty"`
}
-9
View File
@@ -1,9 +0,0 @@
package cdnpro
import (
common "github.com/certimate-go/certimate/pkg/sdk3rd/wangsu/zz-shared-common"
)
func WithAkSk(ak, sk string) common.OptionsFunc {
return common.WithAkSk(ak, sk)
}
-29
View File
@@ -1,29 +0,0 @@
package cdnpro
type sdkResponse interface {
GetCode() string
GetMessage() string
}
type sdkResponseBase struct {
Code *string `json:"code,omitempty"`
Message *string `json:"message,omitempty"`
}
var _ sdkResponse = (*sdkResponseBase)(nil)
func (r *sdkResponseBase) GetCode() string {
if r.Code == nil {
return ""
}
return *r.Code
}
func (r *sdkResponseBase) GetMessage() string {
if r.Message == nil {
return ""
}
return *r.Message
}
@@ -1,42 +0,0 @@
package certificate
import (
"context"
"net/http"
)
type CreateCertificateRequest struct {
Name *string `json:"name,omitempty"`
Certificate *string `json:"certificate,omitempty"`
PrivateKey *string `json:"privateKey,omitempty"`
Comment *string `json:"comment,omitempty" `
}
type CreateCertificateResponse struct {
sdkResponseBase
CertificateLocation string `json:"location,omitempty"`
}
func (c *Client) CreateCertificate(req *CreateCertificateRequest) (*CreateCertificateResponse, error) {
return c.CreateCertificateWithContext(context.Background(), req)
}
func (c *Client) CreateCertificateWithContext(ctx context.Context, req *CreateCertificateRequest) (*CreateCertificateResponse, error) {
httpreq, err := c.newRequest(http.MethodPost, "/api/certificate")
if err != nil {
return nil, err
} else {
httpreq.SetBody(req)
httpreq.SetContext(ctx)
}
result := &CreateCertificateResponse{}
if httpresp, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
} else {
result.CertificateLocation = httpresp.Header().Get("Location")
}
return result, nil
}
@@ -1,32 +0,0 @@
package certificate
import (
"context"
"net/http"
)
type ListCertificatesResponse struct {
sdkResponseBase
Certificates []*Certificate `json:"ssl-certificates,omitempty"`
}
func (c *Client) ListCertificates() (*ListCertificatesResponse, error) {
return c.ListCertificatesWithContext(context.Background())
}
func (c *Client) ListCertificatesWithContext(ctx context.Context) (*ListCertificatesResponse, error) {
httpreq, err := c.newRequest(http.MethodGet, "/api/ssl/certificate")
if err != nil {
return nil, err
} else {
httpreq.SetContext(ctx)
}
result := &ListCertificatesResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}
@@ -1,44 +0,0 @@
package certificate
import (
"context"
"fmt"
"net/http"
"net/url"
)
type UpdateCertificateRequest struct {
Name *string `json:"name,omitempty"`
Certificate *string `json:"certificate,omitempty"`
PrivateKey *string `json:"privateKey,omitempty"`
Comment *string `json:"comment,omitempty" `
}
type UpdateCertificateResponse struct {
sdkResponseBase
}
func (c *Client) UpdateCertificate(certificateId string, req *UpdateCertificateRequest) (*UpdateCertificateResponse, error) {
return c.UpdateCertificateWithContext(context.Background(), certificateId, req)
}
func (c *Client) UpdateCertificateWithContext(ctx context.Context, certificateId string, req *UpdateCertificateRequest) (*UpdateCertificateResponse, error) {
if certificateId == "" {
return nil, fmt.Errorf("sdkerr: bad request: unset certificateId")
}
httpreq, err := c.newRequest(http.MethodPut, fmt.Sprintf("/api/certificate/%s", url.PathEscape(certificateId)))
if err != nil {
return nil, err
} else {
httpreq.SetBody(req)
httpreq.SetContext(ctx)
}
result := &UpdateCertificateResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}
-41
View File
@@ -1,41 +0,0 @@
// A simple SDK client for WangsuCloud CDN.
// API documentation: https://www.wangsu.com/document/api-doc/product
package certificate
import (
"time"
"github.com/go-resty/resty/v2"
common "github.com/certimate-go/certimate/pkg/sdk3rd/wangsu/zz-shared-common"
)
type Client struct {
client *common.Client
}
func NewClient(optFns ...common.OptionsFunc) (*Client, error) {
client, err := common.NewClient(optFns...)
if err != nil {
return nil, err
}
return &Client{client: client}, nil
}
func (c *Client) SetTimeout(timeout time.Duration) *Client {
c.client.SetTimeout(timeout)
return c
}
func (c *Client) newRequest(method string, path string) (*resty.Request, error) {
return c.client.NewRequest(method, path)
}
func (c *Client) doRequest(req *resty.Request) (*resty.Response, error) {
return c.client.DoRequest(req)
}
func (c *Client) doRequestWithResult(req *resty.Request, res sdkResponse) (*resty.Response, error) {
return c.client.DoRequestWithResult(req, res)
}
-10
View File
@@ -1,10 +0,0 @@
package certificate
type Certificate struct {
Id string `json:"certificate-id"`
Name string `json:"name"`
Comment string `json:"comment"`
ValidityFrom string `json:"certificate-validity-from"`
ValidityTo string `json:"certificate-validity-to"`
Serial string `json:"certificate-serial"`
}
-9
View File
@@ -1,9 +0,0 @@
package certificate
import (
common "github.com/certimate-go/certimate/pkg/sdk3rd/wangsu/zz-shared-common"
)
func WithAkSk(ak, sk string) common.OptionsFunc {
return common.WithAkSk(ak, sk)
}
-29
View File
@@ -1,29 +0,0 @@
package certificate
type sdkResponse interface {
GetCode() string
GetMessage() string
}
type sdkResponseBase struct {
Code *string `json:"code,omitempty"`
Message *string `json:"message,omitempty"`
}
var _ sdkResponse = (*sdkResponseBase)(nil)
func (r *sdkResponseBase) GetCode() string {
if r.Code == nil {
return ""
}
return *r.Code
}
func (r *sdkResponseBase) GetMessage() string {
if r.Message == nil {
return ""
}
return *r.Message
}
@@ -1,105 +0,0 @@
package common
import (
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/go-resty/resty/v2"
"github.com/certimate-go/certimate/internal/app"
)
type Client struct {
rc *resty.Client
}
func NewClient(optFns ...OptionsFunc) (*Client, error) {
options := &Options{}
for _, fn := range optFns {
fn(options)
}
if options.AccessKey == "" {
return nil, fmt.Errorf("sdkerr: unset accessKey")
}
if options.SecretKey == "" {
return nil, fmt.Errorf("sdkerr: unset secretKey")
}
signer := NewSigner(options.AccessKey, options.SecretKey)
httper := resty.New().
SetBaseURL("https://open.chinanetcenter.com").
SetHeader("Accept", "application/json").
SetHeader("Content-Type", "application/json").
SetHeader("User-Agent", app.AppUserAgent).
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
if err := signer.Sign(req); err != nil {
return fmt.Errorf("sdkerr: sign error: %w", err)
}
return nil
})
return &Client{rc: httper}, nil
}
func (c *Client) SetTimeout(timeout time.Duration) *Client {
c.rc.SetTimeout(timeout)
return c
}
func (c *Client) NewRequest(method string, path string) (*resty.Request, error) {
if method == "" {
return nil, fmt.Errorf("sdkerr: unset method")
}
if path == "" {
return nil, fmt.Errorf("sdkerr: unset path")
}
req := c.rc.R()
req.Method = method
req.URL = path
// WARN:
// DO NOT CALL `req.SetResult` or `req.SetError` AGAIN! USE `doRequestWithResult` INSTEAD.
return req, nil
}
func (c *Client) DoRequest(req *resty.Request) (*resty.Response, error) {
if req == nil {
return nil, fmt.Errorf("sdkerr: nil request")
}
resp, err := req.Send()
if err != nil {
return resp, fmt.Errorf("sdkerr: failed to send request: %w", err)
} else if resp.IsError() {
return resp, fmt.Errorf("sdkerr: unexpected status code: %d (resp: %s)", resp.StatusCode(), resp.String())
}
return resp, nil
}
func (c *Client) DoRequestWithResult(req *resty.Request, res any) (*resty.Response, error) {
if req == nil {
return nil, fmt.Errorf("sdkerr: nil request")
}
resp, err := c.DoRequest(req)
if err != nil {
if resp != nil {
json.Unmarshal(resp.Body(), &res)
}
return resp, err
}
if len(resp.Body()) != 0 {
if err := json.Unmarshal(resp.Body(), &res); err != nil {
return resp, fmt.Errorf("sdkerr: failed to unmarshal response: %w (resp: %s)", err, resp.String())
}
}
return resp, nil
}
@@ -1,15 +0,0 @@
package common
type Options struct {
AccessKey string
SecretKey string
}
type OptionsFunc func(*Options)
func WithAkSk(ak, sk string) OptionsFunc {
return func(o *Options) {
o.AccessKey = ak
o.SecretKey = sk
}
}
@@ -1,102 +0,0 @@
package common
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"strings"
"time"
)
type signer struct {
accessKey string
secretKey string
}
func NewSigner(ak, sk string) *signer {
return &signer{
accessKey: ak,
secretKey: sk,
}
}
func (s *signer) Sign(req *http.Request) error {
// API 签名机制:
// https://www.wangsu.com/document/openapi/api-authentication
method := strings.ToUpper(req.Method)
path := "/"
if req.URL != nil {
path = req.URL.Path
}
queryStr := ""
if method != http.MethodPost && req.URL != nil {
queryStr = req.URL.RawQuery
s, err := url.QueryUnescape(queryStr)
if err != nil {
return err
}
queryStr = s
}
canonicalHeaders := "" +
"content-type:" + strings.TrimSpace(strings.ToLower(req.Header.Get("Content-Type"))) + "\n" +
"host:" + strings.TrimSpace(strings.ToLower(req.Host)) + "\n"
signedHeaders := "content-type;host"
payloadStr := ""
if method != http.MethodGet && req.Body != nil {
payloadb, err := io.ReadAll(req.Body)
if err != nil {
return err
}
payloadStr = string(payloadb)
req.Body = io.NopCloser(bytes.NewReader(payloadb))
}
payloadHash := sha256.Sum256([]byte(payloadStr))
payloadHashHex := strings.ToLower(hex.EncodeToString(payloadHash[:]))
nowUtc := time.Now().UTC()
timestampStr := req.Header.Get("X-CNC-Timestamp")
if timestampStr == "" {
timestampStr = fmt.Sprintf("%d", nowUtc.Unix())
} else {
timestamp, err := strconv.ParseInt(timestampStr, 10, 64)
if err != nil {
return err
}
nowUtc = time.Unix(timestamp, 0).UTC()
}
canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", method, path, queryStr, canonicalHeaders, signedHeaders, payloadHashHex)
canonicalRequestHash := sha256.Sum256([]byte(canonicalRequest))
canonicalRequestHashHex := strings.ToLower(hex.EncodeToString(canonicalRequestHash[:]))
const signAlgorithmHeader = "CNC-HMAC-SHA256"
stringToSign := fmt.Sprintf("%s\n%s\n%s", signAlgorithmHeader, timestampStr, canonicalRequestHashHex)
h := hmac.New(sha256.New, []byte(s.secretKey))
h.Write([]byte(stringToSign))
signature := strings.ToLower(hex.EncodeToString(h.Sum(nil)))
authorization := fmt.Sprintf("%s Credential=%s, SignedHeaders=%s, Signature=%s", signAlgorithmHeader, s.accessKey, signedHeaders, signature)
req.Header.Set("Authorization", authorization)
req.Header.Set("Date", nowUtc.Format(http.TimeFormat))
req.Header.Set("X-CNC-Auth-Method", "AKSK")
req.Header.Set("X-CNC-AccessKey", s.accessKey)
req.Header.Set("X-CNC-Timestamp", timestampStr)
return nil
}
@@ -18,7 +18,6 @@ import AccessConfigFieldsProviderAWS from "./AccessConfigFieldsProviderAWS";
import AccessConfigFieldsProviderAxisNow from "./AccessConfigFieldsProviderAxisNow";
import AccessConfigFieldsProviderAzure from "./AccessConfigFieldsProviderAzure";
import AccessConfigFieldsProviderBaiduCloud from "./AccessConfigFieldsProviderBaiduCloud";
import AccessConfigFieldsProviderBaishan from "./AccessConfigFieldsProviderBaishan";
import AccessConfigFieldsProviderBaotaPanel from "./AccessConfigFieldsProviderBaotaPanel";
import AccessConfigFieldsProviderBaotaPanelGo from "./AccessConfigFieldsProviderBaotaPanelGo";
import AccessConfigFieldsProviderBaotaWAF from "./AccessConfigFieldsProviderBaotaWAF";
@@ -69,7 +68,6 @@ import AccessConfigFieldsProviderInfomaniak from "./AccessConfigFieldsProviderIn
import AccessConfigFieldsProviderIONOS from "./AccessConfigFieldsProviderIONOS";
import AccessConfigFieldsProviderJDCloud from "./AccessConfigFieldsProviderJDCloud";
import AccessConfigFieldsProviderKong from "./AccessConfigFieldsProviderKong";
import AccessConfigFieldsProviderKsyun from "./AccessConfigFieldsProviderKsyun";
import AccessConfigFieldsProviderLarkBot from "./AccessConfigFieldsProviderLarkBot";
import AccessConfigFieldsProviderLeCDN from "./AccessConfigFieldsProviderLeCDN";
import AccessConfigFieldsProviderLinode from "./AccessConfigFieldsProviderLinode";
@@ -115,7 +113,6 @@ import AccessConfigFieldsProviderUniCloud from "./AccessConfigFieldsProviderUniC
import AccessConfigFieldsProviderVercel from "./AccessConfigFieldsProviderVercel";
import AccessConfigFieldsProviderVolcEngine from "./AccessConfigFieldsProviderVolcEngine";
import AccessConfigFieldsProviderVultr from "./AccessConfigFieldsProviderVultr";
import AccessConfigFieldsProviderWangsu from "./AccessConfigFieldsProviderWangsu";
import AccessConfigFieldsProviderWebhook from "./AccessConfigFieldsProviderWebhook";
import AccessConfigFieldsProviderWeComBot from "./AccessConfigFieldsProviderWeComBot";
import AccessConfigFieldsProviderWestcn from "./AccessConfigFieldsProviderWestcn";
@@ -142,7 +139,6 @@ const providerComponentMap: Partial<Record<AccessProviderType, React.ComponentTy
[ACCESS_PROVIDERS.AXISNOW]: AccessConfigFieldsProviderAxisNow,
[ACCESS_PROVIDERS.AZURE]: AccessConfigFieldsProviderAzure,
[ACCESS_PROVIDERS.BAIDUCLOUD]: AccessConfigFieldsProviderBaiduCloud,
[ACCESS_PROVIDERS.BAISHAN]: AccessConfigFieldsProviderBaishan,
[ACCESS_PROVIDERS.BAOTAPANEL]: AccessConfigFieldsProviderBaotaPanel,
[ACCESS_PROVIDERS.BAOTAPANELGO]: AccessConfigFieldsProviderBaotaPanelGo,
[ACCESS_PROVIDERS.BAOTAWAF]: AccessConfigFieldsProviderBaotaWAF,
@@ -192,7 +188,6 @@ const providerComponentMap: Partial<Record<AccessProviderType, React.ComponentTy
[ACCESS_PROVIDERS.IONOS]: AccessConfigFieldsProviderIONOS,
[ACCESS_PROVIDERS.JDCLOUD]: AccessConfigFieldsProviderJDCloud,
[ACCESS_PROVIDERS.KONG]: AccessConfigFieldsProviderKong,
[ACCESS_PROVIDERS.KSYUN]: AccessConfigFieldsProviderKsyun,
[ACCESS_PROVIDERS.LARKBOT]: AccessConfigFieldsProviderLarkBot,
[ACCESS_PROVIDERS.LECDN]: AccessConfigFieldsProviderLeCDN,
[ACCESS_PROVIDERS.INFOMANIAK]: AccessConfigFieldsProviderInfomaniak,
@@ -239,7 +234,6 @@ const providerComponentMap: Partial<Record<AccessProviderType, React.ComponentTy
[ACCESS_PROVIDERS.VERCEL]: AccessConfigFieldsProviderVercel,
[ACCESS_PROVIDERS.VOLCENGINE]: AccessConfigFieldsProviderVolcEngine,
[ACCESS_PROVIDERS.VULTR]: AccessConfigFieldsProviderVultr,
[ACCESS_PROVIDERS.WANGSU]: AccessConfigFieldsProviderWangsu,
[ACCESS_PROVIDERS.WEBHOOK]: AccessConfigFieldsProviderWebhook,
[ACCESS_PROVIDERS.WECOMBOT]: AccessConfigFieldsProviderWeComBot,
[ACCESS_PROVIDERS.WESTCN]: AccessConfigFieldsProviderWestcn,
@@ -1,46 +0,0 @@
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 AccessConfigFormFieldsProviderBaishan = () => {
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, "apiToken"]} initialValue={initialValues.apiToken} label={t("access.form.baishan_api_token.label")} rules={[formRule]}>
<Input.Password autoComplete="new-password" placeholder={t("access.form.baishan_api_token.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
apiToken: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t: _ } = i18n;
return z.object({
apiToken: z.string().nonempty(),
});
};
const _default = Object.assign(AccessConfigFormFieldsProviderBaishan, {
getInitialValues,
getSchema,
});
export default _default;
@@ -1,64 +0,0 @@
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 AccessConfigFormFieldsProviderKsyun = () => {
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.ksyun_access_key_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ksyun_access_key_id.tooltip") }}></span>}
>
<Input autoComplete="new-password" placeholder={t("access.form.ksyun_access_key_id.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "secretAccessKey"]}
initialValue={initialValues.secretAccessKey}
label={t("access.form.ksyun_secret_access_key.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ksyun_secret_access_key.tooltip") }}></span>}
>
<Input.Password autoComplete="new-password" placeholder={t("access.form.ksyun_secret_access_key.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
accessKeyId: "",
secretAccessKey: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t: _ } = i18n;
return z.object({
accessKeyId: z.string().nonempty(),
secretAccessKey: z.string().nonempty(),
});
};
const _default = Object.assign(AccessConfigFormFieldsProviderKsyun, {
getInitialValues,
getSchema,
});
export default _default;
@@ -1,76 +0,0 @@
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 AccessConfigFormFieldsProviderWangsu = () => {
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.wangsu_access_key_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.wangsu_access_key_id.tooltip") }}></span>}
>
<Input autoComplete="new-password" placeholder={t("access.form.wangsu_access_key_id.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "accessKeySecret"]}
initialValue={initialValues.accessKeySecret}
label={t("access.form.wangsu_access_key_secret.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.wangsu_access_key_secret.tooltip") }}></span>}
>
<Input.Password autoComplete="new-password" placeholder={t("access.form.wangsu_access_key_secret.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "apiKey"]}
initialValue={initialValues.apiKey}
label={t("access.form.wangsu_api_key.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.wangsu_api_key.tooltip") }}></span>}
>
<Input.Password autoComplete="new-password" placeholder={t("access.form.wangsu_api_key.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
accessKeyId: "",
accessKeySecret: "",
apiKey: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t: _ } = i18n;
return z.object({
accessKeyId: z.string().nonempty(),
accessKeySecret: z.string().nonempty(),
apiKey: z.string().nonempty(),
});
};
const _default = Object.assign(AccessConfigFormFieldsProviderWangsu, {
getInitialValues,
getSchema,
});
export default _default;
@@ -34,7 +34,6 @@ import BizDeployNodeConfigFieldsProviderAzureKeyVault from "./BizDeployNodeConfi
import BizDeployNodeConfigFieldsProviderBaiduCloudAppBLB from "./BizDeployNodeConfigFieldsProviderBaiduCloudAppBLB";
import BizDeployNodeConfigFieldsProviderBaiduCloudBLB from "./BizDeployNodeConfigFieldsProviderBaiduCloudBLB";
import BizDeployNodeConfigFieldsProviderBaiduCloudCDN from "./BizDeployNodeConfigFieldsProviderBaiduCloudCDN";
import BizDeployNodeConfigFieldsProviderBaishanCDN from "./BizDeployNodeConfigFieldsProviderBaishanCDN";
import BizDeployNodeConfigFieldsProviderBaotaPanel from "./BizDeployNodeConfigFieldsProviderBaotaPanel";
import BizDeployNodeConfigFieldsProviderBaotaPanelConsole from "./BizDeployNodeConfigFieldsProviderBaotaPanelConsole";
import BizDeployNodeConfigFieldsProviderBaotaPanelGo from "./BizDeployNodeConfigFieldsProviderBaotaPanelGo";
@@ -80,8 +79,6 @@ import BizDeployNodeConfigFieldsProviderJDCloudLive from "./BizDeployNodeConfigF
import BizDeployNodeConfigFieldsProviderJDCloudVOD from "./BizDeployNodeConfigFieldsProviderJDCloudVOD";
import BizDeployNodeConfigFieldsProviderJDCloudWAF from "./BizDeployNodeConfigFieldsProviderJDCloudWAF";
import BizDeployNodeConfigFieldsProviderKong from "./BizDeployNodeConfigFieldsProviderKong";
import BizDeployNodeConfigFieldsProviderKsyunCDN from "./BizDeployNodeConfigFieldsProviderKsyunCDN";
import BizDeployNodeConfigFieldsProviderKsyunSLB from "./BizDeployNodeConfigFieldsProviderKsyunSLB";
import BizDeployNodeConfigFieldsProviderLeCDN from "./BizDeployNodeConfigFieldsProviderLeCDN";
import BizDeployNodeConfigFieldsProviderLinodeLOS from "./BizDeployNodeConfigFieldsProviderLinodeLOS";
import BizDeployNodeConfigFieldsProviderLocal from "./BizDeployNodeConfigFieldsProviderLocal";
@@ -135,9 +132,6 @@ import BizDeployNodeConfigFieldsProviderVolcEngineLive from "./BizDeployNodeConf
import BizDeployNodeConfigFieldsProviderVolcEngineTOS from "./BizDeployNodeConfigFieldsProviderVolcEngineTOS";
import BizDeployNodeConfigFieldsProviderVolcEngineVOD from "./BizDeployNodeConfigFieldsProviderVolcEngineVOD";
import BizDeployNodeConfigFieldsProviderVolcEngineWAF from "./BizDeployNodeConfigFieldsProviderVolcEngineWAF";
import BizDeployNodeConfigFieldsProviderWangsuCDN from "./BizDeployNodeConfigFieldsProviderWangsuCDN";
import BizDeployNodeConfigFieldsProviderWangsuCDNPro from "./BizDeployNodeConfigFieldsProviderWangsuCDNPro";
import BizDeployNodeConfigFieldsProviderWangsuCertificate from "./BizDeployNodeConfigFieldsProviderWangsuCertificate";
import BizDeployNodeConfigFieldsProviderWebhook from "./BizDeployNodeConfigFieldsProviderWebhook";
import BizDeployNodeConfigFieldsProviderYandexCloudCertificateManager from "./BizDeployNodeConfigFieldsProviderYandexCloudCertificateManager";
import SchemaConfigFields from "./SchemaConfigFields";
@@ -178,7 +172,6 @@ const providerComponentMap: Partial<Record<DeploymentProviderType, React.Compone
[DEPLOYMENT_PROVIDERS.BAIDUCLOUD_APPBLB]: BizDeployNodeConfigFieldsProviderBaiduCloudAppBLB,
[DEPLOYMENT_PROVIDERS.BAIDUCLOUD_BLB]: BizDeployNodeConfigFieldsProviderBaiduCloudBLB,
[DEPLOYMENT_PROVIDERS.BAIDUCLOUD_CDN]: BizDeployNodeConfigFieldsProviderBaiduCloudCDN,
[DEPLOYMENT_PROVIDERS.BAISHAN_CDN]: BizDeployNodeConfigFieldsProviderBaishanCDN,
[DEPLOYMENT_PROVIDERS.BAOTAPANEL_CONSOLE]: BizDeployNodeConfigFieldsProviderBaotaPanelConsole,
[DEPLOYMENT_PROVIDERS.BAOTAPANEL]: BizDeployNodeConfigFieldsProviderBaotaPanel,
[DEPLOYMENT_PROVIDERS.BAOTAPANELGO_CONSOLE]: BizDeployNodeConfigFieldsProviderBaotaPanelGoConsole,
@@ -224,8 +217,6 @@ const providerComponentMap: Partial<Record<DeploymentProviderType, React.Compone
[DEPLOYMENT_PROVIDERS.JDCLOUD_VOD]: BizDeployNodeConfigFieldsProviderJDCloudVOD,
[DEPLOYMENT_PROVIDERS.JDCLOUD_WAF]: BizDeployNodeConfigFieldsProviderJDCloudWAF,
[DEPLOYMENT_PROVIDERS.KONG]: BizDeployNodeConfigFieldsProviderKong,
[DEPLOYMENT_PROVIDERS.KSYUN_CDN]: BizDeployNodeConfigFieldsProviderKsyunCDN,
[DEPLOYMENT_PROVIDERS.KSYUN_SLB]: BizDeployNodeConfigFieldsProviderKsyunSLB,
[DEPLOYMENT_PROVIDERS.LECDN]: BizDeployNodeConfigFieldsProviderLeCDN,
[DEPLOYMENT_PROVIDERS.LINODE_LOS]: BizDeployNodeConfigFieldsProviderLinodeLOS,
[DEPLOYMENT_PROVIDERS.LOCAL]: BizDeployNodeConfigFieldsProviderLocal,
@@ -279,9 +270,6 @@ const providerComponentMap: Partial<Record<DeploymentProviderType, React.Compone
[DEPLOYMENT_PROVIDERS.VOLCENGINE_TOS]: BizDeployNodeConfigFieldsProviderVolcEngineTOS,
[DEPLOYMENT_PROVIDERS.VOLCENGINE_VOD]: BizDeployNodeConfigFieldsProviderVolcEngineVOD,
[DEPLOYMENT_PROVIDERS.VOLCENGINE_WAF]: BizDeployNodeConfigFieldsProviderVolcEngineWAF,
[DEPLOYMENT_PROVIDERS.WANGSU_CDN]: BizDeployNodeConfigFieldsProviderWangsuCDN,
[DEPLOYMENT_PROVIDERS.WANGSU_CDNPRO]: BizDeployNodeConfigFieldsProviderWangsuCDNPro,
[DEPLOYMENT_PROVIDERS.WANGSU_CERTIFICATE]: BizDeployNodeConfigFieldsProviderWangsuCertificate,
[DEPLOYMENT_PROVIDERS.WEBHOOK]: BizDeployNodeConfigFieldsProviderWebhook,
[DEPLOYMENT_PROVIDERS.YANDEXCLOUD_CERTIFICATEMANAGER]: BizDeployNodeConfigFieldsProviderYandexCloudCertificateManager,
};
@@ -1,166 +0,0 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Radio, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import Show from "@/components/Show";
import { isDomain } from "@/utils/validator";
import { useFormNestedFieldsContext } from "./_context";
const DEPLOY_TARGET_DOMAIN = "domain" as const;
const DEPLOY_TARGET_CERTIFICATE = "certificate" as const;
const DOMAIN_MATCH_PATTERN_EXACT = "exact" as const;
const BizDeployNodeConfigFieldsProviderBaishanCDN = () => {
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, "deployTarget"], formInst);
const fieldDomainMatchPattern = Form.useWatch([parentNamePath, "domainMatchPattern"], { form: formInst, preserve: true });
return (
<>
<Form.Item
name={[parentNamePath, "deployTarget"]}
initialValue={initialValues.deployTarget}
label={t("workflow_node.deploy.form.shared_deploy_target.label")}
rules={[formRule]}
>
<Select
options={[DEPLOY_TARGET_DOMAIN, DEPLOY_TARGET_CERTIFICATE].map((s) => ({
label: t(`workflow_node.deploy.form.baishan_cdn_deploy_target.option.${s}.label`),
value: s,
}))}
placeholder={t("workflow_node.deploy.form.shared_deploy_target.placeholder")}
/>
</Form.Item>
<Show when={fieldResourceType === DEPLOY_TARGET_DOMAIN}>
<Form.Item
name={[parentNamePath, "domainMatchPattern"]}
initialValue={initialValues.domainMatchPattern}
label={t("workflow_node.deploy.form.shared_domain_match_pattern.label")}
extra={
fieldDomainMatchPattern === DOMAIN_MATCH_PATTERN_EXACT ? (
<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.shared_domain_match_pattern.option.exact.help.wildcard") }}></span>
) : (
void 0
)
}
rules={[formRule]}
>
<Radio.Group
options={[DOMAIN_MATCH_PATTERN_EXACT].map((s) => ({
label: t(`workflow_node.deploy.form.shared_domain_match_pattern.option.${s}.label`),
value: s,
}))}
/>
</Form.Item>
<Form.Item
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.baishan_cdn_domain.label")}
rules={[formRule]}
>
<Input placeholder={t("workflow_node.deploy.form.baishan_cdn_domain.placeholder")} />
</Form.Item>
</Show>
<Show when={fieldResourceType === DEPLOY_TARGET_CERTIFICATE}>
<Form.Item
name={[parentNamePath, "certificateId"]}
initialValue={initialValues.certificateId}
label={t("workflow_node.deploy.form.baishan_cdn_certificate_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.baishan_cdn_certificate_id.tooltip") }}></span>}
>
<Input allowClear type="number" placeholder={t("workflow_node.deploy.form.baishan_cdn_certificate_id.placeholder")} />
</Form.Item>
</Show>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
deployTarget: DEPLOY_TARGET_DOMAIN,
domainMatchPattern: DOMAIN_MATCH_PATTERN_EXACT,
domain: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z
.object({
deployTarget: z.enum([DEPLOY_TARGET_DOMAIN, DEPLOY_TARGET_CERTIFICATE]),
domainMatchPattern: z.string().nonempty().default(DOMAIN_MATCH_PATTERN_EXACT),
domain: z.string().nullish(),
certificateId: z.union([z.string(), z.int().positive()]).nullish(),
})
.superRefine((values, ctx) => {
switch (values.deployTarget) {
case DEPLOY_TARGET_DOMAIN:
{
const scDomainMatchPattern = z.string().nonempty();
const spDomainMatchPattern = scDomainMatchPattern.safeParse(values.domainMatchPattern);
if (!spDomainMatchPattern.success) {
ctx.addIssue({
code: "custom",
message: z.treeifyError(spDomainMatchPattern.error).errors.join(),
path: ["domainMatchPattern"],
});
}
switch (values.domainMatchPattern) {
case DOMAIN_MATCH_PATTERN_EXACT:
{
const scDomain = z.string().refine((v) => isDomain(v, { allowWildcard: true }), t("common.errmsg.domain_invalid"));
const spDomain = scDomain.safeParse(values.domain);
if (!spDomain.success) {
ctx.addIssue({
code: "custom",
message: z.treeifyError(spDomain.error).errors.join(),
path: ["domain"],
});
}
}
break;
}
}
break;
case DEPLOY_TARGET_CERTIFICATE:
{
const scCertificateId = z.string().nonempty();
const spCertificateId = scCertificateId.safeParse(values.certificateId);
if (!spCertificateId.success) {
ctx.addIssue({
code: "custom",
message: z.treeifyError(spCertificateId.error).errors.join(),
path: ["certificateId"],
});
}
}
break;
}
});
};
const _default = Object.assign(BizDeployNodeConfigFieldsProviderBaishanCDN, {
getInitialValues,
getSchema,
});
export default _default;
@@ -1,169 +0,0 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Radio, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import Show from "@/components/Show";
import { isDomain } from "@/utils/validator";
import { useFormNestedFieldsContext } from "./_context";
const DEPLOY_TARGET_DOMAIN = "domain" as const;
const DEPLOY_TARGET_CERTIFICATE = "certificate" as const;
const DOMAIN_MATCH_PATTERN_EXACT = "exact" as const;
const DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard" as const;
const DOMAIN_MATCH_PATTERN_CERTSAN = "certsan" as const;
const BizDeployNodeConfigFieldsProviderKsyunCDN = () => {
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, "deployTarget"], formInst);
const fieldDomainMatchPattern = Form.useWatch([parentNamePath, "domainMatchPattern"], { form: formInst, preserve: true });
return (
<>
<Form.Item
name={[parentNamePath, "deployTarget"]}
initialValue={initialValues.deployTarget}
label={t("workflow_node.deploy.form.shared_deploy_target.label")}
rules={[formRule]}
>
<Select
options={[DEPLOY_TARGET_DOMAIN, DEPLOY_TARGET_CERTIFICATE].map((s) => ({
label: t(`workflow_node.deploy.form.ksyun_cdn_deploy_target.option.${s}.label`),
value: s,
}))}
placeholder={t("workflow_node.deploy.form.shared_deploy_target.placeholder")}
/>
</Form.Item>
<Show when={fieldResourceType === DEPLOY_TARGET_DOMAIN}>
<Form.Item
name={[parentNamePath, "domainMatchPattern"]}
initialValue={initialValues.domainMatchPattern}
label={t("workflow_node.deploy.form.shared_domain_match_pattern.label")}
extra={
fieldDomainMatchPattern === DOMAIN_MATCH_PATTERN_EXACT ? (
<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.shared_domain_match_pattern.option.exact.help.wildcard") }}></span>
) : (
void 0
)
}
rules={[formRule]}
>
<Radio.Group
options={[DOMAIN_MATCH_PATTERN_EXACT, DOMAIN_MATCH_PATTERN_WILDCARD, DOMAIN_MATCH_PATTERN_CERTSAN].map((s) => ({
label: t(`workflow_node.deploy.form.shared_domain_match_pattern.option.${s}.label`),
value: s,
}))}
/>
</Form.Item>
<Form.Item
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.ksyun_cdn_domain.label")}
rules={[formRule]}
>
<Input placeholder={t("workflow_node.deploy.form.ksyun_cdn_domain.placeholder")} />
</Form.Item>
</Show>
<Show when={fieldResourceType === DEPLOY_TARGET_CERTIFICATE}>
<Form.Item
name={[parentNamePath, "certificateId"]}
initialValue={initialValues.certificateId}
label={t("workflow_node.deploy.form.ksyun_cdn_certificate_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.ksyun_cdn_certificate_id.tooltip") }}></span>}
>
<Input allowClear placeholder={t("workflow_node.deploy.form.ksyun_cdn_certificate_id.placeholder")} />
</Form.Item>
</Show>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
deployTarget: DEPLOY_TARGET_DOMAIN,
domainMatchPattern: DOMAIN_MATCH_PATTERN_EXACT,
domain: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z
.object({
deployTarget: z.enum([DEPLOY_TARGET_DOMAIN, DEPLOY_TARGET_CERTIFICATE]),
domainMatchPattern: z.string().nonempty().default(DOMAIN_MATCH_PATTERN_EXACT),
domain: z.string().nullish(),
certificateId: z.string().nullish(),
})
.superRefine((values, ctx) => {
switch (values.deployTarget) {
case DEPLOY_TARGET_DOMAIN:
{
const scDomainMatchPattern = z.string().nonempty();
const spDomainMatchPattern = scDomainMatchPattern.safeParse(values.domainMatchPattern);
if (!spDomainMatchPattern.success) {
ctx.addIssue({
code: "custom",
message: z.treeifyError(spDomainMatchPattern.error).errors.join(),
path: ["domainMatchPattern"],
});
}
switch (values.domainMatchPattern) {
case DOMAIN_MATCH_PATTERN_EXACT:
case DOMAIN_MATCH_PATTERN_WILDCARD:
{
const scDomain = z.string().refine((v) => isDomain(v, { allowWildcard: true }), t("common.errmsg.domain_invalid"));
const spDomain = scDomain.safeParse(values.domain);
if (!spDomain.success) {
ctx.addIssue({
code: "custom",
message: z.treeifyError(spDomain.error).errors.join(),
path: ["domain"],
});
}
}
break;
}
}
break;
case DEPLOY_TARGET_CERTIFICATE:
{
const scCertificateId = z.string().nonempty();
const spCertificateId = scCertificateId.safeParse(values.certificateId);
if (!spCertificateId.success) {
ctx.addIssue({
code: "custom",
message: z.treeifyError(spCertificateId.error).errors.join(),
path: ["certificateId"],
});
}
}
break;
}
});
};
const _default = Object.assign(BizDeployNodeConfigFieldsProviderKsyunCDN, {
getInitialValues,
getSchema,
});
export default _default;
@@ -1,108 +0,0 @@
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 DEPLOY_TARGET_CERTIFICATE = "certificate" as const;
const BizDeployNodeConfigFieldsProviderKsyunSLB = () => {
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, "deployTarget"], formInst);
return (
<>
<Form.Item
name={[parentNamePath, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.ksyun_slb_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.ksyun_slb_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.ksyun_slb_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "deployTarget"]}
initialValue={initialValues.deployTarget}
label={t("workflow_node.deploy.form.shared_deploy_target.label")}
rules={[formRule]}
>
<Select
options={[DEPLOY_TARGET_CERTIFICATE].map((s) => ({
label: t(`workflow_node.deploy.form.ksyun_slb_deploy_target.option.${s}.label`),
value: s,
}))}
placeholder={t("workflow_node.deploy.form.shared_deploy_target.placeholder")}
/>
</Form.Item>
<Show when={fieldResourceType === DEPLOY_TARGET_CERTIFICATE}>
<Form.Item
name={[parentNamePath, "certificateId"]}
initialValue={initialValues.certificateId}
label={t("workflow_node.deploy.form.ksyun_slb_certificate_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.ksyun_slb_certificate_id.tooltip") }}></span>}
>
<Input allowClear placeholder={t("workflow_node.deploy.form.ksyun_slb_certificate_id.placeholder")} />
</Form.Item>
</Show>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
deployTarget: DEPLOY_TARGET_CERTIFICATE,
certificateId: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t: _ } = i18n;
return z
.object({
region: z.string().nonempty(),
deployTarget: z.enum([DEPLOY_TARGET_CERTIFICATE]),
certificateId: z.string().nullish(),
})
.superRefine((values, ctx) => {
switch (values.deployTarget) {
case DEPLOY_TARGET_CERTIFICATE:
{
const scCertificateId = z.string().nonempty();
const spCertificateId = scCertificateId.safeParse(values.certificateId);
if (!spCertificateId.success) {
ctx.addIssue({
code: "custom",
message: z.treeifyError(spCertificateId.error).errors.join(),
path: ["certificateId"],
});
}
}
break;
}
});
};
const _default = Object.assign(BizDeployNodeConfigFieldsProviderKsyunSLB, {
getInitialValues,
getSchema,
});
export default _default;
@@ -1,114 +0,0 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Radio } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import MultipleSplitValueInput from "@/components/MultipleSplitValueInput";
import { isDomain } from "@/utils/validator";
import { useFormNestedFieldsContext } from "./_context";
const MULTIPLE_INPUT_SEPARATOR = ";";
const DOMAIN_MATCH_PATTERN_EXACT = "exact" as const;
const BizDeployNodeConfigFieldsProviderWangsuCDN = () => {
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 fieldDomainMatchPattern = Form.useWatch([parentNamePath, "domainMatchPattern"], { form: formInst, preserve: true });
return (
<>
<Form.Item
name={[parentNamePath, "domainMatchPattern"]}
initialValue={initialValues.domainMatchPattern}
label={t("workflow_node.deploy.form.shared_domain_match_pattern.label")}
extra={
fieldDomainMatchPattern === DOMAIN_MATCH_PATTERN_EXACT ? (
<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.shared_domain_match_pattern.option.exact.help.wildcard") }}></span>
) : (
void 0
)
}
rules={[formRule]}
>
<Radio.Group
options={[DOMAIN_MATCH_PATTERN_EXACT].map((s) => ({
label: t(`workflow_node.deploy.form.shared_domain_match_pattern.option.${s}.label`),
value: s,
}))}
/>
</Form.Item>
<Form.Item
name={[parentNamePath, "domains"]}
initialValue={initialValues.domains}
label={t("workflow_node.deploy.form.wangsu_cdn_domains.label")}
extra={t("workflow_node.deploy.form.wangsu_cdn_domains.help")}
rules={[formRule]}
>
<MultipleSplitValueInput
modalTitle={t("workflow_node.deploy.form.wangsu_cdn_domains.multiple_input_modal.title")}
placeholder={t("workflow_node.deploy.form.wangsu_cdn_domains.placeholder")}
placeholderInModal={t("workflow_node.deploy.form.wangsu_cdn_domains.multiple_input_modal.placeholder")}
separator={MULTIPLE_INPUT_SEPARATOR}
splitOptions={{ removeEmpty: true, trimSpace: true }}
/>
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
domainMatchPattern: DOMAIN_MATCH_PATTERN_EXACT,
domains: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z
.object({
domainMatchPattern: z.string().nonempty().default(DOMAIN_MATCH_PATTERN_EXACT),
domains: z.string().nullish(),
})
.superRefine((values, ctx) => {
if (values.domainMatchPattern) {
switch (values.domainMatchPattern) {
case DOMAIN_MATCH_PATTERN_EXACT:
{
const scDomains = z
.string()
.nonempty()
.refine((v) => v.split(MULTIPLE_INPUT_SEPARATOR).every((e) => isDomain(e, { allowWildcard: true })), t("common.errmsg.domain_invalid"));
const spDomains = scDomains.safeParse(values.domains);
if (!spDomains.success) {
ctx.addIssue({
code: "custom",
message: z.treeifyError(spDomains.error).errors.join(),
path: ["domains"],
});
}
}
break;
}
}
});
};
const _default = Object.assign(BizDeployNodeConfigFieldsProviderWangsuCDN, {
getInitialValues,
getSchema,
});
export default _default;
@@ -1,141 +0,0 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Radio, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { isDomain } from "@/utils/validator";
import { useFormNestedFieldsContext } from "./_context";
const DOMAIN_MATCH_PATTERN_EXACT = "exact" as const;
const BizDeployNodeConfigFieldsProviderWangsuCDNPro = () => {
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 fieldDomainMatchPattern = Form.useWatch([parentNamePath, "domainMatchPattern"], { form: formInst, preserve: true });
return (
<>
<Form.Item
name={[parentNamePath, "environment"]}
initialValue={initialValues.environment}
label={t("workflow_node.deploy.form.wangsu_cdnpro_environment.label")}
rules={[formRule]}
>
<Select
options={["production", "staging"].map((s) => ({
label: t(`workflow_node.deploy.form.wangsu_cdnpro_environment.option.${s}.label`),
value: s,
}))}
placeholder={t("workflow_node.deploy.form.wangsu_cdnpro_environment.placeholder")}
/>
</Form.Item>
<Form.Item
name={[parentNamePath, "domainMatchPattern"]}
initialValue={initialValues.domainMatchPattern}
label={t("workflow_node.deploy.form.shared_domain_match_pattern.label")}
extra={
fieldDomainMatchPattern === DOMAIN_MATCH_PATTERN_EXACT ? (
<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.shared_domain_match_pattern.option.exact.help.wildcard") }}></span>
) : (
void 0
)
}
rules={[formRule]}
>
<Radio.Group
options={[DOMAIN_MATCH_PATTERN_EXACT].map((s) => ({
label: t(`workflow_node.deploy.form.shared_domain_match_pattern.option.${s}.label`),
value: s,
}))}
/>
</Form.Item>
<Form.Item
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.wangsu_cdnpro_domain.label")}
rules={[formRule]}
>
<Input placeholder={t("workflow_node.deploy.form.wangsu_cdnpro_domain.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "certificateId"]}
initialValue={initialValues.certificateId}
label={t("workflow_node.deploy.form.wangsu_cdnpro_certificate_id.label")}
extra={t("workflow_node.deploy.form.wangsu_cdnpro_certificate_id.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.wangsu_cdnpro_certificate_id.tooltip") }}></span>}
>
<Input allowClear placeholder={t("workflow_node.deploy.form.wangsu_cdnpro_certificate_id.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "webhookId"]}
initialValue={initialValues.webhookId}
label={t("workflow_node.deploy.form.wangsu_cdnpro_webhook_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.wangsu_cdnpro_webhook_id.tooltip") }}></span>}
>
<Input allowClear placeholder={t("workflow_node.deploy.form.wangsu_cdnpro_webhook_id.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
environment: "production",
domainMatchPattern: DOMAIN_MATCH_PATTERN_EXACT,
domain: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z
.object({
environment: z.enum(["production", "staging"]).default("production"),
domainMatchPattern: z.string().nonempty().default(DOMAIN_MATCH_PATTERN_EXACT),
domain: z.string().nullish(),
certificateId: z.string().nullish(),
webhookId: z.string().nullish(),
})
.superRefine((values, ctx) => {
if (values.domainMatchPattern) {
switch (values.domainMatchPattern) {
case DOMAIN_MATCH_PATTERN_EXACT:
{
const scDomain = z.string().refine((v) => isDomain(v, { allowWildcard: true }), t("common.errmsg.domain_invalid"));
const spDomain = scDomain.safeParse(values.domain);
if (!spDomain.success) {
ctx.addIssue({
code: "custom",
message: z.treeifyError(spDomain.error).errors.join(),
path: ["domain"],
});
}
}
break;
}
}
});
};
const _default = Object.assign(BizDeployNodeConfigFieldsProviderWangsuCDNPro, {
getInitialValues,
getSchema,
});
export default _default;
@@ -1,51 +0,0 @@
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 BizDeployNodeConfigFieldsProviderWangsuCertificate = () => {
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, "certificateId"]}
initialValue={initialValues.certificateId}
label={t("workflow_node.deploy.form.wangsu_certificate_id.label")}
extra={t("workflow_node.deploy.form.wangsu_certificate_id.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.wangsu_certificate_id.tooltip") }}></span>}
>
<Input allowClear placeholder={t("workflow_node.deploy.form.wangsu_certificate_id.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {};
};
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t: _ } = i18n;
return z.object({
certificateId: z.string().nullish(),
});
};
const _default = Object.assign(BizDeployNodeConfigFieldsProviderWangsuCertificate, {
getInitialValues,
getSchema,
});
export default _default;
@@ -50,7 +50,7 @@ export const NodeConfigDrawer = ({ children, afterClose, anchor, footer = true,
setFormPending(true);
try {
applyTrimmedFormValues(formInst);
formValues = await formInst.validateFields();
formValues = { ...formInst.getFieldsValue(true), ...(await formInst.validateFields()) };
} catch (err) {
message.warning(t("common.errmsg.form_invalid"));
-20
View File
@@ -35,7 +35,6 @@ export const ACCESS_PROVIDERS = Object.freeze({
AXISNOW: "axisnow",
AZURE: "azure",
BAIDUCLOUD: "baiducloud",
BAISHAN: "baishan",
BAOTAPANEL: "baotapanel",
BAOTAPANELGO: "baotapanelgo",
BAOTAWAF: "baotawaf",
@@ -86,7 +85,6 @@ export const ACCESS_PROVIDERS = Object.freeze({
IONOS: "ionos",
JDCLOUD: "jdcloud",
KONG: "kong",
KSYUN: "ksyun",
LARKBOT: "larkbot",
LECDN: "lecdn",
LETSENCRYPT: "letsencrypt",
@@ -135,7 +133,6 @@ export const ACCESS_PROVIDERS = Object.freeze({
VERCEL: "vercel",
VOLCENGINE: "volcengine",
VULTR: "vultr",
WANGSU: "wangsu",
WEBHOOK: "webhook",
WECOMBOT: "wecombot",
WESTCN: "westcn",
@@ -199,9 +196,6 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
[ACCESS_PROVIDERS.VERCEL, "provider.vercel", "/imgs/providers/vercel.svg", [ACCESS_USAGES.DNS, ACCESS_USAGES.HOSTING]],
[ACCESS_PROVIDERS.CPANEL, "provider.cpanel", "/imgs/providers/cpanel.svg", [ACCESS_USAGES.DNS, ACCESS_USAGES.HOSTING]],
[ACCESS_PROVIDERS.BAISHAN, "provider.baishan", "/imgs/providers/baishan.png", [ACCESS_USAGES.HOSTING]],
[ACCESS_PROVIDERS.KSYUN, "provider.ksyun", "/imgs/providers/ksyun.svg", [ACCESS_USAGES.HOSTING]],
[ACCESS_PROVIDERS.WANGSU, "provider.wangsu", "/imgs/providers/wangsu.svg", [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.CACHEFLY, "provider.cachefly", "/imgs/providers/cachefly.png", [ACCESS_USAGES.HOSTING]],
@@ -642,7 +636,6 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
BAIDUCLOUD_BLB: `${ACCESS_PROVIDERS.BAIDUCLOUD}-blb`,
BAIDUCLOUD_CDN: `${ACCESS_PROVIDERS.BAIDUCLOUD}-cdn`,
BAIDUCLOUD_CERT: `${ACCESS_PROVIDERS.BAIDUCLOUD}-cert`,
BAISHAN_CDN: `${ACCESS_PROVIDERS.BAISHAN}-cdn`,
BAOTAPANEL: `${ACCESS_PROVIDERS.BAOTAPANEL}`,
BAOTAPANEL_CONSOLE: `${ACCESS_PROVIDERS.BAOTAPANEL}-console`,
BAOTAPANELGO: `${ACCESS_PROVIDERS.BAOTAPANELGO}`,
@@ -695,9 +688,6 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
JDCLOUD_SSL: `${ACCESS_PROVIDERS.JDCLOUD}-ssl`,
JDCLOUD_WAF: `${ACCESS_PROVIDERS.JDCLOUD}-waf`,
KONG: `${ACCESS_PROVIDERS.KONG}`,
KSYUN_CDN: `${ACCESS_PROVIDERS.KSYUN}-cdn`,
KSYUN_KCM: `${ACCESS_PROVIDERS.KSYUN}-kcm`,
KSYUN_SLB: `${ACCESS_PROVIDERS.KSYUN}-slb`,
LECDN: `${ACCESS_PROVIDERS.LECDN}`,
LINODE_LOS: `${ACCESS_PROVIDERS.LINODE}-los`,
LOCAL: `${ACCESS_PROVIDERS.LOCAL}`,
@@ -753,9 +743,6 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
VOLCENGINE_TOS: `${ACCESS_PROVIDERS.VOLCENGINE}-tos`,
VOLCENGINE_VOD: `${ACCESS_PROVIDERS.VOLCENGINE}-vod`,
VOLCENGINE_WAF: `${ACCESS_PROVIDERS.VOLCENGINE}-waf`,
WANGSU_CDN: `${ACCESS_PROVIDERS.WANGSU}-cdn`,
WANGSU_CDNPRO: `${ACCESS_PROVIDERS.WANGSU}-cdnpro`,
WANGSU_CERTIFICATE: `${ACCESS_PROVIDERS.WANGSU}-certificate`,
WEBHOOK: `${ACCESS_PROVIDERS.WEBHOOK}`,
YANDEXCLOUD_CERTIFICATEMANAGER: `${ACCESS_PROVIDERS.YANDEXCLOUD}-certificatemanager`,
} as const);
@@ -895,9 +882,6 @@ export const deploymentProvidersMap: Map<DeploymentProvider["type"] | string, De
[DEPLOYMENT_PROVIDERS.JDCLOUD_LIVE, "provider.jdcloud_live", DEPLOYMENT_CATEGORIES.AV],
[DEPLOYMENT_PROVIDERS.JDCLOUD_VOD, "provider.jdcloud_vod", DEPLOYMENT_CATEGORIES.AV],
[DEPLOYMENT_PROVIDERS.JDCLOUD_SSL, "provider.jdcloud_ssl_upload", DEPLOYMENT_CATEGORIES.SSL],
[DEPLOYMENT_PROVIDERS.KSYUN_CDN, "provider.ksyun_cdn", DEPLOYMENT_CATEGORIES.CDN],
[DEPLOYMENT_PROVIDERS.KSYUN_SLB, "provider.ksyun_slb", DEPLOYMENT_CATEGORIES.LOADBALANCE],
[DEPLOYMENT_PROVIDERS.KSYUN_KCM, "provider.ksyun_kcm_upload", DEPLOYMENT_CATEGORIES.SSL],
[DEPLOYMENT_PROVIDERS.QINGCLOUD_LB, "provider.qingcloud_lb", DEPLOYMENT_CATEGORIES.LOADBALANCE],
[DEPLOYMENT_PROVIDERS.UCLOUD_US3, "provider.ucloud_us3", DEPLOYMENT_CATEGORIES.STORAGE],
[DEPLOYMENT_PROVIDERS.UCLOUD_UCDN, "provider.ucloud_ucdn", DEPLOYMENT_CATEGORIES.CDN],
@@ -905,10 +889,6 @@ export const deploymentProvidersMap: Map<DeploymentProvider["type"] | string, De
[DEPLOYMENT_PROVIDERS.UCLOUD_UALB, "provider.ucloud_ualb", DEPLOYMENT_CATEGORIES.LOADBALANCE],
[DEPLOYMENT_PROVIDERS.UCLOUD_UEWAF, "provider.ucloud_uewaf", DEPLOYMENT_CATEGORIES.FIREWALL],
[DEPLOYMENT_PROVIDERS.UCLOUD_UPATHX, "provider.ucloud_upathx", DEPLOYMENT_CATEGORIES.ACCELERATOR],
[DEPLOYMENT_PROVIDERS.BAISHAN_CDN, "provider.baishan_cdn", DEPLOYMENT_CATEGORIES.CDN],
[DEPLOYMENT_PROVIDERS.WANGSU_CDN, "provider.wangsu_cdn", DEPLOYMENT_CATEGORIES.CDN],
[DEPLOYMENT_PROVIDERS.WANGSU_CDNPRO, "provider.wangsu_cdnpro", DEPLOYMENT_CATEGORIES.CDN],
[DEPLOYMENT_PROVIDERS.WANGSU_CERTIFICATE, "provider.wangsu_certificate_upload", DEPLOYMENT_CATEGORIES.SSL],
[DEPLOYMENT_PROVIDERS.RAINYUN_RCDN, "provider.rainyun_rcdn", DEPLOYMENT_CATEGORIES.CDN],
[DEPLOYMENT_PROVIDERS.RAINYUN_SSLCENTER, "provider.rainyun_sslcenter_upload", DEPLOYMENT_CATEGORIES.SSL],
[DEPLOYMENT_PROVIDERS.UNICLOUD_WEBHOST, "provider.unicloud_webhost", DEPLOYMENT_CATEGORIES.WEBSITE],

Some files were not shown because too many files have changed in this diff Show More