diff --git a/go.mod b/go.mod index b7f6e5e60..a1c3eb070 100644 --- a/go.mod +++ b/go.mod @@ -43,6 +43,7 @@ require ( github.com/baidubce/bce-sdk-go v0.9.270 github.com/byteplus-sdk/byteplus-go-sdk-v2 v1.0.71 github.com/byteplus-sdk/byteplus-sdk-golang v1.0.71 + github.com/fsnotify/fsnotify v1.10.1 github.com/go-acme/lego/v5 v5.2.2 github.com/go-cmd/cmd v1.4.3 github.com/go-resty/resty/v2 v2.17.2 @@ -63,7 +64,6 @@ require ( github.com/pocketbase/pocketbase v0.39.6 github.com/povsister/scp v0.0.0-20250701154629-777cf82de5df github.com/pquerna/otp v1.5.0 - github.com/qiniu/go-sdk/v7 v7.26.15 github.com/samber/lo v1.53.0 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 @@ -122,9 +122,6 @@ require ( github.com/go-logr/logr v1.4.3 // indirect github.com/go-openapi/jsonreference v0.21.5 // indirect github.com/go-openapi/swag v0.25.5 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.30.2 // indirect github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/goccy/go-yaml v1.19.2 // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect @@ -134,7 +131,6 @@ require ( github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.8 // indirect github.com/kylelemons/godebug v1.1.0 // indirect - github.com/leodido/go-urn v1.4.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/namedotcom/go/v4 v4.0.2 // indirect @@ -146,8 +142,6 @@ require ( github.com/peterhellberg/link v1.2.0 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/qiniu/dyn v1.3.0 // indirect - github.com/qiniu/x v1.17.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/sirupsen/logrus v1.9.4 // indirect github.com/stretchr/objx v0.5.3 // indirect @@ -174,7 +168,6 @@ require ( cloud.google.com/go/auth v0.20.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.9.0 // indirect - github.com/BurntSushi/toml v1.6.0 // indirect github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e // indirect github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect github.com/alibabacloud-go/debug v1.0.1 // indirect @@ -204,7 +197,6 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/fatih/color v1.19.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/gabriel-vasile/mimetype v1.4.13 // indirect github.com/ganigeorgiev/fexpr v0.5.0 // indirect github.com/go-acme/alidns-20150109/v5 v5.4.1 // indirect diff --git a/go.sum b/go.sum index 7f097ce46..96d0fe879 100644 --- a/go.sum +++ b/go.sum @@ -66,8 +66,6 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mo github.com/AzureAD/microsoft-authentication-library-for-go v1.7.2 h1:RHK7bS+HQMslb1sZpAokUt+zTVmue0hKSs2C791hhzU= github.com/AzureAD/microsoft-authentication-library-for-go v1.7.2/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= -github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/G-Core/gcorelabscdn-go v1.0.37 h1:IHqiSfnHp3tnuUSU1FIH5+3XepNnSsyJn0JoWgvUYL8= @@ -417,17 +415,9 @@ github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lE github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es= github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/go-playground/validator/v10 v10.30.2 h1:JiFIMtSSHb2/XBUbWM4i/MpeQm9ZK2xqPNk8vgvu5JQ= -github.com/go-playground/validator/v10 v10.30.2/go.mod h1:mAf2pIOVXjTEBrwUMGKkCWKKPs9NheYGabeB04txQSc= github.com/go-resty/resty/v2 v2.17.2 h1:FQW5oHYcIlkCNrMD2lloGScxcHJ0gkjshV3qcQAyHQk= github.com/go-resty/resty/v2 v2.17.2/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= @@ -675,8 +665,6 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -831,13 +819,6 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/qiniu/dyn v1.3.0 h1:s+xPTeV0H8yikgM4ZMBc7Rrefam8UNI3asBlkaOQg5o= -github.com/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdkk= -github.com/qiniu/go-sdk/v7 v7.26.15 h1:CaKVcP29ZnOp/pqE7U3RAZxqwL7CmtMTWlV1gAV71H0= -github.com/qiniu/go-sdk/v7 v7.26.15/go.mod h1:ri7fGwbio0pRDFr8EK5TUpx0DbnpIMJ2bMSDxGWfCbk= -github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs= -github.com/qiniu/x v1.17.0 h1:OsyDKXzYp5vw9Hc7VAe4Cso1Sp50fLKGsBuDteyTevE= -github.com/qiniu/x v1.17.0/go.mod h1:AiovSOCaRijaf3fj+0CBOpR1457pn24b0Vdb1JpwhII= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= diff --git a/internal/certmgmt/deployers/sp_qiniu_cdn.go b/internal/certmgmt/deployers/sp_qiniu_cdn.go deleted file mode 100644 index fe71ce8b4..000000000 --- a/internal/certmgmt/deployers/sp_qiniu_cdn.go +++ /dev/null @@ -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/qiniu-cdn" - xmaps "github.com/certimate-go/certimate/pkg/utils/maps" -) - -func init() { - Registries.MustRegister(domain.DeploymentProviderTypeQiniuCDN, func(options *ProviderFactoryOptions) (core.Deployer, error) { - credentials := domain.AccessConfigForQiniu{} - 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{ - AccessKey: credentials.AccessKey, - SecretKey: credentials.SecretKey, - DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"), - Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"), - }) - return provider, err - }) -} diff --git a/internal/certmgmt/deployers/sp_qiniu_kodo.go b/internal/certmgmt/deployers/sp_qiniu_kodo.go deleted file mode 100644 index a12e7e7bd..000000000 --- a/internal/certmgmt/deployers/sp_qiniu_kodo.go +++ /dev/null @@ -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/qiniu-kodo" - xmaps "github.com/certimate-go/certimate/pkg/utils/maps" -) - -func init() { - Registries.MustRegister(domain.DeploymentProviderTypeQiniuKodo, func(options *ProviderFactoryOptions) (core.Deployer, error) { - credentials := domain.AccessConfigForQiniu{} - 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{ - AccessKey: credentials.AccessKey, - SecretKey: credentials.SecretKey, - Bucket: xmaps.GetString(options.ProviderExtendedConfig, "bucket"), - Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"), - }) - return provider, err - }) -} diff --git a/internal/certmgmt/deployers/sp_qiniu_pili.go b/internal/certmgmt/deployers/sp_qiniu_pili.go deleted file mode 100644 index 4a5315e86..000000000 --- a/internal/certmgmt/deployers/sp_qiniu_pili.go +++ /dev/null @@ -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/qiniu-pili" - xmaps "github.com/certimate-go/certimate/pkg/utils/maps" -) - -func init() { - Registries.MustRegister(domain.DeploymentProviderTypeQiniuPili, func(options *ProviderFactoryOptions) (core.Deployer, error) { - credentials := domain.AccessConfigForQiniu{} - 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{ - AccessKey: credentials.AccessKey, - SecretKey: credentials.SecretKey, - Hub: xmaps.GetString(options.ProviderExtendedConfig, "hub"), - DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"), - Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"), - }) - return provider, err - }) -} diff --git a/internal/certmgmt/deployers/sp_webhook.go b/internal/certmgmt/deployers/sp_webhook.go new file mode 100644 index 000000000..d21181857 --- /dev/null +++ b/internal/certmgmt/deployers/sp_webhook.go @@ -0,0 +1,51 @@ +package deployers + +import ( + "fmt" + "net/http" + + "github.com/certimate-go/certimate/internal/domain" + "github.com/certimate-go/certimate/pkg/core" + dplyimpl "github.com/certimate-go/certimate/pkg/core/deployer/providers/webhook" + xhttp "github.com/certimate-go/certimate/pkg/utils/http" + xmaps "github.com/certimate-go/certimate/pkg/utils/maps" +) + +func init() { + Registries.MustRegister(domain.DeploymentProviderTypeWebhook, func(options *ProviderFactoryOptions) (core.Deployer, error) { + credentials := domain.AccessConfigForWebhook{} + if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + mergedHeaders := make(map[string]string) + if defaultHeadersString := credentials.HeadersString; defaultHeadersString != "" { + h, err := xhttp.ParseHeaders(defaultHeadersString) + if err != nil { + return nil, fmt.Errorf("failed to parse webhook headers: %w", err) + } + for key := range h { + mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key) + } + } + if extendedHeadersString := xmaps.GetString(options.ProviderExtendedConfig, "headers"); extendedHeadersString != "" { + h, err := xhttp.ParseHeaders(extendedHeadersString) + if err != nil { + return nil, fmt.Errorf("failed to parse webhook headers: %w", err) + } + for key := range h { + mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key) + } + } + + provider, err := dplyimpl.NewDeployer(&dplyimpl.DeployerConfig{ + WebhookUrl: credentials.Url, + WebhookData: xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "webhookData", credentials.DataString), + Method: credentials.Method, + Headers: mergedHeaders, + Timeout: xmaps.GetInt(options.ProviderExtendedConfig, "timeout"), + AllowInsecureConnections: credentials.AllowInsecureConnections, + }) + return provider, err + }) +} diff --git a/pkg/core/certmgr/providers/qiniu-sslcert/qiniu_sslcert.go b/pkg/core/certmgr/providers/qiniu-sslcert/qiniu_sslcert.go deleted file mode 100644 index 5f4d566d3..000000000 --- a/pkg/core/certmgr/providers/qiniu-sslcert/qiniu_sslcert.go +++ /dev/null @@ -1,170 +0,0 @@ -package qiniusslcert - -import ( - "context" - "crypto/x509" - "fmt" - "log/slog" - "slices" - "strings" - "time" - - "github.com/qiniu/go-sdk/v7/auth" - - "github.com/certimate-go/certimate/pkg/core" - qiniusdk "github.com/certimate-go/certimate/pkg/sdk3rd/qiniu" - xcert "github.com/certimate-go/certimate/pkg/utils/cert" -) - -type ( - Provider = core.Certmgr - UploadResult = core.CertmgrUploadResult - ReplaceResult = core.CertmgrReplaceResult -) - -type CertmgrConfig struct { - // 七牛云 AccessKey。 - AccessKey string `json:"accessKey"` - // 七牛云 SecretKey。 - SecretKey string `json:"secretKey"` -} - -type Certmgr struct { - config *CertmgrConfig - logger *slog.Logger - sdkClient *qiniusdk.SslCertManager -} - -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.AccessKey, config.SecretKey) - 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 - } - - // 生成新证书名(需符合七牛云命名规则) - certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli()) - - // 查询已有证书,避免重复上传 - getSslCertListMarker := "" - for { - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: - } - - getSslCertListResp, err := c.sdkClient.GetSslCertList(ctx, getSslCertListMarker, 200) - c.logger.Debug("sdk request 'sslcert.GetList'", slog.Any("params.marker", getSslCertListMarker), slog.Any("response", getSslCertListResp)) - if err != nil { - return nil, fmt.Errorf("failed to execute sdk request 'sslcert.GetList': %w", err) - } - - for _, sslItem := range getSslCertListResp.Certs { - // 对比证书通用名称 - if !strings.EqualFold(certX509.Subject.CommonName, sslItem.CommonName) { - continue - } - - // 对比证书备用名称 - if !slices.Equal(certX509.DNSNames, sslItem.DnsNames) { - continue - } - - // 对比证书有效期 - if certX509.NotBefore.Unix() != sslItem.NotBefore { - continue - } else if certX509.NotAfter.Unix() != sslItem.NotAfter { - continue - } - - // 对比证书公钥算法 - switch certX509.PublicKeyAlgorithm { - case x509.RSA: - if !strings.EqualFold(sslItem.Encrypt, "RSA") { - continue - } - case x509.ECDSA: - if !strings.EqualFold(sslItem.Encrypt, "ECDSA") { - continue - } - case x509.Ed25519: - if !strings.EqualFold(sslItem.Encrypt, "Ed25519") { - continue - } - default: - continue - } - - // 如果以上信息都一致,则视为已存在相同证书,直接返回 - c.logger.Info("ssl certificate already exists") - return &UploadResult{ - CertId: sslItem.CertID, - CertName: sslItem.Name, - }, nil - } - - if len(getSslCertListResp.Certs) == 0 || getSslCertListResp.Marker == "" { - break - } - - getSslCertListMarker = getSslCertListResp.Marker - } - - // 上传新证书 - // REF: https://developer.qiniu.com/fusion/8593/interface-related-certificate - uploadSslCertResp, err := c.sdkClient.UploadSslCert(ctx, certName, certX509.Subject.CommonName, certPEM, privkeyPEM) - c.logger.Debug("sdk request 'sslcert.Upload'", slog.Any("response", uploadSslCertResp)) - if err != nil { - return nil, fmt.Errorf("failed to execute sdk request 'sslcert.Upload': %w", err) - } - - return &UploadResult{ - CertId: uploadSslCertResp.CertID, - CertName: certName, - }, nil -} - -func (c *Certmgr) Replace(ctx context.Context, certIdOrName string, certPEM, privkeyPEM string) (*ReplaceResult, error) { - return nil, core.ErrUnsupported -} - -func createSDKClient(accessKey, secretKey string) (*qiniusdk.SslCertManager, error) { - if secretKey == "" { - return nil, fmt.Errorf("qiniu: invalid access key") - } - if secretKey == "" { - return nil, fmt.Errorf("qiniu: invalid secret key") - } - - credential := auth.New(accessKey, secretKey) - client := qiniusdk.NewSslCertManager(credential) - return client, nil -} diff --git a/pkg/core/certmgr/providers/qiniu-sslcert/qiniu_sslcert_test.go b/pkg/core/certmgr/providers/qiniu-sslcert/qiniu_sslcert_test.go deleted file mode 100644 index 966773112..000000000 --- a/pkg/core/certmgr/providers/qiniu-sslcert/qiniu_sslcert_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package qiniusslcert_test - -import ( - "testing" - - impl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/qiniu-sslcert" - tester "github.com/certimate-go/certimate/pkg/core/certmgr/testing" -) - -var ( - fp = tester.Args("QINIUSSLCERT_") - fTestCertPath string - fTestKeyPath string - fAccessKey string - fSecretKey string -) - -func init() { - fp.DefineString(&fTestCertPath, "TESTCERTPATH") - fp.DefineString(&fTestKeyPath, "TESTKEYPATH") - fp.DefineString(&fAccessKey, "ACCESSKEY") - fp.DefineString(&fSecretKey, "SECRETKEY") -} - -/* -Shell command to run this test: - - go test -v ./qiniu_sslcert_test.go -args \ - --QINIUSSLCERT_TESTCERTPATH="/path/to/your-test-cert.pem" \ - --QINIUSSLCERT_TESTKEYPATH="/path/to/your-test-key.pem" \ - --QINIUSSLCERT_ACCESSKEY="your-access-key" \ - --QINIUSSLCERT_SECRETKEY="your-secret-key" -*/ -func TestProvider(t *testing.T) { - fp.Parse() - - t.Run("Upload", func(t *testing.T) { - provider, err := impl.NewCertmgr(&impl.CertmgrConfig{ - AccessKey: fAccessKey, - SecretKey: fSecretKey, - }) - if err != nil { - t.Errorf("err: %+v", err) - return - } - - tester.TestUpload(t, provider, tester.TestUploadArgs{CertPath: fTestCertPath, KeyPath: fTestKeyPath}) - }) -} diff --git a/pkg/core/deployer/providers/qiniu-cdn/consts.go b/pkg/core/deployer/providers/qiniu-cdn/consts.go deleted file mode 100644 index a4bbfb19e..000000000 --- a/pkg/core/deployer/providers/qiniu-cdn/consts.go +++ /dev/null @@ -1,10 +0,0 @@ -package qiniucdn - -const ( - // 匹配模式:精确匹配。 - DOMAIN_MATCH_PATTERN_EXACT = "exact" - // 匹配模式:通配符匹配。 - DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard" - // 匹配模式:证书 SAN 匹配。 - DOMAIN_MATCH_PATTERN_CERTSAN = "certsan" -) diff --git a/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn.go b/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn.go deleted file mode 100644 index 14b0816c8..000000000 --- a/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn.go +++ /dev/null @@ -1,231 +0,0 @@ -package qiniucdn - -import ( - "context" - "fmt" - "log/slog" - "strings" - - "github.com/qiniu/go-sdk/v7/auth" - "github.com/samber/lo" - - "github.com/certimate-go/certimate/pkg/core" - cmgrimpl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/qiniu-sslcert" - qiniusdk "github.com/certimate-go/certimate/pkg/sdk3rd/qiniu" - 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 { - // 七牛云 AccessKey。 - AccessKey string `json:"accessKey"` - // 七牛云 SecretKey。 - SecretKey string `json:"secretKey"` - // 域名匹配模式。 - // 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。 - DomainMatchPattern string `json:"domainMatchPattern,omitempty"` - // 加速域名(支持泛域名)。 - Domain string `json:"domain"` -} - -type Deployer struct { - config *DeployerConfig - logger *slog.Logger - sdkClient *qiniusdk.CdnManager - 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 := qiniusdk.NewCdnManager(auth.New(config.AccessKey, config.SecretKey)) - - pcertmgr, err := cmgrimpl.NewCertmgr(&cmgrimpl.CertmgrConfig{ - AccessKey: config.AccessKey, - SecretKey: config.SecretKey, - }) - 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)) - } - - // 获取待部署的域名列表 - var domains []string - switch d.config.DomainMatchPattern { - case "", DOMAIN_MATCH_PATTERN_EXACT: - { - domain := normalizeDomain(d.config.Domain) - if domain == "" { - return nil, fmt.Errorf("config `domain` is required") - } - - domains = []string{domain} - } - - case DOMAIN_MATCH_PATTERN_WILDCARD: - { - if d.config.Domain == "" { - return nil, fmt.Errorf("config `domain` is required") - } - - if strings.HasPrefix(d.config.Domain, "*.") { - domainCandidates, err := d.getAllDomains(ctx) - if err != nil { - return nil, err - } - - domains = lo.Filter(domainCandidates, func(domain string, _ int) bool { - return xcerthostname.IsMatch(d.config.Domain, domain) - }) - if len(domains) == 0 { - return nil, fmt.Errorf("could not find any domains matched by wildcard") - } - } else { - domains = []string{d.config.Domain} - } - } - - case DOMAIN_MATCH_PATTERN_CERTSAN: - { - domainCandidates, err := d.getAllDomains(ctx) - if err != nil { - return nil, err - } - - domains = lo.Filter(domainCandidates, func(domain string, _ int) bool { - return xcerthostname.IsMatchByCertificatePEM(certPEM, domain) - }) - if len(domains) == 0 { - return nil, fmt.Errorf("could not find any domains matched by certificate") - } - } - - default: - return nil, fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern) - } - - // 批量更新域名证书 - if len(domains) == 0 { - d.logger.Info("no cdn domains to deploy") - } else { - d.logger.Info("found cdn domains to deploy", slog.Any("domains", domains)) - - if err := xloop.ForRangeAllWithContext(ctx, domains, func(ctx context.Context, domain string, _ int) error { - return d.updateDomainCertificate(ctx, domain, upres.CertId) - }); err != nil { - return nil, err - } - } - - return &DeployResult{}, nil -} - -func (d *Deployer) getAllDomains(ctx context.Context) ([]string, error) { - domains := make([]string, 0) - - // 查询域名列表 - // REF: https://developer.qiniu.com/fusion/4246/the-domain-name - getDomainListMarker := "" - for { - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: - } - - getDomainListResp, err := d.sdkClient.GetDomainList(ctx, getDomainListMarker, 100) - d.logger.Debug("sdk request 'cdn.GetDomainList'", slog.String("params.marker", getDomainListMarker), slog.Any("response", getDomainListResp)) - if err != nil { - return nil, fmt.Errorf("failed to execute sdk request 'cdn.GetDomainList': %w", err) - } - - ignoredStatuses := []string{"frozen", "offlined"} - for _, domainItem := range getDomainListResp.Domains { - if lo.Contains(ignoredStatuses, domainItem.OperatingState) { - continue - } - - domains = append(domains, domainItem.Name) - } - - if len(getDomainListResp.Domains) == 0 || getDomainListResp.Marker == "" { - break - } - - getDomainListMarker = getDomainListResp.Marker - } - - return domains, nil -} - -func (d *Deployer) updateDomainCertificate(ctx context.Context, domain string, cloudCertId string) error { - // 获取域名信息 - // REF: https://developer.qiniu.com/fusion/4246/the-domain-name - getDomainInfoResp, err := d.sdkClient.GetDomainInfo(ctx, domain) - d.logger.Debug("sdk request 'cdn.GetDomainInfo'", slog.String("params.domain", domain), slog.Any("response", getDomainInfoResp)) - if err != nil { - return fmt.Errorf("failed to execute sdk request 'cdn.GetDomainInfo': %w", err) - } - - // 判断域名是否已启用 HTTPS - // 如果已启用,修改域名证书;否则,启用 HTTPS - // REF: https://developer.qiniu.com/fusion/4246/the-domain-name - if getDomainInfoResp.Https == nil || getDomainInfoResp.Https.CertID == "" { - enableDomainHttpsResp, err := d.sdkClient.EnableDomainHttps(ctx, domain, cloudCertId, true, true) - d.logger.Debug("sdk request 'cdn.EnableDomainHttps'", slog.String("params.domain", domain), slog.String("params.certId", cloudCertId), slog.Any("response", enableDomainHttpsResp)) - if err != nil { - return fmt.Errorf("failed to execute sdk request 'cdn.EnableDomainHttps': %w", err) - } - } else if getDomainInfoResp.Https.CertID != cloudCertId { - modifyDomainHttpsConfResp, err := d.sdkClient.ModifyDomainHttpsConf(ctx, domain, cloudCertId, getDomainInfoResp.Https.ForceHttps, getDomainInfoResp.Https.Http2Enable) - d.logger.Debug("sdk request 'cdn.ModifyDomainHttpsConf'", slog.String("params.domain", domain), slog.String("params.certId", cloudCertId), slog.Any("response", modifyDomainHttpsConfResp)) - if err != nil { - return fmt.Errorf("failed to execute sdk request 'cdn.ModifyDomainHttpsConf': %w", err) - } - } - - return nil -} - -func normalizeDomain(domain string) string { - // "*.example.com" → ".example.com",适配七牛云 CDN 的泛域名参数要求 - if strings.HasPrefix(domain, "*.") { - return strings.TrimPrefix(domain, "*") - } - return domain -} diff --git a/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn_test.go b/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn_test.go deleted file mode 100644 index 15a5f0e71..000000000 --- a/pkg/core/deployer/providers/qiniu-cdn/qiniu_cdn_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package qiniucdn_test - -import ( - "testing" - - impl "github.com/certimate-go/certimate/pkg/core/deployer/providers/qiniu-cdn" - tester "github.com/certimate-go/certimate/pkg/core/deployer/testing" -) - -var ( - fp = tester.Args("QINIUCDN_") - fTestCertPath string - fTestKeyPath string - fAccessKey string - fSecretKey string - fDomain string -) - -func init() { - fp.DefineString(&fTestCertPath, "TESTCERTPATH") - fp.DefineString(&fTestKeyPath, "TESTKEYPATH") - fp.DefineString(&fAccessKey, "ACCESSKEY") - fp.DefineString(&fSecretKey, "SECRETKEY") - fp.DefineString(&fDomain, "DOMAIN") -} - -/* -Shell command to run this test: - - go test -v ./qiniu_cdn_test.go -args \ - --QINIUCDN_TESTCERTPATH="/path/to/your-test-cert.pem" \ - --QINIUCDN_TESTKEYPATH="/path/to/your-test-key.pem" \ - --QINIUCDN_ACCESSKEY="your-access-key" \ - --QINIUCDN_SECRETKEY="your-secret-key" \ - --QINIUCDN_DOMAIN="example.com" -*/ -func TestProvider(t *testing.T) { - fp.Parse() - - t.Run("Deploy", func(t *testing.T) { - provider, err := impl.NewDeployer(&impl.DeployerConfig{ - AccessKey: fAccessKey, - SecretKey: fSecretKey, - DomainMatchPattern: impl.DOMAIN_MATCH_PATTERN_EXACT, - Domain: fDomain, - }) - if err != nil { - t.Errorf("err: %+v", err) - return - } - - tester.TestDeploy(t, provider, tester.TestDeployArgs{CertPath: fTestCertPath, KeyPath: fTestKeyPath}) - }) -} diff --git a/pkg/core/deployer/providers/qiniu-kodo/qiniu_kodo.go b/pkg/core/deployer/providers/qiniu-kodo/qiniu_kodo.go deleted file mode 100644 index e4754f2ff..000000000 --- a/pkg/core/deployer/providers/qiniu-kodo/qiniu_kodo.go +++ /dev/null @@ -1,94 +0,0 @@ -package qiniukodo - -import ( - "context" - "fmt" - "log/slog" - - "github.com/qiniu/go-sdk/v7/auth" - - "github.com/certimate-go/certimate/pkg/core" - cmgrimpl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/qiniu-sslcert" - qiniusdk "github.com/certimate-go/certimate/pkg/sdk3rd/qiniu" -) - -type ( - Provider = core.Deployer - DeployResult = core.DeployerDeployResult -) - -type DeployerConfig struct { - // 七牛云 AccessKey。 - AccessKey string `json:"accessKey"` - // 七牛云 SecretKey。 - SecretKey string `json:"secretKey"` - // 存储桶名。暂时无用。 - Bucket string `json:"bucket"` - // 自定义域名(不支持泛域名)。 - Domain string `json:"domain"` -} - -type Deployer struct { - config *DeployerConfig - logger *slog.Logger - sdkClient *qiniusdk.KodoManager - 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 := qiniusdk.NewKodoManager(auth.New(config.AccessKey, config.SecretKey)) - - pcertmgr, err := cmgrimpl.NewCertmgr(&cmgrimpl.CertmgrConfig{ - AccessKey: config.AccessKey, - SecretKey: config.SecretKey, - }) - 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.Domain == "" { - return nil, fmt.Errorf("config `domain` is required") - } - - // 上传证书 - 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)) - } - - // 绑定空间域名证书 - bindBucketCertResp, err := d.sdkClient.BindBucketCert(ctx, d.config.Domain, upres.CertId) - d.logger.Debug("sdk request 'kodo.BindCert'", slog.String("params.domain", d.config.Domain), slog.String("params.certId", upres.CertId), slog.Any("response", bindBucketCertResp)) - if err != nil { - return nil, fmt.Errorf("failed to execute sdk request 'kodo.BindCert': %w", err) - } - - return &DeployResult{}, nil -} diff --git a/pkg/core/deployer/providers/qiniu-kodo/qiniu_kodo_test.go b/pkg/core/deployer/providers/qiniu-kodo/qiniu_kodo_test.go deleted file mode 100644 index cc514bff0..000000000 --- a/pkg/core/deployer/providers/qiniu-kodo/qiniu_kodo_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package qiniukodo_test - -import ( - "testing" - - impl "github.com/certimate-go/certimate/pkg/core/deployer/providers/qiniu-kodo" - tester "github.com/certimate-go/certimate/pkg/core/deployer/testing" -) - -var ( - fp = tester.Args("QINIUKODO_") - fTestCertPath string - fTestKeyPath string - fAccessKey string - fSecretKey string - fBucket string - fDomain string -) - -func init() { - fp.DefineString(&fTestCertPath, "TESTCERTPATH") - fp.DefineString(&fTestKeyPath, "TESTKEYPATH") - fp.DefineString(&fAccessKey, "ACCESSKEY") - fp.DefineString(&fSecretKey, "SECRETKEY") - fp.DefineString(&fBucket, "BUCKET") - fp.DefineString(&fDomain, "DOMAIN") -} - -/* -Shell command to run this test: - - go test -v ./qiniu_kodo_test.go -args \ - --QINIUKODO_TESTCERTPATH="/path/to/your-test-cert.pem" \ - --QINIUKODO_TESTKEYPATH="/path/to/your-test-key.pem" \ - --QINIUKODO_ACCESSKEY="your-access-key" \ - --QINIUKODO_SECRETKEY="your-secret-key" \ - --QINIUKODO_BUCKET="your-bucket" \ - --QINIUKODO_DOMAIN="example.com" -*/ -func TestProvider(t *testing.T) { - fp.Parse() - - t.Run("Deploy", func(t *testing.T) { - provider, err := impl.NewDeployer(&impl.DeployerConfig{ - AccessKey: fAccessKey, - SecretKey: fSecretKey, - Bucket: fBucket, - Domain: fDomain, - }) - if err != nil { - t.Errorf("err: %+v", err) - return - } - - tester.TestDeploy(t, provider, tester.TestDeployArgs{CertPath: fTestCertPath, KeyPath: fTestKeyPath}) - }) -} diff --git a/pkg/core/deployer/providers/qiniu-pili/consts.go b/pkg/core/deployer/providers/qiniu-pili/consts.go deleted file mode 100644 index 256a9beeb..000000000 --- a/pkg/core/deployer/providers/qiniu-pili/consts.go +++ /dev/null @@ -1,8 +0,0 @@ -package qiniupili - -const ( - // 匹配模式:精确匹配。 - DOMAIN_MATCH_PATTERN_EXACT = "exact" - // 匹配模式:证书 SAN 匹配。 - DOMAIN_MATCH_PATTERN_CERTSAN = "certsan" -) diff --git a/pkg/core/deployer/providers/qiniu-pili/qiniu_pili.go b/pkg/core/deployer/providers/qiniu-pili/qiniu_pili.go deleted file mode 100644 index 3035c8725..000000000 --- a/pkg/core/deployer/providers/qiniu-pili/qiniu_pili.go +++ /dev/null @@ -1,174 +0,0 @@ -package qiniupili - -import ( - "context" - "fmt" - "log/slog" - - "github.com/qiniu/go-sdk/v7/pili" - "github.com/samber/lo" - - "github.com/certimate-go/certimate/pkg/core" - cmgrimpl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/qiniu-sslcert" - 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 { - // 七牛云 AccessKey。 - AccessKey string `json:"accessKey"` - // 七牛云 SecretKey。 - SecretKey string `json:"secretKey"` - // 直播空间名。 - Hub string `json:"hub"` - // 域名匹配模式。 - // 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。 - DomainMatchPattern string `json:"domainMatchPattern,omitempty"` - // 直播流域名(不支持泛域名)。 - Domain string `json:"domain"` -} - -type Deployer struct { - config *DeployerConfig - logger *slog.Logger - sdkClient *pili.Manager - 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") - } - - manager := pili.NewManager(pili.ManagerConfig{AccessKey: config.AccessKey, SecretKey: config.SecretKey}) - - pcertmgr, err := cmgrimpl.NewCertmgr(&cmgrimpl.CertmgrConfig{ - AccessKey: config.AccessKey, - SecretKey: config.SecretKey, - }) - if err != nil { - return nil, fmt.Errorf("could not create certmgr: %w", err) - } - - return &Deployer{ - config: config, - logger: slog.Default(), - sdkClient: manager, - 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.Domain == "" { - return nil, fmt.Errorf("config `domain` is required") - } - - // 上传证书 - 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)) - } - - // 获取待部署的域名列表 - var domains []string - switch d.config.DomainMatchPattern { - case "", DOMAIN_MATCH_PATTERN_EXACT: - { - if d.config.Domain == "" { - return nil, fmt.Errorf("config `domain` is required") - } - - domains = []string{d.config.Domain} - } - - case DOMAIN_MATCH_PATTERN_CERTSAN: - { - domainCandidates, err := d.getAllDomainsByHub(ctx, d.config.Hub) - if err != nil { - return nil, err - } - - domains = lo.Filter(domainCandidates, func(domain string, _ int) bool { - return xcerthostname.IsMatchByCertificatePEM(certPEM, domain) - }) - if len(domains) == 0 { - return nil, fmt.Errorf("could not find any domains matched by certificate") - } - } - - default: - return nil, fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern) - } - - // 批量更新域名证书 - if len(domains) == 0 { - d.logger.Info("no pili domains to deploy") - } else { - d.logger.Info("found pili domains to deploy", slog.Any("domains", domains)) - - if err := xloop.ForRangeAllWithContext(ctx, domains, func(ctx context.Context, domain string, _ int) error { - return d.updateDomainCertificate(ctx, d.config.Hub, domain, upres.CertName) - }); err != nil { - return nil, err - } - } - - return &DeployResult{}, nil -} - -func (d *Deployer) getAllDomainsByHub(ctx context.Context, hub string) ([]string, error) { - domains := make([]string, 0) - - // 查询域名列表 - // REF: https://developer.qiniu.com/pili/9910/pili-service-sdk#6 - getDomainListReq := pili.GetDomainsListRequest{ - Hub: hub, - } - getDomainListResp, err := d.sdkClient.GetDomainsList(ctx, getDomainListReq) - d.logger.Debug("sdk request 'pili.GetDomainsList'", slog.Any("request", getDomainListReq), slog.Any("response", getDomainListResp)) - if err != nil { - return nil, fmt.Errorf("failed to execute sdk request 'pili.GetDomainsList': %w", err) - } - - for _, domainItem := range getDomainListResp.Domains { - domains = append(domains, domainItem.Domain) - } - - return domains, nil -} - -func (d *Deployer) updateDomainCertificate(ctx context.Context, hub string, domain string, cloudCertName string) error { - // 修改域名证书配置 - // REF: https://developer.qiniu.com/pili/9910/pili-service-sdk#6 - setDomainCertReq := pili.SetDomainCertRequest{ - Hub: hub, - Domain: domain, - CertName: cloudCertName, - } - err := d.sdkClient.SetDomainCert(ctx, setDomainCertReq) - d.logger.Debug("sdk request 'pili.SetDomainCert'", slog.Any("request", setDomainCertReq)) - if err != nil { - return fmt.Errorf("failed to execute sdk request 'pili.SetDomainCert': %w", err) - } - - return nil -} diff --git a/pkg/core/deployer/providers/qiniu-pili/qiniu_pili_test.go b/pkg/core/deployer/providers/qiniu-pili/qiniu_pili_test.go deleted file mode 100644 index a3f5484e5..000000000 --- a/pkg/core/deployer/providers/qiniu-pili/qiniu_pili_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package qiniupili_test - -import ( - "testing" - - impl "github.com/certimate-go/certimate/pkg/core/deployer/providers/qiniu-pili" - tester "github.com/certimate-go/certimate/pkg/core/deployer/testing" -) - -var ( - fp = tester.Args("QINIUPILI_") - fTestCertPath string - fTestKeyPath string - fAccessKey string - fSecretKey string - fHub string - fDomain string -) - -func init() { - fp.DefineString(&fTestCertPath, "TESTCERTPATH") - fp.DefineString(&fTestKeyPath, "TESTKEYPATH") - fp.DefineString(&fAccessKey, "ACCESSKEY") - fp.DefineString(&fSecretKey, "SECRETKEY") - fp.DefineString(&fHub, "HUB") - fp.DefineString(&fDomain, "DOMAIN") -} - -/* -Shell command to run this test: - - go test -v ./qiniu_pili_test.go -args \ - --QINIUPILI_TESTCERTPATH="/path/to/your-test-cert.pem" \ - --QINIUPILI_TESTKEYPATH="/path/to/your-test-key.pem" \ - --QINIUPILI_ACCESSKEY="your-access-key" \ - --QINIUPILI_SECRETKEY="your-secret-key" \ - --QINIUPILI_HUB="your-hub-name" \ - --QINIUPILI_DOMAIN="example.com" -*/ -func TestProvider(t *testing.T) { - fp.Parse() - - t.Run("Deploy", func(t *testing.T) { - provider, err := impl.NewDeployer(&impl.DeployerConfig{ - AccessKey: fAccessKey, - SecretKey: fSecretKey, - DomainMatchPattern: impl.DOMAIN_MATCH_PATTERN_EXACT, - Domain: fDomain, - }) - if err != nil { - t.Errorf("err: %+v", err) - return - } - - tester.TestDeploy(t, provider, tester.TestDeployArgs{CertPath: fTestCertPath, KeyPath: fTestKeyPath}) - }) -} diff --git a/pkg/core/deployer/providers/webhook/webhook.go b/pkg/core/deployer/providers/webhook/webhook.go new file mode 100644 index 000000000..638e39b0a --- /dev/null +++ b/pkg/core/deployer/providers/webhook/webhook.go @@ -0,0 +1,226 @@ +package webhook + +import ( + "context" + "crypto/tls" + "encoding/json" + "fmt" + "log/slog" + "mime" + "net/http" + "net/url" + "strings" + "time" + + "github.com/go-resty/resty/v2" + + "github.com/certimate-go/certimate/pkg/core" + xcert "github.com/certimate-go/certimate/pkg/utils/cert" + xcertx509 "github.com/certimate-go/certimate/pkg/utils/cert/x509" + xmaps "github.com/certimate-go/certimate/pkg/utils/maps" +) + +type ( + Provider = core.Deployer + DeployResult = core.DeployerDeployResult +) + +type DeployerConfig struct { + // Webhook URL。 + WebhookUrl string `json:"webhookUrl"` + // Webhook 回调数据(application/json 或 application/x-www-form-urlencoded 格式)。 + WebhookData string `json:"webhookData,omitempty"` + // 请求谓词。 + // 零值时默认值 "POST"。 + Method string `json:"method,omitempty"` + // 请求标头。 + Headers map[string]string `json:"headers,omitempty"` + // 请求超时(单位:秒)。 + // 零值时默认值 30。 + Timeout int `json:"timeout,omitempty"` + // 是否允许不安全的连接。 + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` +} + +type Deployer struct { + config *DeployerConfig + logger *slog.Logger + httpClient *resty.Client +} + +var _ Provider = (*Deployer)(nil) + +const ( + contentTypeJson = "application/json" + contentTypeForm = "application/x-www-form-urlencoded" + contentTypeMultipart = "multipart/form-data" +) + +var allowedContentTypes = map[string]bool{ + contentTypeJson: true, + contentTypeForm: true, + contentTypeMultipart: true, +} + +var allowedMethods = map[string]bool{ + http.MethodGet: true, + http.MethodPost: true, + http.MethodPut: true, + http.MethodPatch: true, + http.MethodDelete: true, +} + +func NewDeployer(config *DeployerConfig) (*Deployer, error) { + if config == nil { + return nil, fmt.Errorf("the configuration of the deployer provider is nil") + } + + client := resty.New(). + SetTimeout(30 * time.Second). + SetRetryCount(3). + SetRetryWaitTime(5 * time.Second). + AddRetryCondition(func(resp *resty.Response, _ error) bool { + return resp == nil || resp.StatusCode() >= 500 + }) + if config.Timeout > 0 { + client.SetTimeout(time.Duration(config.Timeout) * time.Second) + } + if config.AllowInsecureConnections { + client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}) + } + + return &Deployer{ + config: config, + logger: slog.Default(), + httpClient: 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) { + // 解析证书内容 + certX509, err := xcert.ParseCertificateFromPEM(certPEM) + if err != nil { + return nil, fmt.Errorf("failed to parse x509: %w", err) + } + + // 提取服务器证书和中间证书 + serverCertPEM, issuerCertPEM, err := xcert.ExtractCertificatesFromPEM(certPEM) + if err != nil { + return nil, fmt.Errorf("failed to extract certs: %w", err) + } + + // 处理 Webhook URL + webhookUrl, err := url.Parse(d.config.WebhookUrl) + if err != nil { + return nil, fmt.Errorf("failed to parse webhook url: %w", err) + } else if webhookUrl.Scheme != "http" && webhookUrl.Scheme != "https" { + return nil, fmt.Errorf("unsupported webhook url scheme '%s'", webhookUrl.Scheme) + } + + // 处理 Webhook 请求谓词 + webhookMethod := strings.ToUpper(d.config.Method) + if webhookMethod == "" { + webhookMethod = http.MethodPost + } else if !allowedMethods[webhookMethod] { + return nil, fmt.Errorf("unsupported webhook request method '%s'", webhookMethod) + } + + // 处理 Webhook 请求标头 + webhookHeaders := make(http.Header) + for k, v := range d.config.Headers { + webhookHeaders.Set(k, v) + } + + // 处理 Webhook 请求内容类型 + webhookContentType := webhookHeaders.Get("Content-Type") + if webhookContentType == "" { + webhookContentType = contentTypeJson + webhookHeaders.Set("Content-Type", contentTypeJson) + } else if mediaType, _, err := mime.ParseMediaType(webhookContentType); err != nil || !allowedContentTypes[mediaType] { + return nil, fmt.Errorf("unsupported webhook content type '%s'", webhookContentType) + } + + // 处理 Webhook 请求数据 + var webhookData any + if d.config.WebhookData == "" { + webhookData = map[string]string{ + "name": strings.Join(xcertx509.GetSubjectAltNames(certX509), ";"), + "cert": certPEM, + "privkey": privkeyPEM, + } + } else { + err = json.Unmarshal([]byte(d.config.WebhookData), &webhookData) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal webhook data: %w", err) + } + + if webhookMethod == http.MethodGet || webhookContentType == contentTypeForm || webhookContentType == contentTypeMultipart { + temp := make(map[string]string) + jsonb, err := json.Marshal(webhookData) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal webhook data: %w", err) + } else if err := json.Unmarshal(jsonb, &temp); err != nil { + return nil, fmt.Errorf("failed to unmarshal webhook data: %w", err) + } else { + webhookData = temp + } + } + } + + // 替换变量值 + webhookUrl.Path = strings.ReplaceAll(webhookUrl.Path, "${CERTIMATE_DEPLOYER_COMMONNAME}", url.PathEscape(xcertx509.GetSubjectCommonName(certX509))) + xmaps.DeepReplaceValueUnsafe(webhookData, "${CERTIMATE_DEPLOYER_COMMONNAME}", xcertx509.GetSubjectCommonName(certX509)) + xmaps.DeepReplaceValueUnsafe(webhookData, "${CERTIMATE_DEPLOYER_SUBJECTALTNAMES}", strings.Join(xcertx509.GetSubjectAltNames(certX509), ";")) + xmaps.DeepReplaceValueUnsafe(webhookData, "${CERTIMATE_DEPLOYER_CERTIFICATE}", certPEM) + xmaps.DeepReplaceValueUnsafe(webhookData, "${CERTIMATE_DEPLOYER_CERTIFICATE_SERVER}", serverCertPEM) + xmaps.DeepReplaceValueUnsafe(webhookData, "${CERTIMATE_DEPLOYER_CERTIFICATE_INTERMEDIA}", issuerCertPEM) + xmaps.DeepReplaceValueUnsafe(webhookData, "${CERTIMATE_DEPLOYER_PRIVATEKEY}", privkeyPEM) + + // 兼容旧版变量 + // TODO: remove in future version + webhookUrl.Path = strings.ReplaceAll(webhookUrl.Path, "${DOMAIN}", url.PathEscape(certX509.Subject.CommonName)) + xmaps.DeepReplaceValueUnsafe(webhookData, "${DOMAIN}", certX509.Subject.CommonName) + xmaps.DeepReplaceValueUnsafe(webhookData, "${DOMAINS}", strings.Join(certX509.DNSNames, ";")) + xmaps.DeepReplaceValueUnsafe(webhookData, "${CERTIFICATE}", certPEM) + xmaps.DeepReplaceValueUnsafe(webhookData, "${SERVER_CERTIFICATE}", serverCertPEM) + xmaps.DeepReplaceValueUnsafe(webhookData, "${INTERMEDIA_CERTIFICATE}", issuerCertPEM) + xmaps.DeepReplaceValueUnsafe(webhookData, "${PRIVATE_KEY}", privkeyPEM) + + // 生成请求 + // 其中 GET 请求需转换为查询参数 + req := d.httpClient.R().SetHeaderMultiValues(webhookHeaders) + req.URL = webhookUrl.String() + req.Method = webhookMethod + if webhookMethod == http.MethodGet { + req.SetQueryParams(webhookData.(map[string]string)) + } else { + switch webhookContentType { + case contentTypeJson: + req.SetBody(webhookData) + case contentTypeForm: + req.SetFormData(webhookData.(map[string]string)) + case contentTypeMultipart: + req.SetMultipartFormData(webhookData.(map[string]string)) + } + } + + // 发送请求 + resp, err := req.Send() + if err != nil { + return nil, fmt.Errorf("failed to send webhook request: %w", err) + } else if resp.IsError() { + return nil, fmt.Errorf("unexpected webhook response status code: %d", resp.StatusCode()) + } + + d.logger.Debug("webhook responded", slog.Any("response", resp.String())) + + return &DeployResult{}, nil +} diff --git a/pkg/core/deployer/providers/webhook/webhook_test.go b/pkg/core/deployer/providers/webhook/webhook_test.go new file mode 100644 index 000000000..41e7ad3b4 --- /dev/null +++ b/pkg/core/deployer/providers/webhook/webhook_test.go @@ -0,0 +1,57 @@ +package webhook_test + +import ( + "testing" + + impl "github.com/certimate-go/certimate/pkg/core/deployer/providers/webhook" + tester "github.com/certimate-go/certimate/pkg/core/deployer/testing" +) + +var ( + fp = tester.Args("WEBHOOK_") + fTestCertPath string + fTestKeyPath string + fWebhookUrl string + fWebhookContentType string + fWebhookData string +) + +func init() { + fp.DefineString(&fTestCertPath, "TESTCERTPATH") + fp.DefineString(&fTestKeyPath, "TESTKEYPATH") + fp.DefineString(&fWebhookUrl, "URL") + fp.DefineString(&fWebhookContentType, "CONTENTTYPE", "application/json") + fp.DefineString(&fWebhookData, "DATA") +} + +/* +Shell command to run this test: + + go test -v ./webhook_test.go -args \ + --WEBHOOK_TESTCERTPATH="/path/to/your-test-cert.pem" \ + --WEBHOOK_TESTKEYPATH="/path/to/your-test-key.pem" \ + --WEBHOOK_URL="https://example.com/your-webhook-url" \ + --WEBHOOK_CONTENTTYPE="application/json" \ + --WEBHOOK_DATA="{\"certificate\":\"${CERTIFICATE}\",\"privateKey\":\"${PRIVATE_KEY}\"}" +*/ +func TestProvider(t *testing.T) { + fp.Parse() + + t.Run("Deploy", func(t *testing.T) { + provider, err := impl.NewDeployer(&impl.DeployerConfig{ + WebhookUrl: fWebhookUrl, + WebhookData: fWebhookData, + Method: "POST", + Headers: map[string]string{ + "Content-Type": fWebhookContentType, + }, + AllowInsecureConnections: true, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + tester.TestDeploy(t, provider, tester.TestDeployArgs{CertPath: fTestCertPath, KeyPath: fTestKeyPath}) + }) +} diff --git a/pkg/sdk3rd/qiniu/auth.go b/pkg/sdk3rd/qiniu/auth.go deleted file mode 100644 index a1668dd76..000000000 --- a/pkg/sdk3rd/qiniu/auth.go +++ /dev/null @@ -1,30 +0,0 @@ -package qiniu - -import ( - "net/http" - - "github.com/qiniu/go-sdk/v7/auth" - "github.com/qiniu/go-sdk/v7/client" -) - -type transport struct { - http.RoundTripper - mac *auth.Credentials -} - -func newTransport(mac *auth.Credentials, tr http.RoundTripper) *transport { - if tr == nil { - tr = client.DefaultTransport - } - return &transport{tr, mac} -} - -func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) { - token, err := t.mac.SignRequestV2(req) - if err != nil { - return nil, err - } - - req.Header.Set("Authorization", "Qiniu "+token) - return t.RoundTripper.RoundTrip(req) -} diff --git a/pkg/sdk3rd/qiniu/cdn.go b/pkg/sdk3rd/qiniu/cdn.go deleted file mode 100644 index 627b7c902..000000000 --- a/pkg/sdk3rd/qiniu/cdn.go +++ /dev/null @@ -1,130 +0,0 @@ -package qiniu - -import ( - "context" - "fmt" - "net/http" - "net/url" - - "github.com/qiniu/go-sdk/v7/auth" - "github.com/qiniu/go-sdk/v7/client" -) - -type CdnManager struct { - client *client.Client -} - -func NewCdnManager(mac *auth.Credentials) *CdnManager { - if mac == nil { - mac = auth.Default() - } - - client := &client.Client{Client: &http.Client{Transport: newTransport(mac, nil)}} - return &CdnManager{client: client} -} - -type GetDomainListResponse struct { - Code *int `json:"code,omitempty"` - Error *string `json:"error,omitempty"` - Marker string `json:"marker"` - Domains []*struct { - Name string `json:"name"` - Type string `json:"type"` - CName string `json:"cname"` - OperatingState string `json:"operatingState"` - OperatingStateDesc string `json:"operatingStateDesc"` - CreateAt string `json:"createAt"` - ModifyAt string `json:"modifyAt"` - } `json:"domains"` -} - -func (m *CdnManager) GetDomainList(ctx context.Context, marker string, limit int) (*GetDomainListResponse, error) { - query := url.Values{} - if marker != "" { - query.Set("marker", marker) - } - if limit > 0 { - query.Set("limit", fmt.Sprintf("%d", limit)) - } - - resp := new(GetDomainListResponse) - if err := m.client.Call(ctx, resp, http.MethodGet, urlf("domain?%s", query.Encode()), nil); err != nil { - return nil, err - } - return resp, nil -} - -type GetDomainInfoResponse struct { - Code *int `json:"code,omitempty"` - Error *string `json:"error,omitempty"` - Name string `json:"name"` - Type string `json:"type"` - CName string `json:"cname"` - Https *struct { - CertID string `json:"certId"` - ForceHttps bool `json:"forceHttps"` - Http2Enable bool `json:"http2Enable"` - } `json:"https"` - PareDomain string `json:"pareDomain"` - OperationType string `json:"operationType"` - OperatingState string `json:"operatingState"` - OperatingStateDesc string `json:"operatingStateDesc"` - CreateAt string `json:"createAt"` - ModifyAt string `json:"modifyAt"` -} - -func (m *CdnManager) GetDomainInfo(ctx context.Context, domain string) (*GetDomainInfoResponse, error) { - resp := new(GetDomainInfoResponse) - if err := m.client.Call(ctx, resp, http.MethodGet, urlf("domain/%s", domain), nil); err != nil { - return nil, err - } - return resp, nil -} - -type ModifyDomainHttpsConfRequest struct { - CertID string `json:"certId"` - ForceHttps bool `json:"forceHttps"` - Http2Enable bool `json:"http2Enable"` -} - -type ModifyDomainHttpsConfResponse struct { - Code *int `json:"code,omitempty"` - Error *string `json:"error,omitempty"` -} - -func (m *CdnManager) ModifyDomainHttpsConf(ctx context.Context, domain string, certId string, forceHttps bool, http2Enable bool) (*ModifyDomainHttpsConfResponse, error) { - req := &ModifyDomainHttpsConfRequest{ - CertID: certId, - ForceHttps: forceHttps, - Http2Enable: http2Enable, - } - resp := new(ModifyDomainHttpsConfResponse) - if err := m.client.CallWithJson(ctx, resp, http.MethodPut, urlf("domain/%s/httpsconf", domain), nil, req); err != nil { - return nil, err - } - return resp, nil -} - -type EnableDomainHttpsRequest struct { - CertID string `json:"certId"` - ForceHttps bool `json:"forceHttps"` - Http2Enable bool `json:"http2Enable"` -} - -type EnableDomainHttpsResponse struct { - Code *int `json:"code,omitempty"` - Error *string `json:"error,omitempty"` -} - -func (m *CdnManager) EnableDomainHttps(ctx context.Context, domain string, certId string, forceHttps bool, http2Enable bool) (*EnableDomainHttpsResponse, error) { - req := &EnableDomainHttpsRequest{ - CertID: certId, - ForceHttps: forceHttps, - Http2Enable: http2Enable, - } - resp := new(EnableDomainHttpsResponse) - 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/doc.go b/pkg/sdk3rd/qiniu/doc.go deleted file mode 100644 index 5931d1ca5..000000000 --- a/pkg/sdk3rd/qiniu/doc.go +++ /dev/null @@ -1,3 +0,0 @@ -// An extension SDK client for Qiniu. -// Based on github.com/qiniu/go-sdk. -package qiniu \ No newline at end of file diff --git a/pkg/sdk3rd/qiniu/kodo.go b/pkg/sdk3rd/qiniu/kodo.go deleted file mode 100644 index 6a3245a28..000000000 --- a/pkg/sdk3rd/qiniu/kodo.go +++ /dev/null @@ -1,44 +0,0 @@ -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 deleted file mode 100644 index f9784270f..000000000 --- a/pkg/sdk3rd/qiniu/sslcert.go +++ /dev/null @@ -1,80 +0,0 @@ -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 deleted file mode 100644 index 0957a3102..000000000 --- a/pkg/sdk3rd/qiniu/util.go +++ /dev/null @@ -1,14 +0,0 @@ -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/components/access/forms/AccessConfigFieldsProvider.tsx b/ui/src/components/access/forms/AccessConfigFieldsProvider.tsx index 47f6c35c1..bc802e1da 100644 --- a/ui/src/components/access/forms/AccessConfigFieldsProvider.tsx +++ b/ui/src/components/access/forms/AccessConfigFieldsProvider.tsx @@ -90,7 +90,6 @@ import AccessConfigFieldsProviderPorkbun from "./AccessConfigFieldsProviderPorkb import AccessConfigFieldsProviderPowerDNS from "./AccessConfigFieldsProviderPowerDNS"; import AccessConfigFieldsProviderProxmoxVE from "./AccessConfigFieldsProviderProxmoxVE"; import AccessConfigFieldsProviderQingCloud from "./AccessConfigFieldsProviderQingCloud"; -import AccessConfigFieldsProviderQiniu from "./AccessConfigFieldsProviderQiniu"; import AccessConfigFieldsProviderRainYun from "./AccessConfigFieldsProviderRainYun"; import AccessConfigFieldsProviderRatPanel from "./AccessConfigFieldsProviderRatPanel"; import AccessConfigFieldsProviderRegru from "./AccessConfigFieldsProviderRegru"; @@ -215,7 +214,6 @@ const providerComponentMap: Partial : initComponent()); // eslint-disable-next-line react-hooks/exhaustive-deps }, [provider, envelope]); - useEffect(() => setComponent(initComponent()), deps); + useEffect(() => { + setComponent(envelope ? : initComponent()); + }, deps); return component; }; diff --git a/ui/src/components/access/forms/AccessConfigFieldsProviderQiniu.tsx b/ui/src/components/access/forms/AccessConfigFieldsProviderQiniu.tsx deleted file mode 100644 index 6c017dbdb..000000000 --- a/ui/src/components/access/forms/AccessConfigFieldsProviderQiniu.tsx +++ /dev/null @@ -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 AccessConfigFormFieldsProviderQiniu = () => { - const { i18n, t } = useTranslation(); - - const { parentNamePath } = useFormNestedFieldsContext(); - const formSchema = z.object({ - [parentNamePath]: getSchema({ i18n }), - }); - const formRule = createSchemaFieldRule(formSchema); - const initialValues = getInitialValues(); - - return ( - <> - } - > - - - - } - > - - - - ); -}; - -const getInitialValues = (): Nullish>> => { - return { - accessKey: "", - secretKey: "", - }; -}; - -const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType }) => { - const { t: _ } = i18n; - - return z.object({ - accessKey: z.string().nonempty(), - secretKey: z.string().nonempty(), - }); -}; - -const _default = Object.assign(AccessConfigFormFieldsProviderQiniu, { - getInitialValues, - getSchema, -}); - -export default _default; diff --git a/ui/src/components/provider/DeploymentProviderPicker.tsx b/ui/src/components/provider/DeploymentProviderPicker.tsx index 102166a66..d221c776d 100644 --- a/ui/src/components/provider/DeploymentProviderPicker.tsx +++ b/ui/src/components/provider/DeploymentProviderPicker.tsx @@ -69,13 +69,13 @@ const DeploymentProviderPicker = forwardRef {t(provider.name) || "\u00A0"} - {provider.source === "plugin" ? ( - - {t("provider.tag.plugin")} - - ) : null} + {provider.source === "plugin" ? ( + + {t("provider.tag.plugin")} + + ) : null} diff --git a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx index 80b7ee7dd..ff9a14f1d 100644 --- a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx +++ b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx @@ -92,9 +92,6 @@ import BizDeployNodeConfigFieldsProviderNginxProxyManager from "./BizDeployNodeC import BizDeployNodeConfigFieldsProviderOracleCloudCertificatesMgmt from "./BizDeployNodeConfigFieldsProviderOracleCloudCertificatesMgmt"; import BizDeployNodeConfigFieldsProviderProxmoxVE from "./BizDeployNodeConfigFieldsProviderProxmoxVE"; import BizDeployNodeConfigFieldsProviderQingCloudLB from "./BizDeployNodeConfigFieldsProviderQingCloudLB"; -import BizDeployNodeConfigFieldsProviderQiniuCDN from "./BizDeployNodeConfigFieldsProviderQiniuCDN"; -import BizDeployNodeConfigFieldsProviderQiniuKodo from "./BizDeployNodeConfigFieldsProviderQiniuKodo"; -import BizDeployNodeConfigFieldsProviderQiniuPili from "./BizDeployNodeConfigFieldsProviderQiniuPili"; import BizDeployNodeConfigFieldsProviderRainYunRCDN from "./BizDeployNodeConfigFieldsProviderRainYunRCDN"; import BizDeployNodeConfigFieldsProviderRainYunSSLCenter from "./BizDeployNodeConfigFieldsProviderRainYunSSLCenter"; import BizDeployNodeConfigFieldsProviderRatPanel from "./BizDeployNodeConfigFieldsProviderRatPanel"; @@ -143,6 +140,7 @@ import BizDeployNodeConfigFieldsProviderVolcEngineWAF from "./BizDeployNodeConfi import BizDeployNodeConfigFieldsProviderWangsuCDN from "./BizDeployNodeConfigFieldsProviderWangsuCDN"; import BizDeployNodeConfigFieldsProviderWangsuCDNPro from "./BizDeployNodeConfigFieldsProviderWangsuCDNPro"; import BizDeployNodeConfigFieldsProviderWangsuCertificate from "./BizDeployNodeConfigFieldsProviderWangsuCertificate"; +import BizDeployNodeConfigFieldsProviderWebhook from "./BizDeployNodeConfigFieldsProviderWebhook"; import BizDeployNodeConfigFieldsProviderZenlayerCDN from "./BizDeployNodeConfigFieldsProviderZenlayerCDN"; import BizDeployNodeConfigFieldsProviderZenlayerGA from "./BizDeployNodeConfigFieldsProviderZenlayerGA"; import SchemaConfigFields from "./SchemaConfigFields"; @@ -241,9 +239,6 @@ const providerComponentMap: Partial : initComponent()); // eslint-disable-next-line react-hooks/exhaustive-deps }, [provider, envelope]); - useEffect(() => setComponent(initComponent()), deps); + useEffect(() => { + setComponent(envelope ? : initComponent()); + }, deps); return component; }; diff --git a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderQiniuCDN.tsx b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderQiniuCDN.tsx deleted file mode 100644 index 1590b3283..000000000 --- a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderQiniuCDN.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { getI18n, useTranslation } from "react-i18next"; -import { Form, Input, Radio } 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 DOMAIN_MATCH_PATTERN_EXACT = "exact" as const; -const DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard" as const; -const DOMAIN_MATCH_PATTERN_CERTSAN = "certsan" as const; - -const BizDeployNodeConfigFieldsProviderQiniuCDN = () => { - 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 ( - <> - - ) : ( - void 0 - ) - } - rules={[formRule]} - > - ({ - label: t(`workflow_node.deploy.form.shared_domain_match_pattern.option.${s}.label`), - value: s, - }))} - /> - - - - - - - - - ); -}; - -const getInitialValues = (): Nullish>> => { - return { - domainMatchPattern: DOMAIN_MATCH_PATTERN_EXACT, - domain: "", - }; -}; - -const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => { - const { t } = i18n; - - return z - .object({ - domainMatchPattern: z.string().nonempty().default(DOMAIN_MATCH_PATTERN_EXACT), - domain: z.string().nullish(), - }) - .superRefine((values, ctx) => { - if (values.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; - } - } - }); -}; - -const _default = Object.assign(BizDeployNodeConfigFieldsProviderQiniuCDN, { - getInitialValues, - getSchema, -}); - -export default _default; diff --git a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderQiniuKodo.tsx b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderQiniuKodo.tsx deleted file mode 100644 index aa6c0e366..000000000 --- a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderQiniuKodo.tsx +++ /dev/null @@ -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 { isDomain } from "@/utils/validator"; - -import { useFormNestedFieldsContext } from "./_context"; - -const BizDeployNodeConfigFieldsProviderQiniuKodo = () => { - const { i18n, t } = useTranslation(); - - const { parentNamePath } = useFormNestedFieldsContext(); - const formSchema = z.object({ - [parentNamePath]: getSchema({ i18n }), - }); - const formRule = createSchemaFieldRule(formSchema); - const initialValues = getInitialValues(); - - return ( - <> - - - - - - - - - ); -}; - -const getInitialValues = (): Nullish>> => { - return { - bucket: "", - domain: "", - }; -}; - -const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => { - const { t } = i18n; - - return z.object({ - bucket: z.string().nonempty(), - domain: z.string().refine((v) => isDomain(v), t("common.errmsg.domain_invalid")), - }); -}; - -const _default = Object.assign(BizDeployNodeConfigFieldsProviderQiniuKodo, { - getInitialValues, - getSchema, -}); - -export default _default; diff --git a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderQiniuPili.tsx b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderQiniuPili.tsx deleted file mode 100644 index 9463f3f99..000000000 --- a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderQiniuPili.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import { getI18n, useTranslation } from "react-i18next"; -import { Form, Input, Radio } 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 DOMAIN_MATCH_PATTERN_EXACT = "exact" as const; -const DOMAIN_MATCH_PATTERN_CERTSAN = "certsan" as const; - -const BizDeployNodeConfigFieldsProviderQiniuPili = () => { - 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 ( - <> - } - > - - - - - ({ - label: t(`workflow_node.deploy.form.shared_domain_match_pattern.option.${s}.label`), - value: s, - }))} - /> - - - - - - - - - ); -}; - -const getInitialValues = (): Nullish>> => { - return { - hub: "", - domainMatchPattern: DOMAIN_MATCH_PATTERN_EXACT, - domain: "", - }; -}; - -const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => { - const { t } = i18n; - - return z - .object({ - hub: z.string().nonempty(), - domainMatchPattern: z.string().nonempty().default(DOMAIN_MATCH_PATTERN_EXACT), - domain: 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), 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(BizDeployNodeConfigFieldsProviderQiniuPili, { - getInitialValues, - getSchema, -}); - -export default _default; diff --git a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderWebhook.tsx b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderWebhook.tsx new file mode 100644 index 000000000..8a628bfd3 --- /dev/null +++ b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderWebhook.tsx @@ -0,0 +1,94 @@ +import { getI18n, useTranslation } from "react-i18next"; +import { IconBulb } from "@tabler/icons-react"; +import { Button, Form, Input, Popover } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import CodeTextInput from "@/components/CodeTextInput"; +import { isJsonObject } from "@/utils/validator"; + +import { useFormNestedFieldsContext } from "./_context"; + +const BizDeployNodeConfigFieldsProviderWebhook = () => { + 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 handleWebhookDataBlur = () => { + const value = formInst.getFieldValue([parentNamePath, "webhookData"]); + try { + const json = JSON.stringify(JSON.parse(value), null, 2); + formInst.setFieldValue([parentNamePath, "webhookData"], json); + } catch { + return; + } + }; + + return ( + <> + +
+ } mouseEnterDelay={1}> + + +
+ + + +
+ + + + + + ); +}; + +const getInitialValues = (): Nullish>> => { + return {}; +}; + +const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => { + const { t } = i18n; + + return z.object({ + webhookData: z + .string() + .nullish() + .refine((v) => { + if (!v) return true; + return isJsonObject(v); + }, t("common.errmsg.json_invalid")), + timeout: z.coerce.number().int().gt(0).or(z.literal("")).nullish(), + }); +}; + +const _default = Object.assign(BizDeployNodeConfigFieldsProviderWebhook, { + getInitialValues, + getSchema, +}); + +export default _default; diff --git a/ui/src/components/workflow/designer/forms/schemaValidation.ts b/ui/src/components/workflow/designer/forms/schemaValidation.ts index b67d91b9c..d05bc82a1 100644 --- a/ui/src/components/workflow/designer/forms/schemaValidation.ts +++ b/ui/src/components/workflow/designer/forms/schemaValidation.ts @@ -1,6 +1,7 @@ import { z } from "zod"; import type { ProviderSchemaEnvelope, SchemaColumn, SchemaCondition, SchemaValidationRule } from "@/api/providerschema"; +import i18n from "@/i18n"; import { isCron, isDomain, @@ -17,7 +18,7 @@ import { const isEmpty = (v: unknown) => v == null || v === ""; export const matchCondition = (cond: SchemaCondition, value: unknown): boolean => { - const present = cond.values.includes(String(value)); + const present = (cond.values ?? []).includes(String(value)); switch (cond.op) { case "eq": case "in": @@ -98,7 +99,7 @@ const fieldBaseType = (col: SchemaColumn): z.ZodType => { const applyRules = (schema: z.ZodType, rules: SchemaValidationRule[], allowEmpty: boolean): z.ZodType => { for (const rule of rules) { const ok = (v: unknown) => (allowEmpty && isEmpty(v)) || runValidator(rule.validator, rule.params, v); - schema = schema.refine(ok, { message: `common.errmsg.${rule.validator}_invalid` }); + schema = schema.refine(ok, { message: i18n.t(`common.errmsg.${rule.validator}_invalid`) }); } return schema; }; @@ -159,13 +160,13 @@ const buildPlainObject = (columns: SchemaColumn[]): z.ZodType => { for (const cond of col.requiredWhen ?? []) { if (!matchCondition(cond, record[cond.field])) continue; if (isEmpty(value)) { - ctx.addIssue({ code: "custom", message: `common.errmsg.required`, path: [col.name] }); + ctx.addIssue({ code: "custom", message: i18n.t("common.errmsg.required"), path: [col.name] }); } } for (const rule of col.validateWhen ?? []) { if (!matchCondition(rule, record[rule.field])) continue; if (!isEmpty(value) && !runValidator(rule.validator, rule.params, value)) { - ctx.addIssue({ code: "custom", message: `common.errmsg.${rule.validator}_invalid`, path: [col.name] }); + ctx.addIssue({ code: "custom", message: i18n.t(`common.errmsg.${rule.validator}_invalid`), path: [col.name] }); } } } diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index f10851f30..29e744ad2 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -108,7 +108,6 @@ export const ACCESS_PROVIDERS = Object.freeze({ POWERDNS: "powerdns", PROXMOXVE: "proxmoxve", QINGCLOUD: "qingcloud", - QINIU: "qiniu", RAINYUN: "rainyun", RATPANEL: "ratpanel", REGRU: "regru", @@ -201,7 +200,6 @@ export const accessProvidersMap: Maphttps://console.qingcloud.com/access_keys/" }, - "qiniu_access_key": { - "label": "Qiniu AccessKey", - "placeholder": "Please enter Qiniu AccessKey", - "tooltip": "For more information, see https://portal.qiniu.com/" - }, - "qiniu_secret_key": { - "label": "Qiniu SecretKey", - "placeholder": "Please enter Qiniu SecretKey", - "tooltip": "For more information, see https://portal.qiniu.com/" - }, "rainyun_api_key": { "label": "Rain Yun API key", "placeholder": "Please enter Rain Yun API key", diff --git a/ui/src/i18n/resources/en/nls.provider.json b/ui/src/i18n/resources/en/nls.provider.json index ec427f2b1..b89c62629 100644 --- a/ui/src/i18n/resources/en/nls.provider.json +++ b/ui/src/i18n/resources/en/nls.provider.json @@ -224,10 +224,6 @@ "qingcloud": "QingCloud", "qingcloud_dns": "QingCloud - DNS", "qingcloud_lb": "QingCloud - LBC (Load Balancer Cluster)", - "qiniu": "Qiniu", - "qiniu_cdn": "Qiniu - CDN (Content Delivery Network)", - "qiniu_kodo": "Qiniu - Kodo", - "qiniu_pili": "Qiniu - Pili", "rainyun": "Rain Yun", "rainyun_rcdn": "Rain Yun - RCDN (Content Delivery Network)", "rainyun_sslcenter_upload": "Rain Yun - Upload to SSL Certificate Center", diff --git a/ui/src/i18n/resources/en/nls.workflow.nodes.json b/ui/src/i18n/resources/en/nls.workflow.nodes.json index 26ace267f..5ed440352 100644 --- a/ui/src/i18n/resources/en/nls.workflow.nodes.json +++ b/ui/src/i18n/resources/en/nls.workflow.nodes.json @@ -457,6 +457,17 @@ } }, "form": { + "webhook_data": { + "label": "Webhook data (Optional)", + "placeholder": "Please enter Webhook data", + "help": "Notes: Leave it blank to use the default Webhook data provided by the selected credential.", + "vartips": "Supported variables:
  1. ${CERTIMATE_DEPLOYER_COMMONNAME}:
    The primary domain or IP address of the certificate.
  2. ${CERTIMATE_DEPLOYER_SUBJECTALTNAMES}:
    The domains or IP addresses of the certificate, separated by semicolons.
  3. ${CERTIMATE_DEPLOYER_CERTIFICATE}:
    The PEM format content of the certificate file.
  4. ${CERTIMATE_DEPLOYER_CERTIFICATE_SERVER}:
    The PEM format content of the server certificate file.
  5. ${CERTIMATE_DEPLOYER_CERTIFICATE_INTERMEDIA}:
    The PEM format content of the intermediate CA certificate file.
  6. ${CERTIMATE_DEPLOYER_PRIVATEKEY}:
    The PEM format content of the private key file.
" + }, + "webhook_timeout": { + "label": "Webhook timeout (Optional)", + "placeholder": "Please enter Webhook timeout", + "unit": "seconds" + }, "certificate_output_node_id": { "label": "Certificate to deploy", "placeholder": "Please select certificate to deploy", @@ -2049,27 +2060,6 @@ "placeholder": "Please enter QingCloud LBC listener ID", "tooltip": "For more information, see https://console.qingcloud.com/pek3a/loadbalancers/" }, - "qiniu_cdn_domain": { - "label": "Qiniu CDN domain", - "placeholder": "Please enter Qiniu CDN domain name" - }, - "qiniu_kodo_bucket": { - "label": "Qiniu Kodo bucket", - "placeholder": "Please enter Qiniu Kodo bucket name" - }, - "qiniu_kodo_domain": { - "label": "Qiniu Kodo custom domain", - "placeholder": "Please enter Qiniu Kodo bucket custom domain name" - }, - "qiniu_pili_hub": { - "label": "Qiniu Pili hub", - "placeholder": "Please enter Qiniu Pili hub name", - "tooltip": "For more information, see https://portal.qiniu.com/hub" - }, - "qiniu_pili_domain": { - "label": "Qiniu Pili streaming domain", - "placeholder": "Please enter Qiniu Pili streaming domain name" - }, "rainyun_rcdn_instance_id": { "label": "Rain Yun RCDN instance ID", "placeholder": "Please enter Rain Yun RCDN instance ID", diff --git a/ui/src/i18n/resources/zh/nls.access.json b/ui/src/i18n/resources/zh/nls.access.json index 1fbf5cb43..797275e33 100644 --- a/ui/src/i18n/resources/zh/nls.access.json +++ b/ui/src/i18n/resources/zh/nls.access.json @@ -1128,16 +1128,6 @@ "placeholder": "请输入青云 SecretAccessKey", "tooltip": "这是什么?请参阅 https://console.qingcloud.com/access_keys/" }, - "qiniu_access_key": { - "label": "七牛云 AccessKey", - "placeholder": "请输入七牛云 AccessKey", - "tooltip": "这是什么?请参阅 https://portal.qiniu.com/" - }, - "qiniu_secret_key": { - "label": "七牛云 SecretKey", - "placeholder": "请输入七牛云 SecretKey", - "tooltip": "这是什么?请参阅 https://portal.qiniu.com/" - }, "rainyun_api_key": { "label": "雨云 API 密钥", "placeholder": "请输入雨云 API 密钥", diff --git a/ui/src/i18n/resources/zh/nls.provider.json b/ui/src/i18n/resources/zh/nls.provider.json index 67af99732..cb1d58b73 100644 --- a/ui/src/i18n/resources/zh/nls.provider.json +++ b/ui/src/i18n/resources/zh/nls.provider.json @@ -224,10 +224,6 @@ "qingcloud": "青云", "qingcloud_dns": "青云 - 云解析 DNS", "qingcloud_lb": "青云 - 负载均衡 LB", - "qiniu": "七牛云", - "qiniu_cdn": "七牛云 - 内容分发网络 CDN", - "qiniu_kodo": "七牛云 - 对象存储 Kodo", - "qiniu_pili": "七牛云 - 视频直播 Pili", "rainyun": "雨云", "rainyun_rcdn": "雨云 - 雨盾 CDN", "rainyun_sslcenter_upload": "雨云 - 上传到 SSL 证书中心", diff --git a/ui/src/i18n/resources/zh/nls.workflow.nodes.json b/ui/src/i18n/resources/zh/nls.workflow.nodes.json index a95704042..6d5f6a311 100644 --- a/ui/src/i18n/resources/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/resources/zh/nls.workflow.nodes.json @@ -456,6 +456,17 @@ } }, "form": { + "webhook_data": { + "label": "Webhook 回调数据(可选)", + "placeholder": "请输入 Webhook 回调数据以覆盖默认值", + "help": "提示:不填写时,将使用所选提供商授权的默认 Webhook 回调数据。", + "vartips": "支持的变量:
  1. ${CERTIMATE_DEPLOYER_COMMONNAME}:
    证书的主域名或 IP。
  2. ${CERTIMATE_DEPLOYER_SUBJECTALTNAMES}:
    证书的多域名或 IP,以半角分号隔开。
  3. ${CERTIMATE_DEPLOYER_CERTIFICATE}:
    证书文件 PEM 格式内容。
  4. ${CERTIMATE_DEPLOYER_CERTIFICATE_SERVER}:
    证书文件(仅含服务器证书)PEM 格式内容。
  5. ${CERTIMATE_DEPLOYER_CERTIFICATE_INTERMEDIA}:
    证书文件(仅含中间证书)PEM 格式内容。
  6. ${CERTIMATE_DEPLOYER_PRIVATEKEY}:
    私钥文件 PEM 格式内容。
" + }, + "webhook_timeout": { + "label": "Webhook 超时时间(可选)", + "placeholder": "请输入 Webhook 超时时间", + "unit": "秒" + }, "certificate_output_node_id": { "label": "待部署证书", "placeholder": "请选择待部署证书", @@ -2048,27 +2059,6 @@ "placeholder": "请输入青云 LB 监听器 ID", "tooltip": "这是什么?请参阅 https://console.qingcloud.com/pek3a/loadbalancers/" }, - "qiniu_cdn_domain": { - "label": "七牛云 CDN 加速域名", - "placeholder": "请输入七牛云 CDN 加速域名" - }, - "qiniu_kodo_bucket": { - "label": "七牛云对象存储桶名", - "placeholder": "请输入七牛云对象存储桶名" - }, - "qiniu_kodo_domain": { - "label": "七牛云对象存储自定义域名", - "placeholder": "请输入七牛云对象存储自定义域名" - }, - "qiniu_pili_hub": { - "label": "七牛云视频直播空间名", - "placeholder": "请输入七牛云视频直播空间名", - "tooltip": "这是什么?请参阅 https://portal.qiniu.com/hub" - }, - "qiniu_pili_domain": { - "label": "七牛云视频直播流域名", - "placeholder": "请输入七牛云视频直播流域名" - }, "rainyun_rcdn_instance_id": { "label": "雨云 RCDN 实例 ID", "placeholder": "请输入雨云 RCDN 实例 ID",