From 2f551dd3e3db9b5ff417f6b2ee1ff64f7e36af4a Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Tue, 8 Jul 2025 10:25:44 +0800 Subject: [PATCH] fix: #854 --- internal/deployer/providers.go | 11 ++- .../providers/qiniu-kodo/qiniu_kodo.go | 88 +++++++++++++++++++ .../providers/qiniu-kodo/qiniu_kodo_test.go | 75 ++++++++++++++++ .../providers/qiniu-sslcert/qiniu_sslcert.go | 81 +++++++++++++++-- .../qiniu-sslcert/qiniu_sslcert_test.go | 72 +++++++++++++++ pkg/sdk3rd/qiniu/cdn.go | 45 +--------- pkg/sdk3rd/qiniu/kodo.go | 44 ++++++++++ pkg/sdk3rd/qiniu/sslcert.go | 80 +++++++++++++++++ pkg/sdk3rd/qiniu/util.go | 14 +++ .../i18n/locales/en/nls.workflow.nodes.json | 28 +++--- .../i18n/locales/zh/nls.workflow.nodes.json | 8 +- 11 files changed, 481 insertions(+), 65 deletions(-) create mode 100644 pkg/core/ssl-deployer/providers/qiniu-kodo/qiniu_kodo.go create mode 100644 pkg/core/ssl-deployer/providers/qiniu-kodo/qiniu_kodo_test.go create mode 100644 pkg/core/ssl-manager/providers/qiniu-sslcert/qiniu_sslcert_test.go create mode 100644 pkg/sdk3rd/qiniu/kodo.go create mode 100644 pkg/sdk3rd/qiniu/sslcert.go create mode 100644 pkg/sdk3rd/qiniu/util.go diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go index 7eb58372..61ae8785 100644 --- a/internal/deployer/providers.go +++ b/internal/deployer/providers.go @@ -69,6 +69,7 @@ import ( pNetlifySite "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/netlify-site" pProxmoxVE "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/proxmoxve" pQiniuCDN "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/qiniu-cdn" + pQiniuKodo "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/qiniu-kodo" pQiniuPili "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/qiniu-pili" pRainYunRCDN "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/rainyun-rcdn" pRatPanelConsole "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/ratpanel-console" @@ -1001,7 +1002,7 @@ func createSSLDeployerProvider(options *deployerProviderOptions) (core.SSLDeploy } switch options.Provider { - case domain.DeploymentProviderTypeQiniuCDN, domain.DeploymentProviderTypeQiniuKodo: + case domain.DeploymentProviderTypeQiniuCDN: deployer, err := pQiniuCDN.NewSSLDeployerProvider(&pQiniuCDN.SSLDeployerProviderConfig{ AccessKey: access.AccessKey, SecretKey: access.SecretKey, @@ -1009,6 +1010,14 @@ func createSSLDeployerProvider(options *deployerProviderOptions) (core.SSLDeploy }) return deployer, err + case domain.DeploymentProviderTypeQiniuKodo: + deployer, err := pQiniuKodo.NewSSLDeployerProvider(&pQiniuKodo.SSLDeployerProviderConfig{ + AccessKey: access.AccessKey, + SecretKey: access.SecretKey, + Domain: xmaps.GetString(options.ProviderServiceConfig, "domain"), + }) + return deployer, err + case domain.DeploymentProviderTypeQiniuPili: deployer, err := pQiniuPili.NewSSLDeployerProvider(&pQiniuPili.SSLDeployerProviderConfig{ AccessKey: access.AccessKey, diff --git a/pkg/core/ssl-deployer/providers/qiniu-kodo/qiniu_kodo.go b/pkg/core/ssl-deployer/providers/qiniu-kodo/qiniu_kodo.go new file mode 100644 index 00000000..7dcec172 --- /dev/null +++ b/pkg/core/ssl-deployer/providers/qiniu-kodo/qiniu_kodo.go @@ -0,0 +1,88 @@ +package qiniukodo + +import ( + "context" + "errors" + "fmt" + "log/slog" + + "github.com/qiniu/go-sdk/v7/auth" + + "github.com/certimate-go/certimate/pkg/core" + sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/qiniu-sslcert" + qiniusdk "github.com/certimate-go/certimate/pkg/sdk3rd/qiniu" +) + +type SSLDeployerProviderConfig struct { + // 七牛云 AccessKey。 + AccessKey string `json:"accessKey"` + // 七牛云 SecretKey。 + SecretKey string `json:"secretKey"` + // 自定义域名(不支持泛域名)。 + Domain string `json:"domain"` +} + +type SSLDeployerProvider struct { + config *SSLDeployerProviderConfig + logger *slog.Logger + sdkClient *qiniusdk.KodoManager + sslManager core.SSLManager +} + +var _ core.SSLDeployer = (*SSLDeployerProvider)(nil) + +func NewSSLDeployerProvider(config *SSLDeployerProviderConfig) (*SSLDeployerProvider, error) { + if config == nil { + return nil, errors.New("the configuration of the ssl deployer provider is nil") + } + + client := qiniusdk.NewKodoManager(auth.New(config.AccessKey, config.SecretKey)) + + sslmgr, err := sslmgrsp.NewSSLManagerProvider(&sslmgrsp.SSLManagerProviderConfig{ + AccessKey: config.AccessKey, + SecretKey: config.SecretKey, + }) + if err != nil { + return nil, fmt.Errorf("could not create ssl manager: %w", err) + } + + return &SSLDeployerProvider{ + config: config, + logger: slog.Default(), + sdkClient: client, + sslManager: sslmgr, + }, nil +} + +func (d *SSLDeployerProvider) SetLogger(logger *slog.Logger) { + if logger == nil { + d.logger = slog.New(slog.DiscardHandler) + } else { + d.logger = logger + } + + d.sslManager.SetLogger(logger) +} + +func (d *SSLDeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*core.SSLDeployResult, error) { + if d.config.Domain == "" { + return nil, fmt.Errorf("config `domain` is required") + } + + // 上传证书 + upres, err := d.sslManager.Upload(ctx, certPEM, privkeyPEM) + if err != nil { + return nil, fmt.Errorf("failed to upload certificate file: %w", err) + } else { + d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) + } + + // 绑定空间域名证书 + bindBucketCertResp, err := d.sdkClient.BindBucketCert(context.TODO(), d.config.Domain, upres.CertId) + d.logger.Debug("sdk request 'kodo.BindCert'", slog.String("request.domain", d.config.Domain), slog.String("request.certId", upres.CertId), slog.Any("response", bindBucketCertResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'kodo.BindCert': %w", err) + } + + return &core.SSLDeployResult{}, nil +} diff --git a/pkg/core/ssl-deployer/providers/qiniu-kodo/qiniu_kodo_test.go b/pkg/core/ssl-deployer/providers/qiniu-kodo/qiniu_kodo_test.go new file mode 100644 index 00000000..3dfcf456 --- /dev/null +++ b/pkg/core/ssl-deployer/providers/qiniu-kodo/qiniu_kodo_test.go @@ -0,0 +1,75 @@ +package qiniukodo_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/qiniu-kodo" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKey string + fSecretKey string + fDomain string +) + +func init() { + argsPrefix := "CERTIMATE_SSLDEPLOYER_QINIUKODO_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKey, argsPrefix+"ACCESSKEY", "", "") + flag.StringVar(&fSecretKey, argsPrefix+"SECRETKEY", "", "") + flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") +} + +/* +Shell command to run this test: + + go test -v ./qiniu_kodo_test.go -args \ + --CERTIMATE_SSLDEPLOYER_QINIUKODO_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_SSLDEPLOYER_QINIUKODO_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_SSLDEPLOYER_QINIUKODO_ACCESSKEY="your-access-key" \ + --CERTIMATE_SSLDEPLOYER_QINIUKODO_SECRETKEY="your-secret-key" \ + --CERTIMATE_SSLDEPLOYER_QINIUKODO_DOMAIN="example.com" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("ACCESSKEY: %v", fAccessKey), + fmt.Sprintf("SECRETKEY: %v", fSecretKey), + fmt.Sprintf("DOMAIN: %v", fDomain), + }, "\n")) + + deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{ + AccessKey: fAccessKey, + SecretKey: fSecretKey, + Domain: fDomain, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/pkg/core/ssl-manager/providers/qiniu-sslcert/qiniu_sslcert.go b/pkg/core/ssl-manager/providers/qiniu-sslcert/qiniu_sslcert.go index 07775b21..dcbe26b0 100644 --- a/pkg/core/ssl-manager/providers/qiniu-sslcert/qiniu_sslcert.go +++ b/pkg/core/ssl-manager/providers/qiniu-sslcert/qiniu_sslcert.go @@ -2,9 +2,12 @@ package qiniusslcert import ( "context" + "crypto/x509" "errors" "fmt" "log/slog" + "slices" + "strings" "time" "github.com/qiniu/go-sdk/v7/auth" @@ -24,7 +27,7 @@ type SSLManagerProviderConfig struct { type SSLManagerProvider struct { config *SSLManagerProviderConfig logger *slog.Logger - sdkClient *qiniusdk.CdnManager + sdkClient *qiniusdk.SslCertManager } var _ core.SSLManager = (*SSLManagerProvider)(nil) @@ -64,12 +67,80 @@ func (m *SSLManagerProvider) Upload(ctx context.Context, certPEM string, privkey // 生成新证书名(需符合七牛云命名规则) certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli()) + // 遍历查询已有证书,避免重复上传 + getSslCertListMarker := "" + getSslCertListLimit := int32(200) + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + + getSslCertListResp, err := m.sdkClient.GetSslCertList(context.TODO(), getSslCertListMarker, getSslCertListLimit) + m.logger.Debug("sdk request 'sslcert.GetList'", slog.Any("request.marker", getSslCertListMarker), slog.Any("response", getSslCertListResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'sslcert.GetList': %w", err) + } + + if getSslCertListResp.Certs != nil { + for _, sslCert := range getSslCertListResp.Certs { + // 先对比证书通用名称 + if !strings.EqualFold(certX509.Subject.CommonName, sslCert.CommonName) { + continue + } + + // 再对比证书多域名 + if !slices.Equal(certX509.DNSNames, sslCert.DnsNames) { + continue + } + + // 再对比证书有效期 + if certX509.NotBefore.Unix() != sslCert.NotBefore || certX509.NotAfter.Unix() != sslCert.NotAfter { + continue + } + + // 最后对比证书公钥算法 + switch certX509.PublicKeyAlgorithm { + case x509.RSA: + if !strings.EqualFold(sslCert.Encrypt, "RSA") { + continue + } + case x509.ECDSA: + if !strings.EqualFold(sslCert.Encrypt, "ECDSA") { + continue + } + case x509.Ed25519: + if !strings.EqualFold(sslCert.Encrypt, "ED25519") { + continue + } + default: + // 未知算法,跳过 + continue + } + + // 如果以上信息都一致,则视为已存在相同证书,直接返回 + m.logger.Info("ssl certificate already exists") + return &core.SSLManageUploadResult{ + CertId: sslCert.CertID, + CertName: sslCert.Name, + }, nil + } + } + + if len(getSslCertListResp.Certs) < int(getSslCertListLimit) || getSslCertListResp.Marker == "" { + break + } else { + getSslCertListMarker = getSslCertListResp.Marker + } + } + // 上传新证书 // REF: https://developer.qiniu.com/fusion/8593/interface-related-certificate uploadSslCertResp, err := m.sdkClient.UploadSslCert(context.TODO(), certName, certX509.Subject.CommonName, certPEM, privkeyPEM) - m.logger.Debug("sdk request 'cdn.UploadSslCert'", slog.Any("response", uploadSslCertResp)) + m.logger.Debug("sdk request 'sslcert.Upload'", slog.Any("response", uploadSslCertResp)) if err != nil { - return nil, fmt.Errorf("failed to execute sdk request 'cdn.UploadSslCert': %w", err) + return nil, fmt.Errorf("failed to execute sdk request 'sslcert.Upload': %w", err) } return &core.SSLManageUploadResult{ @@ -78,7 +149,7 @@ func (m *SSLManagerProvider) Upload(ctx context.Context, certPEM string, privkey }, nil } -func createSDKClient(accessKey, secretKey string) (*qiniusdk.CdnManager, error) { +func createSDKClient(accessKey, secretKey string) (*qiniusdk.SslCertManager, error) { if secretKey == "" { return nil, errors.New("invalid qiniu access key") } @@ -88,6 +159,6 @@ func createSDKClient(accessKey, secretKey string) (*qiniusdk.CdnManager, error) } credential := auth.New(accessKey, secretKey) - client := qiniusdk.NewCdnManager(credential) + client := qiniusdk.NewSslCertManager(credential) return client, nil } diff --git a/pkg/core/ssl-manager/providers/qiniu-sslcert/qiniu_sslcert_test.go b/pkg/core/ssl-manager/providers/qiniu-sslcert/qiniu_sslcert_test.go new file mode 100644 index 00000000..87e14a08 --- /dev/null +++ b/pkg/core/ssl-manager/providers/qiniu-sslcert/qiniu_sslcert_test.go @@ -0,0 +1,72 @@ +package qiniusslcert_test + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/qiniu-sslcert" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKey string + fSecretKey string +) + +func init() { + argsPrefix := "CERTIMATE_SSLMANAGER_QINIUSSLCERT_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKey, argsPrefix+"ACCESSKEY", "", "") + flag.StringVar(&fSecretKey, argsPrefix+"SECRETKEY", "", "") +} + +/* +Shell command to run this test: + + go test -v ./qiniu_sslcert_test.go -args \ + --CERTIMATE_SSLMANAGER_QINIUSSLCERT_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_SSLMANAGER_QINIUSSLCERT_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_SSLMANAGER_QINIUSSLCERT_ACCESSKEY="your-access-key" \ + --CERTIMATE_SSLMANAGER_QINIUSSLCERT_SECRETKEY="your-secret-key" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("ACCESSKEY: %v", fAccessKey), + fmt.Sprintf("SECRETKEY: %v", fSecretKey), + }, "\n")) + + sslmanager, err := provider.NewSSLManagerProvider(&provider.SSLManagerProviderConfig{ + AccessKey: fAccessKey, + SecretKey: fSecretKey, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := sslmanager.Upload(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + sres, _ := json.Marshal(res) + t.Logf("ok: %s", string(sres)) + }) +} diff --git a/pkg/sdk3rd/qiniu/cdn.go b/pkg/sdk3rd/qiniu/cdn.go index 54a56517..74745d0b 100644 --- a/pkg/sdk3rd/qiniu/cdn.go +++ b/pkg/sdk3rd/qiniu/cdn.go @@ -2,16 +2,12 @@ package qiniu import ( "context" - "fmt" "net/http" - "strings" "github.com/qiniu/go-sdk/v7/auth" "github.com/qiniu/go-sdk/v7/client" ) -const qiniuHost = "https://api.qiniu.com" - type CdnManager struct { client *client.Client } @@ -21,16 +17,10 @@ func NewCdnManager(mac *auth.Credentials) *CdnManager { mac = auth.Default() } - client := &client.Client{&http.Client{Transport: newTransport(mac, nil)}} + client := &client.Client{Client: &http.Client{Transport: newTransport(mac, nil)}} return &CdnManager{client: client} } -func (m *CdnManager) urlf(pathf string, pathargs ...any) string { - path := fmt.Sprintf(pathf, pathargs...) - path = strings.TrimPrefix(path, "/") - return qiniuHost + "/" + path -} - type GetDomainInfoResponse struct { Code *int `json:"code,omitempty"` Error *string `json:"error,omitempty"` @@ -52,7 +42,7 @@ type GetDomainInfoResponse struct { func (m *CdnManager) GetDomainInfo(ctx context.Context, domain string) (*GetDomainInfoResponse, error) { resp := new(GetDomainInfoResponse) - if err := m.client.Call(ctx, resp, http.MethodGet, m.urlf("domain/%s", domain), nil); err != nil { + if err := m.client.Call(ctx, resp, http.MethodGet, urlf("domain/%s", domain), nil); err != nil { return nil, err } return resp, nil @@ -76,7 +66,7 @@ func (m *CdnManager) ModifyDomainHttpsConf(ctx context.Context, domain string, c Http2Enable: http2Enable, } resp := new(ModifyDomainHttpsConfResponse) - if err := m.client.CallWithJson(ctx, resp, http.MethodPut, m.urlf("domain/%s/httpsconf", domain), nil, req); err != nil { + if err := m.client.CallWithJson(ctx, resp, http.MethodPut, urlf("domain/%s/httpsconf", domain), nil, req); err != nil { return nil, err } return resp, nil @@ -100,34 +90,7 @@ func (m *CdnManager) EnableDomainHttps(ctx context.Context, domain string, certI Http2Enable: http2Enable, } resp := new(EnableDomainHttpsResponse) - if err := m.client.CallWithJson(ctx, resp, http.MethodPut, m.urlf("domain/%s/sslize", domain), nil, req); err != nil { - return nil, err - } - return resp, nil -} - -type UploadSslCertRequest struct { - Name string `json:"name"` - CommonName string `json:"common_name"` - Certificate string `json:"ca"` - PrivateKey string `json:"pri"` -} - -type UploadSslCertResponse struct { - Code *int `json:"code,omitempty"` - Error *string `json:"error,omitempty"` - CertID string `json:"certID"` -} - -func (m *CdnManager) UploadSslCert(ctx context.Context, name string, commonName string, certificate string, privateKey string) (*UploadSslCertResponse, error) { - req := &UploadSslCertRequest{ - Name: name, - CommonName: commonName, - Certificate: certificate, - PrivateKey: privateKey, - } - resp := new(UploadSslCertResponse) - if err := m.client.CallWithJson(ctx, resp, http.MethodPost, m.urlf("sslcert"), nil, req); err != nil { + if err := m.client.CallWithJson(ctx, resp, http.MethodPut, urlf("domain/%s/sslize", domain), nil, req); err != nil { return nil, err } return resp, nil diff --git a/pkg/sdk3rd/qiniu/kodo.go b/pkg/sdk3rd/qiniu/kodo.go new file mode 100644 index 00000000..6a3245a2 --- /dev/null +++ b/pkg/sdk3rd/qiniu/kodo.go @@ -0,0 +1,44 @@ +package qiniu + +import ( + "context" + "net/http" + + "github.com/qiniu/go-sdk/v7/auth" + "github.com/qiniu/go-sdk/v7/client" +) + +type KodoManager struct { + client *client.Client +} + +func NewKodoManager(mac *auth.Credentials) *KodoManager { + if mac == nil { + mac = auth.Default() + } + + client := &client.Client{Client: &http.Client{Transport: newTransport(mac, nil)}} + return &KodoManager{client: client} +} + +type BindBucketCertRequest struct { + CertID string `json:"certid"` + Domain string `json:"domain"` +} + +type BindBucketCertResponse struct { + Code *int `json:"code,omitempty"` + Error *string `json:"error,omitempty"` +} + +func (m *KodoManager) BindBucketCert(ctx context.Context, domain string, certId string) (*BindBucketCertResponse, error) { + req := &BindBucketCertRequest{ + CertID: certId, + Domain: domain, + } + resp := new(BindBucketCertResponse) + if err := m.client.CallWithJson(ctx, resp, http.MethodPut, urlf("cert/bind"), nil, req); err != nil { + return nil, err + } + return resp, nil +} diff --git a/pkg/sdk3rd/qiniu/sslcert.go b/pkg/sdk3rd/qiniu/sslcert.go new file mode 100644 index 00000000..f9784270 --- /dev/null +++ b/pkg/sdk3rd/qiniu/sslcert.go @@ -0,0 +1,80 @@ +package qiniu + +import ( + "context" + "net/http" + "net/url" + + "github.com/qiniu/go-sdk/v7/auth" + "github.com/qiniu/go-sdk/v7/client" +) + +type SslCertManager struct { + client *client.Client +} + +func NewSslCertManager(mac *auth.Credentials) *SslCertManager { + if mac == nil { + mac = auth.Default() + } + + client := &client.Client{Client: &http.Client{Transport: newTransport(mac, nil)}} + return &SslCertManager{client: client} +} + +type GetSslCertListResponse struct { + Code *int `json:"code,omitempty"` + Error *string `json:"error,omitempty"` + Certs []*struct { + CertID string `json:"certid"` + Name string `json:"name"` + CommonName string `json:"common_name"` + DnsNames []string `json:"dnsnames"` + CreateTime int64 `json:"create_time"` + NotBefore int64 `json:"not_before"` + NotAfter int64 `json:"not_after"` + ProductType string `json:"product_type"` + ProductShortName string `json:"product_short_name,omitempty"` + OrderId string `json:"orderid,omitempty"` + CertType string `json:"cert_type"` + Encrypt string `json:"encrypt"` + EncryptParameter string `json:"encryptParameter,omitempty"` + Enable bool `json:"enable"` + } `json:"certs"` + Marker string `json:"marker"` +} + +func (m *SslCertManager) GetSslCertList(ctx context.Context, marker string, limit int32) (*GetSslCertListResponse, error) { + resp := new(GetSslCertListResponse) + if err := m.client.Call(ctx, resp, http.MethodGet, urlf("sslcert?marker=%s&limit=%d", url.QueryEscape(marker), limit), nil); err != nil { + return nil, err + } + return resp, nil +} + +type UploadSslCertRequest struct { + Name string `json:"name"` + CommonName string `json:"common_name"` + Certificate string `json:"ca"` + PrivateKey string `json:"pri"` +} + +type UploadSslCertResponse struct { + Code *int `json:"code,omitempty"` + Error *string `json:"error,omitempty"` + CertID string `json:"certID"` +} + +func (m *SslCertManager) UploadSslCert(ctx context.Context, name string, commonName string, certificate string, privateKey string) (*UploadSslCertResponse, error) { + req := &UploadSslCertRequest{ + Name: name, + CommonName: commonName, + Certificate: certificate, + PrivateKey: privateKey, + } + resp := new(UploadSslCertResponse) + if err := m.client.CallWithJson(ctx, resp, http.MethodPost, urlf("sslcert"), nil, req); err != nil { + return nil, err + } + return resp, nil +} diff --git a/pkg/sdk3rd/qiniu/util.go b/pkg/sdk3rd/qiniu/util.go new file mode 100644 index 00000000..0957a310 --- /dev/null +++ b/pkg/sdk3rd/qiniu/util.go @@ -0,0 +1,14 @@ +package qiniu + +import ( + "fmt" + "strings" +) + +const qiniuHost = "https://api.qiniu.com" + +func urlf(pathf string, pathargs ...any) string { + path := fmt.Sprintf(pathf, pathargs...) + path = strings.TrimPrefix(path, "/") + return qiniuHost + "/" + path +} diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json index df7f1915..bc5a16a1 100644 --- a/ui/src/i18n/locales/en/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json @@ -268,8 +268,8 @@ "workflow_node.deploy.form.aliyun_oss_bucket.label": "Alibaba Cloud OSS bucket", "workflow_node.deploy.form.aliyun_oss_bucket.placeholder": "Please enter Alibaba Cloud OSS bucket name", "workflow_node.deploy.form.aliyun_oss_bucket.tooltip": "For more information, see https://oss.console.aliyun.com", - "workflow_node.deploy.form.aliyun_oss_domain.label": "Alibaba Cloud OSS domain", - "workflow_node.deploy.form.aliyun_oss_domain.placeholder": "Please enter Alibaba Cloud OSS domain name", + "workflow_node.deploy.form.aliyun_oss_domain.label": "Alibaba Cloud OSS custom domain", + "workflow_node.deploy.form.aliyun_oss_domain.placeholder": "Please enter Alibaba Cloud OSS bucket custom domain name", "workflow_node.deploy.form.aliyun_oss_domain.tooltip": "For more information, see https://oss.console.aliyun.com", "workflow_node.deploy.form.aliyun_vod_region.label": "Alibaba Cloud VOD region", "workflow_node.deploy.form.aliyun_vod_region.placeholder": "Please enter Alibaba Cloud VOD region (e.g. cn-hangzhou)", @@ -601,8 +601,8 @@ "workflow_node.deploy.form.qiniu_cdn_domain.label": "Qiniu CDN domain", "workflow_node.deploy.form.qiniu_cdn_domain.placeholder": "Please enter Qiniu CDN domain name", "workflow_node.deploy.form.qiniu_cdn_domain.tooltip": "For more information, see https://portal.qiniu.com/cdn", - "workflow_node.deploy.form.qiniu_kodo_domain.label": "Qiniu Kodo bucket domain", - "workflow_node.deploy.form.qiniu_kodo_domain.placeholder": "Please enter Qiniu Kodo bucket domain name", + "workflow_node.deploy.form.qiniu_kodo_domain.label": "Qiniu Kodo custom domain", + "workflow_node.deploy.form.qiniu_kodo_domain.placeholder": "Please enter Qiniu Kodo bucket custom domain name", "workflow_node.deploy.form.qiniu_kodo_domain.tooltip": "For more information, see https://portal.qiniu.com/kodo", "workflow_node.deploy.form.qiniu_pili_hub.label": "Qiniu Pili hub", "workflow_node.deploy.form.qiniu_pili_hub.placeholder": "Please enter Qiniu Pili hub name", @@ -706,8 +706,8 @@ "workflow_node.deploy.form.tencentcloud_cos_bucket.label": "Tencent Cloud COS bucket", "workflow_node.deploy.form.tencentcloud_cos_bucket.placeholder": "Please enter Tencent Cloud COS bucket name", "workflow_node.deploy.form.tencentcloud_cos_bucket.tooltip": "For more information, see https://console.tencentcloud.com/cos", - "workflow_node.deploy.form.tencentcloud_cos_domain.label": "Tencent Cloud COS domain", - "workflow_node.deploy.form.tencentcloud_cos_domain.placeholder": "Please enter Tencent Cloud COS domain name", + "workflow_node.deploy.form.tencentcloud_cos_domain.label": "Tencent Cloud COS custom domain", + "workflow_node.deploy.form.tencentcloud_cos_domain.placeholder": "Please enter Tencent Cloud COS bucket custom domain name", "workflow_node.deploy.form.tencentcloud_cos_domain.tooltip": "For more information, see https://console.tencentcloud.com/cos", "workflow_node.deploy.form.tencentcloud_css_endpoint.label": "Tencent Cloud CSS API endpoint (Optional)", "workflow_node.deploy.form.tencentcloud_css_endpoint.placeholder": "Please enter Tencent Cloud CSS API endpoint (e.g. live.intl.tencentcloudapi.com)", @@ -824,8 +824,8 @@ "workflow_node.deploy.form.ucloud_us3_bucket.label": "UCloud US3 bucket", "workflow_node.deploy.form.ucloud_us3_bucket.placeholder": "Please enter UCloud US3 bucket name", "workflow_node.deploy.form.ucloud_us3_bucket.tooltip": "For more information, see https://console.ucloud-global.com/ufile", - "workflow_node.deploy.form.ucloud_us3_domain.label": "UCloud US3 domain", - "workflow_node.deploy.form.ucloud_us3_domain.placeholder": "Please enter UCloud US3 domain name", + "workflow_node.deploy.form.ucloud_us3_domain.label": "UCloud US3 custom domain", + "workflow_node.deploy.form.ucloud_us3_domain.placeholder": "Please enter UCloud US3 bucket custom domain name", "workflow_node.deploy.form.ucloud_us3_domain.tooltip": "For more information, see https://console.ucloud-global.com/ufile", "workflow_node.deploy.form.unicloud_webhost.guide": "Tips: This uses webpage simulator login and does not guarantee stability. If there are any changes to the uniCloud, please create a GitHub Issue.", "workflow_node.deploy.form.unicloud_webhost_space_provider.label": "uniCloud space provider", @@ -842,8 +842,8 @@ "workflow_node.deploy.form.upyun_cdn_domain.placeholder": "Please enter UPYUN CDN domain name", "workflow_node.deploy.form.upyun_cdn_domain.tooltip": "For more information, see https://console.upyun.com/services/cdn/", "workflow_node.deploy.form.upyun_file.guide": "Tips: This uses webpage simulator login and does not guarantee stability. If there are any changes to the UPYUN, please create a GitHub Issue.", - "workflow_node.deploy.form.upyun_file_domain.label": "UPYUN bucket domain", - "workflow_node.deploy.form.upyun_file_domain.placeholder": "Please enter UPYUN bucket domain name", + "workflow_node.deploy.form.upyun_file_domain.label": "UPYUN USS custom domain", + "workflow_node.deploy.form.upyun_file_domain.placeholder": "Please enter UPYUN USS bucket custom domain name", "workflow_node.deploy.form.upyun_file_domain.tooltip": "For more information, see https://console.upyun.com/services/file/", "workflow_node.deploy.form.volcengine_alb_region.label": "VolcEngine ALB region", "workflow_node.deploy.form.volcengine_alb_region.placeholder": "Please enter VolcEngine ALB region (e.g. cn-beijing)", @@ -888,8 +888,8 @@ "workflow_node.deploy.form.volcengine_imagex_service_id.label": "VolcEngine ImageX service ID", "workflow_node.deploy.form.volcengine_imagex_service_id.placeholder": "Please enter VolcEngine ImageX service ID", "workflow_node.deploy.form.volcengine_imagex_service_id.tooltip": "For more information, see https://console.volcengine.com/imagex", - "workflow_node.deploy.form.volcengine_imagex_domain.label": "VolcEngine ImageX domain", - "workflow_node.deploy.form.volcengine_imagex_domain.placeholder": "Please enter VolcEngine ImageX domain name", + "workflow_node.deploy.form.volcengine_imagex_domain.label": "VolcEngine ImageX custom domain", + "workflow_node.deploy.form.volcengine_imagex_domain.placeholder": "Please enter VolcEngine ImageX custom domain name", "workflow_node.deploy.form.volcengine_imagex_domain.tooltip": "For more information, see https://console.volcengine.com/imagex", "workflow_node.deploy.form.volcengine_live_domain.label": "VolcEngine Live streaming domain", "workflow_node.deploy.form.volcengine_live_domain.placeholder": "Please enter VolcEngine Live streaming domain name", @@ -900,8 +900,8 @@ "workflow_node.deploy.form.volcengine_tos_bucket.label": "VolcEngine TOS bucket", "workflow_node.deploy.form.volcengine_tos_bucket.placeholder": "Please enter VolcEngine TOS bucket name", "workflow_node.deploy.form.volcengine_tos_bucket.tooltip": "For more information, see https://console.volcengine.com/tos", - "workflow_node.deploy.form.volcengine_tos_domain.label": "VolcEngine TOS domain", - "workflow_node.deploy.form.volcengine_tos_domain.placeholder": "Please enter VolcEngine TOS domain name", + "workflow_node.deploy.form.volcengine_tos_domain.label": "VolcEngine TOS custom domain", + "workflow_node.deploy.form.volcengine_tos_domain.placeholder": "Please enter VolcEngine TOS bucket custom domain name", "workflow_node.deploy.form.volcengine_tos_domain.tooltip": "For more information, see https://console.volcengine.com/tos", "workflow_node.deploy.form.wangsu_cdn_domains.label": "Wangsu Cloud CDN domains", "workflow_node.deploy.form.wangsu_cdn_domains.placeholder": "Please enter Wangsu Cloud CDN domain names (separated by semicolons)", diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index 061189df..09fc27e9 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -599,8 +599,8 @@ "workflow_node.deploy.form.qiniu_cdn_domain.label": "七牛云 CDN 加速域名", "workflow_node.deploy.form.qiniu_cdn_domain.placeholder": "请输入七牛云 CDN 加速域名(支持泛域名)", "workflow_node.deploy.form.qiniu_cdn_domain.tooltip": "这是什么?请参阅 https://portal.qiniu.com/cdn", - "workflow_node.deploy.form.qiniu_kodo_domain.label": "七牛云对象存储加速域名", - "workflow_node.deploy.form.qiniu_kodo_domain.placeholder": "请输入七牛云对象存储加速域名", + "workflow_node.deploy.form.qiniu_kodo_domain.label": "七牛云对象存储自定义域名", + "workflow_node.deploy.form.qiniu_kodo_domain.placeholder": "请输入七牛云对象存储自定义域名", "workflow_node.deploy.form.qiniu_kodo_domain.tooltip": "这是什么?请参阅 https://portal.qiniu.com/kodo", "workflow_node.deploy.form.qiniu_pili_hub.label": "七牛云视频直播空间名", "workflow_node.deploy.form.qiniu_pili_hub.placeholder": "请输入七牛云视频直播空间名", @@ -840,8 +840,8 @@ "workflow_node.deploy.form.upyun_cdn_domain.placeholder": "请输入又拍云 CDN 加速域名(支持泛域名)", "workflow_node.deploy.form.upyun_cdn_domain.tooltip": "这是什么?请参阅 https://console.upyun.com/services/cdn/", "workflow_node.deploy.form.upyun_file.guide": "小贴士:由于又拍云未公开相关 API,这里将使用网页模拟登录方式部署,但无法保证稳定性。如遇又拍云接口变更,请到 GitHub 发起 Issue 告知。", - "workflow_node.deploy.form.upyun_file_domain.label": "又拍云云存储加速域名", - "workflow_node.deploy.form.upyun_file_domain.placeholder": "请输入又拍云云存储加速域名", + "workflow_node.deploy.form.upyun_file_domain.label": "又拍云云存储自定义域名", + "workflow_node.deploy.form.upyun_file_domain.placeholder": "请输入又拍云云存储自定义域名", "workflow_node.deploy.form.upyun_file_domain.tooltip": "这是什么?请参阅 https://console.upyun.com/services/file/", "workflow_node.deploy.form.volcengine_alb_resource_type.label": "证书部署方式", "workflow_node.deploy.form.volcengine_alb_resource_type.placeholder": "请选择证书部署方式",