diff --git a/go.mod b/go.mod index a4ab4379..37686d8c 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/alibabacloud-go/esa-20240910/v2 v2.32.0 github.com/alibabacloud-go/fc-20230330/v4 v4.3.5 github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12 + github.com/alibabacloud-go/ga-20191120/v3 v3.1.8 github.com/alibabacloud-go/live-20161101 v1.1.1 github.com/alibabacloud-go/nlb-20220430/v2 v2.0.3 github.com/alibabacloud-go/slb-20140515/v4 v4.0.10 diff --git a/go.sum b/go.sum index b9d4247d..5a64f77a 100644 --- a/go.sum +++ b/go.sum @@ -112,6 +112,7 @@ github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+M github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.0/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ= github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.2/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ= github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.5/go.mod h1:kUe8JqFmoVU7lfBauaDD5taFaW7mBI+xVsyHutYtabg= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9/go.mod h1:bb+Io8Sn2RuM3/Rpme6ll86jMyFSrD1bxeV/+v61KeU= github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10/go.mod h1:26a14FGhZVELuz2cc2AolvW4RHmIO3/HRwsdHhaIPDE= github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.11/go.mod h1:wHxkgZT1ClZdcwEVP/pDgYK/9HucsnCfMipmJgCz4xY= github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.7 h1:ASXSBga98QrGMxbIThCD6jAti09gedLfvry6yJtsoBE= @@ -137,6 +138,8 @@ github.com/alibabacloud-go/fc-20230330/v4 v4.3.5 h1:nDNjVzGwkQPbQnAuxAmxvS9x8QGL github.com/alibabacloud-go/fc-20230330/v4 v4.3.5/go.mod h1:vEJimQ6E/e+m2z0/oXdeQWlFw/Pi/Ar6NKcMrSvcILE= github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12 h1:A3D8Mp6qf8DfR6Dt5MpS8aDVaWfS4N85T5CvGUvgrjM= github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12/go.mod h1:F5c0E5UB3k8v6neTtw3FBcJ1YCNFzVoL1JPRHTe33u4= +github.com/alibabacloud-go/ga-20191120/v3 v3.1.8 h1:5GF0PXijDhxRQ3gTg9Ee/CVPtglkxuVdz4yIQgYLPgw= +github.com/alibabacloud-go/ga-20191120/v3 v3.1.8/go.mod h1:RVpR9VL4YECKoZCQijTYfPk8k52O61v6hSRekjxF0kw= github.com/alibabacloud-go/live-20161101 v1.1.1 h1:rUGfA8RHmCMtQ5M3yMSyRde+yRXWqVecmiXBU3XrGJ8= github.com/alibabacloud-go/live-20161101 v1.1.1/go.mod h1:g84w6qeAodT0/IHdc0tEed2a8PyhQhYl7TAj3jGl4A4= github.com/alibabacloud-go/nlb-20220430/v2 v2.0.3 h1:LtyUVlgBEKyzWgQJurzXM6MXCt84sQr9cE5OKqYymko= diff --git a/internal/applicant/acme_ca.go b/internal/applicant/acme_ca.go index 67b7693e..36c0a0a4 100644 --- a/internal/applicant/acme_ca.go +++ b/internal/applicant/acme_ca.go @@ -3,25 +3,26 @@ package applicant import "github.com/usual2970/certimate/internal/domain" const ( - sslProviderLetsEncrypt = string(domain.CAProviderTypeLetsEncrypt) - sslProviderLetsEncryptStaging = string(domain.CAProviderTypeLetsEncryptStaging) - sslProviderBuypass = string(domain.CAProviderTypeBuypass) - sslProviderGoogleTrustServices = string(domain.CAProviderTypeGoogleTrustServices) - sslProviderSSLCom = string(domain.CAProviderTypeSSLCom) - sslProviderZeroSSL = string(domain.CAProviderTypeZeroSSL) + caLetsEncrypt = string(domain.CAProviderTypeLetsEncrypt) + caLetsEncryptStaging = string(domain.CAProviderTypeLetsEncryptStaging) + caBuypass = string(domain.CAProviderTypeBuypass) + caGoogleTrustServices = string(domain.CAProviderTypeGoogleTrustServices) + caSSLCom = string(domain.CAProviderTypeSSLCom) + caZeroSSL = string(domain.CAProviderTypeZeroSSL) + caCustom = string(domain.CAProviderTypeACMECA) - sslProviderDefault = sslProviderLetsEncrypt + caDefault = caLetsEncrypt ) -var sslProviderUrls = map[string]string{ - sslProviderLetsEncrypt: "https://acme-v02.api.letsencrypt.org/directory", - sslProviderLetsEncryptStaging: "https://acme-staging-v02.api.letsencrypt.org/directory", - sslProviderBuypass: "https://api.buypass.com/acme/directory", - sslProviderGoogleTrustServices: "https://dv.acme-v02.api.pki.goog/directory", - sslProviderSSLCom: "https://acme.ssl.com/sslcom-dv-rsa", - sslProviderSSLCom + "RSA": "https://acme.ssl.com/sslcom-dv-rsa", - sslProviderSSLCom + "ECC": "https://acme.ssl.com/sslcom-dv-ecc", - sslProviderZeroSSL: "https://acme.zerossl.com/v2/DV90", +var caDirUrls = map[string]string{ + caLetsEncrypt: "https://acme-v02.api.letsencrypt.org/directory", + caLetsEncryptStaging: "https://acme-staging-v02.api.letsencrypt.org/directory", + caBuypass: "https://api.buypass.com/acme/directory", + caGoogleTrustServices: "https://dv.acme-v02.api.pki.goog/directory", + caSSLCom: "https://acme.ssl.com/sslcom-dv-rsa", + caSSLCom + "RSA": "https://acme.ssl.com/sslcom-dv-rsa", + caSSLCom + "ECC": "https://acme.ssl.com/sslcom-dv-ecc", + caZeroSSL: "https://acme.zerossl.com/v2/DV90", } type acmeSSLProviderConfig struct { diff --git a/internal/applicant/acme_user.go b/internal/applicant/acme_user.go index 29ac80cd..e6e13cb7 100644 --- a/internal/applicant/acme_user.go +++ b/internal/applicant/acme_user.go @@ -7,6 +7,7 @@ import ( "crypto/elliptic" "crypto/rand" "fmt" + "strings" "github.com/go-acme/lego/v4/lego" "github.com/go-acme/lego/v4/registration" @@ -19,22 +20,31 @@ import ( ) type acmeUser struct { - CA string - Email string + // 证书颁发机构标识。 + // 通常等同于 [CAProviderType] 的值。 + // 对于自定义 ACME CA,值为 "custom#{access_id}"。 + CA string + // 邮箱。 + Email string + // 注册信息。 Registration *registration.Resource + // CSR 私钥。 privkey string } -func newAcmeUser(ca, email string) (*acmeUser, error) { +func newAcmeUser(ca, caAccessId, email string) (*acmeUser, error) { repo := repository.NewAcmeAccountRepository() applyUser := &acmeUser{ CA: ca, Email: email, } + if ca == caCustom { + applyUser.CA = fmt.Sprintf("%s#%s", ca, caAccessId) + } - acmeAccount, err := repo.GetByCAAndEmail(ca, email) + acmeAccount, err := repo.GetByCAAndEmail(applyUser.CA, applyUser.Email) if err != nil { key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { @@ -73,6 +83,10 @@ func (u *acmeUser) hasRegistration() bool { return u.Registration != nil } +func (u *acmeUser) getCAProvider() string { + return strings.Split(u.CA, "#")[0] +} + func (u *acmeUser) getPrivateKeyPEM() string { return u.privkey } @@ -94,16 +108,16 @@ func registerAcmeUserWithSingleFlight(client *lego.Client, user *acmeUser, userR func registerAcmeUser(client *lego.Client, user *acmeUser, userRegisterOptions map[string]any) (*registration.Resource, error) { var reg *registration.Resource var err error - switch user.CA { - case sslProviderLetsEncrypt, sslProviderLetsEncryptStaging: + switch user.getCAProvider() { + case caLetsEncrypt, caLetsEncryptStaging: reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) - case sslProviderBuypass: + case caBuypass: { reg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) } - case sslProviderGoogleTrustServices: + case caGoogleTrustServices: { access := domain.AccessConfigForGoogleTrustServices{} if err := maputil.Populate(userRegisterOptions, &access); err != nil { @@ -117,7 +131,7 @@ func registerAcmeUser(client *lego.Client, user *acmeUser, userRegisterOptions m }) } - case sslProviderSSLCom: + case caSSLCom: { access := domain.AccessConfigForSSLCom{} if err := maputil.Populate(userRegisterOptions, &access); err != nil { @@ -131,7 +145,7 @@ func registerAcmeUser(client *lego.Client, user *acmeUser, userRegisterOptions m }) } - case sslProviderZeroSSL: + case caZeroSSL: { access := domain.AccessConfigForZeroSSL{} if err := maputil.Populate(userRegisterOptions, &access); err != nil { @@ -145,6 +159,26 @@ func registerAcmeUser(client *lego.Client, user *acmeUser, userRegisterOptions m }) } + case caCustom: + { + access := domain.AccessConfigForACMECA{} + if err := maputil.Populate(userRegisterOptions, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + if access.EabKid == "" && access.EabHmacKey == "" { + reg, err = client.Registration.Register(registration.RegisterOptions{ + TermsOfServiceAgreed: true, + }) + } else { + reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ + TermsOfServiceAgreed: true, + Kid: access.EabKid, + HmacEncoded: access.EabHmacKey, + }) + } + } + default: err = fmt.Errorf("unsupported ca provider '%s'", user.CA) } diff --git a/internal/applicant/applicant.go b/internal/applicant/applicant.go index e9ed4cb1..e6a04bcd 100644 --- a/internal/applicant/applicant.go +++ b/internal/applicant/applicant.go @@ -20,12 +20,13 @@ import ( "golang.org/x/time/rate" "github.com/usual2970/certimate/internal/domain" + maputil "github.com/usual2970/certimate/internal/pkg/utils/map" sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice" "github.com/usual2970/certimate/internal/repository" ) type ApplyResult struct { - CertificateFullChain string + FullChainCertificate string IssuerCertificate string PrivateKey string ACMEAccountUrl string @@ -53,20 +54,20 @@ func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, err nodeConfig := config.Node.GetConfigForApply() options := &applicantProviderOptions{ - Domains: sliceutil.Filter(strings.Split(nodeConfig.Domains, ";"), func(s string) bool { return s != "" }), - ContactEmail: nodeConfig.ContactEmail, - Provider: domain.ACMEDns01ProviderType(nodeConfig.Provider), - ProviderAccessConfig: make(map[string]any), - ProviderExtendedConfig: nodeConfig.ProviderConfig, - CAProvider: domain.CAProviderType(nodeConfig.CAProvider), - CAProviderAccessConfig: make(map[string]any), - CAProviderExtendedConfig: nodeConfig.CAProviderConfig, - KeyAlgorithm: nodeConfig.KeyAlgorithm, - Nameservers: sliceutil.Filter(strings.Split(nodeConfig.Nameservers, ";"), func(s string) bool { return s != "" }), - DnsPropagationWait: nodeConfig.DnsPropagationWait, - DnsPropagationTimeout: nodeConfig.DnsPropagationTimeout, - DnsTTL: nodeConfig.DnsTTL, - DisableFollowCNAME: nodeConfig.DisableFollowCNAME, + Domains: sliceutil.Filter(strings.Split(nodeConfig.Domains, ";"), func(s string) bool { return s != "" }), + ContactEmail: nodeConfig.ContactEmail, + Provider: domain.ACMEDns01ProviderType(nodeConfig.Provider), + ProviderAccessConfig: make(map[string]any), + ProviderServiceConfig: nodeConfig.ProviderConfig, + CAProvider: domain.CAProviderType(nodeConfig.CAProvider), + CAProviderAccessConfig: make(map[string]any), + CAProviderServiceConfig: nodeConfig.CAProviderConfig, + KeyAlgorithm: nodeConfig.KeyAlgorithm, + Nameservers: sliceutil.Filter(strings.Split(nodeConfig.Nameservers, ";"), func(s string) bool { return s != "" }), + DnsPropagationWait: nodeConfig.DnsPropagationWait, + DnsPropagationTimeout: nodeConfig.DnsPropagationTimeout, + DnsTTL: nodeConfig.DnsTTL, + DisableFollowCNAME: nodeConfig.DisableFollowCNAME, } accessRepo := repository.NewAccessRepository() @@ -81,6 +82,7 @@ func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, err if access, err := accessRepo.GetById(context.Background(), nodeConfig.CAProviderAccessId); err != nil { return nil, fmt.Errorf("failed to get access #%s record: %w", nodeConfig.CAProviderAccessId, err) } else { + options.CAProviderAccessId = access.Id options.CAProviderAccessConfig = access.Config } } @@ -91,13 +93,13 @@ func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, err sslProviderConfig := &acmeSSLProviderConfig{ Config: make(map[domain.CAProviderType]map[string]any), - Provider: sslProviderDefault, + Provider: caDefault, } if settings != nil { if err := json.Unmarshal([]byte(settings.Content), sslProviderConfig); err != nil { return nil, err } else if sslProviderConfig.Provider == "" { - sslProviderConfig.Provider = sslProviderDefault + sslProviderConfig.Provider = caDefault } } @@ -163,7 +165,7 @@ func getLimiter(key string) *rate.Limiter { } func applyUseLego(legoProvider challenge.Provider, options *applicantProviderOptions) (*ApplyResult, error) { - user, err := newAcmeUser(string(options.CAProvider), options.ContactEmail) + user, err := newAcmeUser(string(options.CAProvider), options.CAProviderAccessId, options.ContactEmail) if err != nil { return nil, err } @@ -175,13 +177,26 @@ func applyUseLego(legoProvider challenge.Provider, options *applicantProviderOpt // Create an ACME client config config := lego.NewConfig(user) config.Certificate.KeyType = parseLegoKeyAlgorithm(domain.CertificateKeyAlgorithmType(options.KeyAlgorithm)) - config.CADirURL = sslProviderUrls[user.CA] - if user.CA == sslProviderSSLCom { + switch user.getCAProvider() { + case caSSLCom: if strings.HasPrefix(options.KeyAlgorithm, "RSA") { - config.CADirURL = sslProviderUrls[sslProviderSSLCom+"RSA"] + config.CADirURL = caDirUrls[caSSLCom+"RSA"] } else if strings.HasPrefix(options.KeyAlgorithm, "EC") { - config.CADirURL = sslProviderUrls[sslProviderSSLCom+"ECC"] + config.CADirURL = caDirUrls[caSSLCom+"ECC"] + } else { + config.CADirURL = caDirUrls[caSSLCom] } + + case caCustom: + caDirURL := maputil.GetString(options.CAProviderAccessConfig, "endpoint") + if caDirURL != "" { + config.CADirURL = caDirURL + } else { + return nil, fmt.Errorf("invalid ca provider endpoint") + } + + default: + config.CADirURL = caDirUrls[user.CA] } // Create an ACME client @@ -229,7 +244,7 @@ func applyUseLego(legoProvider challenge.Provider, options *applicantProviderOpt } return &ApplyResult{ - CertificateFullChain: strings.TrimSpace(string(certResource.Certificate)), + FullChainCertificate: strings.TrimSpace(string(certResource.Certificate)), IssuerCertificate: strings.TrimSpace(string(certResource.IssuerCertificate)), PrivateKey: strings.TrimSpace(string(certResource.PrivateKey)), ACMEAccountUrl: user.Registration.URI, diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go index 90a3cf72..de47ae18 100644 --- a/internal/applicant/providers.go +++ b/internal/applicant/providers.go @@ -42,22 +42,23 @@ import ( ) type applicantProviderOptions struct { - Domains []string - ContactEmail string - Provider domain.ACMEDns01ProviderType - ProviderAccessConfig map[string]any - ProviderExtendedConfig map[string]any - CAProvider domain.CAProviderType - CAProviderAccessConfig map[string]any - CAProviderExtendedConfig map[string]any - KeyAlgorithm string - Nameservers []string - DnsPropagationWait int32 - DnsPropagationTimeout int32 - DnsTTL int32 - DisableFollowCNAME bool - ReplacedARIAcct string - ReplacedARICert string + Domains []string + ContactEmail string + Provider domain.ACMEDns01ProviderType + ProviderAccessConfig map[string]any + ProviderServiceConfig map[string]any + CAProvider domain.CAProviderType + CAProviderAccessId string + CAProviderAccessConfig map[string]any + CAProviderServiceConfig map[string]any + KeyAlgorithm string + Nameservers []string + DnsPropagationWait int32 + DnsPropagationTimeout int32 + DnsTTL int32 + DisableFollowCNAME bool + ReplacedARIAcct string + ReplacedARICert string } func createApplicantProvider(options *applicantProviderOptions) (challenge.Provider, error) { @@ -104,7 +105,7 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi applicant, err := pAliyunESA.NewChallengeProvider(&pAliyunESA.ChallengeProviderConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), DnsPropagationTimeout: options.DnsPropagationTimeout, DnsTTL: options.DnsTTL, }) @@ -125,8 +126,8 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi applicant, err := pAWSRoute53.NewChallengeProvider(&pAWSRoute53.ChallengeProviderConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - HostedZoneId: maputil.GetString(options.ProviderExtendedConfig, "hostedZoneId"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + HostedZoneId: maputil.GetString(options.ProviderServiceConfig, "hostedZoneId"), DnsPropagationTimeout: options.DnsPropagationTimeout, DnsTTL: options.DnsTTL, }) @@ -333,7 +334,7 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi applicant, err := pHuaweiCloud.NewChallengeProvider(&pHuaweiCloud.ChallengeProviderConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), DnsPropagationTimeout: options.DnsPropagationTimeout, DnsTTL: options.DnsTTL, }) @@ -350,7 +351,7 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi applicant, err := pJDCloud.NewChallengeProvider(&pJDCloud.ChallengeProviderConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - RegionId: maputil.GetString(options.ProviderExtendedConfig, "regionId"), + RegionId: maputil.GetString(options.ProviderServiceConfig, "regionId"), DnsPropagationTimeout: options.DnsPropagationTimeout, DnsTTL: options.DnsTTL, }) @@ -520,7 +521,7 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi applicant, err := pTencentCloudEO.NewChallengeProvider(&pTencentCloudEO.ChallengeProviderConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - ZoneId: maputil.GetString(options.ProviderExtendedConfig, "zoneId"), + ZoneId: maputil.GetString(options.ProviderServiceConfig, "zoneId"), DnsPropagationTimeout: options.DnsPropagationTimeout, DnsTTL: options.DnsTTL, }) diff --git a/internal/deployer/deployer.go b/internal/deployer/deployer.go index bdacf08e..e4a28746 100644 --- a/internal/deployer/deployer.go +++ b/internal/deployer/deployer.go @@ -31,9 +31,9 @@ func NewWithWorkflowNode(config DeployerWithWorkflowNodeConfig) (Deployer, error nodeConfig := config.Node.GetConfigForDeploy() options := &deployerProviderOptions{ - Provider: domain.DeploymentProviderType(nodeConfig.Provider), - ProviderAccessConfig: make(map[string]any), - ProviderExtendedConfig: nodeConfig.ProviderConfig, + Provider: domain.DeploymentProviderType(nodeConfig.Provider), + ProviderAccessConfig: make(map[string]any), + ProviderServiceConfig: nodeConfig.ProviderConfig, } accessRepo := repository.NewAccessRepository() diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go index d4c61a31..15a8f370 100644 --- a/internal/deployer/providers.go +++ b/internal/deployer/providers.go @@ -19,6 +19,7 @@ import ( pAliyunDDoS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-ddos" pAliyunESA "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-esa" pAliyunFC "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-fc" + pAliyunGA "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-ga" pAliyunLive "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-live" pAliyunNLB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-nlb" pAliyunOSS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-oss" @@ -34,12 +35,15 @@ import ( pBaishanCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baishan-cdn" pBaotaPanelConsole "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baotapanel-console" pBaotaPanelSite "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baotapanel-site" + pBaotaWAFConsole "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baotawaf-console" + pBaotaWAFSite "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baotawaf-site" pBunnyCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/bunny-cdn" pBytePlusCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/byteplus-cdn" pCacheFly "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/cachefly" pCdnfly "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/cdnfly" pDogeCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/dogecloud-cdn" pEdgioApplications "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/edgio-applications" + pFlexCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/flexcdn" pGcoreCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/gcore-cdn" pGoEdge "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/goedge" pHuaweiCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/huaweicloud-cdn" @@ -51,6 +55,7 @@ import ( pJDCloudLive "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-live" pJDCloudVOD "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-vod" pK8sSecret "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/k8s-secret" + pLeCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/lecdn" pLocal "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/local" pNetlifySite "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/netlify-site" pProxmoxVE "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/proxmoxve" @@ -83,7 +88,9 @@ import ( pVolcEngineImageX "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-imagex" pVolcEngineLive "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-live" pVolcEngineTOS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-tos" + pWangsuCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/wangsu-cdn" pWangsuCDNPro "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/wangsu-cdnpro" + pWangsuCertificate "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/wangsu-certificate" pWebhook "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/webhook" httputil "github.com/usual2970/certimate/internal/pkg/utils/http" maputil "github.com/usual2970/certimate/internal/pkg/utils/map" @@ -91,9 +98,9 @@ import ( ) type deployerProviderOptions struct { - Provider domain.DeploymentProviderType - ProviderAccessConfig map[string]any - ProviderExtendedConfig map[string]any + Provider domain.DeploymentProviderType + ProviderAccessConfig map[string]any + ProviderServiceConfig map[string]any } func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer, error) { @@ -113,20 +120,22 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer case domain.DeploymentProviderType1PanelConsole: deployer, err := p1PanelConsole.NewDeployer(&p1PanelConsole.DeployerConfig{ ApiUrl: access.ApiUrl, + ApiVersion: access.ApiVersion, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, - AutoRestart: maputil.GetBool(options.ProviderExtendedConfig, "autoRestart"), + AutoRestart: maputil.GetBool(options.ProviderServiceConfig, "autoRestart"), }) return deployer, err case domain.DeploymentProviderType1PanelSite: deployer, err := p1PanelSite.NewDeployer(&p1PanelSite.DeployerConfig{ ApiUrl: access.ApiUrl, + ApiVersion: access.ApiVersion, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, - ResourceType: p1PanelSite.ResourceType(maputil.GetOrDefaultString(options.ProviderExtendedConfig, "resourceType", string(p1PanelSite.RESOURCE_TYPE_WEBSITE))), - WebsiteId: maputil.GetInt64(options.ProviderExtendedConfig, "websiteId"), - CertificateId: maputil.GetInt64(options.ProviderExtendedConfig, "certificateId"), + ResourceType: p1PanelSite.ResourceType(maputil.GetOrDefaultString(options.ProviderServiceConfig, "resourceType", string(p1PanelSite.RESOURCE_TYPE_WEBSITE))), + WebsiteId: maputil.GetInt64(options.ProviderServiceConfig, "websiteId"), + CertificateId: maputil.GetInt64(options.ProviderServiceConfig, "certificateId"), }) return deployer, err @@ -135,7 +144,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer } } - case domain.DeploymentProviderTypeAliyunALB, domain.DeploymentProviderTypeAliyunAPIGW, domain.DeploymentProviderTypeAliyunCAS, domain.DeploymentProviderTypeAliyunCASDeploy, domain.DeploymentProviderTypeAliyunCDN, domain.DeploymentProviderTypeAliyunCLB, domain.DeploymentProviderTypeAliyunDCDN, domain.DeploymentProviderTypeAliyunDDoS, domain.DeploymentProviderTypeAliyunESA, domain.DeploymentProviderTypeAliyunFC, domain.DeploymentProviderTypeAliyunLive, domain.DeploymentProviderTypeAliyunNLB, domain.DeploymentProviderTypeAliyunOSS, domain.DeploymentProviderTypeAliyunVOD, domain.DeploymentProviderTypeAliyunWAF: + case domain.DeploymentProviderTypeAliyunALB, domain.DeploymentProviderTypeAliyunAPIGW, domain.DeploymentProviderTypeAliyunCAS, domain.DeploymentProviderTypeAliyunCASDeploy, domain.DeploymentProviderTypeAliyunCDN, domain.DeploymentProviderTypeAliyunCLB, domain.DeploymentProviderTypeAliyunDCDN, domain.DeploymentProviderTypeAliyunDDoS, domain.DeploymentProviderTypeAliyunESA, domain.DeploymentProviderTypeAliyunFC, domain.DeploymentProviderTypeAliyunGA, domain.DeploymentProviderTypeAliyunLive, domain.DeploymentProviderTypeAliyunNLB, domain.DeploymentProviderTypeAliyunOSS, domain.DeploymentProviderTypeAliyunVOD, domain.DeploymentProviderTypeAliyunWAF: { access := domain.AccessConfigForAliyun{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -147,11 +156,11 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pAliyunALB.NewDeployer(&pAliyunALB.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - ResourceType: pAliyunALB.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), - LoadbalancerId: maputil.GetString(options.ProviderExtendedConfig, "loadbalancerId"), - ListenerId: maputil.GetString(options.ProviderExtendedConfig, "listenerId"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + ResourceType: pAliyunALB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")), + LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"), + ListenerId: maputil.GetString(options.ProviderServiceConfig, "listenerId"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -159,11 +168,11 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pAliyunAPIGW.NewDeployer(&pAliyunAPIGW.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - ServiceType: pAliyunAPIGW.ServiceType(maputil.GetString(options.ProviderExtendedConfig, "serviceType")), - GatewayId: maputil.GetString(options.ProviderExtendedConfig, "gatewayId"), - GroupId: maputil.GetString(options.ProviderExtendedConfig, "groupId"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + ServiceType: pAliyunAPIGW.ServiceType(maputil.GetString(options.ProviderServiceConfig, "serviceType")), + GatewayId: maputil.GetString(options.ProviderServiceConfig, "gatewayId"), + GroupId: maputil.GetString(options.ProviderServiceConfig, "groupId"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -171,7 +180,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pAliyunCAS.NewDeployer(&pAliyunCAS.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), }) return deployer, err @@ -179,9 +188,9 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pAliyunCASDeploy.NewDeployer(&pAliyunCASDeploy.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - ResourceIds: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderExtendedConfig, "resourceIds"), ";"), func(s string) bool { return s != "" }), - ContactIds: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderExtendedConfig, "contactIds"), ";"), func(s string) bool { return s != "" }), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + ResourceIds: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderServiceConfig, "resourceIds"), ";"), func(s string) bool { return s != "" }), + ContactIds: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderServiceConfig, "contactIds"), ";"), func(s string) bool { return s != "" }), }) return deployer, err @@ -189,7 +198,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pAliyunCDN.NewDeployer(&pAliyunCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -197,11 +206,11 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pAliyunCLB.NewDeployer(&pAliyunCLB.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - ResourceType: pAliyunCLB.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), - LoadbalancerId: maputil.GetString(options.ProviderExtendedConfig, "loadbalancerId"), - ListenerPort: maputil.GetOrDefaultInt32(options.ProviderExtendedConfig, "listenerPort", 443), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + ResourceType: pAliyunCLB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")), + LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"), + ListenerPort: maputil.GetOrDefaultInt32(options.ProviderServiceConfig, "listenerPort", 443), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -209,7 +218,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pAliyunDCDN.NewDeployer(&pAliyunDCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -217,8 +226,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pAliyunDDoS.NewDeployer(&pAliyunDDoS.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -226,8 +235,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pAliyunESA.NewDeployer(&pAliyunESA.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - SiteId: maputil.GetInt64(options.ProviderExtendedConfig, "siteId"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + SiteId: maputil.GetInt64(options.ProviderServiceConfig, "siteId"), }) return deployer, err @@ -235,9 +244,20 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pAliyunFC.NewDeployer(&pAliyunFC.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - ServiceVersion: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "serviceVersion", "3.0"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + ServiceVersion: maputil.GetOrDefaultString(options.ProviderServiceConfig, "serviceVersion", "3.0"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), + }) + return deployer, err + + case domain.DeploymentProviderTypeAliyunGA: + deployer, err := pAliyunGA.NewDeployer(&pAliyunGA.DeployerConfig{ + AccessKeyId: access.AccessKeyId, + AccessKeySecret: access.AccessKeySecret, + ResourceType: pAliyunGA.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")), + AcceleratorId: maputil.GetString(options.ProviderServiceConfig, "acceleratorId"), + ListenerId: maputil.GetString(options.ProviderServiceConfig, "listenerId"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -245,8 +265,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pAliyunLive.NewDeployer(&pAliyunLive.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -254,10 +274,10 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pAliyunNLB.NewDeployer(&pAliyunNLB.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - ResourceType: pAliyunNLB.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), - LoadbalancerId: maputil.GetString(options.ProviderExtendedConfig, "loadbalancerId"), - ListenerId: maputil.GetString(options.ProviderExtendedConfig, "listenerId"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + ResourceType: pAliyunNLB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")), + LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"), + ListenerId: maputil.GetString(options.ProviderServiceConfig, "listenerId"), }) return deployer, err @@ -265,9 +285,9 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pAliyunOSS.NewDeployer(&pAliyunOSS.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - Bucket: maputil.GetString(options.ProviderExtendedConfig, "bucket"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + Bucket: maputil.GetString(options.ProviderServiceConfig, "bucket"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -275,8 +295,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pAliyunVOD.NewDeployer(&pAliyunVOD.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -284,10 +304,10 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pAliyunWAF.NewDeployer(&pAliyunWAF.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - ServiceVersion: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "serviceVersion", "3.0"), - InstanceId: maputil.GetString(options.ProviderExtendedConfig, "instanceId"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + ServiceVersion: maputil.GetOrDefaultString(options.ProviderServiceConfig, "serviceVersion", "3.0"), + InstanceId: maputil.GetString(options.ProviderServiceConfig, "instanceId"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -308,8 +328,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pAWSACM.NewDeployer(&pAWSACM.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - CertificateArn: maputil.GetString(options.ProviderExtendedConfig, "certificateArn"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + CertificateArn: maputil.GetString(options.ProviderServiceConfig, "certificateArn"), }) return deployer, err @@ -317,8 +337,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pAWSCloudFront.NewDeployer(&pAWSCloudFront.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - DistributionId: maputil.GetString(options.ProviderExtendedConfig, "distributionId"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + DistributionId: maputil.GetString(options.ProviderServiceConfig, "distributionId"), }) return deployer, err @@ -341,8 +361,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer ClientId: access.ClientId, ClientSecret: access.ClientSecret, CloudName: access.CloudName, - KeyVaultName: maputil.GetString(options.ProviderExtendedConfig, "keyvaultName"), - CertificateName: maputil.GetString(options.ProviderExtendedConfig, "certificateName"), + KeyVaultName: maputil.GetString(options.ProviderServiceConfig, "keyvaultName"), + CertificateName: maputil.GetString(options.ProviderServiceConfig, "certificateName"), }) return deployer, err @@ -363,11 +383,11 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pBaiduCloudAppBLB.NewDeployer(&pBaiduCloudAppBLB.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - ResourceType: pBaiduCloudAppBLB.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), - LoadbalancerId: maputil.GetString(options.ProviderExtendedConfig, "loadbalancerId"), - ListenerPort: maputil.GetInt32(options.ProviderExtendedConfig, "listenerPort"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + ResourceType: pBaiduCloudAppBLB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")), + LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"), + ListenerPort: maputil.GetInt32(options.ProviderServiceConfig, "listenerPort"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -375,11 +395,11 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pBaiduCloudBLB.NewDeployer(&pBaiduCloudBLB.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - ResourceType: pBaiduCloudBLB.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), - LoadbalancerId: maputil.GetString(options.ProviderExtendedConfig, "loadbalancerId"), - ListenerPort: maputil.GetInt32(options.ProviderExtendedConfig, "listenerPort"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + ResourceType: pBaiduCloudBLB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")), + LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"), + ListenerPort: maputil.GetInt32(options.ProviderServiceConfig, "listenerPort"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -387,7 +407,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pBaiduCloudCDN.NewDeployer(&pBaiduCloudCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -414,8 +434,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer case domain.DeploymentProviderTypeBaishanCDN: deployer, err := pBaishanCDN.NewDeployer(&pBaishanCDN.DeployerConfig{ ApiToken: access.ApiToken, - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), - CertificateId: maputil.GetString(options.ProviderExtendedConfig, "certificateId"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), + CertificateId: maputil.GetString(options.ProviderServiceConfig, "certificateId"), }) return deployer, err @@ -437,7 +457,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer ApiUrl: access.ApiUrl, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, - AutoRestart: maputil.GetBool(options.ProviderExtendedConfig, "autoRestart"), + AutoRestart: maputil.GetBool(options.ProviderServiceConfig, "autoRestart"), }) return deployer, err @@ -446,9 +466,40 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer ApiUrl: access.ApiUrl, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, - SiteType: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "siteType", "other"), - SiteName: maputil.GetString(options.ProviderExtendedConfig, "siteName"), - SiteNames: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderExtendedConfig, "siteNames"), ";"), func(s string) bool { return s != "" }), + SiteType: maputil.GetOrDefaultString(options.ProviderServiceConfig, "siteType", "other"), + SiteName: maputil.GetString(options.ProviderServiceConfig, "siteName"), + SiteNames: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderServiceConfig, "siteNames"), ";"), func(s string) bool { return s != "" }), + }) + return deployer, err + + default: + break + } + } + + case domain.DeploymentProviderTypeBaotaWAFConsole, domain.DeploymentProviderTypeBaotaWAFSite: + { + access := domain.AccessConfigForBaotaWAF{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + switch options.Provider { + case domain.DeploymentProviderTypeBaotaWAFConsole: + deployer, err := pBaotaWAFConsole.NewDeployer(&pBaotaWAFConsole.DeployerConfig{ + ApiUrl: access.ApiUrl, + ApiKey: access.ApiKey, + AllowInsecureConnections: access.AllowInsecureConnections, + }) + return deployer, err + + case domain.DeploymentProviderTypeBaotaWAFSite: + deployer, err := pBaotaWAFSite.NewDeployer(&pBaotaWAFSite.DeployerConfig{ + ApiUrl: access.ApiUrl, + ApiKey: access.ApiKey, + AllowInsecureConnections: access.AllowInsecureConnections, + SiteName: maputil.GetString(options.ProviderServiceConfig, "siteName"), + SitePort: maputil.GetOrDefaultInt32(options.ProviderServiceConfig, "sitePort", 443), }) return deployer, err @@ -466,8 +517,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pBunnyCDN.NewDeployer(&pBunnyCDN.DeployerConfig{ ApiKey: access.ApiKey, - PullZoneId: maputil.GetString(options.ProviderExtendedConfig, "pullZoneId"), - Hostname: maputil.GetString(options.ProviderExtendedConfig, "hostname"), + PullZoneId: maputil.GetString(options.ProviderServiceConfig, "pullZoneId"), + Hostname: maputil.GetString(options.ProviderServiceConfig, "hostname"), }) return deployer, err } @@ -484,7 +535,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pBytePlusCDN.NewDeployer(&pBytePlusCDN.DeployerConfig{ AccessKey: access.AccessKey, SecretKey: access.SecretKey, - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -518,9 +569,9 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer ApiKey: access.ApiKey, ApiSecret: access.ApiSecret, AllowInsecureConnections: access.AllowInsecureConnections, - ResourceType: pCdnfly.ResourceType(maputil.GetOrDefaultString(options.ProviderExtendedConfig, "resourceType", string(pCdnfly.RESOURCE_TYPE_SITE))), - SiteId: maputil.GetString(options.ProviderExtendedConfig, "siteId"), - CertificateId: maputil.GetString(options.ProviderExtendedConfig, "certificateId"), + ResourceType: pCdnfly.ResourceType(maputil.GetOrDefaultString(options.ProviderServiceConfig, "resourceType", string(pCdnfly.RESOURCE_TYPE_SITE))), + SiteId: maputil.GetString(options.ProviderServiceConfig, "siteId"), + CertificateId: maputil.GetString(options.ProviderServiceConfig, "certificateId"), }) return deployer, err } @@ -535,7 +586,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pDogeCDN.NewDeployer(&pDogeCDN.DeployerConfig{ AccessKey: access.AccessKey, SecretKey: access.SecretKey, - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err } @@ -550,7 +601,26 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pEdgioApplications.NewDeployer(&pEdgioApplications.DeployerConfig{ ClientId: access.ClientId, ClientSecret: access.ClientSecret, - EnvironmentId: maputil.GetString(options.ProviderExtendedConfig, "environmentId"), + EnvironmentId: maputil.GetString(options.ProviderServiceConfig, "environmentId"), + }) + return deployer, err + } + + case domain.DeploymentProviderTypeFlexCDN: + { + access := domain.AccessConfigForFlexCDN{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + deployer, err := pFlexCDN.NewDeployer(&pFlexCDN.DeployerConfig{ + ApiUrl: access.ApiUrl, + ApiRole: access.ApiRole, + AccessKeyId: access.AccessKeyId, + AccessKey: access.AccessKey, + AllowInsecureConnections: access.AllowInsecureConnections, + ResourceType: pFlexCDN.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")), + CertificateId: maputil.GetInt64(options.ProviderServiceConfig, "certificateId"), }) return deployer, err } @@ -566,8 +636,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer case domain.DeploymentProviderTypeGcoreCDN: deployer, err := pGcoreCDN.NewDeployer(&pGcoreCDN.DeployerConfig{ ApiToken: access.ApiToken, - ResourceId: maputil.GetInt64(options.ProviderExtendedConfig, "resourceId"), - CertificateId: maputil.GetInt64(options.ProviderExtendedConfig, "certificateId"), + ResourceId: maputil.GetInt64(options.ProviderServiceConfig, "resourceId"), + CertificateId: maputil.GetInt64(options.ProviderServiceConfig, "certificateId"), }) return deployer, err @@ -589,8 +659,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer AccessKeyId: access.AccessKeyId, AccessKey: access.AccessKey, AllowInsecureConnections: access.AllowInsecureConnections, - ResourceType: pGoEdge.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), - CertificateId: maputil.GetInt64(options.ProviderExtendedConfig, "certificateId"), + ResourceType: pGoEdge.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")), + CertificateId: maputil.GetInt64(options.ProviderServiceConfig, "certificateId"), }) return deployer, err } @@ -607,8 +677,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pHuaweiCloudCDN.NewDeployer(&pHuaweiCloudCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -616,11 +686,11 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pHuaweiCloudELB.NewDeployer(&pHuaweiCloudELB.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - ResourceType: pHuaweiCloudELB.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), - CertificateId: maputil.GetString(options.ProviderExtendedConfig, "certificateId"), - LoadbalancerId: maputil.GetString(options.ProviderExtendedConfig, "loadbalancerId"), - ListenerId: maputil.GetString(options.ProviderExtendedConfig, "listenerId"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + ResourceType: pHuaweiCloudELB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")), + CertificateId: maputil.GetString(options.ProviderServiceConfig, "certificateId"), + LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"), + ListenerId: maputil.GetString(options.ProviderServiceConfig, "listenerId"), }) return deployer, err @@ -635,10 +705,10 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pHuaweiCloudWAF.NewDeployer(&pHuaweiCloudWAF.DeployerConfig{ AccessKeyId: access.AccessKeyId, SecretAccessKey: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - ResourceType: pHuaweiCloudWAF.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), - CertificateId: maputil.GetString(options.ProviderExtendedConfig, "certificateId"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + ResourceType: pHuaweiCloudWAF.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")), + CertificateId: maputil.GetString(options.ProviderServiceConfig, "certificateId"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -659,10 +729,10 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pJDCloudALB.NewDeployer(&pJDCloudALB.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - RegionId: maputil.GetString(options.ProviderExtendedConfig, "regionId"), - ResourceType: pJDCloudALB.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), - LoadbalancerId: maputil.GetString(options.ProviderExtendedConfig, "loadbalancerId"), - ListenerId: maputil.GetString(options.ProviderExtendedConfig, "listenerId"), + RegionId: maputil.GetString(options.ProviderServiceConfig, "regionId"), + ResourceType: pJDCloudALB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")), + LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"), + ListenerId: maputil.GetString(options.ProviderServiceConfig, "listenerId"), }) return deployer, err @@ -670,7 +740,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pJDCloudCDN.NewDeployer(&pJDCloudCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -678,7 +748,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pJDCloudLive.NewDeployer(&pJDCloudLive.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -686,7 +756,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pJDCloudVOD.NewDeployer(&pJDCloudVOD.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -695,21 +765,42 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer } } + case domain.DeploymentProviderTypeLeCDN: + { + access := domain.AccessConfigForLeCDN{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + deployer, err := pLeCDN.NewDeployer(&pLeCDN.DeployerConfig{ + ApiUrl: access.ApiUrl, + ApiVersion: access.ApiVersion, + ApiRole: access.ApiRole, + Username: access.Username, + Password: access.Password, + AllowInsecureConnections: access.AllowInsecureConnections, + ResourceType: pLeCDN.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")), + CertificateId: maputil.GetInt64(options.ProviderServiceConfig, "certificateId"), + ClientId: maputil.GetInt64(options.ProviderServiceConfig, "clientId"), + }) + return deployer, err + } + case domain.DeploymentProviderTypeLocal: { deployer, err := pLocal.NewDeployer(&pLocal.DeployerConfig{ - ShellEnv: pLocal.ShellEnvType(maputil.GetString(options.ProviderExtendedConfig, "shellEnv")), - PreCommand: maputil.GetString(options.ProviderExtendedConfig, "preCommand"), - PostCommand: maputil.GetString(options.ProviderExtendedConfig, "postCommand"), - OutputFormat: pLocal.OutputFormatType(maputil.GetOrDefaultString(options.ProviderExtendedConfig, "format", string(pLocal.OUTPUT_FORMAT_PEM))), - OutputCertPath: maputil.GetString(options.ProviderExtendedConfig, "certPath"), - OutputServerCertPath: maputil.GetString(options.ProviderExtendedConfig, "certPathForServerOnly"), - OutputIntermediaCertPath: maputil.GetString(options.ProviderExtendedConfig, "certPathForIntermediaOnly"), - OutputKeyPath: maputil.GetString(options.ProviderExtendedConfig, "keyPath"), - PfxPassword: maputil.GetString(options.ProviderExtendedConfig, "pfxPassword"), - JksAlias: maputil.GetString(options.ProviderExtendedConfig, "jksAlias"), - JksKeypass: maputil.GetString(options.ProviderExtendedConfig, "jksKeypass"), - JksStorepass: maputil.GetString(options.ProviderExtendedConfig, "jksStorepass"), + ShellEnv: pLocal.ShellEnvType(maputil.GetString(options.ProviderServiceConfig, "shellEnv")), + PreCommand: maputil.GetString(options.ProviderServiceConfig, "preCommand"), + PostCommand: maputil.GetString(options.ProviderServiceConfig, "postCommand"), + OutputFormat: pLocal.OutputFormatType(maputil.GetOrDefaultString(options.ProviderServiceConfig, "format", string(pLocal.OUTPUT_FORMAT_PEM))), + OutputCertPath: maputil.GetString(options.ProviderServiceConfig, "certPath"), + OutputServerCertPath: maputil.GetString(options.ProviderServiceConfig, "certPathForServerOnly"), + OutputIntermediaCertPath: maputil.GetString(options.ProviderServiceConfig, "certPathForIntermediaOnly"), + OutputKeyPath: maputil.GetString(options.ProviderServiceConfig, "keyPath"), + PfxPassword: maputil.GetString(options.ProviderServiceConfig, "pfxPassword"), + JksAlias: maputil.GetString(options.ProviderServiceConfig, "jksAlias"), + JksKeypass: maputil.GetString(options.ProviderServiceConfig, "jksKeypass"), + JksStorepass: maputil.GetString(options.ProviderServiceConfig, "jksStorepass"), }) return deployer, err } @@ -723,11 +814,11 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pK8sSecret.NewDeployer(&pK8sSecret.DeployerConfig{ KubeConfig: access.KubeConfig, - Namespace: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "namespace", "default"), - SecretName: maputil.GetString(options.ProviderExtendedConfig, "secretName"), - SecretType: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "secretType", "kubernetes.io/tls"), - SecretDataKeyForCrt: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "secretDataKeyForCrt", "tls.crt"), - SecretDataKeyForKey: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "secretDataKeyForKey", "tls.key"), + Namespace: maputil.GetOrDefaultString(options.ProviderServiceConfig, "namespace", "default"), + SecretName: maputil.GetString(options.ProviderServiceConfig, "secretName"), + SecretType: maputil.GetOrDefaultString(options.ProviderServiceConfig, "secretType", "kubernetes.io/tls"), + SecretDataKeyForCrt: maputil.GetOrDefaultString(options.ProviderServiceConfig, "secretDataKeyForCrt", "tls.crt"), + SecretDataKeyForKey: maputil.GetOrDefaultString(options.ProviderServiceConfig, "secretDataKeyForKey", "tls.key"), }) return deployer, err } @@ -741,7 +832,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pNetlifySite.NewDeployer(&pNetlifySite.DeployerConfig{ ApiToken: access.ApiToken, - SiteId: maputil.GetString(options.ProviderExtendedConfig, "siteId"), + SiteId: maputil.GetString(options.ProviderServiceConfig, "siteId"), }) return deployer, err } @@ -758,8 +849,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer ApiToken: access.ApiToken, ApiTokenSecret: access.ApiTokenSecret, AllowInsecureConnections: access.AllowInsecureConnections, - NodeName: maputil.GetString(options.ProviderExtendedConfig, "nodeName"), - AutoRestart: maputil.GetBool(options.ProviderExtendedConfig, "autoRestart"), + NodeName: maputil.GetString(options.ProviderServiceConfig, "nodeName"), + AutoRestart: maputil.GetBool(options.ProviderServiceConfig, "autoRestart"), }) return deployer, err } @@ -776,7 +867,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pQiniuCDN.NewDeployer(&pQiniuCDN.DeployerConfig{ AccessKey: access.AccessKey, SecretKey: access.SecretKey, - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -784,8 +875,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pQiniuPili.NewDeployer(&pQiniuPili.DeployerConfig{ AccessKey: access.AccessKey, SecretKey: access.SecretKey, - Hub: maputil.GetString(options.ProviderExtendedConfig, "hub"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Hub: maputil.GetString(options.ProviderServiceConfig, "hub"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -805,8 +896,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer case domain.DeploymentProviderTypeTencentCloudCDN: deployer, err := pRainYunRCDN.NewDeployer(&pRainYunRCDN.DeployerConfig{ ApiKey: access.ApiKey, - InstanceId: maputil.GetInt32(options.ProviderExtendedConfig, "instanceId"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + InstanceId: maputil.GetInt32(options.ProviderServiceConfig, "instanceId"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -838,7 +929,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer AccessTokenId: access.AccessTokenId, AccessToken: access.AccessToken, AllowInsecureConnections: access.AllowInsecureConnections, - SiteName: maputil.GetString(options.ProviderExtendedConfig, "siteName"), + SiteName: maputil.GetString(options.ProviderServiceConfig, "siteName"), }) return deployer, err @@ -858,8 +949,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer ApiUrl: access.ApiUrl, ApiToken: access.ApiToken, AllowInsecureConnections: access.AllowInsecureConnections, - ResourceType: pSafeLine.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), - CertificateId: maputil.GetInt32(options.ProviderExtendedConfig, "certificateId"), + ResourceType: pSafeLine.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")), + CertificateId: maputil.GetInt32(options.ProviderServiceConfig, "certificateId"), }) return deployer, err } @@ -878,18 +969,18 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer SshPassword: access.Password, SshKey: access.Key, SshKeyPassphrase: access.KeyPassphrase, - UseSCP: maputil.GetBool(options.ProviderExtendedConfig, "useSCP"), - PreCommand: maputil.GetString(options.ProviderExtendedConfig, "preCommand"), - PostCommand: maputil.GetString(options.ProviderExtendedConfig, "postCommand"), - OutputFormat: pSSH.OutputFormatType(maputil.GetOrDefaultString(options.ProviderExtendedConfig, "format", string(pSSH.OUTPUT_FORMAT_PEM))), - OutputCertPath: maputil.GetString(options.ProviderExtendedConfig, "certPath"), - OutputServerCertPath: maputil.GetString(options.ProviderExtendedConfig, "certPathForServerOnly"), - OutputIntermediaCertPath: maputil.GetString(options.ProviderExtendedConfig, "certPathForIntermediaOnly"), - OutputKeyPath: maputil.GetString(options.ProviderExtendedConfig, "keyPath"), - PfxPassword: maputil.GetString(options.ProviderExtendedConfig, "pfxPassword"), - JksAlias: maputil.GetString(options.ProviderExtendedConfig, "jksAlias"), - JksKeypass: maputil.GetString(options.ProviderExtendedConfig, "jksKeypass"), - JksStorepass: maputil.GetString(options.ProviderExtendedConfig, "jksStorepass"), + UseSCP: maputil.GetBool(options.ProviderServiceConfig, "useSCP"), + PreCommand: maputil.GetString(options.ProviderServiceConfig, "preCommand"), + PostCommand: maputil.GetString(options.ProviderServiceConfig, "postCommand"), + OutputFormat: pSSH.OutputFormatType(maputil.GetOrDefaultString(options.ProviderServiceConfig, "format", string(pSSH.OUTPUT_FORMAT_PEM))), + OutputCertPath: maputil.GetString(options.ProviderServiceConfig, "certPath"), + OutputServerCertPath: maputil.GetString(options.ProviderServiceConfig, "certPathForServerOnly"), + OutputIntermediaCertPath: maputil.GetString(options.ProviderServiceConfig, "certPathForIntermediaOnly"), + OutputKeyPath: maputil.GetString(options.ProviderServiceConfig, "keyPath"), + PfxPassword: maputil.GetString(options.ProviderServiceConfig, "pfxPassword"), + JksAlias: maputil.GetString(options.ProviderServiceConfig, "jksAlias"), + JksKeypass: maputil.GetString(options.ProviderServiceConfig, "jksKeypass"), + JksStorepass: maputil.GetString(options.ProviderServiceConfig, "jksStorepass"), }) return deployer, err } @@ -906,7 +997,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pTencentCloudCDN.NewDeployer(&pTencentCloudCDN.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -914,11 +1005,11 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pTencentCloudCLB.NewDeployer(&pTencentCloudCLB.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - ResourceType: pTencentCloudCLB.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), - LoadbalancerId: maputil.GetString(options.ProviderExtendedConfig, "loadbalancerId"), - ListenerId: maputil.GetString(options.ProviderExtendedConfig, "listenerId"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + ResourceType: pTencentCloudCLB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")), + LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"), + ListenerId: maputil.GetString(options.ProviderServiceConfig, "listenerId"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -926,9 +1017,9 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pTencentCloudCOS.NewDeployer(&pTencentCloudCOS.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - Bucket: maputil.GetString(options.ProviderExtendedConfig, "bucket"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + Bucket: maputil.GetString(options.ProviderServiceConfig, "bucket"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -936,7 +1027,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pTencentCloudCSS.NewDeployer(&pTencentCloudCSS.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -944,7 +1035,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pTencentCloudECDN.NewDeployer(&pTencentCloudECDN.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -952,8 +1043,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pTencentCloudEO.NewDeployer(&pTencentCloudEO.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - ZoneId: maputil.GetString(options.ProviderExtendedConfig, "zoneId"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + ZoneId: maputil.GetString(options.ProviderServiceConfig, "zoneId"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -961,8 +1052,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pTencentCloudSCF.NewDeployer(&pTencentCloudSCF.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -977,9 +1068,9 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pTencentCloudSSLDeploy.NewDeployer(&pTencentCloudSSLDeploy.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - ResourceType: maputil.GetString(options.ProviderExtendedConfig, "resourceType"), - ResourceIds: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderExtendedConfig, "resourceIds"), ";"), func(s string) bool { return s != "" }), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + ResourceType: maputil.GetString(options.ProviderServiceConfig, "resourceType"), + ResourceIds: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderServiceConfig, "resourceIds"), ";"), func(s string) bool { return s != "" }), }) return deployer, err @@ -987,8 +1078,8 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pTencentCloudVOD.NewDeployer(&pTencentCloudVOD.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - SubAppId: maputil.GetInt64(options.ProviderExtendedConfig, "subAppId"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + SubAppId: maputil.GetInt64(options.ProviderServiceConfig, "subAppId"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -996,9 +1087,9 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pTencentCloudWAF.NewDeployer(&pTencentCloudWAF.DeployerConfig{ SecretId: access.SecretId, SecretKey: access.SecretKey, - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), - DomainId: maputil.GetString(options.ProviderExtendedConfig, "domainId"), - InstanceId: maputil.GetString(options.ProviderExtendedConfig, "instanceId"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), + DomainId: maputil.GetString(options.ProviderServiceConfig, "domainId"), + InstanceId: maputil.GetString(options.ProviderServiceConfig, "instanceId"), }) return deployer, err @@ -1020,7 +1111,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer PrivateKey: access.PrivateKey, PublicKey: access.PublicKey, ProjectId: access.ProjectId, - DomainId: maputil.GetString(options.ProviderExtendedConfig, "domainId"), + DomainId: maputil.GetString(options.ProviderServiceConfig, "domainId"), }) return deployer, err @@ -1029,9 +1120,9 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer PrivateKey: access.PrivateKey, PublicKey: access.PublicKey, ProjectId: access.ProjectId, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - Bucket: maputil.GetString(options.ProviderExtendedConfig, "bucket"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + Bucket: maputil.GetString(options.ProviderServiceConfig, "bucket"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -1052,7 +1143,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pUpyunCDN.NewDeployer(&pUpyunCDN.DeployerConfig{ Username: access.Username, Password: access.Password, - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -1073,11 +1164,11 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pVolcEngineALB.NewDeployer(&pVolcEngineALB.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - ResourceType: pVolcEngineALB.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), - LoadbalancerId: maputil.GetString(options.ProviderExtendedConfig, "loadbalancerId"), - ListenerId: maputil.GetString(options.ProviderExtendedConfig, "listenerId"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + ResourceType: pVolcEngineALB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")), + LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"), + ListenerId: maputil.GetString(options.ProviderServiceConfig, "listenerId"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -1085,7 +1176,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pVolcEngineCDN.NewDeployer(&pVolcEngineCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -1093,7 +1184,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pVolcEngineCertCenter.NewDeployer(&pVolcEngineCertCenter.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), }) return deployer, err @@ -1101,10 +1192,10 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pVolcEngineCLB.NewDeployer(&pVolcEngineCLB.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - ResourceType: pVolcEngineCLB.ResourceType(maputil.GetString(options.ProviderExtendedConfig, "resourceType")), - LoadbalancerId: maputil.GetString(options.ProviderExtendedConfig, "loadbalancerId"), - ListenerId: maputil.GetString(options.ProviderExtendedConfig, "listenerId"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + ResourceType: pVolcEngineCLB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")), + LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"), + ListenerId: maputil.GetString(options.ProviderServiceConfig, "listenerId"), }) return deployer, err @@ -1112,7 +1203,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pVolcEngineDCDN.NewDeployer(&pVolcEngineDCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -1120,9 +1211,9 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pVolcEngineImageX.NewDeployer(&pVolcEngineImageX.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - ServiceId: maputil.GetString(options.ProviderExtendedConfig, "serviceId"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + ServiceId: maputil.GetString(options.ProviderServiceConfig, "serviceId"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -1130,7 +1221,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pVolcEngineLive.NewDeployer(&pVolcEngineLive.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -1138,9 +1229,9 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pVolcEngineTOS.NewDeployer(&pVolcEngineTOS.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.SecretAccessKey, - Region: maputil.GetString(options.ProviderExtendedConfig, "region"), - Bucket: maputil.GetString(options.ProviderExtendedConfig, "bucket"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), + Region: maputil.GetString(options.ProviderServiceConfig, "region"), + Bucket: maputil.GetString(options.ProviderServiceConfig, "bucket"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), }) return deployer, err @@ -1149,7 +1240,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer } } - case domain.DeploymentProviderTypeWangsuCDNPro: + case domain.DeploymentProviderTypeWangsuCDN, domain.DeploymentProviderTypeWangsuCDNPro, domain.DeploymentProviderTypeWangsuCertificate: { access := domain.AccessConfigForWangsu{} if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -1157,15 +1248,31 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer } switch options.Provider { + case domain.DeploymentProviderTypeWangsuCDN: + deployer, err := pWangsuCDN.NewDeployer(&pWangsuCDN.DeployerConfig{ + AccessKeyId: access.AccessKeyId, + AccessKeySecret: access.AccessKeySecret, + Domains: sliceutil.Filter(strings.Split(maputil.GetString(options.ProviderServiceConfig, "domains"), ";"), func(s string) bool { return s != "" }), + }) + return deployer, err + case domain.DeploymentProviderTypeWangsuCDNPro: deployer, err := pWangsuCDNPro.NewDeployer(&pWangsuCDNPro.DeployerConfig{ AccessKeyId: access.AccessKeyId, AccessKeySecret: access.AccessKeySecret, ApiKey: access.ApiKey, - Environment: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "environment", "production"), - Domain: maputil.GetString(options.ProviderExtendedConfig, "domain"), - CertificateId: maputil.GetString(options.ProviderExtendedConfig, "certificateId"), - WebhookId: maputil.GetString(options.ProviderExtendedConfig, "webhookId"), + Environment: maputil.GetOrDefaultString(options.ProviderServiceConfig, "environment", "production"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), + CertificateId: maputil.GetString(options.ProviderServiceConfig, "certificateId"), + WebhookId: maputil.GetString(options.ProviderServiceConfig, "webhookId"), + }) + return deployer, err + + case domain.DeploymentProviderTypeWangsuCertificate: + deployer, err := pWangsuCertificate.NewDeployer(&pWangsuCertificate.DeployerConfig{ + AccessKeyId: access.AccessKeyId, + AccessKeySecret: access.AccessKeySecret, + CertificateId: maputil.GetString(options.ProviderServiceConfig, "certificateId"), }) return deployer, err @@ -1191,7 +1298,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key) } } - if extendedHeadersString := maputil.GetString(options.ProviderExtendedConfig, "headers"); extendedHeadersString != "" { + if extendedHeadersString := maputil.GetString(options.ProviderServiceConfig, "headers"); extendedHeadersString != "" { h, err := httputil.ParseHeaders(extendedHeadersString) if err != nil { return nil, fmt.Errorf("failed to parse webhook headers: %w", err) @@ -1203,7 +1310,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer deployer, err := pWebhook.NewDeployer(&pWebhook.DeployerConfig{ WebhookUrl: access.Url, - WebhookData: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "webhookData", access.DefaultDataForDeployment), + WebhookData: maputil.GetOrDefaultString(options.ProviderServiceConfig, "webhookData", access.DefaultDataForDeployment), Method: access.Method, Headers: mergedHeaders, AllowInsecureConnections: access.AllowInsecureConnections, diff --git a/internal/domain/access.go b/internal/domain/access.go index 8bc5d0ef..cb5b7708 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -17,10 +17,17 @@ type Access struct { type AccessConfigFor1Panel struct { ApiUrl string `json:"apiUrl"` + ApiVersion string `json:"apiVersion"` ApiKey string `json:"apiKey"` AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } +type AccessConfigForACMECA struct { + Endpoint string `json:"endpoint"` + EabKid string `json:"eabKid,omitempty"` + EabHmacKey string `json:"eabHmacKey,omitempty"` +} + type AccessConfigForACMEHttpReq struct { Endpoint string `json:"endpoint"` Mode string `json:"mode,omitempty"` @@ -60,6 +67,12 @@ type AccessConfigForBaotaPanel struct { AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } +type AccessConfigForBaotaWAF struct { + ApiUrl string `json:"apiUrl"` + ApiKey string `json:"apiKey"` + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` +} + type AccessConfigForBytePlus struct { AccessKey string `json:"accessKey"` SecretKey string `json:"secretKey"` @@ -133,6 +146,14 @@ type AccessConfigForEmail struct { DefaultReceiverAddress string `json:"defaultReceiverAddress,omitempty"` } +type AccessConfigForFlexCDN struct { + ApiUrl string `json:"apiUrl"` + ApiRole string `json:"apiRole"` + AccessKeyId string `json:"accessKeyId"` + AccessKey string `json:"accessKey"` + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` +} + type AccessConfigForGcore struct { ApiToken string `json:"apiToken"` } @@ -178,6 +199,15 @@ type AccessConfigForLarkBot struct { WebhookUrl string `json:"webhookUrl"` } +type AccessConfigForLeCDN struct { + ApiUrl string `json:"apiUrl"` + ApiVersion string `json:"apiVersion"` + ApiRole string `json:"apiRole"` + Username string `json:"username"` + Password string `json:"password"` + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` +} + type AccessConfigForMattermost struct { ServerUrl string `json:"serverUrl"` Username string `json:"username"` @@ -242,7 +272,7 @@ type AccessConfigForRainYun struct { type AccessConfigForRatPanel struct { ApiUrl string `json:"apiUrl"` - AccessTokenId uint `json:"accessTokenId"` + AccessTokenId int32 `json:"accessTokenId"` AccessToken string `json:"accessToken"` AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } diff --git a/internal/domain/certificate.go b/internal/domain/certificate.go index d2a998da..710ce268 100644 --- a/internal/domain/certificate.go +++ b/internal/domain/certificate.go @@ -20,7 +20,7 @@ type Certificate struct { SerialNumber string `json:"serialNumber" db:"serialNumber"` Certificate string `json:"certificate" db:"certificate"` PrivateKey string `json:"privateKey" db:"privateKey"` - Issuer string `json:"issuer" db:"issuer"` + IssuerOrg string `json:"issuerOrg" db:"issuerOrg"` IssuerCertificate string `json:"issuerCertificate" db:"issuerCertificate"` KeyAlgorithm CertificateKeyAlgorithmType `json:"keyAlgorithm" db:"keyAlgorithm"` EffectAt time.Time `json:"effectAt" db:"effectAt"` @@ -38,7 +38,7 @@ type Certificate struct { func (c *Certificate) PopulateFromX509(certX509 *x509.Certificate) *Certificate { c.SubjectAltNames = strings.Join(certX509.DNSNames, ";") c.SerialNumber = strings.ToUpper(certX509.SerialNumber.Text(16)) - c.Issuer = strings.Join(certX509.Issuer.Organization, ";") + c.IssuerOrg = strings.Join(certX509.Issuer.Organization, ";") c.EffectAt = certX509.NotBefore c.ExpireAt = certX509.NotAfter diff --git a/internal/domain/provider.go b/internal/domain/provider.go index 4a0c05dd..db0de59d 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -10,7 +10,7 @@ type AccessProviderType string */ const ( AccessProviderType1Panel = AccessProviderType("1panel") - AccessProviderTypeACMECA = AccessProviderType("acmeca") // ACME CA(预留) + AccessProviderTypeACMECA = AccessProviderType("acmeca") AccessProviderTypeACMEHttpReq = AccessProviderType("acmehttpreq") AccessProviderTypeAkamai = AccessProviderType("akamai") // Akamai(预留) AccessProviderTypeAliyun = AccessProviderType("aliyun") @@ -19,6 +19,7 @@ const ( AccessProviderTypeBaiduCloud = AccessProviderType("baiducloud") AccessProviderTypeBaishan = AccessProviderType("baishan") AccessProviderTypeBaotaPanel = AccessProviderType("baotapanel") + AccessProviderTypeBaotaWAF = AccessProviderType("baotawaf") AccessProviderTypeBytePlus = AccessProviderType("byteplus") AccessProviderTypeBunny = AccessProviderType("bunny") AccessProviderTypeBuypass = AccessProviderType("buypass") @@ -36,8 +37,8 @@ const ( AccessProviderTypeDynv6 = AccessProviderType("dynv6") AccessProviderTypeEdgio = AccessProviderType("edgio") AccessProviderTypeEmail = AccessProviderType("email") - AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly(预留) - AccessProviderTypeFlexCDN = AccessProviderType("flexcdn") // FlexCDN(预留) + AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly(预留) + AccessProviderTypeFlexCDN = AccessProviderType("flexcdn") AccessProviderTypeGname = AccessProviderType("gname") AccessProviderTypeGcore = AccessProviderType("gcore") AccessProviderTypeGoDaddy = AccessProviderType("godaddy") @@ -49,7 +50,7 @@ const ( AccessProviderTypeLarkBot = AccessProviderType("larkbot") AccessProviderTypeLetsEncrypt = AccessProviderType("letsencrypt") AccessProviderTypeLetsEncryptStaging = AccessProviderType("letsencryptstaging") - AccessProviderTypeLeCDN = AccessProviderType("lecdn") // LeCDN(预留) + AccessProviderTypeLeCDN = AccessProviderType("lecdn") AccessProviderTypeLocal = AccessProviderType("local") AccessProviderTypeMattermost = AccessProviderType("mattermost") AccessProviderTypeNamecheap = AccessProviderType("namecheap") @@ -91,6 +92,7 @@ type CAProviderType string NOTICE: If you add new constant, please keep ASCII order. */ const ( + CAProviderTypeACMECA = CAProviderType(AccessProviderTypeACMECA) CAProviderTypeBuypass = CAProviderType(AccessProviderTypeBuypass) CAProviderTypeGoogleTrustServices = CAProviderType(AccessProviderTypeGoogleTrustServices) CAProviderTypeLetsEncrypt = CAProviderType(AccessProviderTypeLetsEncrypt) @@ -173,7 +175,7 @@ const ( DeploymentProviderTypeAliyunDDoS = DeploymentProviderType(AccessProviderTypeAliyun + "-ddos") DeploymentProviderTypeAliyunESA = DeploymentProviderType(AccessProviderTypeAliyun + "-esa") DeploymentProviderTypeAliyunFC = DeploymentProviderType(AccessProviderTypeAliyun + "-fc") - DeploymentProviderTypeAliyunGA = DeploymentProviderType(AccessProviderTypeAliyun + "-ga") // 阿里云全球加速(预留) + DeploymentProviderTypeAliyunGA = DeploymentProviderType(AccessProviderTypeAliyun + "-ga") DeploymentProviderTypeAliyunLive = DeploymentProviderType(AccessProviderTypeAliyun + "-live") DeploymentProviderTypeAliyunNLB = DeploymentProviderType(AccessProviderTypeAliyun + "-nlb") DeploymentProviderTypeAliyunOSS = DeploymentProviderType(AccessProviderTypeAliyun + "-oss") @@ -189,13 +191,15 @@ const ( DeploymentProviderTypeBaishanCDN = DeploymentProviderType(AccessProviderTypeBaishan + "-cdn") DeploymentProviderTypeBaotaPanelConsole = DeploymentProviderType(AccessProviderTypeBaotaPanel + "-console") DeploymentProviderTypeBaotaPanelSite = DeploymentProviderType(AccessProviderTypeBaotaPanel + "-site") + DeploymentProviderTypeBaotaWAFConsole = DeploymentProviderType(AccessProviderTypeBaotaWAF + "-console") + DeploymentProviderTypeBaotaWAFSite = DeploymentProviderType(AccessProviderTypeBaotaWAF + "-site") DeploymentProviderTypeBunnyCDN = DeploymentProviderType(AccessProviderTypeBunny + "-cdn") DeploymentProviderTypeBytePlusCDN = DeploymentProviderType(AccessProviderTypeBytePlus + "-cdn") DeploymentProviderTypeCacheFly = DeploymentProviderType(AccessProviderTypeCacheFly) DeploymentProviderTypeCdnfly = DeploymentProviderType(AccessProviderTypeCdnfly) DeploymentProviderTypeDogeCloudCDN = DeploymentProviderType(AccessProviderTypeDogeCloud + "-cdn") DeploymentProviderTypeEdgioApplications = DeploymentProviderType(AccessProviderTypeEdgio + "-applications") - DeploymentProviderTypeFlexCDN = DeploymentProviderType(AccessProviderTypeFlexCDN) // FlexCDN(预留) + DeploymentProviderTypeFlexCDN = DeploymentProviderType(AccessProviderTypeFlexCDN) DeploymentProviderTypeGcoreCDN = DeploymentProviderType(AccessProviderTypeGcore + "-cdn") DeploymentProviderTypeGoEdge = DeploymentProviderType(AccessProviderTypeGoEdge) DeploymentProviderTypeHuaweiCloudCDN = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-cdn") @@ -207,7 +211,7 @@ const ( DeploymentProviderTypeJDCloudLive = DeploymentProviderType(AccessProviderTypeJDCloud + "-live") DeploymentProviderTypeJDCloudVOD = DeploymentProviderType(AccessProviderTypeJDCloud + "-vod") DeploymentProviderTypeKubernetesSecret = DeploymentProviderType(AccessProviderTypeKubernetes + "-secret") - DeploymentProviderTypeLeCDN = DeploymentProviderType(AccessProviderTypeLeCDN) // LeCDN(预留) + DeploymentProviderTypeLeCDN = DeploymentProviderType(AccessProviderTypeLeCDN) DeploymentProviderTypeLocal = DeploymentProviderType(AccessProviderTypeLocal) DeploymentProviderTypeNetlifySite = DeploymentProviderType(AccessProviderTypeNetlify + "-site") DeploymentProviderTypeProxmoxVE = DeploymentProviderType(AccessProviderTypeProxmoxVE) @@ -242,9 +246,9 @@ const ( DeploymentProviderTypeVolcEngineImageX = DeploymentProviderType(AccessProviderTypeVolcEngine + "-imagex") DeploymentProviderTypeVolcEngineLive = DeploymentProviderType(AccessProviderTypeVolcEngine + "-live") DeploymentProviderTypeVolcEngineTOS = DeploymentProviderType(AccessProviderTypeVolcEngine + "-tos") - DeploymentProviderTypeWangsuCDN = DeploymentProviderType(AccessProviderTypeWangsu + "-cdn") // 网宿 CDN(预留) + DeploymentProviderTypeWangsuCDN = DeploymentProviderType(AccessProviderTypeWangsu + "-cdn") DeploymentProviderTypeWangsuCDNPro = DeploymentProviderType(AccessProviderTypeWangsu + "-cdnpro") - DeploymentProviderTypeWangsuCert = DeploymentProviderType(AccessProviderTypeWangsu + "-cert") // 网宿证书管理(预留) + DeploymentProviderTypeWangsuCertificate = DeploymentProviderType(AccessProviderTypeWangsu + "-certificate") DeploymentProviderTypeWebhook = DeploymentProviderType(AccessProviderTypeWebhook) ) diff --git a/internal/notify/notifier.go b/internal/notify/notifier.go index 955e88c3..ee3fbd2f 100644 --- a/internal/notify/notifier.go +++ b/internal/notify/notifier.go @@ -31,9 +31,9 @@ func NewWithWorkflowNode(config NotifierWithWorkflowNodeConfig) (Notifier, error nodeConfig := config.Node.GetConfigForNotify() options := ¬ifierProviderOptions{ - Provider: domain.NotificationProviderType(nodeConfig.Provider), - ProviderAccessConfig: make(map[string]any), - ProviderExtendedConfig: nodeConfig.ProviderConfig, + Provider: domain.NotificationProviderType(nodeConfig.Provider), + ProviderAccessConfig: make(map[string]any), + ProviderServiceConfig: nodeConfig.ProviderConfig, } accessRepo := repository.NewAccessRepository() diff --git a/internal/notify/providers.go b/internal/notify/providers.go index 70787480..3a8c575d 100644 --- a/internal/notify/providers.go +++ b/internal/notify/providers.go @@ -18,9 +18,9 @@ import ( ) type notifierProviderOptions struct { - Provider domain.NotificationProviderType - ProviderAccessConfig map[string]any - ProviderExtendedConfig map[string]any + Provider domain.NotificationProviderType + ProviderAccessConfig map[string]any + ProviderServiceConfig map[string]any } func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier, error) { @@ -55,8 +55,8 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier SmtpTls: access.SmtpTls, Username: access.Username, Password: access.Password, - SenderAddress: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "senderAddress", access.DefaultSenderAddress), - ReceiverAddress: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "receiverAddress", access.DefaultReceiverAddress), + SenderAddress: maputil.GetOrDefaultString(options.ProviderServiceConfig, "senderAddress", access.DefaultSenderAddress), + ReceiverAddress: maputil.GetOrDefaultString(options.ProviderServiceConfig, "receiverAddress", access.DefaultReceiverAddress), }) } @@ -83,7 +83,7 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier ServerUrl: access.ServerUrl, Username: access.Username, Password: access.Password, - ChannelId: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "channelId", access.DefaultChannelId), + ChannelId: maputil.GetOrDefaultString(options.ProviderServiceConfig, "channelId", access.DefaultChannelId), }) } @@ -96,7 +96,7 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier return pTelegramBot.NewNotifier(&pTelegramBot.NotifierConfig{ BotToken: access.BotToken, - ChatId: maputil.GetOrDefaultInt64(options.ProviderExtendedConfig, "chatId", access.DefaultChatId), + ChatId: maputil.GetOrDefaultInt64(options.ProviderServiceConfig, "chatId", access.DefaultChatId), }) } @@ -117,7 +117,7 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier mergedHeaders[http.CanonicalHeaderKey(key)] = h.Get(key) } } - if extendedHeadersString := maputil.GetString(options.ProviderExtendedConfig, "headers"); extendedHeadersString != "" { + if extendedHeadersString := maputil.GetString(options.ProviderServiceConfig, "headers"); extendedHeadersString != "" { h, err := httputil.ParseHeaders(extendedHeadersString) if err != nil { return nil, fmt.Errorf("failed to parse webhook headers: %w", err) @@ -129,7 +129,7 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier return pWebhook.NewNotifier(&pWebhook.NotifierConfig{ WebhookUrl: access.Url, - WebhookData: maputil.GetOrDefaultString(options.ProviderExtendedConfig, "webhookData", access.DefaultDataForNotification), + WebhookData: maputil.GetOrDefaultString(options.ProviderServiceConfig, "webhookData", access.DefaultDataForNotification), Method: access.Method, Headers: mergedHeaders, AllowInsecureConnections: access.AllowInsecureConnections, diff --git a/internal/pkg/core/deployer/providers/1panel-console/1panel_console.go b/internal/pkg/core/deployer/providers/1panel-console/1panel_console.go index 069e01f9..e81b264f 100644 --- a/internal/pkg/core/deployer/providers/1panel-console/1panel_console.go +++ b/internal/pkg/core/deployer/providers/1panel-console/1panel_console.go @@ -9,12 +9,15 @@ import ( "net/url" "github.com/usual2970/certimate/internal/pkg/core/deployer" - opsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel" + onepanelsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel" ) type DeployerConfig struct { // 1Panel 地址。 ApiUrl string `json:"apiUrl"` + // 1Panel 版本。 + // 可取值 "v1"、"v2"。 + ApiVersion string `json:"apiVersion"` // 1Panel 接口密钥。 ApiKey string `json:"apiKey"` // 是否允许不安全的连接。 @@ -26,7 +29,7 @@ type DeployerConfig struct { type DeployerProvider struct { config *DeployerConfig logger *slog.Logger - sdkClient *opsdk.Client + sdkClient *onepanelsdk.Client } var _ deployer.Deployer = (*DeployerProvider)(nil) @@ -36,7 +39,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections) + client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } @@ -59,7 +62,7 @@ func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { // 设置面板 SSL 证书 - updateSystemSSLReq := &opsdk.UpdateSystemSSLRequest{ + updateSystemSSLReq := &onepanelsdk.UpdateSystemSSLRequest{ Cert: certPEM, Key: privkeyPEM, SSL: "enable", @@ -79,16 +82,20 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE return &deployer.DeployResult{}, nil } -func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*opsdk.Client, error) { +func createSdkClient(apiUrl, apiVersion, apiKey string, skipTlsVerify bool) (*onepanelsdk.Client, error) { if _, err := url.Parse(apiUrl); err != nil { return nil, errors.New("invalid 1panel api url") } + if apiVersion == "" { + return nil, errors.New("invalid 1panel api version") + } + if apiKey == "" { return nil, errors.New("invalid 1panel api key") } - client := opsdk.NewClient(apiUrl, apiKey) + client := onepanelsdk.NewClient(apiUrl, apiVersion, apiKey) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/1panel-console/1panel_console_test.go b/internal/pkg/core/deployer/providers/1panel-console/1panel_console_test.go index abec586c..88bf961a 100644 --- a/internal/pkg/core/deployer/providers/1panel-console/1panel_console_test.go +++ b/internal/pkg/core/deployer/providers/1panel-console/1panel_console_test.go @@ -15,6 +15,7 @@ var ( fInputCertPath string fInputKeyPath string fApiUrl string + fApiVersion string fApiKey string ) @@ -24,6 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v1", "") flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") } @@ -34,6 +36,7 @@ Shell command to run this test: --CERTIMATE_DEPLOYER_1PANELCONSOLE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_1PANELCONSOLE_INPUTKEYPATH="/path/to/your-input-key.pem" \ --CERTIMATE_DEPLOYER_1PANELCONSOLE_APIURL="http://127.0.0.1:20410" \ + --CERTIMATE_DEPLOYER_1PANELCONSOLE_APIVERSION="v1" \ --CERTIMATE_DEPLOYER_1PANELCONSOLE_APIKEY="your-api-key" */ func TestDeploy(t *testing.T) { @@ -45,11 +48,13 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("APIVERSION: %v", fApiVersion), fmt.Sprintf("APIKEY: %v", fApiKey), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ ApiUrl: fApiUrl, + ApiVersion: fApiVersion, ApiKey: fApiKey, AllowInsecureConnections: true, AutoRestart: true, diff --git a/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go b/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go index 7d360c77..690e5242 100644 --- a/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go +++ b/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go @@ -12,12 +12,15 @@ import ( "github.com/usual2970/certimate/internal/pkg/core/deployer" "github.com/usual2970/certimate/internal/pkg/core/uploader" uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/1panel-ssl" - opsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel" + onepanelsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel" ) type DeployerConfig struct { // 1Panel 地址。 ApiUrl string `json:"apiUrl"` + // 1Panel 版本。 + // 可取值 "v1"、"v2"。 + ApiVersion string `json:"apiVersion"` // 1Panel 接口密钥。 ApiKey string `json:"apiKey"` // 是否允许不安全的连接。 @@ -35,7 +38,7 @@ type DeployerConfig struct { type DeployerProvider struct { config *DeployerConfig logger *slog.Logger - sdkClient *opsdk.Client + sdkClient *onepanelsdk.Client sslUploader uploader.Uploader } @@ -46,14 +49,15 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections) + client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ - ApiUrl: config.ApiUrl, - ApiKey: config.ApiKey, + ApiUrl: config.ApiUrl, + ApiVersion: config.ApiVersion, + ApiKey: config.ApiKey, }) if err != nil { return nil, fmt.Errorf("failed to create ssl uploader: %w", err) @@ -103,7 +107,7 @@ func (d *DeployerProvider) deployToWebsite(ctx context.Context, certPEM string, } // 获取网站 HTTPS 配置 - getHttpsConfReq := &opsdk.GetHttpsConfRequest{ + getHttpsConfReq := &onepanelsdk.GetHttpsConfRequest{ WebsiteID: d.config.WebsiteId, } getHttpsConfResp, err := d.sdkClient.GetHttpsConf(getHttpsConfReq) @@ -122,7 +126,7 @@ func (d *DeployerProvider) deployToWebsite(ctx context.Context, certPEM string, // 修改网站 HTTPS 配置 certId, _ := strconv.ParseInt(upres.CertId, 10, 64) - updateHttpsConfReq := &opsdk.UpdateHttpsConfRequest{ + updateHttpsConfReq := &onepanelsdk.UpdateHttpsConfRequest{ WebsiteID: d.config.WebsiteId, Type: "existed", WebsiteSSLID: certId, @@ -147,7 +151,7 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri } // 获取证书详情 - getWebsiteSSLReq := &opsdk.GetWebsiteSSLRequest{ + getWebsiteSSLReq := &onepanelsdk.GetWebsiteSSLRequest{ SSLID: d.config.CertificateId, } getWebsiteSSLResp, err := d.sdkClient.GetWebsiteSSL(getWebsiteSSLReq) @@ -157,7 +161,7 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri } // 更新证书 - uploadWebsiteSSLReq := &opsdk.UploadWebsiteSSLRequest{ + uploadWebsiteSSLReq := &onepanelsdk.UploadWebsiteSSLRequest{ Type: "paste", SSLID: d.config.CertificateId, Description: getWebsiteSSLResp.Data.Description, @@ -173,16 +177,20 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri return nil } -func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*opsdk.Client, error) { +func createSdkClient(apiUrl, apiVersion, apiKey string, skipTlsVerify bool) (*onepanelsdk.Client, error) { if _, err := url.Parse(apiUrl); err != nil { return nil, errors.New("invalid 1panel api url") } + if apiVersion == "" { + return nil, errors.New("invalid 1panel api version") + } + if apiKey == "" { return nil, errors.New("invalid 1panel api key") } - client := opsdk.NewClient(apiUrl, apiKey) + client := onepanelsdk.NewClient(apiUrl, apiVersion, apiKey) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/1panel-site/1panel_site_test.go b/internal/pkg/core/deployer/providers/1panel-site/1panel_site_test.go index 702584e3..1d5bafef 100644 --- a/internal/pkg/core/deployer/providers/1panel-site/1panel_site_test.go +++ b/internal/pkg/core/deployer/providers/1panel-site/1panel_site_test.go @@ -15,6 +15,7 @@ var ( fInputCertPath string fInputKeyPath string fApiUrl string + fApiVersion string fApiKey string fWebsiteId int64 ) @@ -25,6 +26,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v1", "") flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") flag.Int64Var(&fWebsiteId, argsPrefix+"WEBSITEID", 0, "") } @@ -36,6 +38,7 @@ Shell command to run this test: --CERTIMATE_DEPLOYER_1PANELSITE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_1PANELSITE_INPUTKEYPATH="/path/to/your-input-key.pem" \ --CERTIMATE_DEPLOYER_1PANELSITE_APIURL="http://127.0.0.1:20410" \ + --CERTIMATE_DEPLOYER_1PANELSITE_APIVERSION="v1" \ --CERTIMATE_DEPLOYER_1PANELSITE_APIKEY="your-api-key" \ --CERTIMATE_DEPLOYER_1PANELSITE_WEBSITEID="your-website-id" */ @@ -48,12 +51,14 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("APIVERSION: %v", fApiVersion), fmt.Sprintf("APIKEY: %v", fApiKey), fmt.Sprintf("WEBSITEID: %v", fWebsiteId), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ ApiUrl: fApiUrl, + ApiVersion: fApiVersion, ApiKey: fApiKey, AllowInsecureConnections: true, ResourceType: provider.RESOURCE_TYPE_WEBSITE, diff --git a/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go b/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go index 3dca4a9d..35b4997c 100644 --- a/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go +++ b/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go @@ -157,7 +157,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId if listListenersResp.Body.Listeners != nil { for _, listener := range listListenersResp.Body.Listeners { - listenerIds = append(listenerIds, *listener.ListenerId) + listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId)) } } @@ -192,7 +192,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId if listListenersResp.Body.Listeners != nil { for _, listener := range listListenersResp.Body.Listeners { - listenerIds = append(listenerIds, *listener.ListenerId) + listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId)) } } @@ -211,8 +211,13 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId d.logger.Info("found https/quic listeners to deploy", slog.Any("listenerIds", listenerIds)) for _, listenerId := range listenerIds { - if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil { - errs = append(errs, err) + select { + case <-ctx.Done(): + return ctx.Err() + default: + if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil { + errs = append(errs, err) + } } } diff --git a/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb_test.go b/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb_test.go index c75119e9..11d5b565 100644 --- a/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb_test.go @@ -96,6 +96,7 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), fmt.Sprintf("REGION: %v", fRegion), fmt.Sprintf("LISTENERID: %v", fListenerId), + fmt.Sprintf("DOMAIN: %v", fDomain), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ diff --git a/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go b/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go index d443514e..34c3a49e 100644 --- a/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go +++ b/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go @@ -171,7 +171,6 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId select { case <-ctx.Done(): return ctx.Err() - default: if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, listenerPort, cloudCertId); err != nil { errs = append(errs, err) diff --git a/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb_test.go b/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb_test.go index 3b8ce12d..dfa46173 100644 --- a/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb_test.go +++ b/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb_test.go @@ -18,7 +18,7 @@ var ( fAccessKeySecret string fRegion string fLoadbalancerId string - fListenerPort int + fListenerPort int64 fDomain string ) @@ -31,7 +31,7 @@ func init() { flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "") flag.StringVar(&fRegion, argsPrefix+"REGION", "", "") flag.StringVar(&fLoadbalancerId, argsPrefix+"LOADBALANCERID", "", "") - flag.IntVar(&fListenerPort, argsPrefix+"LISTENERPORT", 443, "") + flag.Int64Var(&fListenerPort, argsPrefix+"LISTENERPORT", 443, "") flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") } diff --git a/internal/pkg/core/deployer/providers/aliyun-fc/aliyun_fc.go b/internal/pkg/core/deployer/providers/aliyun-fc/aliyun_fc.go index 8557068c..426aa3a6 100644 --- a/internal/pkg/core/deployer/providers/aliyun-fc/aliyun_fc.go +++ b/internal/pkg/core/deployer/providers/aliyun-fc/aliyun_fc.go @@ -22,6 +22,7 @@ type DeployerConfig struct { // 阿里云地域。 Region string `json:"region"` // 服务版本。 + // 可取值 "2.0"、"3.0"。 ServiceVersion string `json:"serviceVersion"` // 自定义域名(支持泛域名)。 Domain string `json:"domain"` diff --git a/internal/pkg/core/deployer/providers/aliyun-ga/aliyun_ga.go b/internal/pkg/core/deployer/providers/aliyun-ga/aliyun_ga.go new file mode 100644 index 00000000..f69660a8 --- /dev/null +++ b/internal/pkg/core/deployer/providers/aliyun-ga/aliyun_ga.go @@ -0,0 +1,322 @@ +package aliyunga + +import ( + "context" + "errors" + "fmt" + "log/slog" + "strings" + + aliopen "github.com/alibabacloud-go/darabonba-openapi/v2/client" + aliga "github.com/alibabacloud-go/ga-20191120/v3/client" + "github.com/alibabacloud-go/tea/tea" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/uploader" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/aliyun-cas" + sliceutil "github.com/usual2970/certimate/internal/pkg/utils/slice" +) + +type DeployerConfig struct { + // 阿里云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 阿里云 AccessKeySecret。 + AccessKeySecret string `json:"accessKeySecret"` + // 部署资源类型。 + ResourceType ResourceType `json:"resourceType"` + // 全球加速实例 ID。 + AcceleratorId string `json:"acceleratorId"` + // 全球加速监听 ID。 + // 部署资源类型为 [RESOURCE_TYPE_LISTENER] 时必填。 + ListenerId string `json:"listenerId,omitempty"` + // SNI 域名(不支持泛域名)。 + // 部署资源类型为 [RESOURCE_TYPE_ACCELERATOR]、[RESOURCE_TYPE_LISTENER] 时选填。 + Domain string `json:"domain,omitempty"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger *slog.Logger + sdkClient *aliga.Client + sslUploader uploader.Uploader +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret) + if err != nil { + return nil, fmt.Errorf("failed to create sdk client: %w", err) + } + + uploader, err := createSslUploader(config.AccessKeyId, config.AccessKeySecret) + if err != nil { + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) + } + + return &DeployerProvider{ + config: config, + logger: slog.Default(), + sdkClient: client, + sslUploader: uploader, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { + if logger == nil { + d.logger = slog.Default() + } else { + d.logger = logger + } + d.sslUploader.WithLogger(logger) + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { + // 上传证书到 CAS + upres, err := d.sslUploader.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)) + } + + // 根据部署资源类型决定部署方式 + switch d.config.ResourceType { + case RESOURCE_TYPE_ACCELERATOR: + if err := d.deployToAccelerator(ctx, upres.ExtendedData["certIdentifier"].(string)); err != nil { + return nil, err + } + + case RESOURCE_TYPE_LISTENER: + if err := d.deployToListener(ctx, upres.ExtendedData["certIdentifier"].(string)); err != nil { + return nil, err + } + + default: + return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType) + } + + return &deployer.DeployResult{}, nil +} + +func (d *DeployerProvider) deployToAccelerator(ctx context.Context, cloudCertId string) error { + if d.config.AcceleratorId == "" { + return errors.New("config `acceleratorId` is required") + } + + // 查询 HTTPS 监听列表 + // REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-listlisteners + listenerIds := make([]string, 0) + listListenersPageNumber := int32(1) + listListenersPageSize := int32(50) + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + listListenersReq := &aliga.ListListenersRequest{ + RegionId: tea.String("cn-hangzhou"), + AcceleratorId: tea.String(d.config.AcceleratorId), + PageNumber: tea.Int32(listListenersPageNumber), + PageSize: tea.Int32(listListenersPageSize), + } + listListenersResp, err := d.sdkClient.ListListeners(listListenersReq) + d.logger.Debug("sdk request 'ga.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'ga.ListListeners': %w", err) + } + + if listListenersResp.Body.Listeners != nil { + for _, listener := range listListenersResp.Body.Listeners { + if strings.EqualFold(tea.StringValue(listener.Protocol), "https") { + listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId)) + } + } + } + + if len(listListenersResp.Body.Listeners) < int(listListenersPageSize) { + break + } else { + listListenersPageNumber++ + } + } + + // 遍历更新监听证书 + if len(listenerIds) == 0 { + d.logger.Info("no ga listeners to deploy") + } else { + var errs []error + d.logger.Info("found https listeners to deploy", slog.Any("listenerIds", listenerIds)) + + for _, listenerId := range listenerIds { + select { + case <-ctx.Done(): + return ctx.Err() + default: + if err := d.updateListenerCertificate(ctx, d.config.AcceleratorId, listenerId, cloudCertId); err != nil { + errs = append(errs, err) + } + } + } + + if len(errs) > 0 { + return errors.Join(errs...) + } + } + + return nil +} + +func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId string) error { + if d.config.AcceleratorId == "" { + return errors.New("config `acceleratorId` is required") + } + if d.config.ListenerId == "" { + return errors.New("config `listenerId` is required") + } + + // 更新监听 + if err := d.updateListenerCertificate(ctx, d.config.AcceleratorId, d.config.ListenerId, cloudCertId); err != nil { + return err + } + + return nil +} + +func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudAcceleratorId string, cloudListenerId string, cloudCertId string) error { + // 查询监听绑定的证书列表 + // REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-listlistenercertificates + var listenerDefaultCertificate *aliga.ListListenerCertificatesResponseBodyCertificates + var listenerAdditionalCertificates []*aliga.ListListenerCertificatesResponseBodyCertificates = make([]*aliga.ListListenerCertificatesResponseBodyCertificates, 0) + var listListenerCertificatesNextToken *string + for { + listListenerCertificatesReq := &aliga.ListListenerCertificatesRequest{ + RegionId: tea.String("cn-hangzhou"), + AcceleratorId: tea.String(d.config.AcceleratorId), + NextToken: listListenerCertificatesNextToken, + MaxResults: tea.Int32(20), + } + listListenerCertificatesResp, err := d.sdkClient.ListListenerCertificates(listListenerCertificatesReq) + d.logger.Debug("sdk request 'ga.ListListenerCertificates'", slog.Any("request", listListenerCertificatesReq), slog.Any("response", listListenerCertificatesResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'ga.ListListenerCertificates': %w", err) + } + + if listListenerCertificatesResp.Body.Certificates != nil { + for _, certificate := range listListenerCertificatesResp.Body.Certificates { + if tea.BoolValue(certificate.IsDefault) { + listenerDefaultCertificate = certificate + } else { + listenerAdditionalCertificates = append(listenerAdditionalCertificates, certificate) + } + } + } + + if listListenerCertificatesResp.Body.NextToken == nil { + break + } else { + listListenerCertificatesNextToken = listListenerCertificatesResp.Body.NextToken + } + } + + if d.config.Domain == "" { + // 未指定 SNI,只需部署到监听器 + if listenerDefaultCertificate != nil && tea.StringValue(listenerDefaultCertificate.CertificateId) == cloudCertId { + d.logger.Info("no need to update ga listener default certificate") + return nil + } + + // 修改监听的属性 + // REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-updatelistener + updateListenerReq := &aliga.UpdateListenerRequest{ + RegionId: tea.String("cn-hangzhou"), + ListenerId: tea.String(cloudListenerId), + Certificates: []*aliga.UpdateListenerRequestCertificates{{ + Id: tea.String(cloudCertId), + }}, + } + updateListenerResp, err := d.sdkClient.UpdateListener(updateListenerReq) + d.logger.Debug("sdk request 'ga.UpdateListener'", slog.Any("request", updateListenerReq), slog.Any("response", updateListenerResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'ga.UpdateListener': %w", err) + } + } else { + // 指定 SNI,需部署到扩展域名 + if sliceutil.Some(listenerAdditionalCertificates, func(item *aliga.ListListenerCertificatesResponseBodyCertificates) bool { + return tea.StringValue(item.CertificateId) == cloudCertId + }) { + d.logger.Info("no need to update ga listener additional certificate") + return nil + } + + if sliceutil.Some(listenerAdditionalCertificates, func(item *aliga.ListListenerCertificatesResponseBodyCertificates) bool { + return tea.StringValue(item.Domain) == d.config.Domain + }) { + // 为监听替换扩展证书 + // REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-updateadditionalcertificatewithlistener + updateAdditionalCertificateWithListenerReq := &aliga.UpdateAdditionalCertificateWithListenerRequest{ + RegionId: tea.String("cn-hangzhou"), + AcceleratorId: tea.String(cloudAcceleratorId), + ListenerId: tea.String(cloudListenerId), + CertificateId: tea.String(cloudCertId), + Domain: tea.String(d.config.Domain), + } + updateAdditionalCertificateWithListenerResp, err := d.sdkClient.UpdateAdditionalCertificateWithListener(updateAdditionalCertificateWithListenerReq) + d.logger.Debug("sdk request 'ga.UpdateAdditionalCertificateWithListener'", slog.Any("request", updateAdditionalCertificateWithListenerReq), slog.Any("response", updateAdditionalCertificateWithListenerResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'ga.UpdateAdditionalCertificateWithListener': %w", err) + } + } else { + // 为监听绑定扩展证书 + // REF: https://help.aliyun.com/zh/ga/developer-reference/api-ga-2019-11-20-associateadditionalcertificateswithlistener + associateAdditionalCertificatesWithListenerReq := &aliga.AssociateAdditionalCertificatesWithListenerRequest{ + RegionId: tea.String("cn-hangzhou"), + AcceleratorId: tea.String(cloudAcceleratorId), + ListenerId: tea.String(cloudListenerId), + Certificates: []*aliga.AssociateAdditionalCertificatesWithListenerRequestCertificates{{ + Id: tea.String(cloudCertId), + Domain: tea.String(d.config.Domain), + }}, + } + associateAdditionalCertificatesWithListenerResp, err := d.sdkClient.AssociateAdditionalCertificatesWithListener(associateAdditionalCertificatesWithListenerReq) + d.logger.Debug("sdk request 'ga.AssociateAdditionalCertificatesWithListener'", slog.Any("request", associateAdditionalCertificatesWithListenerReq), slog.Any("response", associateAdditionalCertificatesWithListenerResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'ga.AssociateAdditionalCertificatesWithListener': %w", err) + } + } + } + + return nil +} + +func createSdkClient(accessKeyId, accessKeySecret string) (*aliga.Client, error) { + // 接入点一览 https://api.aliyun.com/product/Ga + config := &aliopen.Config{ + AccessKeyId: tea.String(accessKeyId), + AccessKeySecret: tea.String(accessKeySecret), + Endpoint: tea.String("ga.cn-hangzhou.aliyuncs.com"), + } + + client, err := aliga.NewClient(config) + if err != nil { + return nil, err + } + + return client, nil +} + +func createSslUploader(accessKeyId, accessKeySecret string) (uploader.Uploader, error) { + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + Region: "cn-hangzhou", + }) + return uploader, err +} diff --git a/internal/pkg/core/deployer/providers/aliyun-ga/aliyun_ga_test.go b/internal/pkg/core/deployer/providers/aliyun-ga/aliyun_ga_test.go new file mode 100644 index 00000000..611ddc41 --- /dev/null +++ b/internal/pkg/core/deployer/providers/aliyun-ga/aliyun_ga_test.go @@ -0,0 +1,118 @@ +package aliyunga_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-ga" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKeyId string + fAccessKeySecret string + fAcceleratorId string + fListenerId string + fDomain string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_ALIYUNGA_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "") + flag.StringVar(&fAcceleratorId, argsPrefix+"ACCELERATORID", "", "") + flag.StringVar(&fListenerId, argsPrefix+"LISTENERID", "", "") + flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") +} + +/* +Shell command to run this test: + + go test -v ./aliyun_ga_test.go -args \ + --CERTIMATE_DEPLOYER_ALIYUNGA_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_ALIYUNGA_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_ALIYUNGA_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_DEPLOYER_ALIYUNGA_ACCESSKEYSECRET="your-access-key-secret" \ + --CERTIMATE_DEPLOYER_ALIYUNGA_ACCELERATORID="your-ga-accelerator-id" \ + --CERTIMATE_DEPLOYER_ALIYUNGA_LISTENERID="your-ga-listener-id" \ + --CERTIMATE_DEPLOYER_ALIYUNGA_DOMAIN="your-ga-sni-domain" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy_ToAccelerator", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), + fmt.Sprintf("ACCELERATORID: %v", fAcceleratorId), + fmt.Sprintf("DOMAIN: %v", fDomain), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + ResourceType: provider.RESOURCE_TYPE_ACCELERATOR, + AcceleratorId: fAcceleratorId, + 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) + }) + + t.Run("Deploy_ToListener", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), + fmt.Sprintf("LISTENERID: %v", fListenerId), + fmt.Sprintf("DOMAIN: %v", fDomain), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + ResourceType: provider.RESOURCE_TYPE_LISTENER, + ListenerId: fListenerId, + 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/internal/pkg/core/deployer/providers/aliyun-ga/consts.go b/internal/pkg/core/deployer/providers/aliyun-ga/consts.go new file mode 100644 index 00000000..f96d98d5 --- /dev/null +++ b/internal/pkg/core/deployer/providers/aliyun-ga/consts.go @@ -0,0 +1,10 @@ +package aliyunga + +type ResourceType string + +const ( + // 资源类型:部署到指定全球加速器。 + RESOURCE_TYPE_ACCELERATOR = ResourceType("accelerator") + // 资源类型:部署到指定监听器。 + RESOURCE_TYPE_LISTENER = ResourceType("listener") +) diff --git a/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb.go b/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb.go index b8391144..58015f3d 100644 --- a/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb.go +++ b/internal/pkg/core/deployer/providers/aliyun-nlb/aliyun_nlb.go @@ -145,7 +145,7 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId if listListenersResp.Body.Listeners != nil { for _, listener := range listListenersResp.Body.Listeners { - listenerIds = append(listenerIds, *listener.ListenerId) + listenerIds = append(listenerIds, tea.StringValue(listener.ListenerId)) } } @@ -167,7 +167,6 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId select { case <-ctx.Done(): return ctx.Err() - default: if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil { errs = append(errs, err) diff --git a/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console.go b/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console.go new file mode 100644 index 00000000..811b350b --- /dev/null +++ b/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console.go @@ -0,0 +1,88 @@ +package baotapanelconsole + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "log/slog" + "net/url" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + btsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/btwaf" +) + +type DeployerConfig struct { + // 堡塔云 WAF 地址。 + ApiUrl string `json:"apiUrl"` + // 堡塔云 WAF 接口密钥。 + ApiKey string `json:"apiKey"` + // 是否允许不安全的连接。 + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger *slog.Logger + sdkClient *btsdk.Client +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections) + if err != nil { + return nil, fmt.Errorf("failed to create sdk client: %w", err) + } + + return &DeployerProvider{ + config: config, + logger: slog.Default(), + sdkClient: client, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { + if logger == nil { + d.logger = slog.Default() + } else { + d.logger = logger + } + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { + // 设置面板 SSL + configSetSSLReq := &btsdk.ConfigSetSSLRequest{ + CertContent: certPEM, + KeyContent: privkeyPEM, + } + configSetSSLResp, err := d.sdkClient.ConfigSetSSL(configSetSSLReq) + d.logger.Debug("sdk request 'bt.ConfigSetSSL'", slog.Any("request", configSetSSLReq), slog.Any("response", configSetSSLResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'bt.ConfigSetSSL': %w", err) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*btsdk.Client, error) { + if _, err := url.Parse(apiUrl); err != nil { + return nil, errors.New("invalid baota api url") + } + + if apiKey == "" { + return nil, errors.New("invalid baota api key") + } + + client := btsdk.NewClient(apiUrl, apiKey) + if skipTlsVerify { + client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) + } + + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console_test.go b/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console_test.go new file mode 100644 index 00000000..ba6ddd26 --- /dev/null +++ b/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console_test.go @@ -0,0 +1,73 @@ +package baotapanelconsole_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baotawaf-console" +) + +var ( + fInputCertPath string + fInputKeyPath string + fApiUrl string + fApiKey string + fSiteName string + fSitePort int64 +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") +} + +/* +Shell command to run this test: + + go test -v ./baotawaf_console_test.go -args \ + --CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_APIURL="http://127.0.0.1:8888" \ + --CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_APIKEY="your-api-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("APIURL: %v", fApiUrl), + fmt.Sprintf("APIKEY: %v", fApiKey), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + ApiUrl: fApiUrl, + ApiKey: fApiKey, + AllowInsecureConnections: true, + }) + 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/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site.go b/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site.go new file mode 100644 index 00000000..ed05937a --- /dev/null +++ b/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site.go @@ -0,0 +1,151 @@ +package baotapanelwaf + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "log/slog" + "net/url" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + btsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/btwaf" + typeutil "github.com/usual2970/certimate/internal/pkg/utils/type" +) + +type DeployerConfig struct { + // 堡塔云 WAF 地址。 + ApiUrl string `json:"apiUrl"` + // 堡塔云 WAF 接口密钥。 + ApiKey string `json:"apiKey"` + // 是否允许不安全的连接。 + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` + // 网站名称。 + SiteName string `json:"siteName"` + // 网站 SSL 端口。 + // 零值时默认为 443。 + SitePort int32 `json:"sitePort,omitempty"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger *slog.Logger + sdkClient *btsdk.Client +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections) + if err != nil { + return nil, fmt.Errorf("failed to create sdk client: %w", err) + } + + return &DeployerProvider{ + config: config, + logger: slog.Default(), + sdkClient: client, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { + if logger == nil { + d.logger = slog.Default() + } else { + d.logger = logger + } + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { + if d.config.SiteName == "" { + return nil, errors.New("config `siteName` is required") + } + if d.config.SitePort == 0 { + d.config.SitePort = 443 + } + + // 遍历获取网站列表,获取网站 ID + // REF: https://support.huaweicloud.com/api-waf/ListHost.html + siteId := "" + getSitListPage := int32(1) + getSitListPageSize := int32(100) + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + + getSiteListReq := &btsdk.GetSiteListRequest{ + SiteName: typeutil.ToPtr(d.config.SiteName), + Page: typeutil.ToPtr(getSitListPage), + PageSize: typeutil.ToPtr(getSitListPageSize), + } + getSiteListResp, err := d.sdkClient.GetSiteList(getSiteListReq) + d.logger.Debug("sdk request 'bt.GetSiteList'", slog.Any("request", getSiteListReq), slog.Any("response", getSiteListResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'bt.GetSiteList': %w", err) + } + + if getSiteListResp.Result != nil && getSiteListResp.Result.List != nil { + for _, siteItem := range getSiteListResp.Result.List { + if siteItem.SiteName == d.config.SiteName { + siteId = siteItem.SiteId + break + } + } + } + + if getSiteListResp.Result == nil || len(getSiteListResp.Result.List) < int(getSitListPageSize) { + break + } else { + getSitListPage++ + } + } + if siteId == "" { + return nil, errors.New("site not found") + } + + // 修改站点配置 + modifySiteReq := &btsdk.ModifySiteRequest{ + SiteId: siteId, + Type: typeutil.ToPtr("openCert"), + Server: &btsdk.SiteServerInfo{ + ListenSSLPort: typeutil.ToPtr(d.config.SitePort), + SSL: &btsdk.SiteServerSSLInfo{ + IsSSL: typeutil.ToPtr(int32(1)), + FullChain: typeutil.ToPtr(certPEM), + PrivateKey: typeutil.ToPtr(privkeyPEM), + }, + }, + } + modifySiteResp, err := d.sdkClient.ModifySite(modifySiteReq) + d.logger.Debug("sdk request 'bt.ModifySite'", slog.Any("request", modifySiteReq), slog.Any("response", modifySiteResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'bt.ModifySite': %w", err) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*btsdk.Client, error) { + if _, err := url.Parse(apiUrl); err != nil { + return nil, errors.New("invalid baota api url") + } + + if apiKey == "" { + return nil, errors.New("invalid baota api key") + } + + client := btsdk.NewClient(apiUrl, apiKey) + if skipTlsVerify { + client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) + } + + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site_test.go b/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site_test.go new file mode 100644 index 00000000..4e1ffe34 --- /dev/null +++ b/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site_test.go @@ -0,0 +1,81 @@ +package baotapanelwaf_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baotawaf-site" +) + +var ( + fInputCertPath string + fInputKeyPath string + fApiUrl string + fApiKey string + fSiteName string + fSitePort int64 +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_BAOTAWAFSITE_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") + flag.StringVar(&fSiteName, argsPrefix+"SITENAME", "", "") + flag.Int64Var(&fSitePort, argsPrefix+"SITEPORT", 0, "") +} + +/* +Shell command to run this test: + + go test -v ./baotawaf_site_test.go -args \ + --CERTIMATE_DEPLOYER_BAOTAWAFSITE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_BAOTAWAFSITE_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_BAOTAWAFSITE_APIURL="http://127.0.0.1:8888" \ + --CERTIMATE_DEPLOYER_BAOTAWAFSITE_APIKEY="your-api-key" \ + --CERTIMATE_DEPLOYER_BAOTAWAFSITE_SITENAME="your-site-name"\ + --CERTIMATE_DEPLOYER_BAOTAWAFSITE_SITEPORT=443 +*/ +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("APIURL: %v", fApiUrl), + fmt.Sprintf("APIKEY: %v", fApiKey), + fmt.Sprintf("SITENAME: %v", fSiteName), + fmt.Sprintf("SITEPORT: %v", fSitePort), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + ApiUrl: fApiUrl, + ApiKey: fApiKey, + AllowInsecureConnections: true, + SiteName: fSiteName, + SitePort: int32(fSitePort), + }) + 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/internal/pkg/core/deployer/providers/flexcdn/consts.go b/internal/pkg/core/deployer/providers/flexcdn/consts.go new file mode 100644 index 00000000..be55a475 --- /dev/null +++ b/internal/pkg/core/deployer/providers/flexcdn/consts.go @@ -0,0 +1,8 @@ +package flexcdn + +type ResourceType string + +const ( + // 资源类型:替换指定证书。 + RESOURCE_TYPE_CERTIFICATE = ResourceType("certificate") +) diff --git a/internal/pkg/core/deployer/providers/flexcdn/flexcdn.go b/internal/pkg/core/deployer/providers/flexcdn/flexcdn.go new file mode 100644 index 00000000..a12ed164 --- /dev/null +++ b/internal/pkg/core/deployer/providers/flexcdn/flexcdn.go @@ -0,0 +1,145 @@ +package flexcdn + +import ( + "context" + "crypto/tls" + "encoding/base64" + "errors" + "fmt" + "log/slog" + "net/url" + "time" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + flexcdnsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/flexcdn" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" +) + +type DeployerConfig struct { + // FlexCDN URL。 + ApiUrl string `json:"apiUrl"` + // FlexCDN 用户角色。 + // 可取值 "user"、"admin"。 + ApiRole string `json:"apiRole"` + // FlexCDN AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // FlexCDN AccessKey。 + AccessKey string `json:"accessKey"` + // 是否允许不安全的连接。 + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` + // 部署资源类型。 + ResourceType ResourceType `json:"resourceType"` + // 证书 ID。 + // 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时必填。 + CertificateId int64 `json:"certificateId,omitempty"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger *slog.Logger + sdkClient *flexcdnsdk.Client +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.ApiUrl, config.ApiRole, config.AccessKeyId, config.AccessKey, config.AllowInsecureConnections) + if err != nil { + return nil, fmt.Errorf("failed to create sdk client: %w", err) + } + + return &DeployerProvider{ + config: config, + logger: slog.Default(), + sdkClient: client, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { + if logger == nil { + d.logger = slog.Default() + } else { + d.logger = logger + } + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { + // 根据部署资源类型决定部署方式 + switch d.config.ResourceType { + case RESOURCE_TYPE_CERTIFICATE: + if err := d.deployToCertificate(ctx, certPEM, privkeyPEM); err != nil { + return nil, err + } + + default: + return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType) + } + + return &deployer.DeployResult{}, nil +} + +func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM string, privkeyPEM string) error { + if d.config.CertificateId == 0 { + return errors.New("config `certificateId` is required") + } + + // 解析证书内容 + certX509, err := certutil.ParseCertificateFromPEM(certPEM) + if err != nil { + return err + } + + // 修改证书 + // REF: https://flexcdn.cloud/dev/api/service/SSLCertService?role=user#updateSSLCert + updateSSLCertReq := &flexcdnsdk.UpdateSSLCertRequest{ + SSLCertId: d.config.CertificateId, + IsOn: true, + Name: fmt.Sprintf("certimate-%d", time.Now().UnixMilli()), + Description: "upload from certimate", + ServerName: certX509.Subject.CommonName, + IsCA: false, + CertData: base64.StdEncoding.EncodeToString([]byte(certPEM)), + KeyData: base64.StdEncoding.EncodeToString([]byte(privkeyPEM)), + TimeBeginAt: certX509.NotBefore.Unix(), + TimeEndAt: certX509.NotAfter.Unix(), + DNSNames: certX509.DNSNames, + CommonNames: []string{certX509.Subject.CommonName}, + } + updateSSLCertResp, err := d.sdkClient.UpdateSSLCert(updateSSLCertReq) + d.logger.Debug("sdk request 'flexcdn.UpdateSSLCert'", slog.Any("request", updateSSLCertReq), slog.Any("response", updateSSLCertResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'flexcdn.UpdateSSLCert': %w", err) + } + + return nil +} + +func createSdkClient(apiUrl, apiRole, accessKeyId, accessKey string, skipTlsVerify bool) (*flexcdnsdk.Client, error) { + if _, err := url.Parse(apiUrl); err != nil { + return nil, errors.New("invalid flexcdn api url") + } + + if apiRole != "user" && apiRole != "admin" { + return nil, errors.New("invalid flexcdn api role") + } + + if accessKeyId == "" { + return nil, errors.New("invalid flexcdn access key id") + } + + if accessKey == "" { + return nil, errors.New("invalid flexcdn access key") + } + + client := flexcdnsdk.NewClient(apiUrl, apiRole, accessKeyId, accessKey) + if skipTlsVerify { + client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) + } + + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/flexcdn/flexcdn_test.go b/internal/pkg/core/deployer/providers/flexcdn/flexcdn_test.go new file mode 100644 index 00000000..b9b8de07 --- /dev/null +++ b/internal/pkg/core/deployer/providers/flexcdn/flexcdn_test.go @@ -0,0 +1,83 @@ +package flexcdn_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/flexcdn" +) + +var ( + fInputCertPath string + fInputKeyPath string + fApiUrl string + fAccessKeyId string + fAccessKey string + fCertificateId int64 +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_FLEXCDN_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fAccessKey, argsPrefix+"ACCESSKEY", "", "") + flag.Int64Var(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "") +} + +/* +Shell command to run this test: + + go test -v ./flexcdn_test.go -args \ + --CERTIMATE_DEPLOYER_FLEXCDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_FLEXCDN_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_FLEXCDN_APIURL="http://127.0.0.1:7788" \ + --CERTIMATE_DEPLOYER_FLEXCDN_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_DEPLOYER_FLEXCDN_ACCESSKEY="your-access-key" \ + --CERTIMATE_DEPLOYER_FLEXCDN_CERTIFICATEID="your-cerficiate-id" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy_ToCertificate", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("ACCESSKEY: %v", fAccessKey), + fmt.Sprintf("CERTIFICATEID: %v", fCertificateId), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + ApiUrl: fApiUrl, + ApiRole: "user", + AccessKeyId: fAccessKeyId, + AccessKey: fAccessKey, + AllowInsecureConnections: true, + ResourceType: provider.RESOURCE_TYPE_CERTIFICATE, + CertificateId: fCertificateId, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := 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/internal/pkg/core/deployer/providers/goedge/goedge.go b/internal/pkg/core/deployer/providers/goedge/goedge.go index 73eade64..ecae774e 100644 --- a/internal/pkg/core/deployer/providers/goedge/goedge.go +++ b/internal/pkg/core/deployer/providers/goedge/goedge.go @@ -19,6 +19,7 @@ type DeployerConfig struct { // GoEdge URL。 ApiUrl string `json:"apiUrl"` // GoEdge 用户角色。 + // 可取值 "user"、"admin"。 ApiRole string `json:"apiRole"` // GoEdge AccessKeyId。 AccessKeyId string `json:"accessKeyId"` diff --git a/internal/pkg/core/deployer/providers/goedge/goedge_test.go b/internal/pkg/core/deployer/providers/goedge/goedge_test.go index c8c32b37..d10f931c 100644 --- a/internal/pkg/core/deployer/providers/goedge/goedge_test.go +++ b/internal/pkg/core/deployer/providers/goedge/goedge_test.go @@ -17,7 +17,7 @@ var ( fApiUrl string fAccessKeyId string fAccessKey string - fCertificateId int + fCertificateId int64 ) func init() { @@ -28,7 +28,7 @@ func init() { flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") flag.StringVar(&fAccessKey, argsPrefix+"ACCESSKEY", "", "") - flag.IntVar(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "") + flag.Int64Var(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "") } /* @@ -45,7 +45,7 @@ Shell command to run this test: func TestDeploy(t *testing.T) { flag.Parse() - t.Run("Deploy", func(t *testing.T) { + t.Run("Deploy_ToCertificate", func(t *testing.T) { t.Log(strings.Join([]string{ "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), @@ -58,11 +58,12 @@ func TestDeploy(t *testing.T) { deployer, err := provider.NewDeployer(&provider.DeployerConfig{ ApiUrl: fApiUrl, + ApiRole: "user", AccessKeyId: fAccessKeyId, AccessKey: fAccessKey, AllowInsecureConnections: true, ResourceType: provider.RESOURCE_TYPE_CERTIFICATE, - CertificateId: int64(fCertificateId), + CertificateId: fCertificateId, }) if err != nil { t.Errorf("err: %+v", err) diff --git a/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb.go b/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb.go index 748111dd..23ec4a92 100644 --- a/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb.go +++ b/internal/pkg/core/deployer/providers/huaweicloud-elb/huaweicloud_elb.go @@ -210,7 +210,6 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, certPEM str select { case <-ctx.Done(): return ctx.Err() - default: if err := d.modifyListenerCertificate(ctx, listenerId, upres.CertId); err != nil { errs = append(errs, err) diff --git a/internal/pkg/core/deployer/providers/lecdn/consts.go b/internal/pkg/core/deployer/providers/lecdn/consts.go new file mode 100644 index 00000000..f5b7c0c9 --- /dev/null +++ b/internal/pkg/core/deployer/providers/lecdn/consts.go @@ -0,0 +1,8 @@ +package lecdn + +type ResourceType string + +const ( + // 资源类型:替换指定证书。 + RESOURCE_TYPE_CERTIFICATE = ResourceType("certificate") +) diff --git a/internal/pkg/core/deployer/providers/lecdn/lecdn.go b/internal/pkg/core/deployer/providers/lecdn/lecdn.go new file mode 100644 index 00000000..1ad88dcf --- /dev/null +++ b/internal/pkg/core/deployer/providers/lecdn/lecdn.go @@ -0,0 +1,176 @@ +package lecdn + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "log/slog" + "net/url" + "time" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + leclientsdkv3 "github.com/usual2970/certimate/internal/pkg/sdk3rd/lecdn/v3/client" + lemastersdkv3 "github.com/usual2970/certimate/internal/pkg/sdk3rd/lecdn/v3/master" +) + +type DeployerConfig struct { + // LeCDN URL。 + ApiUrl string `json:"apiUrl"` + // LeCDN 版本。 + // 可取值 "v3"。 + ApiVersion string `json:"apiVersion"` + // LeCDN 用户角色。 + // 可取值 "client"、"master"。 + ApiRole string `json:"apiRole"` + // LeCDN 用户名。 + Username string `json:"accessKeyId"` + // LeCDN 用户密码。 + Password string `json:"accessKey"` + // 是否允许不安全的连接。 + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` + // 部署资源类型。 + ResourceType ResourceType `json:"resourceType"` + // 证书 ID。 + // 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时必填。 + CertificateId int64 `json:"certificateId,omitempty"` + // 客户 ID。 + // 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时选填。 + ClientId int64 `json:"clientId,omitempty"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger *slog.Logger + sdkClient interface{} +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +const ( + apiVersionV3 = "v3" + + apiRoleClient = "client" + apiRoleMaster = "master" +) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiRole, config.Username, config.Password, config.AllowInsecureConnections) + if err != nil { + return nil, fmt.Errorf("failed to create sdk client: %w", err) + } + + return &DeployerProvider{ + config: config, + logger: slog.Default(), + sdkClient: client, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { + if logger == nil { + d.logger = slog.Default() + } else { + d.logger = logger + } + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { + // 根据部署资源类型决定部署方式 + switch d.config.ResourceType { + case RESOURCE_TYPE_CERTIFICATE: + if err := d.deployToCertificate(ctx, certPEM, privkeyPEM); err != nil { + return nil, err + } + + default: + return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType) + } + + return &deployer.DeployResult{}, nil +} + +func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM string, privkeyPEM string) error { + if d.config.CertificateId == 0 { + return errors.New("config `certificateId` is required") + } + + // 修改证书 + // REF: https://wdk0pwf8ul.feishu.cn/wiki/YE1XwCRIHiLYeKkPupgcXrlgnDd + switch sdkClient := d.sdkClient.(type) { + case *leclientsdkv3.Client: + updateSSLCertReq := &leclientsdkv3.UpdateCertificateRequest{ + Name: fmt.Sprintf("certimate-%d", time.Now().UnixMilli()), + Description: "upload from certimate", + Type: "upload", + SSLPEM: certPEM, + SSLKey: privkeyPEM, + AutoRenewal: false, + } + updateSSLCertResp, err := sdkClient.UpdateCertificate(d.config.CertificateId, updateSSLCertReq) + d.logger.Debug("sdk request 'lecdn.UpdateCertificate'", slog.Int64("certId", d.config.CertificateId), slog.Any("request", updateSSLCertReq), slog.Any("response", updateSSLCertResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'lecdn.UpdateCertificate': %w", err) + } + + case *lemastersdkv3.Client: + updateSSLCertReq := &lemastersdkv3.UpdateCertificateRequest{ + ClientId: d.config.ClientId, + Name: fmt.Sprintf("certimate-%d", time.Now().UnixMilli()), + Description: "upload from certimate", + Type: "upload", + SSLPEM: certPEM, + SSLKey: privkeyPEM, + AutoRenewal: false, + } + updateSSLCertResp, err := sdkClient.UpdateCertificate(d.config.CertificateId, updateSSLCertReq) + d.logger.Debug("sdk request 'lecdn.UpdateCertificate'", slog.Int64("certId", d.config.CertificateId), slog.Any("request", updateSSLCertReq), slog.Any("response", updateSSLCertResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'lecdn.UpdateCertificate': %w", err) + } + + default: + panic("sdk client is not implemented") + } + + return nil +} + +func createSdkClient(apiUrl, apiVersion, apiRole, username, password string, skipTlsVerify bool) (interface{}, error) { + if _, err := url.Parse(apiUrl); err != nil { + return nil, errors.New("invalid lecdn api url") + } + + if username == "" { + return nil, errors.New("invalid lecdn username") + } + + if password == "" { + return nil, errors.New("invalid lecdn password") + } + + if apiVersion == apiVersionV3 && apiRole == apiRoleClient { + // v3 版客户端 + client := leclientsdkv3.NewClient(apiUrl, username, password) + if skipTlsVerify { + client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) + } + + return client, nil + } else if apiVersion == apiVersionV3 && apiRole == apiRoleMaster { + // v3 版主控端 + client := lemastersdkv3.NewClient(apiUrl, username, password) + if skipTlsVerify { + client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) + } + + return client, nil + } + + return nil, fmt.Errorf("invalid lecdn api version or user role") +} diff --git a/internal/pkg/core/deployer/providers/lecdn/lecdn_test.go b/internal/pkg/core/deployer/providers/lecdn/lecdn_test.go new file mode 100644 index 00000000..cbaa4523 --- /dev/null +++ b/internal/pkg/core/deployer/providers/lecdn/lecdn_test.go @@ -0,0 +1,87 @@ +package lecdn_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/lecdn" +) + +var ( + fInputCertPath string + fInputKeyPath string + fApiUrl string + fApiVersion string + fUsername string + fPassword string + fCertificateId int64 +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_LECDN_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v3", "") + flag.StringVar(&fUsername, argsPrefix+"USERNAME", "", "") + flag.StringVar(&fPassword, argsPrefix+"PASSWORD", "", "") + flag.Int64Var(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "") +} + +/* +Shell command to run this test: + + go test -v ./lecdn_test.go -args \ + --CERTIMATE_DEPLOYER_LECDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_LECDN_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_LECDN_APIURL="http://127.0.0.1:5090" \ + --CERTIMATE_DEPLOYER_LECDN_USERNAME="your-username" \ + --CERTIMATE_DEPLOYER_LECDN_PASSWORD="your-password" \ + --CERTIMATE_DEPLOYER_LECDN_CERTIFICATEID="your-cerficiate-id" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy_ToCertificate", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("APIVERSION: %v", fApiVersion), + fmt.Sprintf("USERNAME: %v", fUsername), + fmt.Sprintf("PASSWORD: %v", fPassword), + fmt.Sprintf("CERTIFICATEID: %v", fCertificateId), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + ApiUrl: fApiUrl, + ApiVersion: fApiVersion, + ApiRole: "user", + Username: fUsername, + Password: fPassword, + AllowInsecureConnections: true, + ResourceType: provider.RESOURCE_TYPE_CERTIFICATE, + CertificateId: fCertificateId, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := 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/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console.go b/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console.go index ceb31771..51faf4f2 100644 --- a/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console.go +++ b/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console.go @@ -15,8 +15,8 @@ import ( type DeployerConfig struct { // 耗子面板地址。 ApiUrl string `json:"apiUrl"` - // 耗子面板访问令牌ID。 - AccessTokenId uint `json:"accessTokenId"` + // 耗子面板访问令牌 ID。 + AccessTokenId int32 `json:"accessTokenId"` // 耗子面板访问令牌。 AccessToken string `json:"accessToken"` // 是否允许不安全的连接。 @@ -64,15 +64,15 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE PrivateKey: privkeyPEM, } settingCertResp, err := d.sdkClient.SettingCert(settingCertReq) - d.logger.Debug("sdk request 'ratpanel.SettingCertRequest'", slog.Any("request", settingCertReq), slog.Any("response", settingCertResp)) + d.logger.Debug("sdk request 'ratpanel.SettingCert'", slog.Any("request", settingCertReq), slog.Any("response", settingCertResp)) if err != nil { - return nil, fmt.Errorf("failed to execute sdk request 'ratpanel.SettingCertRequest': %w", err) + return nil, fmt.Errorf("failed to execute sdk request 'ratpanel.SettingCert': %w", err) } return &deployer.DeployResult{}, nil } -func createSdkClient(apiUrl string, accessTokenId uint, accessToken string, skipTlsVerify bool) (*rpsdk.Client, error) { +func createSdkClient(apiUrl string, accessTokenId int32, accessToken string, skipTlsVerify bool) (*rpsdk.Client, error) { if _, err := url.Parse(apiUrl); err != nil { return nil, errors.New("invalid ratpanel api url") } @@ -80,6 +80,7 @@ func createSdkClient(apiUrl string, accessTokenId uint, accessToken string, skip if accessTokenId == 0 { return nil, errors.New("invalid ratpanel access token id") } + if accessToken == "" { return nil, errors.New("invalid ratpanel access token") } diff --git a/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console_test.go b/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console_test.go index 40804f87..3f3193b3 100644 --- a/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console_test.go +++ b/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console_test.go @@ -15,8 +15,8 @@ var ( fInputCertPath string fInputKeyPath string fApiUrl string - fTokenId uint - fToken string + fAccessTokenId int64 + fAccessToken string ) func init() { @@ -25,8 +25,8 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") - flag.UintVar(&fTokenId, argsPrefix+"TOKENID", 0, "") - flag.StringVar(&fToken, argsPrefix+"TOKEN", "", "") + flag.Int64Var(&fAccessTokenId, argsPrefix+"ACCESSTOKENID", 0, "") + flag.StringVar(&fAccessToken, argsPrefix+"ACCESSTOKEN", "", "") } /* @@ -36,8 +36,8 @@ Shell command to run this test: --CERTIMATE_DEPLOYER_RATPANELCONSOLE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_RATPANELCONSOLE_INPUTKEYPATH="/path/to/your-input-key.pem" \ --CERTIMATE_DEPLOYER_RATPANELCONSOLE_APIURL="http://127.0.0.1:8888" \ - --CERTIMATE_DEPLOYER_RATPANELCONSOLE_TOKENID=your-access-token-id \ - --CERTIMATE_DEPLOYER_RATPANELCONSOLE_TOKEN="your-access-token" + --CERTIMATE_DEPLOYER_RATPANELCONSOLE_ACCESSTOKENID="your-access-token-id" \ + --CERTIMATE_DEPLOYER_RATPANELCONSOLE_ACCESSTOKEN="your-access-token" */ func TestDeploy(t *testing.T) { flag.Parse() @@ -48,14 +48,14 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), fmt.Sprintf("APIURL: %v", fApiUrl), - fmt.Sprintf("TOKENID: %v", fTokenId), - fmt.Sprintf("TOKEN: %v", fToken), + fmt.Sprintf("ACCESSTOKENID: %v", fAccessTokenId), + fmt.Sprintf("ACCESSTOKEN: %v", fAccessToken), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ ApiUrl: fApiUrl, - AccessTokenId: fTokenId, - AccessToken: fToken, + AccessTokenId: int32(fAccessTokenId), + AccessToken: fAccessToken, AllowInsecureConnections: true, }) if err != nil { diff --git a/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site.go b/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site.go index 85f54ab5..b4e283be 100644 --- a/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site.go +++ b/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site.go @@ -15,14 +15,14 @@ import ( type DeployerConfig struct { // 耗子面板地址。 ApiUrl string `json:"apiUrl"` - // 耗子面板访问令牌ID。 - AccessTokenId uint `json:"accessTokenId"` + // 耗子面板访问令牌 ID。 + AccessTokenId int32 `json:"accessTokenId"` // 耗子面板访问令牌。 AccessToken string `json:"accessToken"` // 是否允许不安全的连接。 AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` // 网站名称。 - SiteName string `json:"siteName,omitempty"` + SiteName string `json:"siteName"` } type DeployerProvider struct { @@ -71,15 +71,15 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE PrivateKey: privkeyPEM, } websiteCertResp, err := d.sdkClient.WebsiteCert(websiteCertReq) - d.logger.Debug("sdk request 'ratpanel.WebsiteCertRequest'", slog.Any("request", websiteCertReq), slog.Any("response", websiteCertResp)) + d.logger.Debug("sdk request 'ratpanel.WebsiteCert'", slog.Any("request", websiteCertReq), slog.Any("response", websiteCertResp)) if err != nil { - return nil, fmt.Errorf("failed to execute sdk request 'ratpanel.WebsiteCertRequest': %w", err) + return nil, fmt.Errorf("failed to execute sdk request 'ratpanel.WebsiteCert': %w", err) } return &deployer.DeployResult{}, nil } -func createSdkClient(apiUrl string, accessTokenId uint, accessToken string, skipTlsVerify bool) (*rpsdk.Client, error) { +func createSdkClient(apiUrl string, accessTokenId int32, accessToken string, skipTlsVerify bool) (*rpsdk.Client, error) { if _, err := url.Parse(apiUrl); err != nil { return nil, errors.New("invalid ratpanel api url") } @@ -87,6 +87,7 @@ func createSdkClient(apiUrl string, accessTokenId uint, accessToken string, skip if accessTokenId == 0 { return nil, errors.New("invalid ratpanel access token id") } + if accessToken == "" { return nil, errors.New("invalid ratpanel access token") } diff --git a/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site_test.go b/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site_test.go index a4cab040..658175fb 100644 --- a/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site_test.go +++ b/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site_test.go @@ -15,8 +15,8 @@ var ( fInputCertPath string fInputKeyPath string fApiUrl string - fTokenId uint - fToken string + fAccessTokenId int64 + fAccessToken string fSiteName string ) @@ -26,8 +26,8 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") - flag.UintVar(&fTokenId, argsPrefix+"TOKENID", 0, "") - flag.StringVar(&fToken, argsPrefix+"TOKEN", "", "") + flag.Int64Var(&fAccessTokenId, argsPrefix+"ACCESSTOKENID", 0, "") + flag.StringVar(&fAccessToken, argsPrefix+"ACCESSTOKEN", "", "") flag.StringVar(&fSiteName, argsPrefix+"SITENAME", "", "") } @@ -38,8 +38,8 @@ Shell command to run this test: --CERTIMATE_DEPLOYER_RATPANELSITE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_RATPANELSITE_INPUTKEYPATH="/path/to/your-input-key.pem" \ --CERTIMATE_DEPLOYER_RATPANELSITE_APIURL="http://127.0.0.1:8888" \ - --CERTIMATE_DEPLOYER_RATPANELSITE_TOKENID=your-access-token-id \ - --CERTIMATE_DEPLOYER_RATPANELSITE_TOKEN="your-access-token" \ + --CERTIMATE_DEPLOYER_RATPANELSITE_ACCESSTOKENID="your-access-token-id" \ + --CERTIMATE_DEPLOYER_RATPANELSITE_ACCESSTOKEN="your-access-token" \ --CERTIMATE_DEPLOYER_RATPANELSITE_SITENAME="your-site-name" */ func TestDeploy(t *testing.T) { @@ -51,15 +51,15 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), fmt.Sprintf("APIURL: %v", fApiUrl), - fmt.Sprintf("TOKENID: %v", fTokenId), - fmt.Sprintf("TOKEN: %v", fToken), + fmt.Sprintf("ACCESSTOKENID: %v", fAccessTokenId), + fmt.Sprintf("ACCESSTOKEN: %v", fAccessToken), fmt.Sprintf("SITENAME: %v", fSiteName), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ ApiUrl: fApiUrl, - AccessTokenId: fTokenId, - AccessToken: fToken, + AccessTokenId: int32(fAccessTokenId), + AccessToken: fAccessToken, AllowInsecureConnections: true, SiteName: fSiteName, }) diff --git a/internal/pkg/core/deployer/providers/safeline/safeline_test.go b/internal/pkg/core/deployer/providers/safeline/safeline_test.go index 294086c8..67fe6755 100644 --- a/internal/pkg/core/deployer/providers/safeline/safeline_test.go +++ b/internal/pkg/core/deployer/providers/safeline/safeline_test.go @@ -16,7 +16,7 @@ var ( fInputKeyPath string fApiUrl string fApiToken string - fCertificateId int + fCertificateId int64 ) func init() { @@ -26,7 +26,7 @@ func init() { flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "") - flag.IntVar(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "") + flag.Int64Var(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "") } /* diff --git a/internal/pkg/core/deployer/providers/ssh/ssh_test.go b/internal/pkg/core/deployer/providers/ssh/ssh_test.go index b63471d1..ae908185 100644 --- a/internal/pkg/core/deployer/providers/ssh/ssh_test.go +++ b/internal/pkg/core/deployer/providers/ssh/ssh_test.go @@ -15,7 +15,7 @@ var ( fInputCertPath string fInputKeyPath string fSshHost string - fSshPort int + fSshPort int64 fSshUsername string fSshPassword string fOutputCertPath string @@ -28,7 +28,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") flag.StringVar(&fSshHost, argsPrefix+"SSHHOST", "", "") - flag.IntVar(&fSshPort, argsPrefix+"SSHPORT", 0, "") + flag.Int64Var(&fSshPort, argsPrefix+"SSHPORT", 0, "") flag.StringVar(&fSshUsername, argsPrefix+"SSHUSERNAME", "", "") flag.StringVar(&fSshPassword, argsPrefix+"SSHPASSWORD", "", "") flag.StringVar(&fOutputCertPath, argsPrefix+"OUTPUTCERTPATH", "", "") diff --git a/internal/pkg/core/deployer/providers/wangsu-cdn/wangsu_cdn.go b/internal/pkg/core/deployer/providers/wangsu-cdn/wangsu_cdn.go new file mode 100644 index 00000000..43c65de2 --- /dev/null +++ b/internal/pkg/core/deployer/providers/wangsu-cdn/wangsu_cdn.go @@ -0,0 +1,104 @@ +package wangsucdn + +import ( + "context" + "errors" + "fmt" + "log/slog" + "strconv" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/uploader" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/wangsu-certificate" + wangsusdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/cdn" +) + +type DeployerConfig struct { + // 网宿云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 网宿云 AccessKeySecret。 + AccessKeySecret string `json:"accessKeySecret"` + // 加速域名数组。 + Domains []string `json:"domains"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger *slog.Logger + sdkClient *wangsusdk.Client + sslUploader uploader.Uploader +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret) + if err != nil { + return nil, fmt.Errorf("failed to create sdk client: %w", err) + } + + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ + AccessKeyId: config.AccessKeyId, + AccessKeySecret: config.AccessKeySecret, + }) + if err != nil { + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) + } + + return &DeployerProvider{ + config: config, + logger: slog.Default(), + sdkClient: client, + sslUploader: uploader, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { + if logger == nil { + d.logger = slog.Default() + } else { + d.logger = logger + } + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { + // 上传证书到证书管理 + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) + if err != nil { + return nil, fmt.Errorf("failed to upload certificate file: %w", err) + } else { + d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) + } + + // 批量修改域名证书配置 + // REF: https://www.wangsu.com/document/api-doc/37447 + certId, _ := strconv.ParseInt(upres.CertId, 10, 64) + batchUpdateCertificateConfigReq := &wangsusdk.BatchUpdateCertificateConfigRequest{ + CertificateId: certId, + DomainNames: d.config.Domains, + } + batchUpdateCertificateConfigResp, err := d.sdkClient.BatchUpdateCertificateConfig(batchUpdateCertificateConfigReq) + d.logger.Debug("sdk request 'cdn.BatchUpdateCertificateConfig'", slog.Any("request", batchUpdateCertificateConfigReq), slog.Any("response", batchUpdateCertificateConfigResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'cdn.BatchUpdateCertificateConfig': %w", err) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(accessKeyId, accessKeySecret string) (*wangsusdk.Client, error) { + if accessKeyId == "" { + return nil, errors.New("invalid wangsu access key id") + } + + if accessKeySecret == "" { + return nil, errors.New("invalid wangsu access key secret") + } + + return wangsusdk.NewClient(accessKeyId, accessKeySecret), nil +} diff --git a/internal/pkg/core/deployer/providers/wangsu-cdn/wangsu_cdn_test.go b/internal/pkg/core/deployer/providers/wangsu-cdn/wangsu_cdn_test.go new file mode 100644 index 00000000..99859b85 --- /dev/null +++ b/internal/pkg/core/deployer/providers/wangsu-cdn/wangsu_cdn_test.go @@ -0,0 +1,75 @@ +package wangsucdn_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/wangsu-cdn" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKeyId string + fAccessKeySecret string + fDomain string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_WANGSUCDN_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "") + flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") +} + +/* +Shell command to run this test: + + go test -v ./wangsu_cdn_test.go -args \ + --CERTIMATE_DEPLOYER_WANGSUCDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_WANGSUCDN_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_WANGSUCDN_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_DEPLOYER_WANGSUCDN_ACCESSKEYSECRET="your-access-key-secret" \ + --CERTIMATE_DEPLOYER_WANGSUCDN_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("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), + fmt.Sprintf("DOMAIN: %v", fDomain), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + Domains: []string{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/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro.go b/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro.go index ee16b08a..4d5f2e10 100644 --- a/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro.go +++ b/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro.go @@ -17,7 +17,7 @@ import ( "time" "github.com/usual2970/certimate/internal/pkg/core/deployer" - wangsucdn "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/cdn" + wangsucdn "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/cdnpro" certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" typeutil "github.com/usual2970/certimate/internal/pkg/utils/type" ) @@ -88,9 +88,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE // 查询已部署加速域名的详情 getHostnameDetailResp, err := d.sdkClient.GetHostnameDetail(d.config.Domain) - d.logger.Debug("sdk request 'cdn.GetHostnameDetail'", slog.String("hostname", d.config.Domain), slog.Any("response", getHostnameDetailResp)) + d.logger.Debug("sdk request 'cdnpro.GetHostnameDetail'", slog.String("hostname", d.config.Domain), slog.Any("response", getHostnameDetailResp)) if err != nil { - return nil, fmt.Errorf("failed to execute sdk request 'cdn.GetHostnameDetail': %w", err) + return nil, fmt.Errorf("failed to execute sdk request 'cdnpro.GetHostnameDetail': %w", err) } // 生成网宿云证书参数 @@ -126,9 +126,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE NewVersion: certificateNewVersionInfo, } createCertificateResp, err := d.sdkClient.CreateCertificate(createCertificateReq) - d.logger.Debug("sdk request 'cdn.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp)) + d.logger.Debug("sdk request 'cdnpro.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp)) if err != nil { - return nil, fmt.Errorf("failed to execute sdk request 'cdn.CreateCertificate': %w", err) + return nil, fmt.Errorf("failed to execute sdk request 'cdnpro.CreateCertificate': %w", err) } wangsuCertUrl = createCertificateResp.CertificateUrl @@ -149,9 +149,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE NewVersion: certificateNewVersionInfo, } updateCertificateResp, err := d.sdkClient.UpdateCertificate(d.config.CertificateId, updateCertificateReq) - d.logger.Debug("sdk request 'cdn.CreateCertificate'", slog.Any("certificateId", d.config.CertificateId), slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp)) + d.logger.Debug("sdk request 'cdnpro.CreateCertificate'", slog.Any("certificateId", d.config.CertificateId), slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp)) if err != nil { - return nil, fmt.Errorf("failed to execute sdk request 'cdn.UpdateCertificate': %w", err) + return nil, fmt.Errorf("failed to execute sdk request 'cdnpro.UpdateCertificate': %w", err) } wangsuCertUrl = updateCertificateResp.CertificateUrl @@ -186,9 +186,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE createDeploymentTaskReq.Webhook = typeutil.ToPtr(d.config.WebhookId) } createDeploymentTaskResp, err := d.sdkClient.CreateDeploymentTask(createDeploymentTaskReq) - d.logger.Debug("sdk request 'cdn.CreateCertificate'", slog.Any("request", createDeploymentTaskReq), slog.Any("response", createDeploymentTaskResp)) + d.logger.Debug("sdk request 'cdnpro.CreateCertificate'", slog.Any("request", createDeploymentTaskReq), slog.Any("response", createDeploymentTaskResp)) if err != nil { - return nil, fmt.Errorf("failed to execute sdk request 'cdn.CreateDeploymentTask': %w", err) + return nil, fmt.Errorf("failed to execute sdk request 'cdnpro.CreateDeploymentTask': %w", err) } // 循环获取部署任务详细信息,等待任务状态变更 @@ -206,9 +206,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE } getDeploymentTaskDetailResp, err := d.sdkClient.GetDeploymentTaskDetail(wangsuTaskId) - d.logger.Info("sdk request 'cdn.GetDeploymentTaskDetail'", slog.Any("taskId", wangsuTaskId), slog.Any("response", getDeploymentTaskDetailResp)) + d.logger.Info("sdk request 'cdnpro.GetDeploymentTaskDetail'", slog.Any("taskId", wangsuTaskId), slog.Any("response", getDeploymentTaskDetailResp)) if err != nil { - return nil, fmt.Errorf("failed to execute sdk request 'cdn.GetDeploymentTaskDetail': %w", err) + return nil, fmt.Errorf("failed to execute sdk request 'cdnpro.GetDeploymentTaskDetail': %w", err) } if getDeploymentTaskDetailResp.Status == "failed" { diff --git a/internal/pkg/core/deployer/providers/wangsu-certificate/wangsu_certificate.go b/internal/pkg/core/deployer/providers/wangsu-certificate/wangsu_certificate.go new file mode 100644 index 00000000..3f691489 --- /dev/null +++ b/internal/pkg/core/deployer/providers/wangsu-certificate/wangsu_certificate.go @@ -0,0 +1,109 @@ +package wangsucertificate + +import ( + "context" + "errors" + "fmt" + "log/slog" + "time" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/uploader" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/wangsu-certificate" + wangsusdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/certificate" + typeutil "github.com/usual2970/certimate/internal/pkg/utils/type" +) + +type DeployerConfig struct { + // 网宿云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 网宿云 AccessKeySecret。 + AccessKeySecret string `json:"accessKeySecret"` + // 证书 ID。 + // 选填。零值时表示新建证书;否则表示更新证书。 + CertificateId string `json:"certificateId,omitempty"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger *slog.Logger + sdkClient *wangsusdk.Client + sslUploader uploader.Uploader +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret) + if err != nil { + return nil, fmt.Errorf("failed to create sdk client: %w", err) + } + + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ + AccessKeyId: config.AccessKeyId, + AccessKeySecret: config.AccessKeySecret, + }) + if err != nil { + return nil, fmt.Errorf("failed to create ssl uploader: %w", err) + } + + return &DeployerProvider{ + config: config, + logger: slog.Default(), + sdkClient: client, + sslUploader: uploader, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { + if logger == nil { + d.logger = slog.Default() + } else { + d.logger = logger + } + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { + if d.config.CertificateId == "" { + // 上传证书到证书管理 + upres, err := d.sslUploader.Upload(ctx, certPEM, privkeyPEM) + if err != nil { + return nil, fmt.Errorf("failed to upload certificate file: %w", err) + } else { + d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) + } + } else { + // 修改证书 + // REF: https://www.wangsu.com/document/api-doc/25568?productCode=certificatemanagement + updateCertificateReq := &wangsusdk.UpdateCertificateRequest{ + Name: typeutil.ToPtr(fmt.Sprintf("certimate_%d", time.Now().UnixMilli())), + Certificate: typeutil.ToPtr(certPEM), + PrivateKey: typeutil.ToPtr(privkeyPEM), + Comment: typeutil.ToPtr("upload from certimate"), + } + updateCertificateResp, err := d.sdkClient.UpdateCertificate(d.config.CertificateId, updateCertificateReq) + d.logger.Debug("sdk request 'certificatemanagement.UpdateCertificate'", slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'certificatemanagement.CreateCertificate': %w", err) + } + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(accessKeyId, accessKeySecret string) (*wangsusdk.Client, error) { + if accessKeyId == "" { + return nil, errors.New("invalid wangsu access key id") + } + + if accessKeySecret == "" { + return nil, errors.New("invalid wangsu access key secret") + } + + return wangsusdk.NewClient(accessKeyId, accessKeySecret), nil +} diff --git a/internal/pkg/core/deployer/providers/wangsu-certificate/wangsu_certificate_test.go b/internal/pkg/core/deployer/providers/wangsu-certificate/wangsu_certificate_test.go new file mode 100644 index 00000000..a6805ec9 --- /dev/null +++ b/internal/pkg/core/deployer/providers/wangsu-certificate/wangsu_certificate_test.go @@ -0,0 +1,75 @@ +package wangsucertificate_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/wangsu-certificate" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKeyId string + fAccessKeySecret string + fCertificateId string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "") + flag.StringVar(&fCertificateId, argsPrefix+"CERTIFICATEID", "", "") +} + +/* +Shell command to run this test: + + go test -v ./wangsu_certificate_test.go -args \ + --CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_ACCESSKEYSECRET="your-access-key-secret" \ + --CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_CERTIFICATEID="your-certificate-id" +*/ +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("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), + fmt.Sprintf("CERTIFICATEID: %v", fCertificateId), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + CertificateId: fCertificateId, + }) + 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/internal/pkg/core/deployer/providers/webhook/webhook.go b/internal/pkg/core/deployer/providers/webhook/webhook.go index 3a21717b..49b07b47 100644 --- a/internal/pkg/core/deployer/providers/webhook/webhook.go +++ b/internal/pkg/core/deployer/providers/webhook/webhook.go @@ -176,7 +176,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE } // 发送请求 - resp, err := req.SetDebug(true).Send() + resp, err := req.Send() if err != nil { return nil, fmt.Errorf("failed to send webhook request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/core/notifier/providers/bark/bark.go b/internal/pkg/core/notifier/providers/bark/bark.go index 4d1902f2..97ece0be 100644 --- a/internal/pkg/core/notifier/providers/bark/bark.go +++ b/internal/pkg/core/notifier/providers/bark/bark.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "log/slog" - "net/http" "github.com/go-resty/resty/v2" @@ -65,7 +64,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s "body": message, "device_key": n.config.DeviceKey, }) - resp, err := req.Execute(http.MethodPost, serverUrl) + resp, err := req.Post(serverUrl) if err != nil { return nil, fmt.Errorf("bark api error: failed to send request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/core/notifier/providers/email/email_test.go b/internal/pkg/core/notifier/providers/email/email_test.go index 30bfba07..cf0669ca 100644 --- a/internal/pkg/core/notifier/providers/email/email_test.go +++ b/internal/pkg/core/notifier/providers/email/email_test.go @@ -17,7 +17,7 @@ const ( var ( fSmtpHost string - fSmtpPort int + fSmtpPort int64 fSmtpTLS bool fUsername string fPassword string @@ -29,7 +29,7 @@ func init() { argsPrefix := "CERTIMATE_NOTIFIER_EMAIL_" flag.StringVar(&fSmtpHost, argsPrefix+"SMTPHOST", "", "") - flag.IntVar(&fSmtpPort, argsPrefix+"SMTPPORT", 0, "") + flag.Int64Var(&fSmtpPort, argsPrefix+"SMTPPORT", 0, "") flag.BoolVar(&fSmtpTLS, argsPrefix+"SMTPTLS", false, "") flag.StringVar(&fUsername, argsPrefix+"USERNAME", "", "") flag.StringVar(&fPassword, argsPrefix+"PASSWORD", "", "") diff --git a/internal/pkg/core/notifier/providers/gotify/gotify.go b/internal/pkg/core/notifier/providers/gotify/gotify.go index 05b2f919..81dcb8ad 100644 --- a/internal/pkg/core/notifier/providers/gotify/gotify.go +++ b/internal/pkg/core/notifier/providers/gotify/gotify.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "log/slog" - "net/http" "strings" "github.com/go-resty/resty/v2" @@ -64,7 +63,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s "message": message, "priority": n.config.Priority, }) - resp, err := req.Execute(http.MethodPost, fmt.Sprintf("%s/message", serverUrl)) + resp, err := req.Post(fmt.Sprintf("%s/message", serverUrl)) if err != nil { return nil, fmt.Errorf("gotify api error: failed to send request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/core/notifier/providers/mattermost/mattermost.go b/internal/pkg/core/notifier/providers/mattermost/mattermost.go index 4f1c3e9c..a9b2f4d6 100644 --- a/internal/pkg/core/notifier/providers/mattermost/mattermost.go +++ b/internal/pkg/core/notifier/providers/mattermost/mattermost.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "log/slog" - "net/http" "strings" "github.com/go-resty/resty/v2" @@ -64,7 +63,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s "login_id": n.config.Username, "password": n.config.Password, }) - loginResp, err := loginReq.Execute(http.MethodPost, fmt.Sprintf("%s/api/v4/users/login", serverUrl)) + loginResp, err := loginReq.Post(fmt.Sprintf("%s/api/v4/users/login", serverUrl)) if err != nil { return nil, fmt.Errorf("mattermost api error: failed to send request: %w", err) } else if loginResp.IsError() { @@ -88,7 +87,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s }, }, }) - postResp, err := postReq.Execute(http.MethodPost, fmt.Sprintf("%s/api/v4/posts", serverUrl)) + postResp, err := postReq.Post(fmt.Sprintf("%s/api/v4/posts", serverUrl)) if err != nil { return nil, fmt.Errorf("mattermost api error: failed to send request: %w", err) } else if postResp.IsError() { diff --git a/internal/pkg/core/notifier/providers/pushover/pushover.go b/internal/pkg/core/notifier/providers/pushover/pushover.go index 459a4950..b7f74bba 100644 --- a/internal/pkg/core/notifier/providers/pushover/pushover.go +++ b/internal/pkg/core/notifier/providers/pushover/pushover.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "log/slog" - "net/http" "github.com/go-resty/resty/v2" @@ -59,7 +58,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s "token": n.config.Token, "user": n.config.User, }) - resp, err := req.Execute(http.MethodPost, "https://api.pushover.net/1/messages.json") + resp, err := req.Post("https://api.pushover.net/1/messages.json") if err != nil { return nil, fmt.Errorf("pushover api error: failed to send request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/core/notifier/providers/pushplus/pushplus.go b/internal/pkg/core/notifier/providers/pushplus/pushplus.go index a8a95ac7..834f9683 100644 --- a/internal/pkg/core/notifier/providers/pushplus/pushplus.go +++ b/internal/pkg/core/notifier/providers/pushplus/pushplus.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "log/slog" - "net/http" "github.com/go-resty/resty/v2" @@ -57,7 +56,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s "content": message, "token": n.config.Token, }) - resp, err := req.Execute(http.MethodPost, "https://www.pushplus.plus/send") + resp, err := req.Post("https://www.pushplus.plus/send") if err != nil { return nil, fmt.Errorf("pushplus api error: failed to send request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/core/notifier/providers/serverchan/serverchan.go b/internal/pkg/core/notifier/providers/serverchan/serverchan.go index cd77ce83..d74b2fcc 100644 --- a/internal/pkg/core/notifier/providers/serverchan/serverchan.go +++ b/internal/pkg/core/notifier/providers/serverchan/serverchan.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "log/slog" - "net/http" "github.com/go-resty/resty/v2" @@ -55,7 +54,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s "text": subject, "desp": message, }) - resp, err := req.Execute(http.MethodPost, n.config.ServerUrl) + resp, err := req.Post(n.config.ServerUrl) if err != nil { return nil, fmt.Errorf("serverchan api error: failed to send request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/core/notifier/providers/telegrambot/telegrambot.go b/internal/pkg/core/notifier/providers/telegrambot/telegrambot.go index a324ee00..99b86a38 100644 --- a/internal/pkg/core/notifier/providers/telegrambot/telegrambot.go +++ b/internal/pkg/core/notifier/providers/telegrambot/telegrambot.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "log/slog" - "net/http" "github.com/go-resty/resty/v2" @@ -57,7 +56,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s "chat_id": n.config.ChatId, "text": subject + "\n" + message, }) - resp, err := req.Execute(http.MethodPost, fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", n.config.BotToken)) + resp, err := req.Post(fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", n.config.BotToken)) if err != nil { return nil, fmt.Errorf("telegram api error: failed to send request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/core/notifier/providers/wecombot/wecombot.go b/internal/pkg/core/notifier/providers/wecombot/wecombot.go index daa771e0..36c179d4 100644 --- a/internal/pkg/core/notifier/providers/wecombot/wecombot.go +++ b/internal/pkg/core/notifier/providers/wecombot/wecombot.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "log/slog" - "net/http" "github.com/go-resty/resty/v2" @@ -57,7 +56,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s "content": subject + "\n\n" + message, }, }) - resp, err := req.Execute(http.MethodPost, n.config.WebhookUrl) + resp, err := req.Post(n.config.WebhookUrl) if err != nil { return nil, fmt.Errorf("wecom api error: failed to send request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go b/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go index e5a0b0ba..63900125 100644 --- a/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go +++ b/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go @@ -10,12 +10,14 @@ import ( "time" "github.com/usual2970/certimate/internal/pkg/core/uploader" - opsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel" + onepanelsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/1panel" ) type UploaderConfig struct { // 1Panel 地址。 ApiUrl string `json:"apiUrl"` + // 1Panel 版本。 + ApiVersion string `json:"apiVersion"` // 1Panel 接口密钥。 ApiKey string `json:"apiKey"` } @@ -23,7 +25,7 @@ type UploaderConfig struct { type UploaderProvider struct { config *UploaderConfig logger *slog.Logger - sdkClient *opsdk.Client + sdkClient *onepanelsdk.Client } var _ uploader.Uploader = (*UploaderProvider)(nil) @@ -33,7 +35,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiKey) + client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } @@ -67,7 +69,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli()) // 上传证书 - uploadWebsiteSSLReq := &opsdk.UploadWebsiteSSLRequest{ + uploadWebsiteSSLReq := &onepanelsdk.UploadWebsiteSSLRequest{ Type: "paste", Description: certName, Certificate: certPEM, @@ -99,7 +101,7 @@ func (u *UploaderProvider) getCertIfExists(ctx context.Context, certPEM string, default: } - searchWebsiteSSLReq := &opsdk.SearchWebsiteSSLRequest{ + searchWebsiteSSLReq := &onepanelsdk.SearchWebsiteSSLRequest{ Page: searchWebsiteSSLPageNumber, PageSize: searchWebsiteSSLPageSize, } @@ -130,15 +132,19 @@ func (u *UploaderProvider) getCertIfExists(ctx context.Context, certPEM string, return nil, nil } -func createSdkClient(apiUrl, apiKey string) (*opsdk.Client, error) { +func createSdkClient(apiUrl, apiVersion, apiKey string) (*onepanelsdk.Client, error) { if _, err := url.Parse(apiUrl); err != nil { return nil, errors.New("invalid 1panel api url") } + if apiVersion == "" { + return nil, errors.New("invalid 1panel api version") + } + if apiKey == "" { return nil, errors.New("invalid 1panel api key") } - client := opsdk.NewClient(apiUrl, apiKey) + client := onepanelsdk.NewClient(apiUrl, apiVersion, apiKey) return client, nil } diff --git a/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl_test.go b/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl_test.go index 257030f5..cfb250be 100644 --- a/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl_test.go +++ b/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl_test.go @@ -16,6 +16,7 @@ var ( fInputCertPath string fInputKeyPath string fApiUrl string + fApiVersion string fApiKey string ) @@ -25,6 +26,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v1", "") flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") } @@ -35,6 +37,7 @@ Shell command to run this test: --CERTIMATE_UPLOADER_1PANELSSL_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_UPLOADER_1PANELSSL_INPUTKEYPATH="/path/to/your-input-key.pem" \ --CERTIMATE_UPLOADER_1PANELSSL_APIURL="http://127.0.0.1:20410" \ + --CERTIMATE_UPLOADER_1PANELSSL_APIVERSION="v1" \ --CERTIMATE_UPLOADER_1PANELSSL_APIKEY="your-api-key" */ func TestDeploy(t *testing.T) { @@ -46,12 +49,14 @@ func TestDeploy(t *testing.T) { fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("APIVERSION: %v", fApiVersion), fmt.Sprintf("APIKEY: %v", fApiKey), }, "\n")) uploader, err := provider.NewUploader(&provider.UploaderConfig{ - ApiUrl: fApiUrl, - ApiKey: fApiKey, + ApiUrl: fApiUrl, + ApiVersion: fApiVersion, + ApiKey: fApiKey, }) if err != nil { t.Errorf("err: %+v", err) diff --git a/internal/pkg/core/uploader/providers/wangsu-certificate/wangsu_certificate.go b/internal/pkg/core/uploader/providers/wangsu-certificate/wangsu_certificate.go new file mode 100644 index 00000000..b512be09 --- /dev/null +++ b/internal/pkg/core/uploader/providers/wangsu-certificate/wangsu_certificate.go @@ -0,0 +1,143 @@ +package jdcloudssl + +import ( + "context" + "errors" + "fmt" + "log/slog" + "regexp" + "strings" + "time" + + wangsusdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/certificate" + + "github.com/usual2970/certimate/internal/pkg/core/uploader" + certutil "github.com/usual2970/certimate/internal/pkg/utils/cert" + typeutil "github.com/usual2970/certimate/internal/pkg/utils/type" +) + +type UploaderConfig struct { + // 网宿云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 网宿云 AccessKeySecret。 + AccessKeySecret string `json:"accessKeySecret"` +} + +type UploaderProvider struct { + config *UploaderConfig + logger *slog.Logger + sdkClient *wangsusdk.Client +} + +var _ uploader.Uploader = (*UploaderProvider)(nil) + +func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.AccessKeyId, config.AccessKeySecret) + if err != nil { + return nil, fmt.Errorf("failed to create sdk client: %w", err) + } + + return &UploaderProvider{ + config: config, + logger: slog.Default(), + sdkClient: client, + }, nil +} + +func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader { + if logger == nil { + u.logger = slog.Default() + } else { + u.logger = logger + } + return u +} + +func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (res *uploader.UploadResult, err error) { + // 解析证书内容 + certX509, err := certutil.ParseCertificateFromPEM(certPEM) + if err != nil { + return nil, err + } + + // 查询证书列表,避免重复上传 + // REF: https://www.wangsu.com/document/api-doc/26426 + listCertificatesResp, err := u.sdkClient.ListCertificates() + u.logger.Debug("sdk request 'certificatemanagement.ListCertificates'", slog.Any("response", listCertificatesResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'certificatemanagement.ListCertificates': %w", err) + } + + if listCertificatesResp.Certificates != nil { + for _, certificate := range listCertificatesResp.Certificates { + // 对比证书序列号 + if !strings.EqualFold(certX509.SerialNumber.Text(16), certificate.Serial) { + continue + } + + // 再对比证书有效期 + cstzone := time.FixedZone("CST", 8*60*60) + oldCertNotBefore, _ := time.ParseInLocation(time.DateTime, certificate.ValidityFrom, cstzone) + oldCertNotAfter, _ := time.ParseInLocation(time.DateTime, certificate.ValidityTo, cstzone) + if !certX509.NotBefore.Equal(oldCertNotBefore) || !certX509.NotAfter.Equal(oldCertNotAfter) { + continue + } + + // 如果以上信息都一致,则视为已存在相同证书,直接返回 + u.logger.Info("ssl certificate already exists") + return &uploader.UploadResult{ + CertId: certificate.CertificateId, + CertName: certificate.Name, + }, nil + } + } + + // 生成新证书名(需符合网宿云命名规则) + var certId string + certName := fmt.Sprintf("certimate_%d", time.Now().UnixMilli()) + + // 新增证书 + // REF: https://www.wangsu.com/document/api-doc/25199?productCode=certificatemanagement + createCertificateReq := &wangsusdk.CreateCertificateRequest{ + Name: typeutil.ToPtr(certName), + Certificate: typeutil.ToPtr(certPEM), + PrivateKey: typeutil.ToPtr(privkeyPEM), + Comment: typeutil.ToPtr("upload from certimate"), + } + createCertificateResp, err := u.sdkClient.CreateCertificate(createCertificateReq) + u.logger.Debug("sdk request 'certificatemanagement.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'certificatemanagement.CreateCertificate': %w", err) + } + + // 网宿云证书 URL 中包含证书 ID + // 格式: + // https://open.chinanetcenter.com/api/certificate/100001 + wangsuCertIdMatches := regexp.MustCompile(`/certificate/([0-9]+)`).FindStringSubmatch(createCertificateResp.CertificateUrl) + if len(wangsuCertIdMatches) > 1 { + certId = wangsuCertIdMatches[1] + } else { + return nil, fmt.Errorf("received empty certificate id") + } + + return &uploader.UploadResult{ + CertId: certId, + CertName: certName, + }, nil +} + +func createSdkClient(accessKeyId, accessKeySecret string) (*wangsusdk.Client, error) { + if accessKeyId == "" { + return nil, errors.New("invalid wangsu access key id") + } + + if accessKeySecret == "" { + return nil, errors.New("invalid wangsu access key secret") + } + + return wangsusdk.NewClient(accessKeyId, accessKeySecret), nil +} diff --git a/internal/pkg/core/uploader/providers/wangsu-certificate/wangsu_certificate_test.go b/internal/pkg/core/uploader/providers/wangsu-certificate/wangsu_certificate_test.go new file mode 100644 index 00000000..bdec8cfe --- /dev/null +++ b/internal/pkg/core/uploader/providers/wangsu-certificate/wangsu_certificate_test.go @@ -0,0 +1,72 @@ +package jdcloudssl_test + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/wangsu-certificate" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKeyId string + fAccessKeySecret string +) + +func init() { + argsPrefix := "CERTIMATE_UPLOADER_JDCLOUDSSL_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "") +} + +/* +Shell command to run this test: + + go test -v ./wangsu_certificate_test.go -args \ + --CERTIMATE_UPLOADER_WANGSUCERTIFICATE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_UPLOADER_WANGSUCERTIFICATE_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_DEPLOYER_WANGSUCERTIFICATE_ACCESSKEYSECRET="your-access-key-secret" +*/ +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("ACCESSKEYID: %v", fAccessKeyId), + fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), + }, "\n")) + + uploader, err := provider.NewUploader(&provider.UploaderConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := uploader.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/internal/pkg/sdk3rd/1panel/client.go b/internal/pkg/sdk3rd/1panel/client.go index e8946bae..003203d3 100644 --- a/internal/pkg/sdk3rd/1panel/client.go +++ b/internal/pkg/sdk3rd/1panel/client.go @@ -14,19 +14,30 @@ import ( ) type Client struct { - apiHost string - apiKey string + apiKey string client *resty.Client } -func NewClient(apiHost, apiKey string) *Client { - client := resty.New() +func NewClient(apiHost, apiVersion, apiKey string) *Client { + if apiVersion == "" { + apiVersion = "v1" + } + + client := resty.New(). + SetBaseURL(strings.TrimRight(apiHost, "/") + "/api/" + apiVersion). + SetPreRequestHook(func(c *resty.Client, req *http.Request) error { + timestamp := fmt.Sprintf("%d", time.Now().Unix()) + tokenMd5 := md5.Sum([]byte("1panel" + apiKey + timestamp)) + tokenMd5Hex := hex.EncodeToString(tokenMd5[:]) + req.Header.Set("1Panel-Timestamp", timestamp) + req.Header.Set("1Panel-Token", tokenMd5Hex) + + return nil + }) return &Client{ - apiHost: strings.TrimRight(apiHost, "/"), - apiKey: apiKey, - client: client, + client: client, } } @@ -40,16 +51,8 @@ func (c *Client) WithTLSConfig(config *tls.Config) *Client { return c } -func (c *Client) generateToken(timestamp string) string { - tokenMd5 := md5.Sum([]byte("1panel" + c.apiKey + timestamp)) - tokenMd5Hex := hex.EncodeToString(tokenMd5[:]) - return tokenMd5Hex -} - func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { req := c.client.R() - req.Method = method - req.URL = c.apiHost + "/api/v1" + path if strings.EqualFold(method, http.MethodGet) { qs := make(map[string]string) if params != nil { @@ -65,17 +68,10 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r req = req.SetQueryParams(qs) } else { - req = req. - SetHeader("Content-Type", "application/json"). - SetBody(params) + req = req.SetHeader("Content-Type", "application/json").SetBody(params) } - timestamp := fmt.Sprintf("%d", time.Now().Unix()) - token := c.generateToken(timestamp) - req.SetHeader("1Panel-Timestamp", timestamp) - req.SetHeader("1Panel-Token", token) - - resp, err := req.Send() + resp, err := req.Execute(method, path) if err != nil { return resp, fmt.Errorf("1panel api error: failed to send request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/sdk3rd/baishan/client.go b/internal/pkg/sdk3rd/baishan/client.go index 81790a21..b3e428ee 100644 --- a/internal/pkg/sdk3rd/baishan/client.go +++ b/internal/pkg/sdk3rd/baishan/client.go @@ -13,17 +13,16 @@ import ( ) type Client struct { - apiToken string - client *resty.Client } func NewClient(apiToken string) *Client { - client := resty.New() + client := resty.New(). + SetBaseURL("https://cdn.api.baishan.com"). + SetHeader("token", apiToken) return &Client{ - apiToken: apiToken, - client: client, + client: client, } } @@ -34,8 +33,6 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client { func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { req := c.client.R() - req.Method = method - req.URL = "https://cdn.api.baishan.com" + path if strings.EqualFold(method, http.MethodGet) { qs := url.Values{} if params != nil { @@ -61,17 +58,12 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r } } - req = req. - SetQueryParam("token", c.apiToken). - SetQueryParamsFromValues(qs) + req = req.SetQueryParamsFromValues(qs) } else { - req = req. - SetHeader("Content-Type", "application/json"). - SetQueryParam("token", c.apiToken). - SetBody(params) + req = req.SetHeader("Content-Type", "application/json").SetBody(params) } - resp, err := req.Send() + resp, err := req.Execute(method, path) if err != nil { return resp, fmt.Errorf("baishan api error: failed to send request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/sdk3rd/btpanel/client.go b/internal/pkg/sdk3rd/btpanel/client.go index e09eed13..1da625da 100644 --- a/internal/pkg/sdk3rd/btpanel/client.go +++ b/internal/pkg/sdk3rd/btpanel/client.go @@ -14,19 +14,18 @@ import ( ) type Client struct { - apiHost string - apiKey string + apiKey string client *resty.Client } func NewClient(apiHost, apiKey string) *Client { - client := resty.New() + client := resty.New(). + SetBaseURL(strings.TrimRight(apiHost, "/")) return &Client{ - apiHost: strings.TrimRight(apiHost, "/"), - apiKey: apiKey, - client: client, + apiKey: apiKey, + client: client, } } @@ -78,11 +77,10 @@ func (c *Client) sendRequest(path string, params interface{}) (*resty.Response, data["request_time"] = fmt.Sprintf("%d", timestamp) data["request_token"] = c.generateSignature(fmt.Sprintf("%d", timestamp)) - url := c.apiHost + path req := c.client.R(). SetHeader("Content-Type", "application/x-www-form-urlencoded"). SetFormData(data) - resp, err := req.Post(url) + resp, err := req.Post(path) if err != nil { return resp, fmt.Errorf("baota api error: failed to send request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/sdk3rd/btwaf/api.go b/internal/pkg/sdk3rd/btwaf/api.go new file mode 100644 index 00000000..bc35dee5 --- /dev/null +++ b/internal/pkg/sdk3rd/btwaf/api.go @@ -0,0 +1,19 @@ +package btwaf + +func (c *Client) GetSiteList(req *GetSiteListRequest) (*GetSiteListResponse, error) { + resp := &GetSiteListResponse{} + err := c.sendRequestWithResult("/wafmastersite/get_site_list", req, resp) + return resp, err +} + +func (c *Client) ModifySite(req *ModifySiteRequest) (*ModifySiteResponse, error) { + resp := &ModifySiteResponse{} + err := c.sendRequestWithResult("/wafmastersite/modify_site", req, resp) + return resp, err +} + +func (c *Client) ConfigSetSSL(req *ConfigSetSSLRequest) (*ConfigSetSSLResponse, error) { + resp := &ConfigSetSSLResponse{} + err := c.sendRequestWithResult("/config/set_cert", req, resp) + return resp, err +} diff --git a/internal/pkg/sdk3rd/btwaf/client.go b/internal/pkg/sdk3rd/btwaf/client.go new file mode 100644 index 00000000..5ae545cc --- /dev/null +++ b/internal/pkg/sdk3rd/btwaf/client.go @@ -0,0 +1,77 @@ +package btwaf + +import ( + "crypto/md5" + "crypto/tls" + "encoding/hex" + "encoding/json" + "fmt" + "net/http" + "strings" + "time" + + "github.com/go-resty/resty/v2" +) + +type Client struct { + client *resty.Client +} + +func NewClient(apiHost, apiKey string) *Client { + client := resty.New(). + SetBaseURL(strings.TrimRight(apiHost, "/") + "/api"). + SetPreRequestHook(func(c *resty.Client, req *http.Request) error { + timestamp := fmt.Sprintf("%d", time.Now().Unix()) + keyMd5 := md5.Sum([]byte(apiKey)) + keyMd5Hex := strings.ToLower(hex.EncodeToString(keyMd5[:])) + signMd5 := md5.Sum([]byte(timestamp + keyMd5Hex)) + signMd5Hex := strings.ToLower(hex.EncodeToString(signMd5[:])) + req.Header.Set("waf_request_time", timestamp) + req.Header.Set("waf_request_token", signMd5Hex) + + return nil + }) + + return &Client{ + client: client, + } +} + +func (c *Client) WithTimeout(timeout time.Duration) *Client { + c.client.SetTimeout(timeout) + return c +} + +func (c *Client) WithTLSConfig(config *tls.Config) *Client { + c.client.SetTLSClientConfig(config) + return c +} + +func (c *Client) sendRequest(path string, params interface{}) (*resty.Response, error) { + req := c.client.R(). + SetHeader("Content-Type", "application/json"). + SetBody(params) + resp, err := req.Post(path) + if err != nil { + return resp, fmt.Errorf("baota api error: failed to send request: %w", err) + } else if resp.IsError() { + return resp, fmt.Errorf("baota api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String()) + } + + return resp, nil +} + +func (c *Client) sendRequestWithResult(path string, params interface{}, result BaseResponse) error { + resp, err := c.sendRequest(path, params) + if err != nil { + return err + } + + if err := json.Unmarshal(resp.Body(), &result); err != nil { + return fmt.Errorf("baota api error: failed to unmarshal response: %w", err) + } else if errcode := result.GetCode(); errcode != 0 { + return fmt.Errorf("baota api error: code='%d'", errcode) + } + + return nil +} diff --git a/internal/pkg/sdk3rd/btwaf/models.go b/internal/pkg/sdk3rd/btwaf/models.go new file mode 100644 index 00000000..16290e88 --- /dev/null +++ b/internal/pkg/sdk3rd/btwaf/models.go @@ -0,0 +1,67 @@ +package btwaf + +type BaseResponse interface { + GetCode() int32 +} + +type baseResponse struct { + Code *int32 `json:"code,omitempty"` +} + +func (r *baseResponse) GetCode() int32 { + if r.Code != nil { + return *r.Code + } + return 0 +} + +type GetSiteListRequest struct { + Page *int32 `json:"p,omitempty"` + PageSize *int32 `json:"p_size,omitempty"` + SiteName *string `json:"site_name,omitempty"` +} + +type GetSiteListResponse struct { + baseResponse + Result *struct { + List []*struct { + SiteId string `json:"site_id"` + SiteName string `json:"site_name"` + Type string `json:"types"` + Status int32 `json:"status"` + CreateTime int64 `json:"create_time"` + UpdateTime int64 `json:"update_time"` + } `json:"list"` + Total int32 `json:"total"` + } `json:"res,omitempty"` +} + +type SiteServerInfo struct { + ListenSSLPort *int32 `json:"listen_ssl_port,omitempty"` + SSL *SiteServerSSLInfo `json:"ssl,omitempty"` +} + +type SiteServerSSLInfo struct { + IsSSL *int32 `json:"is_ssl,omitempty"` + FullChain *string `json:"full_chain,omitempty"` + PrivateKey *string `json:"private_key,omitempty"` +} + +type ModifySiteRequest struct { + SiteId string `json:"site_id"` + Type *string `json:"types,omitempty"` + Server *SiteServerInfo `json:"server,omitempty"` +} + +type ModifySiteResponse struct { + baseResponse +} + +type ConfigSetSSLRequest struct { + CertContent string `json:"certContent"` + KeyContent string `json:"keyContent"` +} + +type ConfigSetSSLResponse struct { + baseResponse +} diff --git a/internal/pkg/sdk3rd/bunny/client.go b/internal/pkg/sdk3rd/bunny/client.go index 0f0dbea5..8d50e1fc 100644 --- a/internal/pkg/sdk3rd/bunny/client.go +++ b/internal/pkg/sdk3rd/bunny/client.go @@ -11,17 +11,16 @@ import ( ) type Client struct { - apiToken string - client *resty.Client } func NewClient(apiToken string) *Client { - client := resty.New() + client := resty.New(). + SetBaseURL("https://api.bunny.net"). + SetHeader("AccessKey", apiToken) return &Client{ - apiToken: apiToken, - client: client, + client: client, } } @@ -32,9 +31,6 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client { func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { req := c.client.R() - req.Method = method - req.URL = "https://api.bunny.net" + path - req = req.SetHeader("AccessKey", c.apiToken) if strings.EqualFold(method, http.MethodGet) { qs := make(map[string]string) if params != nil { @@ -50,12 +46,10 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r req = req.SetQueryParams(qs) } else { - req = req. - SetHeader("Content-Type", "application/json"). - SetBody(params) + req = req.SetHeader("Content-Type", "application/json").SetBody(params) } - resp, err := req.Send() + resp, err := req.Execute(method, path) if err != nil { return resp, fmt.Errorf("bunny api error: failed to send request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/sdk3rd/cachefly/client.go b/internal/pkg/sdk3rd/cachefly/client.go index 37a82ccc..342e329d 100644 --- a/internal/pkg/sdk3rd/cachefly/client.go +++ b/internal/pkg/sdk3rd/cachefly/client.go @@ -11,17 +11,16 @@ import ( ) type Client struct { - apiToken string - client *resty.Client } func NewClient(apiToken string) *Client { - client := resty.New() + client := resty.New(). + SetBaseURL("https://api.cachefly.com/api/2.5"). + SetHeader("x-cf-authorization", "Bearer "+apiToken) return &Client{ - apiToken: apiToken, - client: client, + client: client, } } @@ -32,9 +31,6 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client { func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { req := c.client.R() - req.Method = method - req.URL = "https://api.cachefly.com/api/2.5" + path - req = req.SetHeader("x-cf-authorization", "Bearer "+c.apiToken) if strings.EqualFold(method, http.MethodGet) { qs := make(map[string]string) if params != nil { @@ -50,12 +46,10 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r req = req.SetQueryParams(qs) } else { - req = req. - SetHeader("Content-Type", "application/json"). - SetBody(params) + req = req.SetHeader("Content-Type", "application/json").SetBody(params) } - resp, err := req.Send() + resp, err := req.Execute(method, path) if err != nil { return resp, fmt.Errorf("cachefly api error: failed to send request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/sdk3rd/cdnfly/client.go b/internal/pkg/sdk3rd/cdnfly/client.go index 5d80395c..c8753ed5 100644 --- a/internal/pkg/sdk3rd/cdnfly/client.go +++ b/internal/pkg/sdk3rd/cdnfly/client.go @@ -12,21 +12,17 @@ import ( ) type Client struct { - apiHost string - apiKey string - apiSecret string - client *resty.Client } func NewClient(apiHost, apiKey, apiSecret string) *Client { - client := resty.New() + client := resty.New(). + SetBaseURL(strings.TrimRight(apiHost, "/")). + SetHeader("api-key", apiKey). + SetHeader("api-secret", apiSecret) return &Client{ - apiHost: strings.TrimRight(apiHost, "/"), - apiKey: apiKey, - apiSecret: apiSecret, - client: client, + client: client, } } @@ -42,11 +38,6 @@ func (c *Client) WithTLSConfig(config *tls.Config) *Client { func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { req := c.client.R() - req.Method = method - req.URL = c.apiHost + path - req = req. - SetHeader("api-key", c.apiKey). - SetHeader("api-secret", c.apiSecret) if strings.EqualFold(method, http.MethodGet) { qs := make(map[string]string) if params != nil { @@ -62,12 +53,10 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r req = req.SetQueryParams(qs) } else { - req = req. - SetHeader("Content-Type", "application/json"). - SetBody(params) + req = req.SetHeader("Content-Type", "application/json").SetBody(params) } - resp, err := req.Send() + resp, err := req.Execute(method, path) if err != nil { return resp, fmt.Errorf("cdnfly api error: failed to send request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/sdk3rd/dnsla/client.go b/internal/pkg/sdk3rd/dnsla/client.go index a0659011..d9a86fc5 100644 --- a/internal/pkg/sdk3rd/dnsla/client.go +++ b/internal/pkg/sdk3rd/dnsla/client.go @@ -11,19 +11,16 @@ import ( ) type Client struct { - apiId string - apiSecret string - client *resty.Client } func NewClient(apiId, apiSecret string) *Client { - client := resty.New() + client := resty.New(). + SetBaseURL("https://api.dns.la/api"). + SetBasicAuth(apiId, apiSecret) return &Client{ - apiId: apiId, - apiSecret: apiSecret, - client: client, + client: client, } } @@ -33,9 +30,7 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client { } func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { - req := c.client.R().SetBasicAuth(c.apiId, c.apiSecret) - req.Method = method - req.URL = "https://api.dns.la/api" + path + req := c.client.R() if strings.EqualFold(method, http.MethodGet) { qs := make(map[string]string) if params != nil { @@ -51,12 +46,10 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r req = req.SetQueryParams(qs) } else { - req = req. - SetHeader("Content-Type", "application/json"). - SetBody(params) + req = req.SetHeader("Content-Type", "application/json").SetBody(params) } - resp, err := req.Send() + resp, err := req.Execute(method, path) if err != nil { return resp, fmt.Errorf("dnsla api error: failed to send request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/sdk3rd/dogecloud/client.go b/internal/pkg/sdk3rd/dogecloud/client.go index 46f3513d..75342907 100644 --- a/internal/pkg/sdk3rd/dogecloud/client.go +++ b/internal/pkg/sdk3rd/dogecloud/client.go @@ -164,8 +164,8 @@ func (c *Client) sendReq(method string, path string, data map[string]interface{} if err != nil { return nil, err } - req.Header.Add("Content-Type", mime) - req.Header.Add("Authorization", auth) + req.Header.Set("Content-Type", mime) + req.Header.Set("Authorization", auth) client := http.Client{} resp, err := client.Do(req) @@ -174,10 +174,10 @@ func (c *Client) sendReq(method string, path string, data map[string]interface{} } defer resp.Body.Close() - r, err := io.ReadAll(resp.Body) + bytes, err := io.ReadAll(resp.Body) if err != nil { return nil, err } - return r, nil + return bytes, nil } diff --git a/internal/pkg/sdk3rd/flexcdn/api.go b/internal/pkg/sdk3rd/flexcdn/api.go new file mode 100644 index 00000000..5008fdf4 --- /dev/null +++ b/internal/pkg/sdk3rd/flexcdn/api.go @@ -0,0 +1,48 @@ +package flexcdn + +import ( + "encoding/json" + "fmt" + "net/http" + "time" +) + +func (c *Client) ensureAccessTokenExists() error { + c.accessTokenMtx.Lock() + defer c.accessTokenMtx.Unlock() + if c.accessToken != "" && c.accessTokenExp.After(time.Now()) { + return nil + } + + req := &getAPIAccessTokenRequest{ + Type: c.apiRole, + AccessKeyId: c.accessKeyId, + AccessKey: c.accessKey, + } + res, err := c.sendRequest(http.MethodPost, "/APIAccessTokenService/getAPIAccessToken", req) + if err != nil { + return err + } + + resp := &getAPIAccessTokenResponse{} + if err := json.Unmarshal(res.Body(), &resp); err != nil { + return fmt.Errorf("flexcdn api error: failed to unmarshal response: %w", err) + } else if resp.GetCode() != 200 { + return fmt.Errorf("flexcdn get access token failed: code='%d', message='%s'", resp.GetCode(), resp.GetMessage()) + } + + c.accessToken = resp.Data.Token + c.accessTokenExp = time.Unix(resp.Data.ExpiresAt, 0) + + return nil +} + +func (c *Client) UpdateSSLCert(req *UpdateSSLCertRequest) (*UpdateSSLCertResponse, error) { + if err := c.ensureAccessTokenExists(); err != nil { + return nil, err + } + + resp := &UpdateSSLCertResponse{} + err := c.sendRequestWithResult(http.MethodPost, "/SSLCertService/updateSSLCert", req, resp) + return resp, err +} diff --git a/internal/pkg/sdk3rd/flexcdn/client.go b/internal/pkg/sdk3rd/flexcdn/client.go new file mode 100644 index 00000000..beae469a --- /dev/null +++ b/internal/pkg/sdk3rd/flexcdn/client.go @@ -0,0 +1,102 @@ +package flexcdn + +import ( + "crypto/tls" + "encoding/json" + "fmt" + "net/http" + "strings" + "sync" + "time" + + "github.com/go-resty/resty/v2" +) + +type Client struct { + apiRole string + accessKeyId string + accessKey string + + accessToken string + accessTokenExp time.Time + accessTokenMtx sync.Mutex + + client *resty.Client +} + +func NewClient(apiHost, apiRole, accessKeyId, accessKey string) *Client { + client := &Client{ + apiRole: apiRole, + accessKeyId: accessKeyId, + accessKey: accessKey, + } + client.client = resty.New(). + SetBaseURL(strings.TrimRight(apiHost, "/")). + SetPreRequestHook(func(c *resty.Client, req *http.Request) error { + if client.accessToken != "" { + req.Header.Set("X-Cloud-Access-Token", client.accessToken) + } + + return nil + }) + + return client +} + +func (c *Client) WithTimeout(timeout time.Duration) *Client { + c.client.SetTimeout(timeout) + return c +} + +func (c *Client) WithTLSConfig(config *tls.Config) *Client { + c.client.SetTLSClientConfig(config) + return c +} + +func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { + req := c.client.R() + if strings.EqualFold(method, http.MethodGet) { + qs := make(map[string]string) + if params != nil { + temp := make(map[string]any) + jsonb, _ := json.Marshal(params) + json.Unmarshal(jsonb, &temp) + for k, v := range temp { + if v != nil { + qs[k] = fmt.Sprintf("%v", v) + } + } + } + + req = req.SetQueryParams(qs) + } else { + req = req.SetHeader("Content-Type", "application/json").SetBody(params) + } + + resp, err := req.Execute(method, path) + if err != nil { + return resp, fmt.Errorf("flexcdn api error: failed to send request: %w", err) + } else if resp.IsError() { + return resp, fmt.Errorf("flexcdn api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String()) + } + + return resp, nil +} + +func (c *Client) sendRequestWithResult(method string, path string, params interface{}, result BaseResponse) error { + resp, err := c.sendRequest(method, path, params) + if err != nil { + if resp != nil { + json.Unmarshal(resp.Body(), &result) + } + return err + } + + if err := json.Unmarshal(resp.Body(), &result); err != nil { + return fmt.Errorf("flexcdn api error: failed to unmarshal response: %w", err) + } else if errcode := result.GetCode(); errcode != 200 { + return fmt.Errorf("flexcdn api error: code='%d', message='%s'", errcode, result.GetMessage()) + } + + return nil +} diff --git a/internal/pkg/sdk3rd/flexcdn/models.go b/internal/pkg/sdk3rd/flexcdn/models.go new file mode 100644 index 00000000..c976eccc --- /dev/null +++ b/internal/pkg/sdk3rd/flexcdn/models.go @@ -0,0 +1,52 @@ +package flexcdn + +type BaseResponse interface { + GetCode() int32 + GetMessage() string +} + +type baseResponse struct { + Code int32 `json:"code"` + Message string `json:"message"` +} + +func (r *baseResponse) GetCode() int32 { + return r.Code +} + +func (r *baseResponse) GetMessage() string { + return r.Message +} + +type getAPIAccessTokenRequest struct { + Type string `json:"type"` + AccessKeyId string `json:"accessKeyId"` + AccessKey string `json:"accessKey"` +} + +type getAPIAccessTokenResponse struct { + baseResponse + Data *struct { + Token string `json:"token"` + ExpiresAt int64 `json:"expiresAt"` + } `json:"data,omitempty"` +} + +type UpdateSSLCertRequest struct { + SSLCertId int64 `json:"sslCertId"` + IsOn bool `json:"isOn"` + Name string `json:"name"` + Description string `json:"description"` + ServerName string `json:"serverName"` + IsCA bool `json:"isCA"` + CertData string `json:"certData"` + KeyData string `json:"keyData"` + TimeBeginAt int64 `json:"timeBeginAt"` + TimeEndAt int64 `json:"timeEndAt"` + DNSNames []string `json:"dnsNames"` + CommonNames []string `json:"commonNames"` +} + +type UpdateSSLCertResponse struct { + baseResponse +} diff --git a/internal/pkg/sdk3rd/goedge/api.go b/internal/pkg/sdk3rd/goedge/api.go index f0f60944..4589f70c 100644 --- a/internal/pkg/sdk3rd/goedge/api.go +++ b/internal/pkg/sdk3rd/goedge/api.go @@ -7,7 +7,13 @@ import ( "time" ) -func (c *Client) getAccessToken() error { +func (c *Client) ensureAccessTokenExists() error { + c.accessTokenMtx.Lock() + defer c.accessTokenMtx.Unlock() + if c.accessToken != "" && c.accessTokenExp.After(time.Now()) { + return nil + } + req := &getAPIAccessTokenRequest{ Type: c.apiRole, AccessKeyId: c.accessKeyId, @@ -22,22 +28,18 @@ func (c *Client) getAccessToken() error { if err := json.Unmarshal(res.Body(), &resp); err != nil { return fmt.Errorf("goedge api error: failed to unmarshal response: %w", err) } else if resp.GetCode() != 200 { - return fmt.Errorf("goedge get access token failed: code: %d, message: %s", resp.GetCode(), resp.GetMessage()) + return fmt.Errorf("goedge get access token failed: code='%d', message='%s'", resp.GetCode(), resp.GetMessage()) } - c.accessTokenMtx.Lock() c.accessToken = resp.Data.Token c.accessTokenExp = time.Unix(resp.Data.ExpiresAt, 0) - c.accessTokenMtx.Unlock() return nil } func (c *Client) UpdateSSLCert(req *UpdateSSLCertRequest) (*UpdateSSLCertResponse, error) { - if c.accessToken == "" || c.accessTokenExp.Before(time.Now()) { - if err := c.getAccessToken(); err != nil { - return nil, err - } + if err := c.ensureAccessTokenExists(); err != nil { + return nil, err } resp := &UpdateSSLCertResponse{} diff --git a/internal/pkg/sdk3rd/goedge/client.go b/internal/pkg/sdk3rd/goedge/client.go index e0a2ce49..3dc961e3 100644 --- a/internal/pkg/sdk3rd/goedge/client.go +++ b/internal/pkg/sdk3rd/goedge/client.go @@ -13,7 +13,6 @@ import ( ) type Client struct { - apiHost string apiRole string accessKeyId string accessKey string @@ -26,15 +25,22 @@ type Client struct { } func NewClient(apiHost, apiRole, accessKeyId, accessKey string) *Client { - client := resty.New() - - return &Client{ - apiHost: strings.TrimRight(apiHost, "/"), + client := &Client{ apiRole: apiRole, accessKeyId: accessKeyId, accessKey: accessKey, - client: client, } + client.client = resty.New(). + SetBaseURL(strings.TrimRight(apiHost, "/")). + SetPreRequestHook(func(c *resty.Client, req *http.Request) error { + if client.accessToken != "" { + req.Header.Set("X-Edge-Access-Token", client.accessToken) + } + + return nil + }) + + return client } func (c *Client) WithTimeout(timeout time.Duration) *Client { @@ -48,9 +54,7 @@ func (c *Client) WithTLSConfig(config *tls.Config) *Client { } func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { - req := c.client.R().SetBasicAuth(c.accessKeyId, c.accessKey) - req.Method = method - req.URL = c.apiHost + path + req := c.client.R() if strings.EqualFold(method, http.MethodGet) { qs := make(map[string]string) if params != nil { @@ -64,17 +68,12 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r } } - req = req. - SetQueryParams(qs). - SetHeader("X-Edge-Access-Token", c.accessToken) + req = req.SetQueryParams(qs) } else { - req = req. - SetHeader("Content-Type", "application/json"). - SetHeader("X-Edge-Access-Token", c.accessToken). - SetBody(params) + req = req.SetHeader("Content-Type", "application/json").SetBody(params) } - resp, err := req.Send() + resp, err := req.Execute(method, path) if err != nil { return resp, fmt.Errorf("goedge api error: failed to send request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/sdk3rd/lecdn/v3/client/api.go b/internal/pkg/sdk3rd/lecdn/v3/client/api.go new file mode 100644 index 00000000..89f9cdc0 --- /dev/null +++ b/internal/pkg/sdk3rd/lecdn/v3/client/api.go @@ -0,0 +1,50 @@ +package client + +import ( + "encoding/json" + "fmt" + "net/http" +) + +func (c *Client) ensureAccessTokenExists() error { + c.accessTokenMtx.Lock() + defer c.accessTokenMtx.Unlock() + if c.accessToken != "" { + return nil + } + + req := &loginRequest{ + Email: c.username, + Username: c.username, + Password: c.password, + } + res, err := c.sendRequest(http.MethodPost, "/login", req) + if err != nil { + return err + } + + resp := &loginResponse{} + if err := json.Unmarshal(res.Body(), &resp); err != nil { + return fmt.Errorf("lecdn api error: failed to unmarshal response: %w", err) + } else if resp.GetCode() != 200 { + return fmt.Errorf("lecdn get token failed: code='%d', message='%s'", resp.GetCode(), resp.GetMessage()) + } + + c.accessToken = resp.Data.Token + + return nil +} + +func (c *Client) UpdateCertificate(certId int64, req *UpdateCertificateRequest) (*UpdateCertificateResponse, error) { + if certId == 0 { + return nil, fmt.Errorf("lecdn api error: invalid parameter: CertId") + } + + if err := c.ensureAccessTokenExists(); err != nil { + return nil, err + } + + resp := &UpdateCertificateResponse{} + err := c.sendRequestWithResult(http.MethodPut, fmt.Sprintf("/certificate/%d", certId), req, resp) + return resp, err +} diff --git a/internal/pkg/sdk3rd/lecdn/v3/client/client.go b/internal/pkg/sdk3rd/lecdn/v3/client/client.go new file mode 100644 index 00000000..ad3a752e --- /dev/null +++ b/internal/pkg/sdk3rd/lecdn/v3/client/client.go @@ -0,0 +1,99 @@ +package client + +import ( + "crypto/tls" + "encoding/json" + "fmt" + "net/http" + "strings" + "sync" + "time" + + "github.com/go-resty/resty/v2" +) + +type Client struct { + username string + password string + + accessToken string + accessTokenMtx sync.Mutex + + client *resty.Client +} + +func NewClient(apiHost, username, password string) *Client { + client := &Client{ + username: username, + password: password, + } + client.client = resty.New(). + SetBaseURL(strings.TrimRight(apiHost, "/") + "/prod-api"). + SetPreRequestHook(func(c *resty.Client, req *http.Request) error { + if client.accessToken != "" { + req.Header.Set("Authorization", "Bearer "+client.accessToken) + } + + return nil + }) + + return client +} + +func (c *Client) WithTimeout(timeout time.Duration) *Client { + c.client.SetTimeout(timeout) + return c +} + +func (c *Client) WithTLSConfig(config *tls.Config) *Client { + c.client.SetTLSClientConfig(config) + return c +} + +func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { + req := c.client.R() + if strings.EqualFold(method, http.MethodGet) { + qs := make(map[string]string) + if params != nil { + temp := make(map[string]any) + jsonb, _ := json.Marshal(params) + json.Unmarshal(jsonb, &temp) + for k, v := range temp { + if v != nil { + qs[k] = fmt.Sprintf("%v", v) + } + } + } + + req = req.SetQueryParams(qs) + } else { + req = req.SetHeader("Content-Type", "application/json").SetBody(params) + } + + resp, err := req.Execute(method, path) + if err != nil { + return resp, fmt.Errorf("lecdn api error: failed to send request: %w", err) + } else if resp.IsError() { + return resp, fmt.Errorf("lecdn api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String()) + } + + return resp, nil +} + +func (c *Client) sendRequestWithResult(method string, path string, params interface{}, result BaseResponse) error { + resp, err := c.sendRequest(method, path, params) + if err != nil { + if resp != nil { + json.Unmarshal(resp.Body(), &result) + } + return err + } + + if err := json.Unmarshal(resp.Body(), &result); err != nil { + return fmt.Errorf("lecdn api error: failed to unmarshal response: %w", err) + } else if errcode := result.GetCode(); errcode != 200 { + return fmt.Errorf("lecdn api error: code='%d', message='%s'", errcode, result.GetMessage()) + } + + return nil +} diff --git a/internal/pkg/sdk3rd/lecdn/v3/client/models.go b/internal/pkg/sdk3rd/lecdn/v3/client/models.go new file mode 100644 index 00000000..6d63ea79 --- /dev/null +++ b/internal/pkg/sdk3rd/lecdn/v3/client/models.go @@ -0,0 +1,47 @@ +package client + +type BaseResponse interface { + GetCode() int32 + GetMessage() string +} + +type baseResponse struct { + Code int32 `json:"code"` + Message string `json:"msg"` +} + +func (r *baseResponse) GetCode() int32 { + return r.Code +} + +func (r *baseResponse) GetMessage() string { + return r.Message +} + +type loginRequest struct { + Email string `json:"email"` + Username string `json:"username"` + Password string `json:"password"` +} + +type loginResponse struct { + baseResponse + Data *struct { + UserId int64 `json:"user_id"` + Username string `json:"username"` + Token string `json:"token"` + } `json:"data,omitempty"` +} + +type UpdateCertificateRequest struct { + Name string `json:"name"` + Description string `json:"description"` + Type string `json:"type"` + SSLPEM string `json:"ssl_pem"` + SSLKey string `json:"ssl_key"` + AutoRenewal bool `json:"auto_renewal"` +} + +type UpdateCertificateResponse struct { + baseResponse +} diff --git a/internal/pkg/sdk3rd/lecdn/v3/master/api.go b/internal/pkg/sdk3rd/lecdn/v3/master/api.go new file mode 100644 index 00000000..00f24a70 --- /dev/null +++ b/internal/pkg/sdk3rd/lecdn/v3/master/api.go @@ -0,0 +1,49 @@ +package master + +import ( + "encoding/json" + "fmt" + "net/http" +) + +func (c *Client) ensureAccessTokenExists() error { + c.accessTokenMtx.Lock() + defer c.accessTokenMtx.Unlock() + if c.accessToken != "" { + return nil + } + + req := &loginRequest{ + Username: c.username, + Password: c.password, + } + res, err := c.sendRequest(http.MethodPost, "/auth/login", req) + if err != nil { + return err + } + + resp := &loginResponse{} + if err := json.Unmarshal(res.Body(), &resp); err != nil { + return fmt.Errorf("lecdn api error: failed to unmarshal response: %w", err) + } else if resp.GetCode() != 200 { + return fmt.Errorf("lecdn get token failed: code='%d', message='%s'", resp.GetCode(), resp.GetMessage()) + } + + c.accessToken = resp.Data.Token + + return nil +} + +func (c *Client) UpdateCertificate(certId int64, req *UpdateCertificateRequest) (*UpdateCertificateResponse, error) { + if certId == 0 { + return nil, fmt.Errorf("lecdn api error: invalid parameter: CertId") + } + + if err := c.ensureAccessTokenExists(); err != nil { + return nil, err + } + + resp := &UpdateCertificateResponse{} + err := c.sendRequestWithResult(http.MethodPut, fmt.Sprintf("/certificate/%d", certId), req, resp) + return resp, err +} diff --git a/internal/pkg/sdk3rd/lecdn/v3/master/client.go b/internal/pkg/sdk3rd/lecdn/v3/master/client.go new file mode 100644 index 00000000..2da0c0c4 --- /dev/null +++ b/internal/pkg/sdk3rd/lecdn/v3/master/client.go @@ -0,0 +1,99 @@ +package master + +import ( + "crypto/tls" + "encoding/json" + "fmt" + "net/http" + "strings" + "sync" + "time" + + "github.com/go-resty/resty/v2" +) + +type Client struct { + username string + password string + + accessToken string + accessTokenMtx sync.Mutex + + client *resty.Client +} + +func NewClient(apiHost, username, password string) *Client { + client := &Client{ + username: username, + password: password, + } + client.client = resty.New(). + SetBaseURL(strings.TrimRight(apiHost, "/") + "/prod-api"). + SetPreRequestHook(func(c *resty.Client, req *http.Request) error { + if client.accessToken != "" { + req.Header.Set("Authorization", "Bearer "+client.accessToken) + } + + return nil + }) + + return client +} + +func (c *Client) WithTimeout(timeout time.Duration) *Client { + c.client.SetTimeout(timeout) + return c +} + +func (c *Client) WithTLSConfig(config *tls.Config) *Client { + c.client.SetTLSClientConfig(config) + return c +} + +func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { + req := c.client.R() + if strings.EqualFold(method, http.MethodGet) { + qs := make(map[string]string) + if params != nil { + temp := make(map[string]any) + jsonb, _ := json.Marshal(params) + json.Unmarshal(jsonb, &temp) + for k, v := range temp { + if v != nil { + qs[k] = fmt.Sprintf("%v", v) + } + } + } + + req = req.SetQueryParams(qs) + } else { + req = req.SetHeader("Content-Type", "application/json").SetBody(params) + } + + resp, err := req.Execute(method, path) + if err != nil { + return resp, fmt.Errorf("lecdn api error: failed to send request: %w", err) + } else if resp.IsError() { + return resp, fmt.Errorf("lecdn api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String()) + } + + return resp, nil +} + +func (c *Client) sendRequestWithResult(method string, path string, params interface{}, result BaseResponse) error { + resp, err := c.sendRequest(method, path, params) + if err != nil { + if resp != nil { + json.Unmarshal(resp.Body(), &result) + } + return err + } + + if err := json.Unmarshal(resp.Body(), &result); err != nil { + return fmt.Errorf("lecdn api error: failed to unmarshal response: %w", err) + } else if errcode := result.GetCode(); errcode != 200 { + return fmt.Errorf("lecdn api error: code='%d', message='%s'", errcode, result.GetMessage()) + } + + return nil +} diff --git a/internal/pkg/sdk3rd/lecdn/v3/master/models.go b/internal/pkg/sdk3rd/lecdn/v3/master/models.go new file mode 100644 index 00000000..2e896f42 --- /dev/null +++ b/internal/pkg/sdk3rd/lecdn/v3/master/models.go @@ -0,0 +1,47 @@ +package master + +type BaseResponse interface { + GetCode() int32 + GetMessage() string +} + +type baseResponse struct { + Code int32 `json:"code"` + Message string `json:"message"` +} + +func (r *baseResponse) GetCode() int32 { + return r.Code +} + +func (r *baseResponse) GetMessage() string { + return r.Message +} + +type loginRequest struct { + Username string `json:"username"` + Password string `json:"password"` +} + +type loginResponse struct { + baseResponse + Data *struct { + UserId int64 `json:"user_id"` + Username string `json:"username"` + Token string `json:"token"` + } `json:"data,omitempty"` +} + +type UpdateCertificateRequest struct { + ClientId int64 `json:"client_id"` + Name string `json:"name"` + Description string `json:"description"` + Type string `json:"type"` + SSLPEM string `json:"ssl_pem"` + SSLKey string `json:"ssl_key"` + AutoRenewal bool `json:"auto_renewal"` +} + +type UpdateCertificateResponse struct { + baseResponse +} diff --git a/internal/pkg/sdk3rd/netlify/client.go b/internal/pkg/sdk3rd/netlify/client.go index e3110077..d270e35e 100644 --- a/internal/pkg/sdk3rd/netlify/client.go +++ b/internal/pkg/sdk3rd/netlify/client.go @@ -11,17 +11,16 @@ import ( ) type Client struct { - apiToken string - client *resty.Client } func NewClient(apiToken string) *Client { - client := resty.New() + client := resty.New(). + SetBaseURL("https://api.netlify.com/api/v1"). + SetHeader("Authorization", "Bearer "+apiToken) return &Client{ - apiToken: apiToken, - client: client, + client: client, } } @@ -31,9 +30,7 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client { } func (c *Client) sendRequest(method string, path string, queryParams interface{}, payloadParams interface{}) (*resty.Response, error) { - req := c.client.R().SetHeader("Authorization", "Bearer "+c.apiToken) - req.Method = method - req.URL = "https://api.netlify.com/api/v1" + path + req := c.client.R() if queryParams != nil { qs := make(map[string]string) @@ -63,12 +60,10 @@ func (c *Client) sendRequest(method string, path string, queryParams interface{} req = req.SetQueryParams(qs) } else { - req = req. - SetHeader("Content-Type", "application/json"). - SetBody(payloadParams) + req = req.SetHeader("Content-Type", "application/json").SetBody(payloadParams) } - resp, err := req.Send() + resp, err := req.Execute(method, path) if err != nil { return resp, fmt.Errorf("netlify api error: failed to send request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/sdk3rd/rainyun/client.go b/internal/pkg/sdk3rd/rainyun/client.go index eb34efd1..80113f0d 100644 --- a/internal/pkg/sdk3rd/rainyun/client.go +++ b/internal/pkg/sdk3rd/rainyun/client.go @@ -11,16 +11,15 @@ import ( ) type Client struct { - apiKey string - client *resty.Client } func NewClient(apiKey string) *Client { - client := resty.New() + client := resty.New(). + SetBaseURL("https://api.v2.rainyun.com"). + SetHeader("x-api-key", apiKey) return &Client{ - apiKey: apiKey, client: client, } } @@ -31,21 +30,17 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client { } func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { - req := c.client.R().SetHeader("x-api-key", c.apiKey) - req.Method = method - req.URL = "https://api.v2.rainyun.com" + path + req := c.client.R() if strings.EqualFold(method, http.MethodGet) { if params != nil { jsonb, _ := json.Marshal(params) req = req.SetQueryParam("options", string(jsonb)) } } else { - req = req. - SetHeader("Content-Type", "application/json"). - SetBody(params) + req = req.SetHeader("Content-Type", "application/json").SetBody(params) } - resp, err := req.Send() + resp, err := req.Execute(method, path) if err != nil { return resp, fmt.Errorf("rainyun api error: failed to send request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/sdk3rd/ratpanel/client.go b/internal/pkg/sdk3rd/ratpanel/client.go index e0562410..47202a04 100644 --- a/internal/pkg/sdk3rd/ratpanel/client.go +++ b/internal/pkg/sdk3rd/ratpanel/client.go @@ -20,7 +20,7 @@ type Client struct { client *resty.Client } -func NewClient(apiHost string, accessTokenId uint, accessToken string) *Client { +func NewClient(apiHost string, accessTokenId int32, accessToken string) *Client { client := resty.New(). SetBaseURL(strings.TrimRight(apiHost, "/")+"/api"). SetHeader("Accept", "application/json"). @@ -81,8 +81,6 @@ func (c *Client) WithTLSConfig(config *tls.Config) *Client { func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { req := c.client.R() - req.Method = method - req.URL = path if strings.EqualFold(method, http.MethodGet) { qs := make(map[string]string) if params != nil { @@ -98,12 +96,10 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r req = req.SetQueryParams(qs) } else { - req = req. - SetHeader("Content-Type", "application/json"). - SetBody(params) + req = req.SetHeader("Content-Type", "application/json").SetBody(params) } - resp, err := req.Send() + resp, err := req.Execute(method, path) if err != nil { return resp, fmt.Errorf("ratpanel api error: failed to send request: %w", err) } else if resp.IsError() { @@ -123,9 +119,9 @@ func (c *Client) sendRequestWithResult(method string, path string, params interf } if err = json.Unmarshal(resp.Body(), &result); err != nil { - return fmt.Errorf("ratpanel api error: failed to parse response: %w", err) - } else if errmessage := result.GetMessage(); errmessage != "success" { - return fmt.Errorf("ratpanel api error: %d - %s", resp.StatusCode(), errmessage) + return fmt.Errorf("ratpanel api error: failed to unmarshal response: %w", err) + } else if errmsg := result.GetMessage(); errmsg != "success" { + return fmt.Errorf("ratpanel api error: message='%s'", errmsg) } return nil diff --git a/internal/pkg/sdk3rd/safeline/client.go b/internal/pkg/sdk3rd/safeline/client.go index 0412514c..efcd3bd6 100644 --- a/internal/pkg/sdk3rd/safeline/client.go +++ b/internal/pkg/sdk3rd/safeline/client.go @@ -11,19 +11,16 @@ import ( ) type Client struct { - apiHost string - apiToken string - client *resty.Client } func NewClient(apiHost, apiToken string) *Client { - client := resty.New() + client := resty.New(). + SetBaseURL(strings.TrimRight(apiHost, "/")). + SetHeader("X-SLCE-API-TOKEN", apiToken) return &Client{ - apiHost: strings.TrimRight(apiHost, "/"), - apiToken: apiToken, - client: client, + client: client, } } @@ -38,12 +35,10 @@ func (c *Client) WithTLSConfig(config *tls.Config) *Client { } func (c *Client) sendRequest(path string, params interface{}) (*resty.Response, error) { - url := c.apiHost + path req := c.client.R(). SetHeader("Content-Type", "application/json"). - SetHeader("X-SLCE-API-TOKEN", c.apiToken). SetBody(params) - resp, err := req.Post(url) + resp, err := req.Post(path) if err != nil { return resp, fmt.Errorf("safeline api error: failed to send request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/sdk3rd/upyun/console/api.go b/internal/pkg/sdk3rd/upyun/console/api.go index 32b4626e..ce62d3a6 100644 --- a/internal/pkg/sdk3rd/upyun/console/api.go +++ b/internal/pkg/sdk3rd/upyun/console/api.go @@ -7,7 +7,11 @@ import ( "net/http" ) -func (c *Client) getCookie() error { +func (c *Client) ensureCookieExists() error { + if c.loginCookie != "" { + return nil + } + req := &signinRequest{Username: c.username, Password: c.password} res, err := c.sendRequest(http.MethodPost, "/accounts/signin/", req) if err != nil { @@ -27,10 +31,8 @@ func (c *Client) getCookie() error { } func (c *Client) UploadHttpsCertificate(req *UploadHttpsCertificateRequest) (*UploadHttpsCertificateResponse, error) { - if c.loginCookie == "" { - if err := c.getCookie(); err != nil { - return nil, err - } + if err := c.ensureCookieExists(); err != nil { + return nil, err } resp := &UploadHttpsCertificateResponse{} @@ -39,10 +41,8 @@ func (c *Client) UploadHttpsCertificate(req *UploadHttpsCertificateRequest) (*Up } func (c *Client) GetHttpsCertificateManager(certificateId string) (*GetHttpsCertificateManagerResponse, error) { - if c.loginCookie == "" { - if err := c.getCookie(); err != nil { - return nil, err - } + if err := c.ensureCookieExists(); err != nil { + return nil, err } req := &GetHttpsCertificateManagerRequest{CertificateId: certificateId} @@ -52,10 +52,8 @@ func (c *Client) GetHttpsCertificateManager(certificateId string) (*GetHttpsCert } func (c *Client) UpdateHttpsCertificateManager(req *UpdateHttpsCertificateManagerRequest) (*UpdateHttpsCertificateManagerResponse, error) { - if c.loginCookie == "" { - if err := c.getCookie(); err != nil { - return nil, err - } + if err := c.ensureCookieExists(); err != nil { + return nil, err } resp := &UpdateHttpsCertificateManagerResponse{} @@ -64,10 +62,8 @@ func (c *Client) UpdateHttpsCertificateManager(req *UpdateHttpsCertificateManage } func (c *Client) GetHttpsServiceManager(domain string) (*GetHttpsServiceManagerResponse, error) { - if c.loginCookie == "" { - if err := c.getCookie(); err != nil { - return nil, err - } + if err := c.ensureCookieExists(); err != nil { + return nil, err } req := &GetHttpsServiceManagerRequest{Domain: domain} @@ -77,10 +73,8 @@ func (c *Client) GetHttpsServiceManager(domain string) (*GetHttpsServiceManagerR } func (c *Client) MigrateHttpsDomain(req *MigrateHttpsDomainRequest) (*MigrateHttpsDomainResponse, error) { - if c.loginCookie == "" { - if err := c.getCookie(); err != nil { - return nil, err - } + if err := c.ensureCookieExists(); err != nil { + return nil, err } resp := &MigrateHttpsDomainResponse{} diff --git a/internal/pkg/sdk3rd/upyun/console/client.go b/internal/pkg/sdk3rd/upyun/console/client.go index 7a7ea7de..b207549e 100644 --- a/internal/pkg/sdk3rd/upyun/console/client.go +++ b/internal/pkg/sdk3rd/upyun/console/client.go @@ -20,13 +20,21 @@ type Client struct { } func NewClient(username, password string) *Client { - client := resty.New() - - return &Client{ + client := &Client{ username: username, password: password, - client: client, } + client.client = resty.New(). + SetBaseURL("https://console.upyun.com"). + SetPreRequestHook(func(c *resty.Client, req *http.Request) error { + if client.loginCookie != "" { + req.Header.Set("Cookie", client.loginCookie) + } + + return nil + }) + + return client } func (c *Client) WithTimeout(timeout time.Duration) *Client { @@ -35,9 +43,7 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client { } func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { - req := c.client.R().SetBasicAuth(c.username, c.password) - req.Method = method - req.URL = "https://console.upyun.com" + path + req := c.client.R() if strings.EqualFold(method, http.MethodGet) { qs := make(map[string]string) if params != nil { @@ -51,17 +57,12 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r } } - req = req. - SetQueryParams(qs). - SetHeader("Cookie", c.loginCookie) + req = req.SetQueryParams(qs) } else { - req = req. - SetHeader("Content-Type", "application/json"). - SetHeader("Cookie", c.loginCookie). - SetBody(params) + req = req.SetHeader("Content-Type", "application/json").SetBody(params) } - resp, err := req.Send() + resp, err := req.Execute(method, path) if err != nil { return resp, fmt.Errorf("upyun api error: failed to send request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/sdk3rd/wangsu/cdn/api.go b/internal/pkg/sdk3rd/wangsu/cdn/api.go index 0da647c8..997c05bf 100644 --- a/internal/pkg/sdk3rd/wangsu/cdn/api.go +++ b/internal/pkg/sdk3rd/wangsu/cdn/api.go @@ -1,70 +1,15 @@ package cdn import ( - "fmt" "net/http" - "net/url" - - "github.com/go-resty/resty/v2" ) -func (c *Client) CreateCertificate(req *CreateCertificateRequest) (*CreateCertificateResponse, error) { - resp := &CreateCertificateResponse{} - r, err := c.client.SendRequestWithResult(http.MethodPost, "/cdn/certificates", req, resp, func(r *resty.Request) { - r.SetHeader("x-cnc-timestamp", fmt.Sprintf("%d", req.Timestamp)) - }) +func (c *Client) BatchUpdateCertificateConfig(req *BatchUpdateCertificateConfigRequest) (*BatchUpdateCertificateConfigResponse, error) { + resp := &BatchUpdateCertificateConfigResponse{} + _, err := c.client.SendRequestWithResult(http.MethodPut, "/api/config/certificate/batch", req, resp) if err != nil { return resp, err } - resp.CertificateUrl = r.Header().Get("Location") - return resp, err -} - -func (c *Client) UpdateCertificate(certificateId string, req *UpdateCertificateRequest) (*UpdateCertificateResponse, error) { - if certificateId == "" { - return nil, fmt.Errorf("wangsu api error: invalid parameter: certificateId") - } - - resp := &UpdateCertificateResponse{} - r, err := c.client.SendRequestWithResult(http.MethodPatch, fmt.Sprintf("/cdn/certificates/%s", url.PathEscape(certificateId)), req, resp, func(r *resty.Request) { - r.SetHeader("x-cnc-timestamp", fmt.Sprintf("%d", req.Timestamp)) - }) - if err != nil { - return resp, err - } - - resp.CertificateUrl = r.Header().Get("Location") - return resp, err -} - -func (c *Client) GetHostnameDetail(hostname string) (*GetHostnameDetailResponse, error) { - if hostname == "" { - return nil, fmt.Errorf("wangsu api error: invalid parameter: hostname") - } - - resp := &GetHostnameDetailResponse{} - _, err := c.client.SendRequestWithResult(http.MethodGet, fmt.Sprintf("/cdn/hostnames/%s", url.PathEscape(hostname)), nil, resp) - return resp, err -} - -func (c *Client) CreateDeploymentTask(req *CreateDeploymentTaskRequest) (*CreateDeploymentTaskResponse, error) { - resp := &CreateDeploymentTaskResponse{} - r, err := c.client.SendRequestWithResult(http.MethodPost, "/cdn/deploymentTasks", req, resp) - if err != nil { - return resp, err - } - - resp.DeploymentTaskUrl = r.Header().Get("Location") - return resp, err -} - -func (c *Client) GetDeploymentTaskDetail(deploymentTaskId string) (*GetDeploymentTaskDetailResponse, error) { - if deploymentTaskId == "" { - return nil, fmt.Errorf("wangsu api error: invalid parameter: deploymentTaskId") - } - - resp := &GetDeploymentTaskDetailResponse{} - _, err := c.client.SendRequestWithResult(http.MethodGet, fmt.Sprintf("/cdn/deploymentTasks/%s", url.PathEscape(deploymentTaskId)), nil, resp) return resp, err } diff --git a/internal/pkg/sdk3rd/wangsu/cdn/models.go b/internal/pkg/sdk3rd/wangsu/cdn/models.go index a9a9ec74..5bf934af 100644 --- a/internal/pkg/sdk3rd/wangsu/cdn/models.go +++ b/internal/pkg/sdk3rd/wangsu/cdn/models.go @@ -5,7 +5,7 @@ import ( ) type baseResponse struct { - RequestId *string `json:"-"` + RequestId *string `json:"requestId,omitempty"` Code *string `json:"code,omitempty"` Message *string `json:"message,omitempty"` } @@ -16,93 +16,11 @@ func (r *baseResponse) SetRequestId(requestId string) { r.RequestId = &requestId } -type CertificateVersion struct { - Comments *string `json:"comments,omitempty"` - PrivateKey *string `json:"privateKey,omitempty"` - Certificate *string `json:"certificate,omitempty"` - ChainCert *string `json:"chainCert,omitempty"` - IdentificationInfo *CertificateVersionIdentificationInfo `json:"identificationInfo,omitempty"` +type BatchUpdateCertificateConfigRequest struct { + CertificateId int64 `json:"certificateId" required:"true"` + DomainNames []string `json:"domainNames" required:"true"` } -type CertificateVersionIdentificationInfo struct { - Country *string `json:"country,omitempty"` - State *string `json:"state,omitempty"` - City *string `json:"city,omitempty"` - Company *string `json:"company,omitempty"` - Department *string `json:"department,omitempty"` - CommonName *string `json:"commonName,omitempty" required:"true"` - Email *string `json:"email,omitempty"` - SubjectAlternativeNames *[]string `json:"subjectAlternativeNames,omitempty" required:"true"` -} - -type CreateCertificateRequest struct { - Timestamp int64 `json:"-"` - Name *string `json:"name,omitempty" required:"true"` - Description *string `json:"description,omitempty"` - AutoRenew *string `json:"autoRenew,omitempty"` - ForceRenew *bool `json:"forceRenew,omitempty"` - NewVersion *CertificateVersion `json:"newVersion,omitempty" required:"true"` -} - -type CreateCertificateResponse struct { +type BatchUpdateCertificateConfigResponse struct { baseResponse - CertificateUrl string `json:"location,omitempty"` -} - -type UpdateCertificateRequest struct { - Timestamp int64 `json:"-"` - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - AutoRenew *string `json:"autoRenew,omitempty"` - ForceRenew *bool `json:"forceRenew,omitempty"` - NewVersion *CertificateVersion `json:"newVersion,omitempty" required:"true"` -} - -type UpdateCertificateResponse struct { - baseResponse - CertificateUrl string `json:"location,omitempty"` -} - -type HostnameProperty struct { - PropertyId string `json:"propertyId"` - Version int32 `json:"version"` - CertificateId *string `json:"certificateId,omitempty"` -} - -type GetHostnameDetailResponse struct { - baseResponse - Hostname string `json:"hostname"` - PropertyInProduction *HostnameProperty `json:"propertyInProduction,omitempty"` - PropertyInStaging *HostnameProperty `json:"propertyInStaging,omitempty"` -} - -type DeploymentTaskAction struct { - Action *string `json:"action,omitempty" required:"true"` - PropertyId *string `json:"propertyId,omitempty"` - CertificateId *string `json:"certificateId,omitempty"` - Version *int32 `json:"version,omitempty"` -} - -type CreateDeploymentTaskRequest struct { - Name *string `json:"name,omitempty"` - Target *string `json:"target,omitempty" required:"true"` - Actions *[]DeploymentTaskAction `json:"actions,omitempty" required:"true"` - Webhook *string `json:"webhook,omitempty"` -} - -type CreateDeploymentTaskResponse struct { - baseResponse - DeploymentTaskUrl string `json:"location,omitempty"` -} - -type GetDeploymentTaskDetailResponse struct { - baseResponse - Name string `json:"name"` - Target string `json:"target"` - Actions []DeploymentTaskAction `json:"actions"` - Status string `json:"status"` - StatusDetails string `json:"statusDetails"` - SubmissionTime string `json:"submissionTime"` - FinishTime string `json:"finishTime"` - ApiRequestId string `json:"apiRequestId"` } diff --git a/internal/pkg/sdk3rd/wangsu/cdnpro/api.go b/internal/pkg/sdk3rd/wangsu/cdnpro/api.go new file mode 100644 index 00000000..c6f8da04 --- /dev/null +++ b/internal/pkg/sdk3rd/wangsu/cdnpro/api.go @@ -0,0 +1,70 @@ +package cdnpro + +import ( + "fmt" + "net/http" + "net/url" + + "github.com/go-resty/resty/v2" +) + +func (c *Client) CreateCertificate(req *CreateCertificateRequest) (*CreateCertificateResponse, error) { + resp := &CreateCertificateResponse{} + rres, err := c.client.SendRequestWithResult(http.MethodPost, "/cdn/certificates", req, resp, func(r *resty.Request) { + r.SetHeader("x-cnc-timestamp", fmt.Sprintf("%d", req.Timestamp)) + }) + if err != nil { + return resp, err + } + + resp.CertificateUrl = rres.Header().Get("Location") + return resp, err +} + +func (c *Client) UpdateCertificate(certificateId string, req *UpdateCertificateRequest) (*UpdateCertificateResponse, error) { + if certificateId == "" { + return nil, fmt.Errorf("wangsu api error: invalid parameter: certificateId") + } + + resp := &UpdateCertificateResponse{} + rres, err := c.client.SendRequestWithResult(http.MethodPatch, fmt.Sprintf("/cdn/certificates/%s", url.PathEscape(certificateId)), req, resp, func(r *resty.Request) { + r.SetHeader("x-cnc-timestamp", fmt.Sprintf("%d", req.Timestamp)) + }) + if err != nil { + return resp, err + } + + resp.CertificateUrl = rres.Header().Get("Location") + return resp, err +} + +func (c *Client) GetHostnameDetail(hostname string) (*GetHostnameDetailResponse, error) { + if hostname == "" { + return nil, fmt.Errorf("wangsu api error: invalid parameter: hostname") + } + + resp := &GetHostnameDetailResponse{} + _, err := c.client.SendRequestWithResult(http.MethodGet, fmt.Sprintf("/cdn/hostnames/%s", url.PathEscape(hostname)), nil, resp) + return resp, err +} + +func (c *Client) CreateDeploymentTask(req *CreateDeploymentTaskRequest) (*CreateDeploymentTaskResponse, error) { + resp := &CreateDeploymentTaskResponse{} + rres, err := c.client.SendRequestWithResult(http.MethodPost, "/cdn/deploymentTasks", req, resp) + if err != nil { + return resp, err + } + + resp.DeploymentTaskUrl = rres.Header().Get("Location") + return resp, err +} + +func (c *Client) GetDeploymentTaskDetail(deploymentTaskId string) (*GetDeploymentTaskDetailResponse, error) { + if deploymentTaskId == "" { + return nil, fmt.Errorf("wangsu api error: invalid parameter: deploymentTaskId") + } + + resp := &GetDeploymentTaskDetailResponse{} + _, err := c.client.SendRequestWithResult(http.MethodGet, fmt.Sprintf("/cdn/deploymentTasks/%s", url.PathEscape(deploymentTaskId)), nil, resp) + return resp, err +} diff --git a/internal/pkg/sdk3rd/wangsu/cdnpro/client.go b/internal/pkg/sdk3rd/wangsu/cdnpro/client.go new file mode 100644 index 00000000..b5c0f530 --- /dev/null +++ b/internal/pkg/sdk3rd/wangsu/cdnpro/client.go @@ -0,0 +1,20 @@ +package cdnpro + +import ( + "time" + + "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/openapi" +) + +type Client struct { + client *openapi.Client +} + +func NewClient(accessKey, secretKey string) *Client { + return &Client{client: openapi.NewClient(accessKey, secretKey)} +} + +func (c *Client) WithTimeout(timeout time.Duration) *Client { + c.client.WithTimeout(timeout) + return c +} diff --git a/internal/pkg/sdk3rd/wangsu/cdnpro/models.go b/internal/pkg/sdk3rd/wangsu/cdnpro/models.go new file mode 100644 index 00000000..9cb1e648 --- /dev/null +++ b/internal/pkg/sdk3rd/wangsu/cdnpro/models.go @@ -0,0 +1,108 @@ +package cdnpro + +import ( + "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/openapi" +) + +type baseResponse struct { + RequestId *string `json:"requestId,omitempty"` + Code *string `json:"code,omitempty"` + Message *string `json:"message,omitempty"` +} + +var _ openapi.Result = (*baseResponse)(nil) + +func (r *baseResponse) SetRequestId(requestId string) { + r.RequestId = &requestId +} + +type CertificateVersion struct { + Comments *string `json:"comments,omitempty"` + PrivateKey *string `json:"privateKey,omitempty"` + Certificate *string `json:"certificate,omitempty"` + ChainCert *string `json:"chainCert,omitempty"` + IdentificationInfo *CertificateVersionIdentificationInfo `json:"identificationInfo,omitempty"` +} + +type CertificateVersionIdentificationInfo struct { + Country *string `json:"country,omitempty"` + State *string `json:"state,omitempty"` + City *string `json:"city,omitempty"` + Company *string `json:"company,omitempty"` + Department *string `json:"department,omitempty"` + CommonName *string `json:"commonName,omitempty" required:"true"` + Email *string `json:"email,omitempty"` + SubjectAlternativeNames *[]string `json:"subjectAlternativeNames,omitempty" required:"true"` +} + +type CreateCertificateRequest struct { + Timestamp int64 `json:"-"` + Name *string `json:"name,omitempty" required:"true"` + Description *string `json:"description,omitempty"` + AutoRenew *string `json:"autoRenew,omitempty"` + ForceRenew *bool `json:"forceRenew,omitempty"` + NewVersion *CertificateVersion `json:"newVersion,omitempty" required:"true"` +} + +type CreateCertificateResponse struct { + baseResponse + CertificateUrl string `json:"location,omitempty"` +} + +type UpdateCertificateRequest struct { + Timestamp int64 `json:"-"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + AutoRenew *string `json:"autoRenew,omitempty"` + ForceRenew *bool `json:"forceRenew,omitempty"` + NewVersion *CertificateVersion `json:"newVersion,omitempty" required:"true"` +} + +type UpdateCertificateResponse struct { + baseResponse + CertificateUrl string `json:"location,omitempty"` +} + +type HostnameProperty struct { + PropertyId string `json:"propertyId"` + Version int32 `json:"version"` + CertificateId *string `json:"certificateId,omitempty"` +} + +type GetHostnameDetailResponse struct { + baseResponse + Hostname string `json:"hostname"` + PropertyInProduction *HostnameProperty `json:"propertyInProduction,omitempty"` + PropertyInStaging *HostnameProperty `json:"propertyInStaging,omitempty"` +} + +type DeploymentTaskAction struct { + Action *string `json:"action,omitempty" required:"true"` + PropertyId *string `json:"propertyId,omitempty"` + CertificateId *string `json:"certificateId,omitempty"` + Version *int32 `json:"version,omitempty"` +} + +type CreateDeploymentTaskRequest struct { + Name *string `json:"name,omitempty"` + Target *string `json:"target,omitempty" required:"true"` + Actions *[]DeploymentTaskAction `json:"actions,omitempty" required:"true"` + Webhook *string `json:"webhook,omitempty"` +} + +type CreateDeploymentTaskResponse struct { + baseResponse + DeploymentTaskUrl string `json:"location,omitempty"` +} + +type GetDeploymentTaskDetailResponse struct { + baseResponse + Name string `json:"name"` + Target string `json:"target"` + Actions []DeploymentTaskAction `json:"actions"` + Status string `json:"status"` + StatusDetails string `json:"statusDetails"` + SubmissionTime string `json:"submissionTime"` + FinishTime string `json:"finishTime"` + ApiRequestId string `json:"apiRequestId"` +} diff --git a/internal/pkg/sdk3rd/wangsu/certificate/api.go b/internal/pkg/sdk3rd/wangsu/certificate/api.go new file mode 100644 index 00000000..037fb6e7 --- /dev/null +++ b/internal/pkg/sdk3rd/wangsu/certificate/api.go @@ -0,0 +1,42 @@ +package certificate + +import ( + "fmt" + "net/http" + "net/url" +) + +func (c *Client) ListCertificates() (*ListCertificatesResponse, error) { + resp := &ListCertificatesResponse{} + _, err := c.client.SendRequestWithResult(http.MethodGet, "/api/certificate", nil, resp) + if err != nil { + return resp, err + } + + return resp, err +} + +func (c *Client) CreateCertificate(req *CreateCertificateRequest) (*CreateCertificateResponse, error) { + resp := &CreateCertificateResponse{} + rres, err := c.client.SendRequestWithResult(http.MethodPost, "/api/certificate", req, resp) + if err != nil { + return resp, err + } + + resp.CertificateUrl = rres.Header().Get("Location") + return resp, err +} + +func (c *Client) UpdateCertificate(certificateId string, req *UpdateCertificateRequest) (*UpdateCertificateResponse, error) { + if certificateId == "" { + return nil, fmt.Errorf("wangsu api error: invalid parameter: certificateId") + } + + resp := &UpdateCertificateResponse{} + _, err := c.client.SendRequestWithResult(http.MethodPut, fmt.Sprintf("/api/certificate/%s", url.PathEscape(certificateId)), req, resp) + if err != nil { + return resp, err + } + + return resp, err +} diff --git a/internal/pkg/sdk3rd/wangsu/certificate/client.go b/internal/pkg/sdk3rd/wangsu/certificate/client.go new file mode 100644 index 00000000..19f4cfaa --- /dev/null +++ b/internal/pkg/sdk3rd/wangsu/certificate/client.go @@ -0,0 +1,20 @@ +package certificate + +import ( + "time" + + "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/openapi" +) + +type Client struct { + client *openapi.Client +} + +func NewClient(accessKey, secretKey string) *Client { + return &Client{client: openapi.NewClient(accessKey, secretKey)} +} + +func (c *Client) WithTimeout(timeout time.Duration) *Client { + c.client.WithTimeout(timeout) + return c +} diff --git a/internal/pkg/sdk3rd/wangsu/certificate/models.go b/internal/pkg/sdk3rd/wangsu/certificate/models.go new file mode 100644 index 00000000..4e882e7c --- /dev/null +++ b/internal/pkg/sdk3rd/wangsu/certificate/models.go @@ -0,0 +1,52 @@ +package certificate + +import ( + "github.com/usual2970/certimate/internal/pkg/sdk3rd/wangsu/openapi" +) + +type baseResponse struct { + RequestId *string `json:"requestId,omitempty"` + Code *string `json:"code,omitempty"` + Message *string `json:"message,omitempty"` +} + +var _ openapi.Result = (*baseResponse)(nil) + +func (r *baseResponse) SetRequestId(requestId string) { + r.RequestId = &requestId +} + +type CreateCertificateRequest struct { + Name *string `json:"name,omitempty" required:"true"` + Certificate *string `json:"certificate,omitempty" required:"true"` + PrivateKey *string `json:"privateKey,omitempty"` + Comment *string `json:"comment,omitempty" ` +} + +type CreateCertificateResponse struct { + baseResponse + CertificateUrl string `json:"location,omitempty"` +} + +type UpdateCertificateRequest struct { + Name *string `json:"name,omitempty" required:"true"` + Certificate *string `json:"certificate,omitempty"` + PrivateKey *string `json:"privateKey,omitempty"` + Comment *string `json:"comment,omitempty" ` +} + +type UpdateCertificateResponse struct { + baseResponse +} + +type ListCertificatesResponse struct { + baseResponse + Certificates []*struct { + CertificateId string `json:"certificate-id"` + Name string `json:"name"` + Comment string `json:"comment"` + ValidityFrom string `json:"certificate-validity-from"` + ValidityTo string `json:"certificate-validity-to"` + Serial string `json:"certificate-serial"` + } `json:"ssl-certificates,omitempty"` +} diff --git a/internal/pkg/sdk3rd/wangsu/openapi/client.go b/internal/pkg/sdk3rd/wangsu/openapi/client.go index 95d17bb0..a8f4f2af 100644 --- a/internal/pkg/sdk3rd/wangsu/openapi/client.go +++ b/internal/pkg/sdk3rd/wangsu/openapi/client.go @@ -134,8 +134,6 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client { func (c *Client) sendRequest(method string, path string, params interface{}, configureReq ...func(req *resty.Request)) (*resty.Response, error) { req := c.client.R() - req.Method = method - req.URL = path if strings.EqualFold(method, http.MethodGet) { qs := make(map[string]string) if params != nil { @@ -151,14 +149,16 @@ func (c *Client) sendRequest(method string, path string, params interface{}, con req = req.SetQueryParams(qs) } else { - req = req.SetBody(params) + req = req.SetHeader("Content-Type", "application/json").SetBody(params) } - for _, fn := range configureReq { - fn(req) + if configureReq != nil { + for _, fn := range configureReq { + fn(req) + } } - resp, err := req.Send() + resp, err := req.Execute(method, path) if err != nil { return resp, fmt.Errorf("wangsu api error: failed to send request: %w", err) } else if resp.IsError() { diff --git a/internal/pkg/utils/map/getter.go b/internal/pkg/utils/map/getter.go index f30f6d33..512da3ee 100644 --- a/internal/pkg/utils/map/getter.go +++ b/internal/pkg/utils/map/getter.go @@ -68,31 +68,42 @@ func GetOrDefaultInt32(dict map[string]any, key string, defaultValue int32) int3 } if value, ok := dict[key]; ok { - if result, ok := value.(int32); ok { - if result != 0 { - return result + var result int32 + + switch v := value.(type) { + case int: + result = int32(v) + case int8: + result = int32(v) + case int16: + result = int32(v) + case int32: + result = v + case int64: + result = int32(v) + case uint: + result = int32(v) + case uint8: + result = int32(v) + case uint16: + result = int32(v) + case uint32: + result = int32(v) + case uint64: + result = int32(v) + case float32: + result = int32(v) + case float64: + result = int32(v) + case string: + // 兼容字符串类型的值 + if t, err := strconv.ParseInt(v, 10, 32); err == nil { + result = int32(t) } } - if result, ok := value.(int64); ok { - if result != 0 { - return int32(result) - } - } - - if result, ok := value.(int); ok { - if result != 0 { - return int32(result) - } - } - - // 兼容字符串类型的值 - if str, ok := value.(string); ok { - if result, err := strconv.ParseInt(str, 10, 32); err == nil { - if result != 0 { - return int32(result) - } - } + if result != 0 { + return int32(result) } } @@ -126,31 +137,42 @@ func GetOrDefaultInt64(dict map[string]any, key string, defaultValue int64) int6 } if value, ok := dict[key]; ok { - if result, ok := value.(int64); ok { - if result != 0 { - return result + var result int64 + + switch v := value.(type) { + case int: + result = int64(v) + case int8: + result = int64(v) + case int16: + result = int64(v) + case int32: + result = int64(v) + case int64: + result = v + case uint: + result = int64(v) + case uint8: + result = int64(v) + case uint16: + result = int64(v) + case uint32: + result = int64(v) + case uint64: + result = int64(v) + case float32: + result = int64(v) + case float64: + result = int64(v) + case string: + // 兼容字符串类型的值 + if t, err := strconv.ParseInt(v, 10, 32); err == nil { + result = t } } - if result, ok := value.(int32); ok { - if result != 0 { - return int64(result) - } - } - - if result, ok := value.(int); ok { - if result != 0 { - return int64(result) - } - } - - // 兼容字符串类型的值 - if str, ok := value.(string); ok { - if result, err := strconv.ParseInt(str, 10, 64); err == nil { - if result != 0 { - return result - } - } + if result != 0 { + return int64(result) } } diff --git a/internal/repository/certificate.go b/internal/repository/certificate.go index 13d2c094..95bfd713 100644 --- a/internal/repository/certificate.go +++ b/internal/repository/certificate.go @@ -101,7 +101,7 @@ func (r *CertificateRepository) Save(ctx context.Context, certificate *domain.Ce record.Set("serialNumber", certificate.SerialNumber) record.Set("certificate", certificate.Certificate) record.Set("privateKey", certificate.PrivateKey) - record.Set("issuer", certificate.Issuer) + record.Set("issuerOrg", certificate.IssuerOrg) record.Set("issuerCertificate", certificate.IssuerCertificate) record.Set("keyAlgorithm", string(certificate.KeyAlgorithm)) record.Set("effectAt", certificate.EffectAt) @@ -162,7 +162,7 @@ func (r *CertificateRepository) castRecordToModel(record *core.Record) (*domain. SerialNumber: record.GetString("serialNumber"), Certificate: record.GetString("certificate"), PrivateKey: record.GetString("privateKey"), - Issuer: record.GetString("issuer"), + IssuerOrg: record.GetString("issuerOrg"), IssuerCertificate: record.GetString("issuerCertificate"), KeyAlgorithm: domain.CertificateKeyAlgorithmType(record.GetString("keyAlgorithm")), EffectAt: record.GetDateTime("effectAt").Time(), diff --git a/internal/workflow/node-processor/apply_node.go b/internal/workflow/node-processor/apply_node.go index 97b7575d..ff8c573d 100644 --- a/internal/workflow/node-processor/apply_node.go +++ b/internal/workflow/node-processor/apply_node.go @@ -66,14 +66,14 @@ func (n *applyNode) Process(ctx context.Context) error { } // 解析证书并生成实体 - certX509, err := certutil.ParseCertificateFromPEM(applyResult.CertificateFullChain) + certX509, err := certutil.ParseCertificateFromPEM(applyResult.FullChainCertificate) if err != nil { n.logger.Warn("failed to parse certificate, may be the CA responded error") return err } certificate := &domain.Certificate{ Source: domain.CertificateSourceTypeWorkflow, - Certificate: applyResult.CertificateFullChain, + Certificate: applyResult.FullChainCertificate, PrivateKey: applyResult.PrivateKey, IssuerCertificate: applyResult.IssuerCertificate, ACMEAccountUrl: applyResult.ACMEAccountUrl, diff --git a/migrations/1747389600_upgrade.go b/migrations/1747389600_upgrade.go new file mode 100644 index 00000000..a145679a --- /dev/null +++ b/migrations/1747389600_upgrade.go @@ -0,0 +1,73 @@ +package migrations + +import ( + "github.com/pocketbase/pocketbase/core" + m "github.com/pocketbase/pocketbase/migrations" +) + +func init() { + m.Register(func(app core.App) error { + // update collection `certificate` + { + collection, err := app.FindCollectionByNameOrId("4szxr9x43tpj6np") + if err != nil { + return err + } + + if err := collection.Fields.AddMarshaledJSONAt(6, []byte(`{ + "autogeneratePattern": "", + "hidden": false, + "id": "text2910474005", + "max": 0, + "min": 0, + "name": "issuerOrg", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }`)); err != nil { + return err + } + + if err := app.Save(collection); err != nil { + return err + } + } + + // migrate data + { + accesses, err := app.FindAllRecords("access") + if err != nil { + return err + } + + for _, access := range accesses { + changed := false + + if access.GetString("provider") == "1panel" { + config := make(map[string]any) + if err := access.UnmarshalJSONField("config", &config); err != nil { + return err + } + + config["apiVersion"] = "v1" + access.Set("config", config) + changed = true + } + + if changed { + err = app.Save(access) + if err != nil { + return err + } + } + } + } + + return nil + }, func(app core.App) error { + return nil + }) +} diff --git a/ui/public/imgs/providers/acmeca.svg b/ui/public/imgs/providers/acmeca.svg new file mode 100644 index 00000000..260530c9 --- /dev/null +++ b/ui/public/imgs/providers/acmeca.svg @@ -0,0 +1 @@ + diff --git a/ui/public/imgs/providers/baotawaf.svg b/ui/public/imgs/providers/baotawaf.svg new file mode 100644 index 00000000..34ab8ec8 --- /dev/null +++ b/ui/public/imgs/providers/baotawaf.svg @@ -0,0 +1 @@ + diff --git a/ui/public/imgs/providers/flexcdn.png b/ui/public/imgs/providers/flexcdn.png new file mode 100644 index 00000000..00805598 Binary files /dev/null and b/ui/public/imgs/providers/flexcdn.png differ diff --git a/ui/public/imgs/providers/lecdn.svg b/ui/public/imgs/providers/lecdn.svg new file mode 100644 index 00000000..f9c18fa7 --- /dev/null +++ b/ui/public/imgs/providers/lecdn.svg @@ -0,0 +1 @@ +LeCDN \ No newline at end of file diff --git a/ui/public/imgs/providers/ratpanel.png b/ui/public/imgs/providers/ratpanel.png new file mode 100644 index 00000000..f89808dd Binary files /dev/null and b/ui/public/imgs/providers/ratpanel.png differ diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx index 1b618cbc..fdf4f93f 100644 --- a/ui/src/components/access/AccessForm.tsx +++ b/ui/src/components/access/AccessForm.tsx @@ -12,6 +12,7 @@ import { ACCESS_PROVIDERS, ACCESS_USAGES, type AccessProvider } from "@/domain/p import { useAntdForm, useAntdFormName } from "@/hooks"; import AccessForm1PanelConfig from "./AccessForm1PanelConfig"; +import AccessFormACMECAConfig from "./AccessFormACMECAConfig"; import AccessFormACMEHttpReqConfig from "./AccessFormACMEHttpReqConfig"; import AccessFormAliyunConfig from "./AccessFormAliyunConfig"; import AccessFormAWSConfig from "./AccessFormAWSConfig"; @@ -19,6 +20,7 @@ import AccessFormAzureConfig from "./AccessFormAzureConfig"; import AccessFormBaiduCloudConfig from "./AccessFormBaiduCloudConfig"; import AccessFormBaishanConfig from "./AccessFormBaishanConfig"; import AccessFormBaotaPanelConfig from "./AccessFormBaotaPanelConfig"; +import AccessFormBaotaWAFConfig from "./AccessFormBaotaWAFConfig"; import AccessFormBunnyConfig from "./AccessFormBunnyConfig"; import AccessFormBytePlusConfig from "./AccessFormBytePlusConfig"; import AccessFormCacheFlyConfig from "./AccessFormCacheFlyConfig"; @@ -33,6 +35,7 @@ import AccessFormDogeCloudConfig from "./AccessFormDogeCloudConfig"; import AccessFormDynv6Config from "./AccessFormDynv6Config"; import AccessFormEdgioConfig from "./AccessFormEdgioConfig"; import AccessFormEmailConfig from "./AccessFormEmailConfig"; +import AccessFormFlexCDNConfig from "./AccessFormFlexCDNConfig"; import AccessFormGcoreConfig from "./AccessFormGcoreConfig"; import AccessFormGnameConfig from "./AccessFormGnameConfig"; import AccessFormGoDaddyConfig from "./AccessFormGoDaddyConfig"; @@ -42,6 +45,7 @@ import AccessFormHuaweiCloudConfig from "./AccessFormHuaweiCloudConfig"; import AccessFormJDCloudConfig from "./AccessFormJDCloudConfig"; import AccessFormKubernetesConfig from "./AccessFormKubernetesConfig"; import AccessFormLarkBotConfig from "./AccessFormLarkBotConfig"; +import AccessFormLeCDNConfig from "./AccessFormLeCDNConfig"; import AccessFormMattermostConfig from "./AccessFormMattermostConfig"; import AccessFormNamecheapConfig from "./AccessFormNamecheapConfig"; import AccessFormNameDotComConfig from "./AccessFormNameDotComConfig"; @@ -54,6 +58,7 @@ import AccessFormPowerDNSConfig from "./AccessFormPowerDNSConfig"; import AccessFormProxmoxVEConfig from "./AccessFormProxmoxVEConfig"; import AccessFormQiniuConfig from "./AccessFormQiniuConfig"; import AccessFormRainYunConfig from "./AccessFormRainYunConfig"; +import AccessFormRatPanelConfig from "./AccessFormRatPanelConfig"; import AccessFormSafeLineConfig from "./AccessFormSafeLineConfig"; import AccessFormSSHConfig from "./AccessFormSSHConfig"; import AccessFormSSLComConfig from "./AccessFormSSLComConfig"; @@ -176,6 +181,8 @@ const AccessForm = forwardRef(({ className, switch (fieldProvider) { case ACCESS_PROVIDERS["1PANEL"]: return ; + case ACCESS_PROVIDERS.ACMECA: + return ; case ACCESS_PROVIDERS.ACMEHTTPREQ: return ; case ACCESS_PROVIDERS.ALIYUN: @@ -190,6 +197,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.BAOTAPANEL: return ; + case ACCESS_PROVIDERS.BAOTAWAF: + return ; case ACCESS_PROVIDERS.BUNNY: return ; case ACCESS_PROVIDERS.BYTEPLUS: @@ -214,6 +223,12 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.DYNV6: return ; + case ACCESS_PROVIDERS.EDGIO: + return ; + case ACCESS_PROVIDERS.EMAIL: + return ; + case ACCESS_PROVIDERS.FLEXCDN: + return ; case ACCESS_PROVIDERS.GCORE: return ; case ACCESS_PROVIDERS.GNAME: @@ -224,10 +239,6 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.GOOGLETRUSTSERVICES: return ; - case ACCESS_PROVIDERS.EDGIO: - return ; - case ACCESS_PROVIDERS.EMAIL: - return ; case ACCESS_PROVIDERS.HUAWEICLOUD: return ; case ACCESS_PROVIDERS.JDCLOUD: @@ -236,6 +247,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.LARKBOT: return ; + case ACCESS_PROVIDERS.LECDN: + return ; case ACCESS_PROVIDERS.MATTERMOST: return ; case ACCESS_PROVIDERS.NAMECHEAP: @@ -260,6 +273,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.RAINYUN: return ; + case ACCESS_PROVIDERS.RATPANEL: + return ; case ACCESS_PROVIDERS.SAFELINE: return ; case ACCESS_PROVIDERS.SSH: diff --git a/ui/src/components/access/AccessForm1PanelConfig.tsx b/ui/src/components/access/AccessForm1PanelConfig.tsx index c0762bbd..29481f15 100644 --- a/ui/src/components/access/AccessForm1PanelConfig.tsx +++ b/ui/src/components/access/AccessForm1PanelConfig.tsx @@ -1,5 +1,5 @@ import { useTranslation } from "react-i18next"; -import { Form, type FormInstance, Input, Switch } from "antd"; +import { Form, type FormInstance, Input, Select, Switch } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; @@ -18,6 +18,7 @@ export type AccessForm1PanelConfigProps = { const initFormModel = (): AccessForm1PanelConfigFieldValues => { return { apiUrl: "http://:20410/", + apiVersion: "v1", apiKey: "", }; }; @@ -27,6 +28,7 @@ const AccessForm1PanelConfig = ({ form: formInst, formName, disabled, initialVal const formSchema = z.object({ apiUrl: z.string().url(t("common.errmsg.url_invalid")), + apiVersion: z.string().nonempty(t("access.form.1panel_api_version.placeholder")), apiKey: z .string() .min(1, t("access.form.1panel_api_key.placeholder")) @@ -53,6 +55,10 @@ const AccessForm1PanelConfig = ({ form: formInst, formName, disabled, initialVal + + + + + } + > + + + + } + > + + + + ); +}; + +export default AccessFormACMECAConfig; diff --git a/ui/src/components/access/AccessFormACMEHttpReqConfig.tsx b/ui/src/components/access/AccessFormACMEHttpReqConfig.tsx index 03cf163a..57cbc22d 100644 --- a/ui/src/components/access/AccessFormACMEHttpReqConfig.tsx +++ b/ui/src/components/access/AccessFormACMEHttpReqConfig.tsx @@ -84,7 +84,7 @@ const AccessFormACMEHttpReqConfig = ({ form: formInst, formName, disabled, initi rules={[formRule]} tooltip={} > - + } > - + ); diff --git a/ui/src/components/access/AccessFormBaotaPanelConfig.tsx b/ui/src/components/access/AccessFormBaotaPanelConfig.tsx index d03c0f1b..dd355b5e 100644 --- a/ui/src/components/access/AccessFormBaotaPanelConfig.tsx +++ b/ui/src/components/access/AccessFormBaotaPanelConfig.tsx @@ -27,11 +27,7 @@ const AccessFormBaotaPanelConfig = ({ form: formInst, formName, disabled, initia const formSchema = z.object({ apiUrl: z.string().url(t("common.errmsg.url_invalid")), - apiKey: z - .string() - .min(1, t("access.form.baotapanel_api_key.placeholder")) - .max(64, t("common.errmsg.string_max", { max: 64 })) - .trim(), + apiKey: z.string().nonempty(t("access.form.baotapanel_api_key.placeholder")).trim(), allowInsecureConnections: z.boolean().nullish(), }); const formRule = createSchemaFieldRule(formSchema); diff --git a/ui/src/components/access/AccessFormBaotaWAFConfig.tsx b/ui/src/components/access/AccessFormBaotaWAFConfig.tsx new file mode 100644 index 00000000..e87ed596 --- /dev/null +++ b/ui/src/components/access/AccessFormBaotaWAFConfig.tsx @@ -0,0 +1,71 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input, Switch } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForBaotaWAF } from "@/domain/access"; + +type AccessFormBaotaWAFConfigFieldValues = Nullish; + +export type AccessFormBaotaWAFConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormBaotaWAFConfigFieldValues; + onValuesChange?: (values: AccessFormBaotaWAFConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormBaotaWAFConfigFieldValues => { + return { + apiUrl: "http://:8379/", + apiKey: "", + }; +}; + +const AccessFormBaotaWAFConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormBaotaWAFConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + apiUrl: z.string().url(t("common.errmsg.url_invalid")), + apiKey: z.string().nonempty(t("access.form.baotawaf_api_key.placeholder")).trim(), + allowInsecureConnections: z.boolean().nullish(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ + + + + } + > + + + + + + +
+ ); +}; + +export default AccessFormBaotaWAFConfig; diff --git a/ui/src/components/access/AccessFormCloudflareConfig.tsx b/ui/src/components/access/AccessFormCloudflareConfig.tsx index a06d753d..79b33e3a 100644 --- a/ui/src/components/access/AccessFormCloudflareConfig.tsx +++ b/ui/src/components/access/AccessFormCloudflareConfig.tsx @@ -66,7 +66,7 @@ const AccessFormCloudflareConfig = ({ form: formInst, formName, disabled, initia rules={[formRule]} tooltip={} > - + ); diff --git a/ui/src/components/access/AccessFormFlexCDNConfig.tsx b/ui/src/components/access/AccessFormFlexCDNConfig.tsx new file mode 100644 index 00000000..6ca020bf --- /dev/null +++ b/ui/src/components/access/AccessFormFlexCDNConfig.tsx @@ -0,0 +1,90 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input, Radio, Switch } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForFlexCDN } from "@/domain/access"; + +type AccessFormFlexCDNConfigFieldValues = Nullish; + +export type AccessFormFlexCDNConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormFlexCDNConfigFieldValues; + onValuesChange?: (values: AccessFormFlexCDNConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormFlexCDNConfigFieldValues => { + return { + apiUrl: "http://:8000/", + apiRole: "user", + accessKeyId: "", + accessKey: "", + }; +}; + +const AccessFormFlexCDNConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormFlexCDNConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + apiUrl: z.string().url(t("common.errmsg.url_invalid")), + role: z.union([z.literal("user"), z.literal("admin")], { + message: t("access.form.flexcdn_api_role.placeholder"), + }), + accessKeyId: z.string().nonempty(t("access.form.flexcdn_access_key_id.placeholder")).trim(), + accessKey: z.string().nonempty(t("access.form.flexcdn_access_key.placeholder")).trim(), + allowInsecureConnections: z.boolean().nullish(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ + + + + + ({ label: t(`access.form.flexcdn_api_role.option.${s}.label`), value: s }))} /> + + + } + > + + + + } + > + + + + + + +
+ ); +}; + +export default AccessFormFlexCDNConfig; diff --git a/ui/src/components/access/AccessFormGoEdgeConfig.tsx b/ui/src/components/access/AccessFormGoEdgeConfig.tsx index ced9b09a..9c03f2be 100644 --- a/ui/src/components/access/AccessFormGoEdgeConfig.tsx +++ b/ui/src/components/access/AccessFormGoEdgeConfig.tsx @@ -32,16 +32,8 @@ const AccessFormGoEdgeConfig = ({ form: formInst, formName, disabled, initialVal role: z.union([z.literal("user"), z.literal("admin")], { message: t("access.form.goedge_api_role.placeholder"), }), - accessKeyId: z - .string() - .min(1, t("access.form.goedge_access_key_id.placeholder")) - .max(64, t("common.errmsg.string_max", { max: 64 })) - .trim(), - accessKey: z - .string() - .min(1, t("access.form.goedge_access_key.placeholder")) - .max(64, t("common.errmsg.string_max", { max: 64 })) - .trim(), + accessKeyId: z.string().nonempty(t("access.form.goedge_access_key_id.placeholder")).trim(), + accessKey: z.string().nonempty(t("access.form.goedge_access_key.placeholder")).trim(), allowInsecureConnections: z.boolean().nullish(), }); const formRule = createSchemaFieldRule(formSchema); diff --git a/ui/src/components/access/AccessFormLeCDNConfig.tsx b/ui/src/components/access/AccessFormLeCDNConfig.tsx new file mode 100644 index 00000000..4af5a639 --- /dev/null +++ b/ui/src/components/access/AccessFormLeCDNConfig.tsx @@ -0,0 +1,85 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input, Radio, Select, Switch } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForLeCDN } from "@/domain/access"; + +type AccessFormLeCDNConfigFieldValues = Nullish; + +export type AccessFormLeCDNConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormLeCDNConfigFieldValues; + onValuesChange?: (values: AccessFormLeCDNConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormLeCDNConfigFieldValues => { + return { + apiUrl: "http://:5090/", + apiVersion: "v3", + apiRole: "user", + username: "", + password: "", + }; +}; + +const AccessFormLeCDNConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormLeCDNConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + apiUrl: z.string().url(t("common.errmsg.url_invalid")), + role: z.union([z.literal("client"), z.literal("master")], { + message: t("access.form.lecdn_api_role.placeholder"), + }), + username: z.string().nonempty(t("access.form.lecdn_username.placeholder")).trim(), + password: z.string().nonempty(t("access.form.lecdn_password.placeholder")).trim(), + allowInsecureConnections: z.boolean().nullish(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ + + + + + + + + + + + + + + +
+ ); +}; + +export default AccessFormLeCDNConfig; diff --git a/ui/src/components/access/AccessFormProxmoxVEConfig.tsx b/ui/src/components/access/AccessFormProxmoxVEConfig.tsx index afdc02de..d0a66745 100644 --- a/ui/src/components/access/AccessFormProxmoxVEConfig.tsx +++ b/ui/src/components/access/AccessFormProxmoxVEConfig.tsx @@ -65,7 +65,7 @@ const AccessFormProxmoxVEConfig = ({ form: formInst, formName, disabled, initial rules={[formRule]} tooltip={} > - + diff --git a/ui/src/components/access/AccessFormRatPanelConfig.tsx b/ui/src/components/access/AccessFormRatPanelConfig.tsx new file mode 100644 index 00000000..ca3d2182 --- /dev/null +++ b/ui/src/components/access/AccessFormRatPanelConfig.tsx @@ -0,0 +1,82 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input, Switch } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForRatPanel } from "@/domain/access"; + +type AccessFormRatPanelConfigFieldValues = Nullish; + +export type AccessFormRatPanelConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormRatPanelConfigFieldValues; + onValuesChange?: (values: AccessFormRatPanelConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormRatPanelConfigFieldValues => { + return { + apiUrl: "http://:8888/", + accessTokenId: 1, + accessToken: "", + }; +}; + +const AccessFormRatPanelConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormRatPanelConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + apiUrl: z.string().url(t("common.errmsg.url_invalid")), + accessTokenId: z.preprocess((v) => Number(v), z.number().positive(t("access.form.ratpanel_access_token_id.placeholder"))), + accessToken: z.string().nonempty(t("access.form.ratpanel_access_token.placeholder")).trim(), + allowInsecureConnections: z.boolean().nullish(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ + + + + } + > + + + + } + > + + + + + + +
+ ); +}; + +export default AccessFormRatPanelConfig; diff --git a/ui/src/components/access/AccessFormUCloudConfig.tsx b/ui/src/components/access/AccessFormUCloudConfig.tsx index 495d21b9..fd623925 100644 --- a/ui/src/components/access/AccessFormUCloudConfig.tsx +++ b/ui/src/components/access/AccessFormUCloudConfig.tsx @@ -81,7 +81,7 @@ const AccessFormUCloudConfig = ({ form: formInst, formName, disabled, initialVal rules={[formRule]} tooltip={} > - +
); diff --git a/ui/src/components/access/AccessFormVercelConfig.tsx b/ui/src/components/access/AccessFormVercelConfig.tsx index b1ed7b6f..4483a9f9 100644 --- a/ui/src/components/access/AccessFormVercelConfig.tsx +++ b/ui/src/components/access/AccessFormVercelConfig.tsx @@ -66,7 +66,7 @@ const AccessFormVercelConfig = ({ form: formInst, formName, disabled, initialVal rules={[formRule]} tooltip={} > - + ); diff --git a/ui/src/components/access/AccessFormWebhookConfig.tsx b/ui/src/components/access/AccessFormWebhookConfig.tsx index 0dea7f7c..6e6ec87a 100644 --- a/ui/src/components/access/AccessFormWebhookConfig.tsx +++ b/ui/src/components/access/AccessFormWebhookConfig.tsx @@ -67,7 +67,6 @@ const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialVa } return true; }, t("access.form.webhook_headers.errmsg.invalid")), - allowInsecureConnections: z.boolean().nullish(), defaultDataForDeployment: z .string() .nullish() @@ -96,11 +95,12 @@ const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialVa return false; } }, t("access.form.webhook_default_data.errmsg.json_invalid")), + allowInsecureConnections: z.boolean().nullish(), }); const formRule = createSchemaFieldRule(formSchema); - const handleWebhookHeadersBlur = (e: React.FocusEvent) => { - let value = e.target.value; + const handleWebhookHeadersBlur = () => { + let value = formInst.getFieldValue("headers"); value = value.trim(); value = value.replace(/(?} > - + diff --git a/ui/src/components/certificate/CertificateDetail.tsx b/ui/src/components/certificate/CertificateDetail.tsx index 1023bf16..2fc0d9d0 100644 --- a/ui/src/components/certificate/CertificateDetail.tsx +++ b/ui/src/components/certificate/CertificateDetail.tsx @@ -42,7 +42,7 @@ const CertificateDetail = ({ data, ...props }: CertificateDetailProps) => { - + diff --git a/ui/src/components/provider/DeploymentProviderPicker.tsx b/ui/src/components/provider/DeploymentProviderPicker.tsx index 0ea5a97c..b1bcd6fe 100644 --- a/ui/src/components/provider/DeploymentProviderPicker.tsx +++ b/ui/src/components/provider/DeploymentProviderPicker.tsx @@ -72,8 +72,10 @@ const DeploymentProviderPicker = ({ className, style, autoFocus, filter, placeho DEPLOYMENT_CATEGORIES.LOADBALANCE, DEPLOYMENT_CATEGORIES.FIREWALL, DEPLOYMENT_CATEGORIES.AV, + DEPLOYMENT_CATEGORIES.APIGATEWAY, DEPLOYMENT_CATEGORIES.SERVERLESS, DEPLOYMENT_CATEGORIES.WEBSITE, + DEPLOYMENT_CATEGORIES.SSL, DEPLOYMENT_CATEGORIES.NAS, DEPLOYMENT_CATEGORIES.OTHER, ].map((key) => ({ diff --git a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx index 9e4ade18..49ed12ec 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx @@ -27,6 +27,7 @@ import DeployNodeConfigFormAliyunDCDNConfig from "./DeployNodeConfigFormAliyunDC import DeployNodeConfigFormAliyunDDoSConfig from "./DeployNodeConfigFormAliyunDDoSConfig"; import DeployNodeConfigFormAliyunESAConfig from "./DeployNodeConfigFormAliyunESAConfig"; import DeployNodeConfigFormAliyunFCConfig from "./DeployNodeConfigFormAliyunFCConfig"; +import DeployNodeConfigFormAliyunGAConfig from "./DeployNodeConfigFormAliyunGAConfig"; import DeployNodeConfigFormAliyunLiveConfig from "./DeployNodeConfigFormAliyunLiveConfig"; import DeployNodeConfigFormAliyunNLBConfig from "./DeployNodeConfigFormAliyunNLBConfig"; import DeployNodeConfigFormAliyunOSSConfig from "./DeployNodeConfigFormAliyunOSSConfig"; @@ -41,11 +42,13 @@ import DeployNodeConfigFormBaiduCloudCDNConfig from "./DeployNodeConfigFormBaidu import DeployNodeConfigFormBaishanCDNConfig from "./DeployNodeConfigFormBaishanCDNConfig"; import DeployNodeConfigFormBaotaPanelConsoleConfig from "./DeployNodeConfigFormBaotaPanelConsoleConfig"; import DeployNodeConfigFormBaotaPanelSiteConfig from "./DeployNodeConfigFormBaotaPanelSiteConfig"; +import DeployNodeConfigFormBaotaWAFSiteConfig from "./DeployNodeConfigFormBaotaWAFSiteConfig"; import DeployNodeConfigFormBunnyCDNConfig from "./DeployNodeConfigFormBunnyCDNConfig.tsx"; import DeployNodeConfigFormBytePlusCDNConfig from "./DeployNodeConfigFormBytePlusCDNConfig"; import DeployNodeConfigFormCdnflyConfig from "./DeployNodeConfigFormCdnflyConfig"; import DeployNodeConfigFormDogeCloudCDNConfig from "./DeployNodeConfigFormDogeCloudCDNConfig"; import DeployNodeConfigFormEdgioApplicationsConfig from "./DeployNodeConfigFormEdgioApplicationsConfig"; +import DeployNodeConfigFormFlexCDNConfig from "./DeployNodeConfigFormFlexCDNConfig"; import DeployNodeConfigFormGcoreCDNConfig from "./DeployNodeConfigFormGcoreCDNConfig"; import DeployNodeConfigFormGoEdgeConfig from "./DeployNodeConfigFormGoEdgeConfig"; import DeployNodeConfigFormHuaweiCloudCDNConfig from "./DeployNodeConfigFormHuaweiCloudCDNConfig"; @@ -56,6 +59,7 @@ import DeployNodeConfigFormJDCloudCDNConfig from "./DeployNodeConfigFormJDCloudC import DeployNodeConfigFormJDCloudLiveConfig from "./DeployNodeConfigFormJDCloudLiveConfig"; import DeployNodeConfigFormJDCloudVODConfig from "./DeployNodeConfigFormJDCloudVODConfig"; import DeployNodeConfigFormKubernetesSecretConfig from "./DeployNodeConfigFormKubernetesSecretConfig"; +import DeployNodeConfigFormLeCDNConfig from "./DeployNodeConfigFormLeCDNConfig"; import DeployNodeConfigFormLocalConfig from "./DeployNodeConfigFormLocalConfig"; import DeployNodeConfigFormNetlifySiteConfig from "./DeployNodeConfigFormNetlifySiteConfig"; import DeployNodeConfigFormProxmoxVEConfig from "./DeployNodeConfigFormProxmoxVEConfig"; @@ -63,6 +67,7 @@ import DeployNodeConfigFormQiniuCDNConfig from "./DeployNodeConfigFormQiniuCDNCo import DeployNodeConfigFormQiniuKodoConfig from "./DeployNodeConfigFormQiniuKodoConfig"; import DeployNodeConfigFormQiniuPiliConfig from "./DeployNodeConfigFormQiniuPiliConfig"; import DeployNodeConfigFormRainYunRCDNConfig from "./DeployNodeConfigFormRainYunRCDNConfig"; +import DeployNodeConfigFormRatPanelSiteConfig from "./DeployNodeConfigFormRatPanelSiteConfig"; import DeployNodeConfigFormSafeLineConfig from "./DeployNodeConfigFormSafeLineConfig"; import DeployNodeConfigFormSSHConfig from "./DeployNodeConfigFormSSHConfig.tsx"; import DeployNodeConfigFormTencentCloudCDNConfig from "./DeployNodeConfigFormTencentCloudCDNConfig.tsx"; @@ -87,7 +92,9 @@ import DeployNodeConfigFormVolcEngineDCDNConfig from "./DeployNodeConfigFormVolc import DeployNodeConfigFormVolcEngineImageXConfig from "./DeployNodeConfigFormVolcEngineImageXConfig.tsx"; import DeployNodeConfigFormVolcEngineLiveConfig from "./DeployNodeConfigFormVolcEngineLiveConfig.tsx"; import DeployNodeConfigFormVolcEngineTOSConfig from "./DeployNodeConfigFormVolcEngineTOSConfig.tsx"; +import DeployNodeConfigFormWangsuCDNConfig from "./DeployNodeConfigFormWangsuCDNConfig.tsx"; import DeployNodeConfigFormWangsuCDNProConfig from "./DeployNodeConfigFormWangsuCDNProConfig.tsx"; +import DeployNodeConfigFormWangsuCertificateConfig from "./DeployNodeConfigFormWangsuCertificateConfig.tsx"; import DeployNodeConfigFormWebhookConfig from "./DeployNodeConfigFormWebhookConfig.tsx"; type DeployNodeConfigFormFieldValues = Partial; @@ -201,6 +208,8 @@ const DeployNodeConfigForm = forwardRef; case DEPLOYMENT_PROVIDERS.ALIYUN_FC: return ; + case DEPLOYMENT_PROVIDERS.ALIYUN_GA: + return ; case DEPLOYMENT_PROVIDERS.ALIYUN_LIVE: return ; case DEPLOYMENT_PROVIDERS.ALIYUN_NLB: @@ -229,6 +238,8 @@ const DeployNodeConfigForm = forwardRef; case DEPLOYMENT_PROVIDERS.BAOTAPANEL_SITE: return ; + case DEPLOYMENT_PROVIDERS.BAOTAWAF_SITE: + return ; case DEPLOYMENT_PROVIDERS.BUNNY_CDN: return ; case DEPLOYMENT_PROVIDERS.BYTEPLUS_CDN: @@ -239,6 +250,8 @@ const DeployNodeConfigForm = forwardRef; case DEPLOYMENT_PROVIDERS.EDGIO_APPLICATIONS: return ; + case DEPLOYMENT_PROVIDERS.FLEXCDN: + return ; case DEPLOYMENT_PROVIDERS.GCORE_CDN: return ; case DEPLOYMENT_PROVIDERS.GOEDGE: @@ -259,6 +272,8 @@ const DeployNodeConfigForm = forwardRef; case DEPLOYMENT_PROVIDERS.KUBERNETES_SECRET: return ; + case DEPLOYMENT_PROVIDERS.LECDN: + return ; case DEPLOYMENT_PROVIDERS.LOCAL: return ; case DEPLOYMENT_PROVIDERS.NETLIFY_SITE: @@ -273,6 +288,8 @@ const DeployNodeConfigForm = forwardRef; case DEPLOYMENT_PROVIDERS.RAINYUN_RCDN: return ; + case DEPLOYMENT_PROVIDERS.RATPANEL_SITE: + return ; case DEPLOYMENT_PROVIDERS.SAFELINE: return ; case DEPLOYMENT_PROVIDERS.SSH: @@ -321,8 +338,12 @@ const DeployNodeConfigForm = forwardRef; case DEPLOYMENT_PROVIDERS.VOLCENGINE_TOS: return ; + case DEPLOYMENT_PROVIDERS.WANGSU_CDN: + return ; case DEPLOYMENT_PROVIDERS.WANGSU_CDNPRO: return ; + case DEPLOYMENT_PROVIDERS.WANGSU_CERTIFICATE: + return ; case DEPLOYMENT_PROVIDERS.WEBHOOK: return ; } diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormAWSACMConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormAWSACMConfig.tsx index f0964493..2e539453 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormAWSACMConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormAWSACMConfig.tsx @@ -28,7 +28,7 @@ const DeployNodeConfigFormAWSACMConfig = ({ form: formInst, formName, disabled, .string({ message: t("workflow_node.deploy.form.aws_acm_region.placeholder") }) .nonempty(t("workflow_node.deploy.form.aws_acm_region.placeholder")) .trim(), - certificateArn: z.string({ message: t("workflow_node.deploy.form.aws_acm_certificate_arn.placeholder") }).nullish(), + certificateArn: z.string().nullish(), }); const formRule = createSchemaFieldRule(formSchema); @@ -60,7 +60,7 @@ const DeployNodeConfigFormAWSACMConfig = ({ form: formInst, formName, disabled, rules={[formRule]} tooltip={} > - + ); diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunALBConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunALBConfig.tsx index 3afcb7a1..bbfca5e6 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunALBConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunALBConfig.tsx @@ -132,7 +132,7 @@ const DeployNodeConfigFormAliyunALBConfig = ({ rules={[formRule]} tooltip={} > - + diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunCLBConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunCLBConfig.tsx index 2c2e43b6..e666800e 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunCLBConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunCLBConfig.tsx @@ -132,7 +132,7 @@ const DeployNodeConfigFormAliyunCLBConfig = ({ rules={[formRule]} tooltip={} > - + diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunGAConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunGAConfig.tsx new file mode 100644 index 00000000..20dd1ae1 --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunGAConfig.tsx @@ -0,0 +1,118 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input, Select } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import Show from "@/components/Show"; +import { validDomainName } from "@/utils/validators"; + +type DeployNodeConfigFormAliyunGAConfigFieldValues = Nullish<{ + resourceType: string; + acceleratorId?: string; + listenerId?: string; + domain?: string; +}>; + +export type DeployNodeConfigFormAliyunGAConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormAliyunGAConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormAliyunGAConfigFieldValues) => void; +}; + +const RESOURCE_TYPE_ACCELERATOR = "accelerator" as const; +const RESOURCE_TYPE_LISTENER = "listener" as const; + +const initFormModel = (): DeployNodeConfigFormAliyunGAConfigFieldValues => { + return {}; +}; + +const DeployNodeConfigFormAliyunGAConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: DeployNodeConfigFormAliyunGAConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + resourceType: z.union([z.literal(RESOURCE_TYPE_ACCELERATOR), z.literal(RESOURCE_TYPE_LISTENER)], { + message: t("workflow_node.deploy.form.aliyun_ga_resource_type.placeholder"), + }), + acceleratorId: z + .string() + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + listenerId: z + .string() + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim() + .nullish() + .refine((v) => fieldResourceType !== RESOURCE_TYPE_LISTENER || !!v?.trim(), t("workflow_node.deploy.form.aliyun_ga_listener_id.placeholder")), + domain: z + .string() + .nullish() + .refine((v) => { + if (![RESOURCE_TYPE_ACCELERATOR, RESOURCE_TYPE_LISTENER].includes(fieldResourceType)) return true; + return !v || validDomainName(v!); + }, t("common.errmsg.domain_invalid")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const fieldResourceType = Form.useWatch("resourceType", formInst); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ + + + + } + > + + + + + } + > + + + + + + } + > + + + +
+ ); +}; + +export default DeployNodeConfigFormAliyunGAConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunWAFConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunWAFConfig.tsx index 5f81cf71..a46c7327 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunWAFConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunWAFConfig.tsx @@ -102,7 +102,7 @@ const DeployNodeConfigFormAliyunWAFConfig = ({ rules={[formRule]} tooltip={} > - + ); diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormAzureKeyVaultConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormAzureKeyVaultConfig.tsx index 2a54bb99..bd2347df 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormAzureKeyVaultConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormAzureKeyVaultConfig.tsx @@ -35,7 +35,7 @@ const DeployNodeConfigFormAzureKeyVaultConfig = ({ .nonempty(t("workflow_node.deploy.form.azure_keyvault_name.placeholder")) .trim(), certificateName: z - .string({ message: t("workflow_node.deploy.form.azure_keyvault_certificate_name.placeholder") }) + .string() .nullish() .refine((v) => { if (!v) return true; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormBaiduCloudAppBLBConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormBaiduCloudAppBLBConfig.tsx index 7bd40b82..875d254b 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormBaiduCloudAppBLBConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormBaiduCloudAppBLBConfig.tsx @@ -135,7 +135,7 @@ const DeployNodeConfigFormBaiduCloudAppBLBConfig = ({ rules={[formRule]} tooltip={} > - + diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormBaiduCloudBLBConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormBaiduCloudBLBConfig.tsx index 20bb22f1..99c0b059 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormBaiduCloudBLBConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormBaiduCloudBLBConfig.tsx @@ -135,7 +135,7 @@ const DeployNodeConfigFormBaiduCloudBLBConfig = ({ rules={[formRule]} tooltip={} > - + diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormBaishanCDNConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormBaishanCDNConfig.tsx index ad05b6a8..7d32bef5 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormBaishanCDNConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormBaishanCDNConfig.tsx @@ -73,7 +73,7 @@ const DeployNodeConfigFormBaishanCDNConfig = ({ rules={[formRule]} tooltip={} > - + ); diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormBaotaWAFSiteConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormBaotaWAFSiteConfig.tsx new file mode 100644 index 00000000..6f992fb8 --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormBaotaWAFSiteConfig.tsx @@ -0,0 +1,78 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input, InputNumber } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { validPortNumber } from "@/utils/validators"; + +type DeployNodeConfigFormBaotaWAFSiteConfigFieldValues = Nullish<{ + siteName: string; + sitePort: number; +}>; + +export type DeployNodeConfigFormBaotaWAFSiteConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormBaotaWAFSiteConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormBaotaWAFSiteConfigFieldValues) => void; +}; + +const initFormModel = (): DeployNodeConfigFormBaotaWAFSiteConfigFieldValues => { + return { + siteName: "", + sitePort: 443, + }; +}; + +const DeployNodeConfigFormBaotaWAFSiteConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormBaotaWAFSiteConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + siteName: z.string().nonempty(t("workflow_node.deploy.form.baotawaf_site_name.placeholder")).trim(), + sitePort: z.preprocess( + (v) => Number(v), + z + .number() + .int(t("workflow_node.deploy.form.baotawaf_site_port.placeholder")) + .refine((v) => validPortNumber(v), t("common.errmsg.port_invalid")) + ), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + + + + + +
+ ); +}; + +export default DeployNodeConfigFormBaotaWAFSiteConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormCdnflyConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormCdnflyConfig.tsx index 45662e75..4d6ae25c 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormCdnflyConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormCdnflyConfig.tsx @@ -79,13 +79,23 @@ const DeployNodeConfigFormCdnflyConfig = ({ form: formInst, formName, disabled, - + } + > - + } + > diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormFlexCDNConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormFlexCDNConfig.tsx new file mode 100644 index 00000000..e24652be --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormFlexCDNConfig.tsx @@ -0,0 +1,84 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input, Select } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import Show from "@/components/Show"; + +type DeployNodeConfigFormFlexCDNConfigFieldValues = Nullish<{ + resourceType: string; + certificateId?: string | number; +}>; + +export type DeployNodeConfigFormFlexCDNConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormFlexCDNConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormFlexCDNConfigFieldValues) => void; +}; + +const RESOURCE_TYPE_CERTIFICATE = "certificate" as const; + +const initFormModel = (): DeployNodeConfigFormFlexCDNConfigFieldValues => { + return { + resourceType: RESOURCE_TYPE_CERTIFICATE, + certificateId: "", + }; +}; + +const DeployNodeConfigFormFlexCDNConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: DeployNodeConfigFormFlexCDNConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + resourceType: z.literal(RESOURCE_TYPE_CERTIFICATE, { + message: t("workflow_node.deploy.form.flexcdn_resource_type.placeholder"), + }), + certificateId: z + .union([z.string(), z.number().int()]) + .nullish() + .refine((v) => { + if (fieldResourceType !== RESOURCE_TYPE_CERTIFICATE) return true; + return /^\d+$/.test(v + "") && +v! > 0; + }, t("workflow_node.deploy.form.flexcdn_certificate_id.placeholder")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const fieldResourceType = Form.useWatch("resourceType", formInst); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ + + + + + } + > + + + +
+ ); +}; + +export default DeployNodeConfigFormFlexCDNConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormGcoreCDNConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormGcoreCDNConfig.tsx index 4d548949..f21a4bb9 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormGcoreCDNConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormGcoreCDNConfig.tsx @@ -67,7 +67,7 @@ const DeployNodeConfigFormGcoreCDNConfig = ({ form: formInst, formName, disabled rules={[formRule]} tooltip={} > - + ); diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormGoEdgeConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormGoEdgeConfig.tsx index 89dffb5f..5e0f7f85 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormGoEdgeConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormGoEdgeConfig.tsx @@ -68,7 +68,12 @@ const DeployNodeConfigFormGoEdgeConfig = ({ form: formInst, formName, disabled, - + } + > diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudALBConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudALBConfig.tsx index f54477ce..22c5bf08 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudALBConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudALBConfig.tsx @@ -132,7 +132,7 @@ const DeployNodeConfigFormJDCloudALBConfig = ({ rules={[formRule]} tooltip={} > - + diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormLeCDNConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormLeCDNConfig.tsx new file mode 100644 index 00000000..0636cedf --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormLeCDNConfig.tsx @@ -0,0 +1,103 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input, Select } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import Show from "@/components/Show"; + +type DeployNodeConfigFormLeCDNConfigFieldValues = Nullish<{ + resourceType: string; + certificateId?: string | number; + clientId?: string | number; +}>; + +export type DeployNodeConfigFormLeCDNConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormLeCDNConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormLeCDNConfigFieldValues) => void; +}; + +const RESOURCE_TYPE_CERTIFICATE = "certificate" as const; + +const initFormModel = (): DeployNodeConfigFormLeCDNConfigFieldValues => { + return { + resourceType: RESOURCE_TYPE_CERTIFICATE, + certificateId: "", + clientId: "", + }; +}; + +const DeployNodeConfigFormLeCDNConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: DeployNodeConfigFormLeCDNConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + resourceType: z.literal(RESOURCE_TYPE_CERTIFICATE, { + message: t("workflow_node.deploy.form.lecdn_resource_type.placeholder"), + }), + certificateId: z + .union([z.string(), z.number().int()]) + .nullish() + .refine((v) => { + if (fieldResourceType !== RESOURCE_TYPE_CERTIFICATE) return true; + return /^\d+$/.test(v + "") && +v! > 0; + }, t("workflow_node.deploy.form.lecdn_certificate_id.placeholder")), + clientId: z + .union([z.string(), z.number().int()]) + .nullish() + .refine((v) => { + if (fieldResourceType !== RESOURCE_TYPE_CERTIFICATE) return true; + if (v == null || v === "") return true; + return /^\d+$/.test(v + "") && +v! > 0; + }, t("workflow_node.deploy.form.lecdn_client_id.placeholder")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const fieldResourceType = Form.useWatch("resourceType", formInst); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ + + + + + } + > + + + + } + > + + + +
+ ); +}; + +export default DeployNodeConfigFormLeCDNConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormLocalConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormLocalConfig.tsx index 75853eb7..282503e5 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormLocalConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormLocalConfig.tsx @@ -351,7 +351,7 @@ const DeployNodeConfigFormLocalConfig = ({ form: formInst, formName, disabled, i rules={[formRule]} tooltip={} > - + } > - + diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormRatPanelSiteConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormRatPanelSiteConfig.tsx new file mode 100644 index 00000000..03f86913 --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormRatPanelSiteConfig.tsx @@ -0,0 +1,63 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +type DeployNodeConfigFormRatPanelSiteConfigFieldValues = Nullish<{ + siteName: string; +}>; + +export type DeployNodeConfigFormRatPanelSiteConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormRatPanelSiteConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormRatPanelSiteConfigFieldValues) => void; +}; + +const initFormModel = (): DeployNodeConfigFormRatPanelSiteConfigFieldValues => { + return { + siteName: "", + }; +}; + +const DeployNodeConfigFormRatPanelSiteConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormRatPanelSiteConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + siteName: z.string().nonempty(t("workflow_node.deploy.form.ratpanel_site_name.placeholder")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default DeployNodeConfigFormRatPanelSiteConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormSSHConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormSSHConfig.tsx index e99a2431..49110ce9 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormSSHConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormSSHConfig.tsx @@ -363,7 +363,7 @@ const DeployNodeConfigFormSSHConfig = ({ form: formInst, formName, disabled, ini rules={[formRule]} tooltip={} > - + } > - + diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormSafeLineConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormSafeLineConfig.tsx index 2da3ef16..c5905e14 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormSafeLineConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormSafeLineConfig.tsx @@ -68,7 +68,12 @@ const DeployNodeConfigFormSafeLineConfig = ({ form: formInst, formName, disabled - + } + > diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudCLBConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudCLBConfig.tsx index 2e7dc127..760c6fac 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudCLBConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudCLBConfig.tsx @@ -144,7 +144,7 @@ const DeployNodeConfigFormTencentCloudCLBConfig = ({ rules={[formRule]} tooltip={} > - + diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormVolcEngineALBConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormVolcEngineALBConfig.tsx index 348f4d8d..650323ab 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormVolcEngineALBConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormVolcEngineALBConfig.tsx @@ -140,7 +140,7 @@ const DeployNodeConfigFormVolcEngineALBConfig = ({ rules={[formRule]} tooltip={} > - + diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCDNConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCDNConfig.tsx new file mode 100644 index 00000000..57d0d381 --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCDNConfig.tsx @@ -0,0 +1,146 @@ +import { memo } from "react"; +import { useTranslation } from "react-i18next"; +import { FormOutlined as FormOutlinedIcon } from "@ant-design/icons"; +import { Button, Form, type FormInstance, Input, Space } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import ModalForm from "@/components/ModalForm"; +import MultipleInput from "@/components/MultipleInput"; +import { useAntdForm } from "@/hooks"; +import { validDomainName } from "@/utils/validators"; + +type DeployNodeConfigFormWangsuCDNConfigFieldValues = Nullish<{ + domains: string; +}>; + +export type DeployNodeConfigFormWangsuCDNConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormWangsuCDNConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormWangsuCDNConfigFieldValues) => void; +}; + +const MULTIPLE_INPUT_DELIMITER = ";"; + +const initFormModel = (): DeployNodeConfigFormWangsuCDNConfigFieldValues => { + return { + domains: "", + }; +}; + +const DeployNodeConfigFormWangsuCDNConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormWangsuCDNConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + domains: z + .string() + .nullish() + .refine((v) => { + if (!v) return false; + return String(v) + .split(MULTIPLE_INPUT_DELIMITER) + .every((e) => validDomainName(e)); + }, t("workflow_node.deploy.form.wangsu_cdn_domains.placeholder")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const fieldDomains = Form.useWatch("domains", formInst); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + + { + formInst.setFieldValue("domains", e.target.value); + }} + onClear={() => { + formInst.setFieldValue("domains", ""); + }} + /> + + + + + } + onChange={(value) => { + formInst.setFieldValue("domains", value); + }} + /> + + +
+ ); +}; + +const SiteNamesModalInput = memo(({ value, trigger, onChange }: { value?: string; trigger?: React.ReactNode; onChange?: (value: string) => void }) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + domains: z.array(z.string()).refine((v) => { + return v.every((e) => validDomainName(e)); + }, t("workflow_node.deploy.form.wangsu_cdn_domains.errmsg.invalid")), + }); + const formRule = createSchemaFieldRule(formSchema); + const { form: formInst, formProps } = useAntdForm({ + name: "workflowNodeDeployConfigFormWangsuCDNNamesModalInput", + initialValues: { domains: value?.split(MULTIPLE_INPUT_DELIMITER) }, + onSubmit: (values) => { + onChange?.( + values.domains + .map((e) => e.trim()) + .filter((e) => !!e) + .join(MULTIPLE_INPUT_DELIMITER) + ); + }, + }); + + return ( + + + + + + ); +}); + +export default DeployNodeConfigFormWangsuCDNConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCDNProConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCDNProConfig.tsx index 90bdb064..e89e1e8d 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCDNProConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCDNProConfig.tsx @@ -5,37 +5,37 @@ import { z } from "zod"; import { validDomainName } from "@/utils/validators"; -type DeployNodeConfigFormBaishanCDNConfigFieldValues = Nullish<{ +type DeployNodeConfigFormWangsuCDNProConfigFieldValues = Nullish<{ environment: string; domain: string; certificateId?: string; webhookId?: string; }>; -export type DeployNodeConfigFormBaishanCDNConfigProps = { +export type DeployNodeConfigFormWangsuCDNProConfigProps = { form: FormInstance; formName: string; disabled?: boolean; - initialValues?: DeployNodeConfigFormBaishanCDNConfigFieldValues; - onValuesChange?: (values: DeployNodeConfigFormBaishanCDNConfigFieldValues) => void; + initialValues?: DeployNodeConfigFormWangsuCDNProConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormWangsuCDNProConfigFieldValues) => void; }; const ENVIRONMENT_PRODUCTION = "production" as const; const ENVIRONMENT_STAGING = "stating" as const; -const initFormModel = (): DeployNodeConfigFormBaishanCDNConfigFieldValues => { +const initFormModel = (): DeployNodeConfigFormWangsuCDNProConfigFieldValues => { return { environment: ENVIRONMENT_PRODUCTION, }; }; -const DeployNodeConfigFormBaishanCDNConfig = ({ +const DeployNodeConfigFormWangsuCDNProConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange, -}: DeployNodeConfigFormBaishanCDNConfigProps) => { +}: DeployNodeConfigFormWangsuCDNProConfigProps) => { const { t } = useTranslation(); const formSchema = z.object({ @@ -89,7 +89,7 @@ const DeployNodeConfigFormBaishanCDNConfig = ({ rules={[formRule]} tooltip={} > - + } > - + ); }; -export default DeployNodeConfigFormBaishanCDNConfig; +export default DeployNodeConfigFormWangsuCDNProConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCertificateConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCertificateConfig.tsx new file mode 100644 index 00000000..739e4b28 --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCertificateConfig.tsx @@ -0,0 +1,61 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +type DeployNodeConfigFormWangsuCertificateConfigFieldValues = Nullish<{ + certificateId?: string; +}>; + +export type DeployNodeConfigFormWangsuCertificateConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormWangsuCertificateConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormWangsuCertificateConfigFieldValues) => void; +}; + +const initFormModel = (): DeployNodeConfigFormWangsuCertificateConfigFieldValues => { + return {}; +}; + +const DeployNodeConfigFormWangsuCertificateConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormWangsuCertificateConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + certificateId: z.string().nullish(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default DeployNodeConfigFormWangsuCertificateConfig; diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts index 9e2d7c35..f60f39c8 100644 --- a/ui/src/domain/access.ts +++ b/ui/src/domain/access.ts @@ -7,6 +7,7 @@ export interface AccessModel extends BaseModel { */ Record & ( | AccessConfigFor1Panel + | AccessConfigForACMECA | AccessConfigForACMEHttpReq | AccessConfigForAliyun | AccessConfigForAWS @@ -14,6 +15,7 @@ export interface AccessModel extends BaseModel { | AccessConfigForBaiduCloud | AccessConfigForBaishan | AccessConfigForBaotaPanel + | AccessConfigForBaotaWAF | AccessConfigForBunny | AccessConfigForBytePlus | AccessConfigForCacheFly @@ -28,6 +30,7 @@ export interface AccessModel extends BaseModel { | AccessConfigForDynv6 | AccessConfigForEdgio | AccessConfigForEmail + | AccessConfigForFlexCDN | AccessConfigForGcore | AccessConfigForGname | AccessConfigForGoDaddy @@ -37,6 +40,7 @@ export interface AccessModel extends BaseModel { | AccessConfigForJDCloud | AccessConfigForKubernetes | AccessConfigForLarkBot + | AccessConfigForLeCDN | AccessConfigForMattermost | AccessConfigForNamecheap | AccessConfigForNameDotCom @@ -48,6 +52,7 @@ export interface AccessModel extends BaseModel { | AccessConfigForProxmoxVE | AccessConfigForQiniu | AccessConfigForRainYun + | AccessConfigForRatPanel | AccessConfigForSafeLine | AccessConfigForSSH | AccessConfigForSSLCom @@ -69,10 +74,17 @@ export interface AccessModel extends BaseModel { // #region AccessConfig export type AccessConfigFor1Panel = { apiUrl: string; + apiVersion: string; apiKey: string; allowInsecureConnections?: boolean; }; +export type AccessConfigForACMECA = { + endpoint: string; + eabKid?: string; + eabHmacKey?: string; +}; + export type AccessConfigForACMEHttpReq = { endpoint: string; mode?: string; @@ -112,6 +124,12 @@ export type AccessConfigForBaotaPanel = { allowInsecureConnections?: boolean; }; +export type AccessConfigForBaotaWAF = { + apiUrl: string; + apiKey: string; + allowInsecureConnections?: boolean; +}; + export type AccessConfigForBunny = { apiKey: string; }; @@ -185,6 +203,14 @@ export type AccessConfigForEmail = { defaultReceiverAddress?: string; }; +export type AccessConfigForFlexCDN = { + apiUrl: string; + apiRole: string; + accessKeyId: string; + accessKey: string; + allowInsecureConnections?: boolean; +}; + export type AccessConfigForGcore = { apiToken: string; }; @@ -230,6 +256,15 @@ export type AccessConfigForLarkBot = { webhookUrl: string; }; +export type AccessConfigForLeCDN = { + apiUrl: string; + apiVersion: string; + apiRole: string; + username: string; + password: string; + allowInsecureConnections?: boolean; +}; + export type AccessConfigForMattermost = { serverUrl: string; username: string; @@ -292,6 +327,13 @@ export type AccessConfigForRainYun = { apiKey: string; }; +export type AccessConfigForRatPanel = { + apiUrl: string; + accessTokenId: number; + accessToken: string; + allowInsecureConnections?: boolean; +}; + export type AccessConfigForSafeLine = { apiUrl: string; apiToken: string; diff --git a/ui/src/domain/certificate.ts b/ui/src/domain/certificate.ts index 563d2476..c4bc8710 100644 --- a/ui/src/domain/certificate.ts +++ b/ui/src/domain/certificate.ts @@ -6,7 +6,7 @@ export interface CertificateModel extends BaseModel { serialNumber: string; certificate: string; privateKey: string; - issuer: string; + issuerOrg: string; keyAlgorithm: string; effectAt: ISO8601String; expireAt: ISO8601String; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 878814f5..15542455 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -5,6 +5,7 @@ */ export const ACCESS_PROVIDERS = Object.freeze({ ["1PANEL"]: "1panel", + ACMECA: "acmeca", ACMEHTTPREQ: "acmehttpreq", ALIYUN: "aliyun", AWS: "aws", @@ -12,6 +13,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ BAIDUCLOUD: "baiducloud", BAISHAN: "baishan", BAOTAPANEL: "baotapanel", + BAOTAWAF: "baotawaf", BUNNY: "bunny", BYTEPLUS: "byteplus", BUYPASS: "buypass", @@ -27,6 +29,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ DYNV6: "dynv6", EDGIO: "edgio", EMAIL: "email", + FLEXCDN: "flexcdn", GCORE: "gcore", GNAME: "gname", GODADDY: "godaddy", @@ -36,6 +39,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ JDCLOUD: "jdcloud", KUBERNETES: "k8s", LARKBOT: "larkbot", + LECDN: "lecdn", LETSENCRYPT: "letsencrypt", LETSENCRYPTSTAGING: "letsencryptstaging", LOCAL: "local", @@ -51,6 +55,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ PROXMOXVE: "proxmoxve", QINIU: "qiniu", RAINYUN: "rainyun", + RATPANEL: "ratpanel", SAFELINE: "safeline", SSH: "ssh", SSLCOM: "sslcom", @@ -120,10 +125,14 @@ export const accessProvidersMap: Map = new [CA_PROVIDERS.GOOGLETRUSTSERVICES], [CA_PROVIDERS.SSLCOM], [CA_PROVIDERS.ZEROSSL], + [CA_PROVIDERS.ACMECA], ].map(([type, builtin]) => [ type, { @@ -345,6 +357,7 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({ ALIYUN_DDOS: `${ACCESS_PROVIDERS.ALIYUN}-ddospro`, ALIYUN_ESA: `${ACCESS_PROVIDERS.ALIYUN}-esa`, ALIYUN_FC: `${ACCESS_PROVIDERS.ALIYUN}-fc`, + ALIYUN_GA: `${ACCESS_PROVIDERS.ALIYUN}-ga`, ALIYUN_LIVE: `${ACCESS_PROVIDERS.ALIYUN}-live`, ALIYUN_NLB: `${ACCESS_PROVIDERS.ALIYUN}-nlb`, ALIYUN_OSS: `${ACCESS_PROVIDERS.ALIYUN}-oss`, @@ -360,12 +373,15 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({ BAISHAN_CDN: `${ACCESS_PROVIDERS.BAISHAN}-cdn`, BAOTAPANEL_CONSOLE: `${ACCESS_PROVIDERS.BAOTAPANEL}-console`, BAOTAPANEL_SITE: `${ACCESS_PROVIDERS.BAOTAPANEL}-site`, + BAOTAWAF_CONSOLE: `${ACCESS_PROVIDERS.BAOTAWAF}-console`, + BAOTAWAF_SITE: `${ACCESS_PROVIDERS.BAOTAWAF}-site`, BUNNY_CDN: `${ACCESS_PROVIDERS.BUNNY}-cdn`, BYTEPLUS_CDN: `${ACCESS_PROVIDERS.BYTEPLUS}-cdn`, CACHEFLY: `${ACCESS_PROVIDERS.CACHEFLY}`, CDNFLY: `${ACCESS_PROVIDERS.CDNFLY}`, DOGECLOUD_CDN: `${ACCESS_PROVIDERS.DOGECLOUD}-cdn`, EDGIO_APPLICATIONS: `${ACCESS_PROVIDERS.EDGIO}-applications`, + FLEXCDN: `${ACCESS_PROVIDERS.FLEXCDN}`, GCORE_CDN: `${ACCESS_PROVIDERS.GCORE}-cdn`, GOEDGE: `${ACCESS_PROVIDERS.GOEDGE}`, HUAWEICLOUD_CDN: `${ACCESS_PROVIDERS.HUAWEICLOUD}-cdn`, @@ -377,6 +393,7 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({ JDCLOUD_LIVE: `${ACCESS_PROVIDERS.JDCLOUD}-live`, JDCLOUD_VOD: `${ACCESS_PROVIDERS.JDCLOUD}-vod`, KUBERNETES_SECRET: `${ACCESS_PROVIDERS.KUBERNETES}-secret`, + LECDN: `${ACCESS_PROVIDERS.LECDN}`, LOCAL: `${ACCESS_PROVIDERS.LOCAL}`, NETLIFY_SITE: `${ACCESS_PROVIDERS.NETLIFY}-site`, PROXMOXVE: `${ACCESS_PROVIDERS.PROXMOXVE}`, @@ -384,6 +401,8 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({ QINIU_KODO: `${ACCESS_PROVIDERS.QINIU}-kodo`, QINIU_PILI: `${ACCESS_PROVIDERS.QINIU}-pili`, RAINYUN_RCDN: `${ACCESS_PROVIDERS.RAINYUN}-rcdn`, + RATPANEL_CONSOLE: `${ACCESS_PROVIDERS.RATPANEL}-console`, + RATPANEL_SITE: `${ACCESS_PROVIDERS.RATPANEL}-site`, SAFELINE: `${ACCESS_PROVIDERS.SAFELINE}`, SSH: `${ACCESS_PROVIDERS.SSH}`, TENCENTCLOUD_CDN: `${ACCESS_PROVIDERS.TENCENTCLOUD}-cdn`, @@ -409,7 +428,9 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({ VOLCENGINE_IMAGEX: `${ACCESS_PROVIDERS.VOLCENGINE}-imagex`, VOLCENGINE_LIVE: `${ACCESS_PROVIDERS.VOLCENGINE}-live`, VOLCENGINE_TOS: `${ACCESS_PROVIDERS.VOLCENGINE}-tos`, + WANGSU_CDN: `${ACCESS_PROVIDERS.WANGSU}-cdn`, WANGSU_CDNPRO: `${ACCESS_PROVIDERS.WANGSU}-cdnpro`, + WANGSU_CERTIFICATE: `${ACCESS_PROVIDERS.WANGSU}-certificate`, WEBHOOK: `${ACCESS_PROVIDERS.WEBHOOK}`, } as const); @@ -422,8 +443,10 @@ export const DEPLOYMENT_CATEGORIES = Object.freeze({ LOADBALANCE: "loadbalance", FIREWALL: "firewall", AV: "av", + APIGATEWAY: "apigw", SERVERLESS: "serverless", WEBSITE: "website", + SSL: "ssl", NAS: "nas", OTHER: "other", } as const); @@ -461,9 +484,10 @@ export const deploymentProvidersMap: Map [ diff --git a/ui/src/i18n/locales/en/nls.access.json b/ui/src/i18n/locales/en/nls.access.json index 32ae2885..589b2572 100644 --- a/ui/src/i18n/locales/en/nls.access.json +++ b/ui/src/i18n/locales/en/nls.access.json @@ -36,12 +36,21 @@ "access.form.notification_channel.placeholder": "Please select a notification channel", "access.form.1panel_api_url.label": "1Panel URL", "access.form.1panel_api_url.placeholder": "Please enter 1Panel URL", + "access.form.1panel_api_version.label": "1Panel version", + "access.form.1panel_api_version.placeholder": "Please select 1Panel version", "access.form.1panel_api_key.label": "1Panel API key", "access.form.1panel_api_key.placeholder": "Please enter 1Panel API key", "access.form.1panel_api_key.tooltip": "For more information, see https://docs.1panel.pro/dev_manual/api_manual/", "access.form.1panel_allow_insecure_conns.label": "Insecure SSL/TLS connections", "access.form.1panel_allow_insecure_conns.switch.on": "Allow", "access.form.1panel_allow_insecure_conns.switch.off": "Disallow", + "access.form.acmeca_endpoint.label": "Endpoint", + "access.form.acmeca_endpoint.placeholder": "Please enter endpoint", + "access.form.acmeca_endpoint.tooltip": "For more information, see https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.1", + "access.form.acmeca_eab_kid.label": "ACME EAB KID (Optional)", + "access.form.acmeca_eab_kid.placeholder": "Please enter ACME EAB KID", + "access.form.acmeca_eab_hmac_key.label": "ACME EAB HMAC key (Optional)", + "access.form.acmeca_eab_hmac_key.placeholder": "Please enter ACME EAB HMAC key", "access.form.acmehttpreq_endpoint.label": "Endpoint", "access.form.acmehttpreq_endpoint.placeholder": "Please enter endpoint", "access.form.acmehttpreq_endpoint.tooltip": "For more information, see https://go-acme.github.io/lego/dns/httpreq/", @@ -103,6 +112,14 @@ "access.form.baotapanel_allow_insecure_conns.label": "Insecure SSL/TLS connections", "access.form.baotapanel_allow_insecure_conns.switch.on": "Allow", "access.form.baotapanel_allow_insecure_conns.switch.off": "Disallow", + "access.form.baotawaf_api_url.label": "aaWAF URL", + "access.form.baotawaf_api_url.placeholder": "Please enter aaWAF URL", + "access.form.baotawaf_api_key.label": "aaWAF API key", + "access.form.baotawaf_api_key.placeholder": "Please enter aaWAF API key", + "access.form.baotawaf_api_key.tooltip": "For more information, see https://github.com/aaPanel/aaWAF/blob/main/API.md", + "access.form.baotawaf_allow_insecure_conns.label": "Insecure SSL/TLS connections", + "access.form.baotawaf_allow_insecure_conns.switch.on": "Allow", + "access.form.baotawaf_allow_insecure_conns.switch.off": "Disallow", "access.form.byteplus_access_key.label": "BytePlus AccessKey", "access.form.byteplus_access_key.placeholder": "Please enter BytePlus AccessKey", "access.form.byteplus_access_key.tooltip": "For more information, see https://docs.byteplus.com/en/docs/byteplus-platform/docs-managing-keys", @@ -112,8 +129,8 @@ "access.form.cachefly_api_token.label": "CacheFly API token", "access.form.cachefly_api_token.placeholder": "Please enter CacheFly API token", "access.form.cachefly_api_token.tooltip": "For more information, see https://kb.cachefly.com/kb/guide/en/generating-tokens-and-keys-Oll9Irt5TI/Steps/2460228", - "access.form.cdnfly_api_url.label": "Cdnfly API URL", - "access.form.cdnfly_api_url.placeholder": "Please enter Cdnfly API URL", + "access.form.cdnfly_api_url.label": "Cdnfly URL", + "access.form.cdnfly_api_url.placeholder": "Please enter Cdnfly URL", "access.form.cdnfly_api_key.label": "Cdnfly user API key", "access.form.cdnfly_api_key.placeholder": "Please enter Cdnfly user API key", "access.form.cdnfly_api_key.tooltip": "For more information, see https://doc.cdnfly.cn/shiyongjieshao.html", @@ -184,6 +201,21 @@ "access.form.email_default_sender_address.placeholder": "Please enter default sender email address", "access.form.email_default_receiver_address.label": "Default receiver email address (Optional)", "access.form.email_default_receiver_address.placeholder": "Please enter default receiver email address", + "access.form.flexcdn_api_url.label": "FlexCDN URL", + "access.form.flexcdn_api_url.placeholder": "Please enter FlexCDN URL", + "access.form.flexcdn_api_role.label": "FlexCDN user role", + "access.form.flexcdn_api_role.placeholder": "Please select FlexCDN user role", + "access.form.flexcdn_api_role.option.user.label": "Platform user", + "access.form.flexcdn_api_role.option.admin.label": "Administrator user", + "access.form.flexcdn_access_key_id.label": "FlexCDN AccessKeyId", + "access.form.flexcdn_access_key_id.placeholder": "Please enter FlexCDN AccessKeyId", + "access.form.flexcdn_access_key_id.tooltip": "For more information, see https://flexcdn.cn/docs/api/auth", + "access.form.flexcdn_access_key.label": "FlexCDN AccessKey", + "access.form.flexcdn_access_key.placeholder": "Please enter FlexCDN AccessKey", + "access.form.flexcdn_access_key.tooltip": "For more information, see https://flexcdn.cn/docs/api/auth", + "access.form.flexcdn_allow_insecure_conns.label": "Insecure SSL/TLS connections", + "access.form.flexcdn_allow_insecure_conns.switch.on": "Allow", + "access.form.flexcdn_allow_insecure_conns.switch.off": "Disallow", "access.form.gcore_api_token.label": "Gcore API token", "access.form.gcore_api_token.placeholder": "Please enter Gcore API token", "access.form.gcore_api_token.tooltip": "For more information, see https://api.gcore.com/docs/iam#section/Authentication", @@ -199,8 +231,8 @@ "access.form.godaddy_api_secret.label": "GoDaddy API secret", "access.form.godaddy_api_secret.placeholder": "Please enter GoDaddy API secret", "access.form.godaddy_api_secret.tooltip": "For more information, see https://developer.godaddy.com/", - "access.form.goedge_api_url.label": "GoEdge API URL", - "access.form.goedge_api_url.placeholder": "Please enter GoEdge API URL", + "access.form.goedge_api_url.label": "GoEdge URL", + "access.form.goedge_api_url.placeholder": "Please enter GoEdge URL", "access.form.goedge_api_role.label": "GoEdge user role", "access.form.goedge_api_role.placeholder": "Please select GoEdge user role", "access.form.goedge_api_role.option.user.label": "Platform user", @@ -238,6 +270,21 @@ "access.form.larkbot_webhook_url.label": "Lark bot Webhook URL", "access.form.larkbot_webhook_url.placeholder": "Please enter Lark bot Webhook URL", "access.form.larkbot_webhook_url.tooltip": "For more information, see https://www.feishu.cn/hc/en-US/articles/807992406756", + "access.form.lecdn_api_url.label": "LeCDN URL", + "access.form.lecdn_api_url.placeholder": "Please enter LeCDN URL", + "access.form.lecdn_api_version.label": "LeCDN version", + "access.form.lecdn_api_version.placeholder": "Please select LeCDN version", + "access.form.lecdn_api_role.label": "LeCDN user role", + "access.form.lecdn_api_role.placeholder": "Please select LeCDN user role", + "access.form.lecdn_api_role.option.client.label": "Client", + "access.form.lecdn_api_role.option.master.label": "Master", + "access.form.lecdn_username.label": "LeCDN username", + "access.form.lecdn_username.placeholder": "Please enter LeCDN username", + "access.form.lecdn_password.label": "LeCDN password", + "access.form.lecdn_password.placeholder": "Please enter GoEdge password", + "access.form.lecdn_allow_insecure_conns.label": "Insecure SSL/TLS connections", + "access.form.lecdn_allow_insecure_conns.switch.on": "Allow", + "access.form.lecdn_allow_insecure_conns.switch.off": "Disallow", "access.form.mattermost_server_url.label": "Mattermost server URL", "access.form.mattermost_server_url.placeholder": "Please enter Mattermost server URL", "access.form.mattermost_username.label": "Mattermost username", @@ -262,8 +309,8 @@ "access.form.namesilo_api_key.label": "NameSilo API key", "access.form.namesilo_api_key.placeholder": "Please enter NameSilo API key", "access.form.namesilo_api_key.tooltip": "For more information, see https://www.namesilo.com/support/v2/articles/account-options/api-manager", - "access.form.netlify_api_token.label": "netlify API token", - "access.form.netlify_api_token.placeholder": "Please enter netlify API token", + "access.form.netlify_api_token.label": "Netlify API token", + "access.form.netlify_api_token.placeholder": "Please enter Netlify API token", "access.form.netlify_api_token.tooltip": "For more information, see https://docs.netlify.com/api/get-started/#authentication", "access.form.netcup_customer_number.label": "netcup customer number", "access.form.netcup_customer_number.placeholder": "Please enter netcup customer number", @@ -283,8 +330,8 @@ "access.form.porkbun_secret_api_key.label": "Porkbun secret API key", "access.form.porkbun_secret_api_key.placeholder": "Please enter Porkbun secret API key", "access.form.porkbun_secret_api_key.tooltip": "For more information, see https://porkbun.com/api/json/v3/documentation", - "access.form.powerdns_api_url.label": "PowerDNS API URL", - "access.form.powerdns_api_url.placeholder": "Please enter PowerDNS API URL", + "access.form.powerdns_api_url.label": "PowerDNS URL", + "access.form.powerdns_api_url.placeholder": "Please enter PowerDNS URL", "access.form.powerdns_api_key.label": "PowerDNS API key", "access.form.powerdns_api_key.placeholder": "Please enter PowerDNS API key", "access.form.powerdns_api_key.tooltip": "For more information, see https://doc.powerdns.com/authoritative/http-api/index.html#enabling-the-api", @@ -311,6 +358,17 @@ "access.form.rainyun_api_key.label": "Rain Yun API key", "access.form.rainyun_api_key.placeholder": "Please enter Rain Yun API key", "access.form.rainyun_api_key.tooltip": "For more information, see https://app.rainyun.com/account/settings/api-key", + "access.form.ratpanel_api_url.label": "RatPanel URL", + "access.form.ratpanel_api_url.placeholder": "Please enter RatPanel URL", + "access.form.ratpanel_access_token_id.label": "RatPanel access token ID", + "access.form.ratpanel_access_token_id.placeholder": "Please enter RatPanel access token ID", + "access.form.ratpanel_access_token_id.tooltip": "For more information, see https://ratpanel.github.io/advanced/api.html", + "access.form.ratpanel_access_token.label": "RatPanel access token", + "access.form.ratpanel_access_token.placeholder": "Please enter RatPanel access token", + "access.form.ratpanel_access_token.tooltip": "For more information, see https://ratpanel.github.io/advanced/api.html", + "access.form.ratpanel_allow_insecure_conns.label": "Insecure SSL/TLS connections", + "access.form.ratpanel_allow_insecure_conns.switch.on": "Allow", + "access.form.ratpanel_allow_insecure_conns.switch.off": "Disallow", "access.form.safeline_api_url.label": "SafeLine URL", "access.form.safeline_api_url.placeholder": "Please enter SafeLine URL", "access.form.safeline_api_token.label": "SafeLine API token", diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json index 82eab327..a3bbfecc 100644 --- a/ui/src/i18n/locales/en/nls.provider.json +++ b/ui/src/i18n/locales/en/nls.provider.json @@ -2,7 +2,8 @@ "provider.1panel": "1Panel", "provider.1panel.console": "1Panel - Console", "provider.1panel.site": "1Panel - Website", - "provider.acmehttpreq": "Http Request (ACME Proxy)", + "provider.acmeca": "ACME Custom CA Endpoint", + "provider.acmehttpreq": "ACME Custom HTTP Endpoint", "provider.aliyun": "Alibaba Cloud", "provider.aliyun.alb": "Alibaba Cloud - ALB (Application Load Balancer)", "provider.aliyun.apigw": "Alibaba Cloud - API Gateway", @@ -15,6 +16,7 @@ "provider.aliyun.dns": "Alibaba Cloud - DNS (Domain Name Service)", "provider.aliyun.esa": "Alibaba Cloud - ESA (Edge Security Acceleration)", "provider.aliyun.fc": "Alibaba Cloud - FC (Function Compute)", + "provider.aliyun.ga": "Alibaba Cloud - GA (Global Accelerator)", "provider.aliyun.live": "Alibaba Cloud - ApsaraVideo Live", "provider.aliyun.nlb": "Alibaba Cloud - NLB (Network Load Balancer)", "provider.aliyun.oss": "Alibaba Cloud - OSS (Object Storage Service)", @@ -40,6 +42,9 @@ "provider.baotapanel": "aaPanel (aka BaoTaPanel)", "provider.baotapanel.console": "aaPanel (aka BaoTaPanel) - Console", "provider.baotapanel.site": "aaPanel (aka BaoTaPanel) - Website", + "provider.baotawaf": "aaWAF (aka BaotaWAF)", + "provider.baotawaf.console": "aaWAF (aka BaotaWAF) - Console", + "provider.baotawaf.site": "aaWAF (aka BaotaWAF) - Website", "provider.bunny": "Bunny", "provider.bunny.cdn": "Bunny - CDN (Content Delivery Network)", "provider.byteplus": "BytePlus", @@ -62,6 +67,7 @@ "provider.edgio.applications": "Edgio - Applications", "provider.email": "Email", "provider.fastly": "Fastly", + "provider.flexcdn": "FlexCDN", "provider.gcore": "Gcore", "provider.gcore.cdn": "Gcore - CDN (Content Delivery Network)", "provider.gname": "GNAME", @@ -83,6 +89,7 @@ "provider.kubernetes": "Kubernetes", "provider.kubernetes.secret": "Kubernetes - Secret", "provider.larkbot": "Lark Bot", + "provider.lecdn": "LeCDN", "provider.letsencrypt": "Let's Encrypt", "provider.letsencryptstaging": "Let's Encrypt Staging Environment", "provider.local": "Local deployment", @@ -91,8 +98,8 @@ "provider.namedotcom": "Name.com", "provider.namesilo": "NameSilo", "provider.netcup": "netcup", - "provider.netlify": "netlify", - "provider.netlify.site": "netlify - Site", + "provider.netlify": "Netlify", + "provider.netlify.site": "Netlify - Site", "provider.ns1": "NS1 (IBM NS1 Connect)", "provider.porkbun": "Porkbun", "provider.powerdns": "PowerDNS", @@ -103,6 +110,9 @@ "provider.qiniu.pili": "Qiniu - Pili", "provider.rainyun": "Rain Yun", "provider.rainyun.rcdn": "Rain Yun - RCDN (Rain Content Delivery Network)", + "provider.ratpanel": "RatPanel", + "provider.ratpanel.console": "RatPanel - Console", + "provider.ratpanel.site": "RatPanel - Website", "provider.safeline": "SafeLine", "provider.ssh": "SSH deployment", "provider.sslcom": "SSL.com", @@ -138,7 +148,9 @@ "provider.volcengine.live": "Volcengine - Live", "provider.volcengine.tos": "Volcengine - TOS (Tinder Object Storage)", "provider.wangsu": "Wangsu Cloud", - "provider.wangsu.cdnpro": "Wangsu Cloud - CDN Pro", + "provider.wangsu.cdn": "Wangsu Cloud - CDN (Content Delivery Network)", + "provider.wangsu.cdnpro": "Wangsu Cloud - CDN Pro (CDN 360)", + "provider.wangsu.certificate_upload": "Wangsu Cloud - Upload to Certificate Management", "provider.webhook": "Webhook", "provider.wecombot": "WeCom Bot", "provider.westcn": "West.cn", @@ -150,8 +162,10 @@ "provider.category.loadbalance": "Loadbalance", "provider.category.firewall": "Firewall", "provider.category.av": "Audio/Video", + "provider.category.apigw": "API Gateway", "provider.category.serverless": "Serverless", "provider.category.website": "Website", + "provider.category.ssl": "SSL", "provider.category.nas": "NAS", "provider.category.other": "Other", diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json index 7b53e6e4..80237287 100644 --- a/ui/src/i18n/locales/en/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json @@ -213,6 +213,19 @@ "workflow_node.deploy.form.aliyun_fc_domain.label": "Alibaba Cloud FC domain", "workflow_node.deploy.form.aliyun_fc_domain.placeholder": "Please enter Alibaba Cloud FC domain name", "workflow_node.deploy.form.aliyun_fc_domain.tooltip": "For more information, see https://fcnext.console.aliyun.com", + "workflow_node.deploy.form.aliyun_ga_resource_type.label": "Resource type", + "workflow_node.deploy.form.aliyun_ga_resource_type.placeholder": "Please select resource type", + "workflow_node.deploy.form.aliyun_ga_resource_type.option.accelerator.label": "GA accelerator", + "workflow_node.deploy.form.aliyun_ga_resource_type.option.listener.label": "GA listener", + "workflow_node.deploy.form.aliyun_ga_accelerator_id.label": "Alibaba Cloud GA accelerator ID", + "workflow_node.deploy.form.aliyun_ga_accelerator_id.placeholder": "Please enter Alibaba Cloud GA accelerator ID", + "workflow_node.deploy.form.aliyun_ga_accelerator_id.tooltip": "For more information, https://ga.console.aliyun.com", + "workflow_node.deploy.form.aliyun_ga_listener_id.label": "Alibaba Cloud GA listener ID", + "workflow_node.deploy.form.aliyun_ga_listener_id.placeholder": "Please enter Alibaba Cloud GA listener ID", + "workflow_node.deploy.form.aliyun_ga_listener_id.tooltip": "For more information, https://ga.console.aliyun.com", + "workflow_node.deploy.form.aliyun_ga_snidomain.label": "Alibaba Cloud GA SNI domain (Optional)", + "workflow_node.deploy.form.aliyun_ga_snidomain.placeholder": "Please enter Alibaba Cloud GA SNI domain name", + "workflow_node.deploy.form.aliyun_ga_snidomain.tooltip": "For more information, https://ga.console.aliyun.com", "workflow_node.deploy.form.aliyun_live_region.label": "Alibaba Cloud Live region", "workflow_node.deploy.form.aliyun_live_region.placeholder": "Please enter Alibaba Cloud Live region (e.g. cn-hangzhou)", "workflow_node.deploy.form.aliyun_live_region.tooltip": "For more information, see https://www.alibabacloud.com/help/en/live/product-overview/supported-regions", @@ -325,13 +338,18 @@ "workflow_node.deploy.form.baotapanel_site_type.option.other.label": "Other sites", "workflow_node.deploy.form.baotapanel_site_name.label": "aaPanel site name", "workflow_node.deploy.form.baotapanel_site_name.placeholder": "Please enter aaPanel site name", - "workflow_node.deploy.form.baotapanel_site_name.tooltip": "Usually equal to the website domain name.", + "workflow_node.deploy.form.baotapanel_site_name.tooltip": "You can find it on aaPanel WebUI.", "workflow_node.deploy.form.baotapanel_site_names.label": "aaPanel site names", "workflow_node.deploy.form.baotapanel_site_names.placeholder": "Please enter aaPanel site names (separated by semicolons)", "workflow_node.deploy.form.baotapanel_site_names.errmsg.invalid": "Please enter a valid aaPanel site name", - "workflow_node.deploy.form.baotapanel_site_names.tooltip": "Usually equal to the websites domain name.", + "workflow_node.deploy.form.baotapanel_site_names.tooltip": "You can find it on aaPanel WebUI.", "workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.title": "Change aaPanel site names", "workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.placeholder": "Please enter aaPanel site name", + "workflow_node.deploy.form.baotawaf_site_name.label": "aaWAF site name", + "workflow_node.deploy.form.baotawaf_site_name.placeholder": "Please enter aaWAF site name", + "workflow_node.deploy.form.baotawaf_site_name.tooltip": "You can find it on aaWAF WebUI.", + "workflow_node.deploy.form.baotawaf_site_port.label": "aaWAF site SSL port", + "workflow_node.deploy.form.baotawaf_site_port.placeholder": "Please enter aaWAF SSL port", "workflow_node.deploy.form.bunny_cdn_pull_zone_id.label": "Bunny CDN pull zone ID", "workflow_node.deploy.form.bunny_cdn_pull_zone_id.placeholder": "Please enter Bunny CDN pull zone ID", "workflow_node.deploy.form.bunny_cdn_pull_zone_id.tooltip": "What is this? See https://dash.bunny.net/cdn", @@ -347,14 +365,22 @@ "workflow_node.deploy.form.cdnfly_resource_type.option.certificate.label": "Certificate", "workflow_node.deploy.form.cdnfly_site_id.label": "Cdnfly site ID", "workflow_node.deploy.form.cdnfly_site_id.placeholder": "Please enter Cdnfly site ID", + "workflow_node.deploy.form.cdnfly_site_id.tooltip": "You can find it on Cdnfly WebUI.", "workflow_node.deploy.form.cdnfly_certificate_id.label": "Cdnfly certificate ID", "workflow_node.deploy.form.cdnfly_certificate_id.placeholder": "Please enter Cdnfly certificate ID", + "workflow_node.deploy.form.cdnfly_certificate_id.tooltip": "You can find it on Cdnfly WebUI.", "workflow_node.deploy.form.dogecloud_cdn_domain.label": "Doge Cloud CDN domain", "workflow_node.deploy.form.dogecloud_cdn_domain.placeholder": "Please enter Doge Cloud CDN domain name", "workflow_node.deploy.form.dogecloud_cdn_domain.tooltip": "For more information, see https://console.dogecloud.com/", "workflow_node.deploy.form.edgio_applications_environment_id.label": "Edgio Applications environment ID", "workflow_node.deploy.form.edgio_applications_environment_id.placeholder": "Please enter Edgio Applications environment ID", "workflow_node.deploy.form.edgio_applications_environment_id.tooltip": "For more information, see https://edgio.app/", + "workflow_node.deploy.form.flexcdn_resource_type.label": "Resource type", + "workflow_node.deploy.form.flexcdn_resource_type.placeholder": "Please select resource type", + "workflow_node.deploy.form.flexcdn_resource_type.option.certificate.label": "Certificate", + "workflow_node.deploy.form.flexcdn_certificate_id.label": "FlexCDN certificate ID", + "workflow_node.deploy.form.flexcdn_certificate_id.placeholder": "Please enter FlexCDN certificate ID", + "workflow_node.deploy.form.flexcdn_certificate_id.tooltip": "You can find it on FlexCDN WebUI.", "workflow_node.deploy.form.gcore_cdn_resource_id.label": "Gcore CDN resource ID", "workflow_node.deploy.form.gcore_cdn_resource_id.placeholder": "Please enter Gcore CDN resource ID", "workflow_node.deploy.form.gcore_cdn_resource_id.tooltip": "For more information, see https://cdn.gcore.com/resources/list", @@ -366,6 +392,7 @@ "workflow_node.deploy.form.goedge_resource_type.option.certificate.label": "Certificate", "workflow_node.deploy.form.goedge_certificate_id.label": "GoEdge certificate ID", "workflow_node.deploy.form.goedge_certificate_id.placeholder": "Please enter GoEdge certificate ID", + "workflow_node.deploy.form.goedge_certificate_id.tooltip": "You can find it on GoEdge WebUI.", "workflow_node.deploy.form.huaweicloud_cdn_region.label": "Huawei Cloud CDN region", "workflow_node.deploy.form.huaweicloud_cdn_region.placeholder": "Please enter Huawei Cloud CDN region (e.g. cn-north-1)", "workflow_node.deploy.form.huaweicloud_cdn_region.tooltip": "For more information, see https://console-intl.huaweicloud.com/apiexplorer/#/endpoint", @@ -443,6 +470,15 @@ "workflow_node.deploy.form.k8s_secret_data_key_for_key.label": "Kubernetes Secret data key for private key", "workflow_node.deploy.form.k8s_secret_data_key_for_key.placeholder": "Please enter Kubernetes Secret data key for private key", "workflow_node.deploy.form.k8s_secret_data_key_for_key.tooltip": "For more information, see https://kubernetes.io/docs/concepts/configuration/secret/", + "workflow_node.deploy.form.lecdn_resource_type.label": "Resource type", + "workflow_node.deploy.form.lecdn_resource_type.placeholder": "Please select resource type", + "workflow_node.deploy.form.lecdn_resource_type.option.certificate.label": "Certificate", + "workflow_node.deploy.form.lecdn_certificate_id.label": "LeCDN certificate ID", + "workflow_node.deploy.form.lecdn_certificate_id.placeholder": "Please enter LeCDN certificate ID", + "workflow_node.deploy.form.lecdn_certificate_id.tooltip": "You can find it on LeCDN WebUI.", + "workflow_node.deploy.form.lecdn_client_id.label": "LeCDN user ID (Optional)", + "workflow_node.deploy.form.lecdn_client_id.placeholder": "Please enter LeCDN user ID", + "workflow_node.deploy.form.lecdn_client_id.tooltip": "You can find it on LeCDN WebUI.

Required when using administrator's authorization. It Must be the same as the user to which the certificate belongs.", "workflow_node.deploy.form.local.guide": "Tips: If you are running Certimate in Docker, the \"Local\" refers to the container rather than the host.", "workflow_node.deploy.form.local_format.label": "File format", "workflow_node.deploy.form.local_format.placeholder": "Please select file format", @@ -489,8 +525,8 @@ "workflow_node.deploy.form.local_preset_scripts.option.ps_binding_iis.label": "PowerShell - Binding IIS", "workflow_node.deploy.form.local_preset_scripts.option.ps_binding_netsh.label": "PowerShell - Binding netsh", "workflow_node.deploy.form.local_preset_scripts.option.ps_.label": "PowerShell - Binding RDP", - "workflow_node.deploy.form.netlify_site_id.label": "netlify site ID", - "workflow_node.deploy.form.netlify_site_id.placeholder": "Please enter netlify site ID", + "workflow_node.deploy.form.netlify_site_id.label": "Netlify site ID", + "workflow_node.deploy.form.netlify_site_id.placeholder": "Please enter Netlify site ID", "workflow_node.deploy.form.netlify_site_id.tooltip": "For more information, see https://docs.netlify.com/api/get-started/#get-site", "workflow_node.deploy.form.proxmoxve_node_name.label": "Proxmox VE cluster node name", "workflow_node.deploy.form.proxmoxve_node_name.placeholder": "Please enter Proxmox VE cluster node name", @@ -513,11 +549,15 @@ "workflow_node.deploy.form.rainyun_rcdn_domain.label": "Rain Yun RCDN domain", "workflow_node.deploy.form.rainyun_rcdn_domain.placeholder": "Please enter Rain Yun RCDN domain name", "workflow_node.deploy.form.rainyun_rcdn_domain.tooltip": "For more information, see https://app.rainyun.com/apps/rcdn/list", + "workflow_node.deploy.form.ratpanel_site_name.label": "RatPanel site name", + "workflow_node.deploy.form.ratpanel_site_name.placeholder": "Please enter RatPanel site name", + "workflow_node.deploy.form.ratpanel_site_name.tooltip": "You can find it on RatPanel WebUI.", "workflow_node.deploy.form.safeline_resource_type.label": "Resource type", "workflow_node.deploy.form.safeline_resource_type.placeholder": "Please select resource type", "workflow_node.deploy.form.safeline_resource_type.option.certificate.label": "Certificate", "workflow_node.deploy.form.safeline_certificate_id.label": "SafeLine certificate ID", "workflow_node.deploy.form.safeline_certificate_id.placeholder": "Please enter SafeLine certificate ID", + "workflow_node.deploy.form.safeline_certificate_id.tooltip": "You can find it on SafeLine WebUI.", "workflow_node.deploy.form.ssh_format.label": "File format", "workflow_node.deploy.form.ssh_format.placeholder": "Please select file format", "workflow_node.deploy.form.ssh_format.option.pem.label": "PEM (*.pem, *.crt, *.key)", @@ -720,6 +760,11 @@ "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.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)", + "workflow_node.deploy.form.wangsu_cdn_domains.tooltip": "For more information, see https://cdn.console.wangsu.com/v2/index/#/property/list", + "workflow_node.deploy.form.wangsu_cdn_domains.multiple_input_modal.title": "Change Wangsu Cloud CDN domains", + "workflow_node.deploy.form.wangsu_cdn_domains.multiple_input_modal.placeholder": "Please enter Wangsu Cloud CDN domain", "workflow_node.deploy.form.wangsu_cdnpro_environment.label": "Wangsu Cloud environment", "workflow_node.deploy.form.wangsu_cdnpro_environment.placeholder": "Please select Wangsu Cloud environment", "workflow_node.deploy.form.wangsu_cdnpro_environment.option.production.label": "Production environment", @@ -733,6 +778,9 @@ "workflow_node.deploy.form.wangsu_cdnpro_webhook_id.label": "Wangsu Cloud CDN Webhook ID (Optional)", "workflow_node.deploy.form.wangsu_cdnpro_webhook_id.placeholder": "Please enter Wangsu Cloud CDN Webhook ID", "workflow_node.deploy.form.wangsu_cdnpro_webhook_id.tooltip": "For more information, see https://cdnpro.console.wangsu.com/v2/index/#/certificate", + "workflow_node.deploy.form.wangsu_certificate_id.label": "Wangsu Cloud certificate ID (Optional)", + "workflow_node.deploy.form.wangsu_certificate_id.placeholder": "Please enter Wangsu Cloud certificate ID", + "workflow_node.deploy.form.wangsu_certificate_id.tooltip": "For more information, see https://cdn.console.wangsu.com/v2/index#/certificate/list", "workflow_node.deploy.form.webhook_data.label": "Webhook data (Optional)", "workflow_node.deploy.form.webhook_data.placeholder": "Please enter Webhook data to override the default value", "workflow_node.deploy.form.webhook_data.tooltip": "Leave it blank to use the default Webhook data provided by the authorization.", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index 5af00a92..f4ee2c2a 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -36,12 +36,21 @@ "access.form.notification_channel.placeholder": "请选择通知渠道", "access.form.1panel_api_url.label": "1Panel URL", "access.form.1panel_api_url.placeholder": "请输入 1Panel URL", + "access.form.1panel_api_version.label": "1Panel 版本", + "access.form.1panel_api_version.placeholder": "请选择 1Panel 版本", "access.form.1panel_api_key.label": "1Panel 接口密钥", "access.form.1panel_api_key.placeholder": "请输入 1Panel 接口密钥", "access.form.1panel_api_key.tooltip": "这是什么?请参阅 https://1panel.cn/docs/dev_manual/api_manual/", "access.form.1panel_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", "access.form.1panel_allow_insecure_conns.switch.on": "允许", "access.form.1panel_allow_insecure_conns.switch.off": "不允许", + "access.form.acmeca_endpoint.label": "服务端点", + "access.form.acmeca_endpoint.placeholder": "请输入服务端点", + "access.form.acmeca_endpoint.tooltip": "这是什么?请参阅 https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.1", + "access.form.acmeca_eab_kid.label": "ACME EAB KID(可选)", + "access.form.acmeca_eab_kid.placeholder": "请输入 ACME EAB KID", + "access.form.acmeca_eab_hmac_key.label": "ACME EAB HMAC Key(可选)", + "access.form.acmeca_eab_hmac_key.placeholder": "请输入 ACME EAB HMAC Key", "access.form.acmehttpreq_endpoint.label": "服务端点", "access.form.acmehttpreq_endpoint.placeholder": "请输入服务端点", "access.form.acmehttpreq_endpoint.tooltip": "这是什么?请参阅 https://go-acme.github.io/lego/dns/httpreq/", @@ -94,6 +103,14 @@ "access.form.baotapanel_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", "access.form.baotapanel_allow_insecure_conns.switch.on": "允许", "access.form.baotapanel_allow_insecure_conns.switch.off": "不允许", + "access.form.baotawaf_api_url.label": "堡塔云 WAF URL", + "access.form.baotawaf_api_url.placeholder": "请输入堡塔云 WAF URL", + "access.form.baotawaf_api_key.label": "堡塔云 WAF 接口密钥", + "access.form.baotawaf_api_key.placeholder": "请输入 堡塔云 WAF 接口密钥", + "access.form.baotawaf_api_key.tooltip": "这是什么?请参阅 https://github.com/aaPanel/aaWAF/blob/main/API.md", + "access.form.baotawaf_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", + "access.form.baotawaf_allow_insecure_conns.switch.on": "允许", + "access.form.baotawaf_allow_insecure_conns.switch.off": "不允许", "access.form.bunny_api_key.label": "Bunny API Key", "access.form.bunny_api_key.placeholder": "请输入 Bunny API Key", "access.form.bunny_api_key.tooltip": "这是什么?请参阅 https://docs.bunny.net/reference/bunnynet-api-overview", @@ -106,8 +123,8 @@ "access.form.cachefly_api_token.label": "CacheFly API Token", "access.form.cachefly_api_token.placeholder": "请输入 CacheFly API Token", "access.form.cachefly_api_token.tooltip": "这是什么?请参阅 https://kb.cachefly.com/kb/guide/en/generating-tokens-and-keys-Oll9Irt5TI/Steps/2460228", - "access.form.cdnfly_api_url.label": "Cdnfly API URL", - "access.form.cdnfly_api_url.placeholder": "请输入 Cdnfly API URL", + "access.form.cdnfly_api_url.label": "Cdnfly URL", + "access.form.cdnfly_api_url.placeholder": "请输入 Cdnfly URL", "access.form.cdnfly_api_key.label": "Cdnfly 用户端 API Key", "access.form.cdnfly_api_key.placeholder": "请输入 Cdnfly 用户端 API Key", "access.form.cdnfly_api_key.tooltip": "这是什么?请参阅 https://doc.cdnfly.cn/shiyongjieshao.html", @@ -178,6 +195,21 @@ "access.form.email_default_sender_address.placeholder": "请输入默认的发送邮箱地址", "access.form.email_default_receiver_address.label": "默认的接收邮箱地址(可选)", "access.form.email_default_receiver_address.placeholder": "请输入默认的接收邮箱地址", + "access.form.flexcdn_api_url.label": "FlexCDN URL", + "access.form.flexcdn_api_url.placeholder": "请输入 FlexCDN URL", + "access.form.flexcdn_api_role.label": "FlexCDN 用户角色", + "access.form.flexcdn_api_role.placeholder": "请选择 FlexCDN 用户角色", + "access.form.flexcdn_api_role.option.user.label": "平台用户", + "access.form.flexcdn_api_role.option.admin.label": "系统管理员", + "access.form.flexcdn_access_key_id.label": "FlexCDN AccessKeyId", + "access.form.flexcdn_access_key_id.placeholder": "请输入 FlexCDN AccessKeyId", + "access.form.flexcdn_access_key_id.tooltip": "这是什么?请参阅 https://flexcdn.cn/docs/api/auth", + "access.form.flexcdn_access_key.label": "FlexCDN AccessKey", + "access.form.flexcdn_access_key.placeholder": "请输入 FlexCDN AccessKey", + "access.form.flexcdn_access_key.tooltip": "这是什么?请参阅 https://flexcdn.cn/docs/api/auth", + "access.form.flexcdn_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", + "access.form.flexcdn_allow_insecure_conns.switch.on": "允许", + "access.form.flexcdn_allow_insecure_conns.switch.off": "不允许", "access.form.gcore_api_token.label": "Gcore API Token", "access.form.gcore_api_token.placeholder": "请输入 Gcore API Token", "access.form.gcore_api_token.tooltip": "这是什么?请参阅 https://api.gcore.com/docs/iam#section/Authentication", @@ -193,8 +225,8 @@ "access.form.godaddy_api_secret.label": "GoDaddy API Secret", "access.form.godaddy_api_secret.placeholder": "请输入 GoDaddy API Secret", "access.form.godaddy_api_secret.tooltip": "这是什么?请参阅 https://developer.godaddy.com/", - "access.form.goedge_api_url.label": "GoEdge API URL", - "access.form.goedge_api_url.placeholder": "请输入 GoEdge API URL", + "access.form.goedge_api_url.label": "GoEdge URL", + "access.form.goedge_api_url.placeholder": "请输入 GoEdge URL", "access.form.goedge_api_role.label": "GoEdge 用户角色", "access.form.goedge_api_role.placeholder": "请选择 GoEdge 用户角色", "access.form.goedge_api_role.option.user.label": "平台用户", @@ -232,6 +264,21 @@ "access.form.larkbot_webhook_url.label": "飞书群机器人 Webhook 地址", "access.form.larkbot_webhook_url.placeholder": "请输入飞书群机器人 Webhook 地址", "access.form.larkbot_webhook_url.tooltip": "这是什么?请参阅 https://www.feishu.cn/hc/zh-CN/articles/807992406756", + "access.form.lecdn_api_url.label": "LeCDN URL", + "access.form.lecdn_api_url.placeholder": "请输入 LeCDN URL", + "access.form.lecdn_api_version.label": "LeCDN 版本", + "access.form.lecdn_api_version.placeholder": "请选择 LeCDN 版本", + "access.form.lecdn_api_role.label": "LeCDN 用户角色", + "access.form.lecdn_api_role.placeholder": "请选择 LeCDN 用户角色", + "access.form.lecdn_api_role.option.client.label": "客户用户", + "access.form.lecdn_api_role.option.master.label": "主控管理员", + "access.form.lecdn_username.label": "LeCDN 用户名", + "access.form.lecdn_username.placeholder": "请输入 LeCDN 用户名", + "access.form.lecdn_password.label": "LeCDN 用户密码", + "access.form.lecdn_password.placeholder": "请输入 LeCDN 用户密码", + "access.form.lecdn_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", + "access.form.lecdn_allow_insecure_conns.switch.on": "允许", + "access.form.lecdn_allow_insecure_conns.switch.off": "不允许", "access.form.mattermost_server_url.label": "Mattermost 服务地址", "access.form.mattermost_server_url.placeholder": "请输入 Mattermost 服务地址", "access.form.mattermost_username.label": "Mattermost 用户名", @@ -256,8 +303,8 @@ "access.form.namesilo_api_key.label": "NameSilo API Key", "access.form.namesilo_api_key.placeholder": "请输入 NameSilo API Key", "access.form.namesilo_api_key.tooltip": "这是什么?请参阅 https://www.namesilo.com/support/v2/articles/account-options/api-manager", - "access.form.netlify_api_token.label": "netlify API Token", - "access.form.netlify_api_token.placeholder": "请输入 netlify API Token", + "access.form.netlify_api_token.label": "Netlify API Token", + "access.form.netlify_api_token.placeholder": "请输入 Netlify API Token", "access.form.netlify_api_token.tooltip": "这是什么?请参阅 https://docs.netlify.com/api/get-started/#authentication", "access.form.netcup_customer_number.label": "netcup 客户编号", "access.form.netcup_customer_number.placeholder": "请输入 netcup 客户编号", @@ -277,8 +324,8 @@ "access.form.porkbun_secret_api_key.label": "Porkbun Secret API Key", "access.form.porkbun_secret_api_key.placeholder": "请输入 Porkbun Secret API Key", "access.form.porkbun_secret_api_key.tooltip": "这是什么?请参阅 https://porkbun.com/api/json/v3/documentation", - "access.form.powerdns_api_url.label": "PowerDNS API URL", - "access.form.powerdns_api_url.placeholder": "请输入 PowerDNS API URL", + "access.form.powerdns_api_url.label": "PowerDNS URL", + "access.form.powerdns_api_url.placeholder": "请输入 PowerDNS URL", "access.form.powerdns_api_key.label": "PowerDNS API Key", "access.form.powerdns_api_key.placeholder": "请输入 PowerDNS API Key", "access.form.powerdns_api_key.tooltip": "这是什么?请参阅 https://doc.powerdns.com/authoritative/http-api/index.html#enabling-the-api", @@ -305,6 +352,17 @@ "access.form.rainyun_api_key.label": "雨云 API 密钥", "access.form.rainyun_api_key.placeholder": "请输入雨云 API 密钥", "access.form.rainyun_api_key.tooltip": "这是什么?请参阅 https://app.rainyun.com/account/settings/api-key", + "access.form.ratpanel_api_url.label": "耗子面板 URL", + "access.form.ratpanel_api_url.placeholder": "请输入耗子面板 URL", + "access.form.ratpanel_access_token_id.label": "耗子面板 AccessToken ID", + "access.form.ratpanel_access_token_id.placeholder": "请输入耗子面板 AccessToken ID", + "access.form.ratpanel_access_token_id.tooltip": "这是什么?请参阅 https://ratpanel.github.io/advanced/api.html", + "access.form.ratpanel_access_token.label": "耗子面板 AccessToken", + "access.form.ratpanel_access_token.placeholder": "请输入耗子面板 AccessToken", + "access.form.ratpanel_access_token.tooltip": "这是什么?请参阅 https://ratpanel.github.io/advanced/api.html", + "access.form.ratpanel_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", + "access.form.ratpanel_allow_insecure_conns.switch.on": "允许", + "access.form.ratpanel_allow_insecure_conns.switch.off": "不允许", "access.form.safeline_api_url.label": "雷池 URL", "access.form.safeline_api_url.placeholder": "请输入雷池 URL", "access.form.safeline_api_token.label": "雷池 API Token", diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json index dc635f68..b9fb8777 100644 --- a/ui/src/i18n/locales/zh/nls.provider.json +++ b/ui/src/i18n/locales/zh/nls.provider.json @@ -1,8 +1,9 @@ { "provider.1panel": "1Panel", - "provider.1panel.console": "1Panel - 面板", + "provider.1panel.console": "1Panel - 控制台", "provider.1panel.site": "1Panel - 网站", - "provider.acmehttpreq": "Http Request (ACME Proxy)", + "provider.acmeca": "ACME 自定义 CA 端点", + "provider.acmehttpreq": "ACME 自定义 HTTP 端点", "provider.aliyun": "阿里云", "provider.aliyun.alb": "阿里云 - 应用型负载均衡 ALB", "provider.aliyun.apigw": "阿里云 - API 网关", @@ -15,6 +16,7 @@ "provider.aliyun.dns": "阿里云 - 云解析 DNS", "provider.aliyun.esa": "阿里云 - 边缘安全加速 ESA", "provider.aliyun.fc": "阿里云 - 函数计算 FC", + "provider.aliyun.ga": "阿里云 - 全球加速 GA", "provider.aliyun.live": "阿里云 - 视频直播 Live", "provider.aliyun.nlb": "阿里云 - 网络型负载均衡 NLB", "provider.aliyun.oss": "阿里云 - 对象存储 OSS", @@ -38,8 +40,11 @@ "provider.baishan": "白山云", "provider.baishan.cdn": "白山云 - 内容分发网络 CDN", "provider.baotapanel": "宝塔面板", - "provider.baotapanel.console": "宝塔面板 - 面板", + "provider.baotapanel.console": "宝塔面板 - 控制台", "provider.baotapanel.site": "宝塔面板 - 网站", + "provider.baotawaf": "堡塔云 WAF", + "provider.baotawaf.console": "堡塔云 WAF - 控制台", + "provider.baotawaf.site": "堡塔云 WAF - 网站", "provider.bunny": "Bunny", "provider.bunny.cdn": "Bunny - 内容分发网络 CDN", "provider.byteplus": "BytePlus", @@ -62,6 +67,7 @@ "provider.edgio.applications": "Edgio - Applications", "provider.email": "邮件", "provider.fastly": "Fastly", + "provider.flexcdn": "FlexCDN", "provider.gcore": "Gcore", "provider.gcore.cdn": "Gcore - 内容分发网络 CDN", "provider.gname": "GNAME", @@ -83,6 +89,7 @@ "provider.kubernetes": "Kubernetes", "provider.kubernetes.secret": "Kubernetes - Secret", "provider.larkbot": "飞书群机器人", + "provider.lecdn": "LeCDN", "provider.letsencrypt": "Let's Encrypt", "provider.letsencryptstaging": "Let's Encrypt 测试环境", "provider.local": "本地部署", @@ -91,8 +98,8 @@ "provider.namedotcom": "Name.com", "provider.namesilo": "NameSilo", "provider.netcup": "netcup", - "provider.netlify": "netlify", - "provider.netlify.site": "netlify - Site", + "provider.netlify": "Netlify", + "provider.netlify.site": "Netlify - Site", "provider.ns1": "NS1 (IBM NS1 Connect)", "provider.porkbun": "Porkbun", "provider.powerdns": "PowerDNS", @@ -103,6 +110,9 @@ "provider.qiniu.pili": "七牛云 - 视频直播 Pili", "provider.rainyun": "雨云", "provider.rainyun.rcdn": "雨云 - 雨盾 CDN", + "provider.ratpanel": "耗子面板", + "provider.ratpanel.console": "耗子面板 - 控制台", + "provider.ratpanel.site": "耗子面板 - 网站", "provider.safeline": "雷池", "provider.ssh": "SSH 部署", "provider.sslcom": "SSL.com", @@ -138,7 +148,9 @@ "provider.volcengine.live": "火山引擎 - 视频直播 Live", "provider.volcengine.tos": "火山引擎 - 对象存储 TOS", "provider.wangsu": "网宿云", - "provider.wangsu.cdnpro": "网宿云 - CDN Pro", + "provider.wangsu.cdn": "网宿云 - 内容分发网络 CDN", + "provider.wangsu.cdnpro": "网宿云 - CDN Pro (CDN 360)", + "provider.wangsu.certificate_upload": "网宿云 - 上传到证书管理", "provider.webhook": "Webhook", "provider.wecombot": "企业微信群机器人", "provider.westcn": "西部数码", @@ -150,8 +162,10 @@ "provider.category.loadbalance": "负载均衡", "provider.category.firewall": "防火墙", "provider.category.av": "音视频", + "provider.category.apigw": "API 网关", "provider.category.serverless": "Serverless", "provider.category.website": "网站托管", + "provider.category.ssl": "证书托管", "provider.category.nas": "NAS", "provider.category.other": "其他", diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index 89cbfc11..faf40816 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -109,18 +109,18 @@ "workflow_node.deploy.form.certificate.tooltip": "待部署证书来自之前的申请或上传节点。如果选项为空请先确保前序节点配置正确。", "workflow_node.deploy.form.params_config.label": "参数设置", "workflow_node.deploy.form.1panel_console_auto_restart.label": "部署后自动重启宝塔面板服务", - "workflow_node.deploy.form.1panel_site_resource_type.label": "证书替换方式", - "workflow_node.deploy.form.1panel_site_resource_type.placeholder": "请选择证书替换方式", + "workflow_node.deploy.form.1panel_site_resource_type.label": "证书部署方式", + "workflow_node.deploy.form.1panel_site_resource_type.placeholder": "请选择证书部署方式", "workflow_node.deploy.form.1panel_site_resource_type.option.website.label": "替换指定网站的证书", "workflow_node.deploy.form.1panel_site_resource_type.option.certificate.label": "替换指定证书", "workflow_node.deploy.form.1panel_site_website_id.label": "1Panel 网站 ID", "workflow_node.deploy.form.1panel_site_website_id.placeholder": "请输入 1Panel 网站 ID", - "workflow_node.deploy.form.1panel_site_website_id.tooltip": "请在 1Panel 管理面板查看。", + "workflow_node.deploy.form.1panel_site_website_id.tooltip": "请登录 1Panel 面板查看。", "workflow_node.deploy.form.1panel_site_certificate_id.label": "1Panel 证书 ID", "workflow_node.deploy.form.1panel_site_certificate_id.placeholder": "请输入 1Panel 证书 ID", - "workflow_node.deploy.form.1panel_site_certificate_id.tooltip": "请在 1Panel 管理面板查看。", - "workflow_node.deploy.form.aliyun_alb_resource_type.label": "证书替换方式", - "workflow_node.deploy.form.aliyun_alb_resource_type.placeholder": "请选择证书替换方式", + "workflow_node.deploy.form.1panel_site_certificate_id.tooltip": "请登录 1Panel 面板查看。", + "workflow_node.deploy.form.aliyun_alb_resource_type.label": "证书部署方式", + "workflow_node.deploy.form.aliyun_alb_resource_type.placeholder": "请选择证书部署方式", "workflow_node.deploy.form.aliyun_alb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS/QUIC 监听的证书", "workflow_node.deploy.form.aliyun_alb_resource_type.option.listener.label": "替换指定负载均衡监听器的证书", "workflow_node.deploy.form.aliyun_alb_region.label": "阿里云 ALB 服务地域", @@ -170,8 +170,8 @@ "workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.tooltip": "这是什么?请参阅 https://help.aliyun.com/zh/ssl-certificate/developer-reference/api-cas-2020-04-07-listcontact

不填写时,将使用系统联系人列表中的第一个。", "workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.multiple_input_modal.title": "修改阿里云联系人 ID", "workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.multiple_input_modal.placeholder": "请输入阿里云联系人 ID", - "workflow_node.deploy.form.aliyun_clb_resource_type.label": "证书替换方式", - "workflow_node.deploy.form.aliyun_clb_resource_type.placeholder": "请选择证书替换方式", + "workflow_node.deploy.form.aliyun_clb_resource_type.label": "证书部署方式", + "workflow_node.deploy.form.aliyun_clb_resource_type.placeholder": "请选择证书部署方式", "workflow_node.deploy.form.aliyun_clb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS 监听的证书", "workflow_node.deploy.form.aliyun_clb_resource_type.option.listener.label": "替换指定负载均衡监听的证书", "workflow_node.deploy.form.aliyun_clb_region.label": "阿里云 CLB 服务地域", @@ -212,14 +212,27 @@ "workflow_node.deploy.form.aliyun_fc_domain.label": "阿里云 FC 自定义域名", "workflow_node.deploy.form.aliyun_fc_domain.placeholder": "请输入阿里云 FC 自定义域名(支持泛域名)", "workflow_node.deploy.form.aliyun_fc_domain.tooltip": "这是什么?请参阅 see https://fcnext.console.aliyun.com/", + "workflow_node.deploy.form.aliyun_ga_resource_type.label": "证书部署方式", + "workflow_node.deploy.form.aliyun_ga_resource_type.placeholder": "请选择证书部署方式", + "workflow_node.deploy.form.aliyun_ga_resource_type.option.accelerator.label": "替换指定全球加速器下的全部 HTTPS 监听的证书", + "workflow_node.deploy.form.aliyun_ga_resource_type.option.listener.label": "替换指定全球加速器监听器的证书", + "workflow_node.deploy.form.aliyun_ga_accelerator_id.label": "阿里云全球加速实例 ID", + "workflow_node.deploy.form.aliyun_ga_accelerator_id.placeholder": "请输入阿里云全球加速实例 ID", + "workflow_node.deploy.form.aliyun_ga_accelerator_id.tooltip": "这是什么?请参阅 https://ga.console.aliyun.com", + "workflow_node.deploy.form.aliyun_ga_listener_id.label": "阿里云全球加速监听 ID", + "workflow_node.deploy.form.aliyun_ga_listener_id.placeholder": "请输入阿里云全球加速监听 ID", + "workflow_node.deploy.form.aliyun_ga_listener_id.tooltip": "这是什么?请参阅 https://ga.console.aliyun.com", + "workflow_node.deploy.form.aliyun_ga_snidomain.label": "阿里云全球加速扩展域名(可选)", + "workflow_node.deploy.form.aliyun_ga_snidomain.placeholder": "请输入阿里云全球加速扩展域名", + "workflow_node.deploy.form.aliyun_ga_snidomain.tooltip": "这是什么?请参阅 https://ga.console.aliyun.com

不填写时,将替换监听器的默认证书;否则,将替换扩展域名证书。", "workflow_node.deploy.form.aliyun_live_region.label": "阿里云视频直播服务地域", "workflow_node.deploy.form.aliyun_live_region.placeholder": "请输入阿里云视频直播服务地域(例如:cn-hangzhou)", "workflow_node.deploy.form.aliyun_live_region.tooltip": "这是什么?请参阅 https://help.aliyun.com/zh/live/product-overview/supported-regions", "workflow_node.deploy.form.aliyun_live_domain.label": "阿里云视频直播流域名", "workflow_node.deploy.form.aliyun_live_domain.placeholder": "请输入阿里云视频直播流域名(支持泛域名)", "workflow_node.deploy.form.aliyun_live_domain.tooltip": "这是什么?请参阅 https://live.console.aliyun.com", - "workflow_node.deploy.form.aliyun_nlb_resource_type.label": "证书替换方式", - "workflow_node.deploy.form.aliyun_nlb_resource_type.placeholder": "请选择证书替换方式", + "workflow_node.deploy.form.aliyun_nlb_resource_type.label": "证书部署方式", + "workflow_node.deploy.form.aliyun_nlb_resource_type.placeholder": "请选择证书部署方式", "workflow_node.deploy.form.aliyun_nlb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS/QUIC 监听的证书", "workflow_node.deploy.form.aliyun_nlb_resource_type.option.listener.label": "替换指定负载均衡监听器的证书", "workflow_node.deploy.form.aliyun_nlb_region.label": "阿里云 NLB 服务地域", @@ -276,8 +289,8 @@ "workflow_node.deploy.form.azure_keyvault_certificate_name.placeholder": "请输入 Azure KeyVault 证书名称", "workflow_node.deploy.form.azure_keyvault_certificate_name.tooltip": "不填写时,将由 Certimate 自动生成证书名称。", "workflow_node.deploy.form.azure_keyvault_certificate_name.errmsg.invalid": "证书名称只能包含字母、数字和连字符(-),长度限制为 1 到 127 个字符", - "workflow_node.deploy.form.baiducloud_appblb_resource_type.label": "证书替换方式", - "workflow_node.deploy.form.baiducloud_appblb_resource_type.placeholder": "请选择证书替换方式", + "workflow_node.deploy.form.baiducloud_appblb_resource_type.label": "证书部署方式", + "workflow_node.deploy.form.baiducloud_appblb_resource_type.placeholder": "请选择证书部署方式", "workflow_node.deploy.form.baiducloud_appblb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS/SSL 监听的证书", "workflow_node.deploy.form.baiducloud_appblb_resource_type.option.listener.label": "替换指定负载均衡监听的证书", "workflow_node.deploy.form.baiducloud_appblb_region.label": "百度智能云 BLB 服务地域", @@ -292,8 +305,8 @@ "workflow_node.deploy.form.baiducloud_appblb_snidomain.label": "百度智能云 BLB 扩展域名(可选)", "workflow_node.deploy.form.baiducloud_appblb_snidomain.placeholder": "请输入百度智能云 BLB 扩展域名(支持泛域名)", "workflow_node.deploy.form.baiducloud_appblb_snidomain.tooltip": "这是什么?请参阅 https://console.bce.baidu.com/blb/#/appblb/list

不填写时,将替换监听器的默认证书;否则,将替换扩展域名证书。", - "workflow_node.deploy.form.baiducloud_blb_resource_type.label": "证书替换方式", - "workflow_node.deploy.form.baiducloud_blb_resource_type.placeholder": "请选择证书替换方式", + "workflow_node.deploy.form.baiducloud_blb_resource_type.label": "证书部署方式", + "workflow_node.deploy.form.baiducloud_blb_resource_type.placeholder": "请选择证书部署方式", "workflow_node.deploy.form.baiducloud_blb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS/SSL 监听的证书", "workflow_node.deploy.form.baiducloud_blb_resource_type.option.listener.label": "替换指定负载均衡监听的证书", "workflow_node.deploy.form.baiducloud_blb_region.label": "百度智能云 BLB 服务地域", @@ -324,13 +337,18 @@ "workflow_node.deploy.form.baotapanel_site_type.option.other.label": "其他", "workflow_node.deploy.form.baotapanel_site_name.label": "宝塔面板网站名称", "workflow_node.deploy.form.baotapanel_site_name.placeholder": "请输入宝塔面板网站名称", - "workflow_node.deploy.form.baotapanel_site_name.tooltip": "通常为网站域名。", + "workflow_node.deploy.form.baotapanel_site_name.tooltip": "请登录宝塔面板查看。", "workflow_node.deploy.form.baotapanel_site_names.label": "宝塔面板网站名称", "workflow_node.deploy.form.baotapanel_site_names.placeholder": "请输入宝塔面板网站名称(多个值请用半角分号隔开)", "workflow_node.deploy.form.baotapanel_site_names.errmsg.invalid": "请输入正确的宝塔面板网站名称", - "workflow_node.deploy.form.baotapanel_site_names.tooltip": "通常为网站域名。", + "workflow_node.deploy.form.baotapanel_site_names.tooltip": "请登录宝塔面板查看。", "workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.title": "修改宝塔面板网站名称", "workflow_node.deploy.form.baotapanel_site_names.multiple_input_modal.placeholder": "请输入宝塔面板网站名称", + "workflow_node.deploy.form.baotawaf_site_name.label": "堡塔云 WAF 网站名称", + "workflow_node.deploy.form.baotawaf_site_name.placeholder": "请输入堡塔云 WAF 网站名称", + "workflow_node.deploy.form.baotawaf_site_name.tooltip": "请登录堡塔云 WAF 面板查看。", + "workflow_node.deploy.form.baotawaf_site_port.label": "堡塔云 WAF 网站 SSL 端口", + "workflow_node.deploy.form.baotawaf_site_port.placeholder": "请输入堡塔云 WAF 网站 SSL 端口", "workflow_node.deploy.form.bunny_cdn_pull_zone_id.label": "Bunny CDN 拉取区域 ID", "workflow_node.deploy.form.bunny_cdn_pull_zone_id.placeholder": "请输入 Bunny CDN 拉取区域 ID", "workflow_node.deploy.form.bunny_cdn_pull_zone_id.tooltip": "这是什么?请参阅 https://dash.bunny.net/cdn", @@ -340,39 +358,48 @@ "workflow_node.deploy.form.byteplus_cdn_domain.label": "BytePlus CDN 域名", "workflow_node.deploy.form.byteplus_cdn_domain.placeholder": "请输入 BytePlus CDN 域名(支持泛域名)", "workflow_node.deploy.form.byteplus_cdn_domain.tooltip": "这是什么?请参阅 https://console.byteplus.com/cdn", - "workflow_node.deploy.form.cdnfly_resource_type.label": "证书替换方式", - "workflow_node.deploy.form.cdnfly_resource_type.placeholder": "请选择证书替换方式", + "workflow_node.deploy.form.cdnfly_resource_type.label": "证书部署方式", + "workflow_node.deploy.form.cdnfly_resource_type.placeholder": "请选择证书部署方式", "workflow_node.deploy.form.cdnfly_resource_type.option.site.label": "替换指定网站的证书", "workflow_node.deploy.form.cdnfly_resource_type.option.certificate.label": "替换指定证书", "workflow_node.deploy.form.cdnfly_site_id.label": "Cdnfly 网站 ID", "workflow_node.deploy.form.cdnfly_site_id.placeholder": "请输入 Cdnfly 网站 ID", + "workflow_node.deploy.form.cdnfly_site_id.tooltip": "请登录 Cdnfly 控制台查看。", "workflow_node.deploy.form.cdnfly_certificate_id.label": "Cdnfly 证书 ID", "workflow_node.deploy.form.cdnfly_certificate_id.placeholder": "请输入 Cdnfly 证书 ID", + "workflow_node.deploy.form.cdnfly_certificate_id.tooltip": "请登录 Cdnfly 控制台查看。", "workflow_node.deploy.form.dogecloud_cdn_domain.label": "多吉云 CDN 加速域名", "workflow_node.deploy.form.dogecloud_cdn_domain.placeholder": "请输入多吉云 CDN 加速域名", "workflow_node.deploy.form.dogecloud_cdn_domain.tooltip": "这是什么?请参阅 https://console.dogecloud.com", "workflow_node.deploy.form.edgio_applications_environment_id.label": "Edgio Applications 环境 ID", "workflow_node.deploy.form.edgio_applications_environment_id.placeholder": "请输入 Edgio Applications 环境 ID", "workflow_node.deploy.form.edgio_applications_environment_id.tooltip": "这是什么?请参阅 https://edgio.app/", + "workflow_node.deploy.form.flexcdn_resource_type.label": "证书部署方式", + "workflow_node.deploy.form.flexcdn_resource_type.placeholder": "请选择证书部署方式", + "workflow_node.deploy.form.flexcdn_resource_type.option.certificate.label": "替换指定证书", + "workflow_node.deploy.form.flexcdn_certificate_id.label": "FlexCDN 证书 ID", + "workflow_node.deploy.form.flexcdn_certificate_id.placeholder": "请输入 FlexCDN 证书 ID", + "workflow_node.deploy.form.flexcdn_certificate_id.tooltip": "请登录 FlexCDN 控制台查看。", "workflow_node.deploy.form.gcore_cdn_resource_id.label": "Gcore CDN 资源 ID", "workflow_node.deploy.form.gcore_cdn_resource_id.placeholder": "请输入 Gcore CDN 资源 ID", "workflow_node.deploy.form.gcore_cdn_resource_id.tooltip": "这是什么?请参阅 https://cdn.gcore.com/resources/list", "workflow_node.deploy.form.gcore_cdn_certificate_id.label": "Gcore CDN 原证书 ID(可选)", "workflow_node.deploy.form.gcore_cdn_certificate_id.placeholder": "请输入 Gcore CDN 原证书 ID", "workflow_node.deploy.form.gcore_cdn_certificate_id.tooltip": "这是什么?请参阅 https://cdn.gcore.com/ssl

不填写时,将上传新证书;否则,将替换原证书。", - "workflow_node.deploy.form.goedge_resource_type.label": "证书替换方式", - "workflow_node.deploy.form.goedge_resource_type.placeholder": "请选择证书替换方式", + "workflow_node.deploy.form.goedge_resource_type.label": "证书部署方式", + "workflow_node.deploy.form.goedge_resource_type.placeholder": "请选择证书部署方式", "workflow_node.deploy.form.goedge_resource_type.option.certificate.label": "替换指定证书", "workflow_node.deploy.form.goedge_certificate_id.label": "GoEdge 证书 ID", "workflow_node.deploy.form.goedge_certificate_id.placeholder": "请输入 GoEdge 证书 ID", + "workflow_node.deploy.form.goedge_certificate_id.tooltip": "请登录 GoEdge 控制台查看。", "workflow_node.deploy.form.huaweicloud_cdn_region.label": "华为云 CDN 服务区域", "workflow_node.deploy.form.huaweicloud_cdn_region.placeholder": "请输入华为云 CDN 服务区域(例如:cn-north-1)", "workflow_node.deploy.form.huaweicloud_cdn_region.tooltip": "这是什么?请参阅 https://console.huaweicloud.com/apiexplorer/#/endpoint", "workflow_node.deploy.form.huaweicloud_cdn_domain.label": "华为云 CDN 加速域名", "workflow_node.deploy.form.huaweicloud_cdn_domain.placeholder": "请输入华为云 CDN 加速域名", "workflow_node.deploy.form.huaweicloud_cdn_domain.tooltip": "这是什么?请参阅 https://console.huaweicloud.com/cdn", - "workflow_node.deploy.form.huaweicloud_elb_resource_type.label": "证书替换方式", - "workflow_node.deploy.form.huaweicloud_elb_resource_type.placeholder": "请选择证书替换方式", + "workflow_node.deploy.form.huaweicloud_elb_resource_type.label": "证书部署方式", + "workflow_node.deploy.form.huaweicloud_elb_resource_type.placeholder": "请选择证书部署方式", "workflow_node.deploy.form.huaweicloud_elb_resource_type.option.certificate.label": "替换指定证书", "workflow_node.deploy.form.huaweicloud_elb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS 监听器的证书", "workflow_node.deploy.form.huaweicloud_elb_resource_type.option.listener.label": "替换指定监听器的证书", @@ -388,8 +415,8 @@ "workflow_node.deploy.form.huaweicloud_elb_listener_id.label": "华为云 ELB 监听器 ID", "workflow_node.deploy.form.huaweicloud_elb_listener_id.placeholder": "请输入华为云 ELB 监听器 ID", "workflow_node.deploy.form.huaweicloud_elb_listener_id.tooltip": "这是什么?请参阅 https://console.huaweicloud.com/vpc/#/elb/list/grid", - "workflow_node.deploy.form.huaweicloud_waf_resource_type.label": "证书替换方式", - "workflow_node.deploy.form.huaweicloud_waf_resource_type.placeholder": "请选择证书替换方式", + "workflow_node.deploy.form.huaweicloud_waf_resource_type.label": "证书部署方式", + "workflow_node.deploy.form.huaweicloud_waf_resource_type.placeholder": "请选择证书部署方式", "workflow_node.deploy.form.huaweicloud_waf_resource_type.option.certificate.label": "替换指定证书", "workflow_node.deploy.form.huaweicloud_waf_resource_type.option.cloudserver.label": "替换指定云模式防护网站的证书", "workflow_node.deploy.form.huaweicloud_waf_resource_type.option.premiumhost.label": "替换指定独享模式防护网站的证书", @@ -402,8 +429,8 @@ "workflow_node.deploy.form.huaweicloud_waf_domain.label": "华为云 WAF 防护域名", "workflow_node.deploy.form.huaweicloud_waf_domain.placeholder": "请输入华为云 WAF 防护域名(支持泛域名)", "workflow_node.deploy.form.huaweicloud_waf_domain.tooltip": "这是什么?请参阅 https://console.huaweicloud.com/console/#/waf/domain/list", - "workflow_node.deploy.form.jdcloud_alb_resource_type.label": "证书替换方式", - "workflow_node.deploy.form.jdcloud_alb_resource_type.placeholder": "请选择证书替换方式", + "workflow_node.deploy.form.jdcloud_alb_resource_type.label": "证书部署方式", + "workflow_node.deploy.form.jdcloud_alb_resource_type.placeholder": "请选择证书部署方式", "workflow_node.deploy.form.jdcloud_alb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS/TLS 监听的证书", "workflow_node.deploy.form.jdcloud_alb_resource_type.option.listener.label": "替换指定负载均衡监听器的证书", "workflow_node.deploy.form.jdcloud_alb_region_id.label": "京东云 ALB 服务地域 ID", @@ -442,6 +469,15 @@ "workflow_node.deploy.form.k8s_secret_data_key_for_key.label": "Kubernetes Secret 数据键(用于存放私钥的字段)", "workflow_node.deploy.form.k8s_secret_data_key_for_key.placeholder": "请输入 Kubernetes Secret 中用于存放私钥的数据键", "workflow_node.deploy.form.k8s_secret_data_key_for_key.tooltip": "这是什么?请参阅 https://kubernetes.io/zh-cn/docs/concepts/configuration/secret/", + "workflow_node.deploy.form.lecdn_resource_type.label": "证书部署方式", + "workflow_node.deploy.form.lecdn_resource_type.placeholder": "请选择证书部署方式", + "workflow_node.deploy.form.lecdn_resource_type.option.certificate.label": "替换指定证书", + "workflow_node.deploy.form.lecdn_certificate_id.label": "LeCDN 证书 ID", + "workflow_node.deploy.form.lecdn_certificate_id.placeholder": "请输入 LeCDN 证书 ID", + "workflow_node.deploy.form.lecdn_certificate_id.tooltip": "请登录 LeCDN 控制台查看。", + "workflow_node.deploy.form.lecdn_client_id.label": "LeCDN 客户 ID(可选)", + "workflow_node.deploy.form.lecdn_client_id.placeholder": "请输入 LeCDN 客户 ID", + "workflow_node.deploy.form.lecdn_client_id.tooltip": "请登录 LeCDN 控制台查看。

使用的是系统管理员的授权信息时必填,需与证书所属客户相同。", "workflow_node.deploy.form.local.guide": "小贴士:如果你正在使用 Docker 运行 Certimate,“本地”指的是容器内而非宿主机。", "workflow_node.deploy.form.local_format.label": "文件格式", "workflow_node.deploy.form.local_format.placeholder": "请选择文件格式", @@ -488,7 +524,7 @@ "workflow_node.deploy.form.local_preset_scripts.option.ps_binding_iis.label": "PowerShell - 导入并绑定到 IIS", "workflow_node.deploy.form.local_preset_scripts.option.ps_binding_netsh.label": "PowerShell - 导入并绑定到 netsh", "workflow_node.deploy.form.local_preset_scripts.option.ps_binding_rdp.label": "PowerShell - 导入并绑定到 RDP", - "workflow_node.deploy.form.netlify_site_id.label": "netlify 网站 ID", + "workflow_node.deploy.form.netlify_site_id.label": "Netlify 网站 ID", "workflow_node.deploy.form.netlify_site_id.placeholder": "请输入 netlify 网站 ID", "workflow_node.deploy.form.netlify_site_id.tooltip": "这是什么?请参阅 https://docs.netlify.com/api/get-started/#get-site", "workflow_node.deploy.form.proxmoxve_node_name.label": "Proxmox VE 集群节点名称", @@ -512,11 +548,15 @@ "workflow_node.deploy.form.rainyun_rcdn_domain.label": "雨云 RCDN 加速域名", "workflow_node.deploy.form.rainyun_rcdn_domain.placeholder": "请输入雨云 RCDN 加速域名(支持泛域名)", "workflow_node.deploy.form.rainyun_rcdn_domain.tooltip": "这是什么?请参阅 https://app.rainyun.com/apps/rcdn/list", - "workflow_node.deploy.form.safeline_resource_type.label": "证书替换方式", - "workflow_node.deploy.form.safeline_resource_type.placeholder": "请选择证书替换方式", + "workflow_node.deploy.form.ratpanel_site_name.label": "耗子面板网站名称", + "workflow_node.deploy.form.ratpanel_site_name.placeholder": "请输入耗子面板网站名称", + "workflow_node.deploy.form.ratpanel_site_name.tooltip": "请登录耗子面板查看。", + "workflow_node.deploy.form.safeline_resource_type.label": "证书部署方式", + "workflow_node.deploy.form.safeline_resource_type.placeholder": "请选择证书部署方式", "workflow_node.deploy.form.safeline_resource_type.option.certificate.label": "替换指定证书", "workflow_node.deploy.form.safeline_certificate_id.label": "雷池证书 ID", "workflow_node.deploy.form.safeline_certificate_id.placeholder": "请输入雷池证书 ID", + "workflow_node.deploy.form.safeline_certificate_id.tooltip": "请登录雷池控制台查看。", "workflow_node.deploy.form.ssh_format.label": "文件格式", "workflow_node.deploy.form.ssh_format.placeholder": "请选择文件格式", "workflow_node.deploy.form.ssh_format.option.pem.label": "PEM 格式(*.pem, *.crt, *.key)", @@ -564,8 +604,8 @@ "workflow_node.deploy.form.tencentcloud_cdn_domain.label": "腾讯云 CDN 加速域名", "workflow_node.deploy.form.tencentcloud_cdn_domain.placeholder": "请输入腾讯云 CDN 加速域名(支持泛域名)", "workflow_node.deploy.form.tencentcloud_cdn_domain.tooltip": "这是什么?请参阅 https://console.cloud.tencent.com/cdn", - "workflow_node.deploy.form.tencentcloud_clb_resource_type.label": "证书替换方式", - "workflow_node.deploy.form.tencentcloud_clb_resource_type.placeholder": "请选择证书替换方式", + "workflow_node.deploy.form.tencentcloud_clb_resource_type.label": "证书部署方式", + "workflow_node.deploy.form.tencentcloud_clb_resource_type.placeholder": "请选择证书部署方式", "workflow_node.deploy.form.tencentcloud_clb_resource_type.option.ssl_deploy.label": "通过 SSL 服务部署到云资源实例", "workflow_node.deploy.form.tencentcloud_clb_resource_type.option.loadbalancer.label": "替换指定实例下的全部 HTTPS/TCPSSL/QUIC 监听器的证书", "workflow_node.deploy.form.tencentcloud_clb_resource_type.option.listener.label": "替换指定监听器的证书", @@ -661,8 +701,8 @@ "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": "请选择证书替换方式", + "workflow_node.deploy.form.volcengine_alb_resource_type.label": "证书部署方式", + "workflow_node.deploy.form.volcengine_alb_resource_type.placeholder": "请选择证书部署方式", "workflow_node.deploy.form.volcengine_alb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS 监听的证书", "workflow_node.deploy.form.volcengine_alb_resource_type.option.listener.label": "替换指定监听器的证书", "workflow_node.deploy.form.volcengine_alb_region.label": "火山引擎 ALB 服务地域", @@ -682,8 +722,8 @@ "workflow_node.deploy.form.volcengine_cdn_domain.tooltip": "这是什么?请参阅 https://console.volcengine.com/cdn/homepage", "workflow_node.deploy.form.volcengine_certcenter_region.label": "火山引擎证书中心服务地域", "workflow_node.deploy.form.volcengine_certcenter_region.placeholder": "请输入火山引擎证书中心服务地域(例如:cn-beijing)", - "workflow_node.deploy.form.volcengine_clb_resource_type.label": "证书替换方式", - "workflow_node.deploy.form.volcengine_clb_resource_type.placeholder": "请选择证书替换方式", + "workflow_node.deploy.form.volcengine_clb_resource_type.label": "证书部署方式", + "workflow_node.deploy.form.volcengine_clb_resource_type.placeholder": "请选择证书部署方式", "workflow_node.deploy.form.volcengine_clb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS 监听的证书", "workflow_node.deploy.form.volcengine_clb_resource_type.option.listener.label": "替换指定监听器的证书", "workflow_node.deploy.form.volcengine_clb_region.label": "火山引擎 CLB 服务地域", @@ -719,6 +759,11 @@ "workflow_node.deploy.form.volcengine_tos_domain.label": "火山引擎 TOS 自定义域名", "workflow_node.deploy.form.volcengine_tos_domain.placeholder": "请输入火山引擎 TOS 自定义域名", "workflow_node.deploy.form.volcengine_tos_domain.tooltip": "这是什么?请参阅 see https://console.volcengine.com/tos", + "workflow_node.deploy.form.wangsu_cdn_domains.label": "网宿云 CDN 加速域名", + "workflow_node.deploy.form.wangsu_cdn_domains.placeholder": "请输入网宿云 CDN 加速域名(多个值请用半角分号隔开)", + "workflow_node.deploy.form.wangsu_cdn_domains.tooltip": "这是什么?请参阅 https://cdn.console.wangsu.com/v2/index/#/property/list", + "workflow_node.deploy.form.wangsu_cdn_domains.multiple_input_modal.title": "修改网宿云 CDN 加速域名", + "workflow_node.deploy.form.wangsu_cdn_domains.multiple_input_modal.placeholder": "请输入网宿云 CDN 加速域名", "workflow_node.deploy.form.wangsu_cdnpro_environment.label": "网宿云环境", "workflow_node.deploy.form.wangsu_cdnpro_environment.placeholder": "请选择网宿云环境", "workflow_node.deploy.form.wangsu_cdnpro_environment.option.production.label": "生产环境", @@ -732,6 +777,9 @@ "workflow_node.deploy.form.wangsu_cdnpro_webhook_id.label": "网宿云 CDN Pro 部署任务 Webhook ID(可选)", "workflow_node.deploy.form.wangsu_cdnpro_webhook_id.placeholder": "请输入网宿云 CDN Pro 部署任务 Webhook ID", "workflow_node.deploy.form.wangsu_cdnpro_webhook_id.tooltip": "这是什么?请参阅 https://cdnpro.console.wangsu.com/v2/index/#/certificate", + "workflow_node.deploy.form.wangsu_certificate_id.label": "网宿云证书 ID(可选)", + "workflow_node.deploy.form.wangsu_certificate_id.placeholder": "请输入网宿云证书 ID", + "workflow_node.deploy.form.wangsu_certificate_id.tooltip": "这是什么?请参阅 https://cdn.console.wangsu.com/v2/index#/certificate/list

不填写时,将上传新证书;否则,将替换原证书。", "workflow_node.deploy.form.webhook_data.label": "Webhook 回调数据(可选)", "workflow_node.deploy.form.webhook_data.placeholder": "请输入 Webhook 回调数据以覆盖默认值", "workflow_node.deploy.form.webhook_data.tooltip": "不填写时,将使用所选部署目标授权的默认 Webhook 回调数据。", diff --git a/ui/src/pages/certificates/CertificateList.tsx b/ui/src/pages/certificates/CertificateList.tsx index 46bc1745..97eab0ef 100644 --- a/ui/src/pages/certificates/CertificateList.tsx +++ b/ui/src/pages/certificates/CertificateList.tsx @@ -126,11 +126,11 @@ const CertificateList = () => { }, }, { - key: "issuer", + key: "brand", title: t("certificate.props.brand"), render: (_, record) => ( - {record.issuer} + {record.issuerOrg} {record.keyAlgorithm} ),