mirror of
https://github.com/usual2970/certimate.git
synced 2025-06-08 05:29:51 +00:00
commit
1ed1c62f76
1
go.mod
1
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
|
||||
|
3
go.sum
3
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=
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
// 证书颁发机构标识。
|
||||
// 通常等同于 [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)
|
||||
}
|
||||
|
@ -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
|
||||
@ -57,10 +58,10 @@ func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, err
|
||||
ContactEmail: nodeConfig.ContactEmail,
|
||||
Provider: domain.ACMEDns01ProviderType(nodeConfig.Provider),
|
||||
ProviderAccessConfig: make(map[string]any),
|
||||
ProviderExtendedConfig: nodeConfig.ProviderConfig,
|
||||
ProviderServiceConfig: nodeConfig.ProviderConfig,
|
||||
CAProvider: domain.CAProviderType(nodeConfig.CAProvider),
|
||||
CAProviderAccessConfig: make(map[string]any),
|
||||
CAProviderExtendedConfig: nodeConfig.CAProviderConfig,
|
||||
CAProviderServiceConfig: nodeConfig.CAProviderConfig,
|
||||
KeyAlgorithm: nodeConfig.KeyAlgorithm,
|
||||
Nameservers: sliceutil.Filter(strings.Split(nodeConfig.Nameservers, ";"), func(s string) bool { return s != "" }),
|
||||
DnsPropagationWait: nodeConfig.DnsPropagationWait,
|
||||
@ -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,
|
||||
|
@ -46,10 +46,11 @@ type applicantProviderOptions struct {
|
||||
ContactEmail string
|
||||
Provider domain.ACMEDns01ProviderType
|
||||
ProviderAccessConfig map[string]any
|
||||
ProviderExtendedConfig map[string]any
|
||||
ProviderServiceConfig map[string]any
|
||||
CAProvider domain.CAProviderType
|
||||
CAProviderAccessId string
|
||||
CAProviderAccessConfig map[string]any
|
||||
CAProviderExtendedConfig map[string]any
|
||||
CAProviderServiceConfig map[string]any
|
||||
KeyAlgorithm string
|
||||
Nameservers []string
|
||||
DnsPropagationWait int32
|
||||
@ -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,
|
||||
})
|
||||
|
@ -33,7 +33,7 @@ func NewWithWorkflowNode(config DeployerWithWorkflowNodeConfig) (Deployer, error
|
||||
options := &deployerProviderOptions{
|
||||
Provider: domain.DeploymentProviderType(nodeConfig.Provider),
|
||||
ProviderAccessConfig: make(map[string]any),
|
||||
ProviderExtendedConfig: nodeConfig.ProviderConfig,
|
||||
ProviderServiceConfig: nodeConfig.ProviderConfig,
|
||||
}
|
||||
|
||||
accessRepo := repository.NewAccessRepository()
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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"`
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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")
|
||||
@ -37,7 +38,7 @@ const (
|
||||
AccessProviderTypeEdgio = AccessProviderType("edgio")
|
||||
AccessProviderTypeEmail = AccessProviderType("email")
|
||||
AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly(预留)
|
||||
AccessProviderTypeFlexCDN = AccessProviderType("flexcdn") // FlexCDN(预留)
|
||||
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)
|
||||
)
|
||||
|
||||
|
@ -33,7 +33,7 @@ func NewWithWorkflowNode(config NotifierWithWorkflowNodeConfig) (Notifier, error
|
||||
options := ¬ifierProviderOptions{
|
||||
Provider: domain.NotificationProviderType(nodeConfig.Provider),
|
||||
ProviderAccessConfig: make(map[string]any),
|
||||
ProviderExtendedConfig: nodeConfig.ProviderConfig,
|
||||
ProviderServiceConfig: nodeConfig.ProviderConfig,
|
||||
}
|
||||
|
||||
accessRepo := repository.NewAccessRepository()
|
||||
|
@ -20,7 +20,7 @@ import (
|
||||
type notifierProviderOptions struct {
|
||||
Provider domain.NotificationProviderType
|
||||
ProviderAccessConfig map[string]any
|
||||
ProviderExtendedConfig 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,
|
||||
|
@ -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})
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,13 +49,14 @@ 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,
|
||||
ApiVersion: config.ApiVersion,
|
||||
ApiKey: config.ApiKey,
|
||||
})
|
||||
if err != nil {
|
||||
@ -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})
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,10 +211,15 @@ 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 {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
|
@ -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{
|
||||
|
@ -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)
|
||||
|
@ -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", "", "")
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ type DeployerConfig struct {
|
||||
// 阿里云地域。
|
||||
Region string `json:"region"`
|
||||
// 服务版本。
|
||||
// 可取值 "2.0"、"3.0"。
|
||||
ServiceVersion string `json:"serviceVersion"`
|
||||
// 自定义域名(支持泛域名)。
|
||||
Domain string `json:"domain"`
|
||||
|
322
internal/pkg/core/deployer/providers/aliyun-ga/aliyun_ga.go
Normal file
322
internal/pkg/core/deployer/providers/aliyun-ga/aliyun_ga.go
Normal file
@ -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
|
||||
}
|
118
internal/pkg/core/deployer/providers/aliyun-ga/aliyun_ga_test.go
Normal file
118
internal/pkg/core/deployer/providers/aliyun-ga/aliyun_ga_test.go
Normal file
@ -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)
|
||||
})
|
||||
}
|
10
internal/pkg/core/deployer/providers/aliyun-ga/consts.go
Normal file
10
internal/pkg/core/deployer/providers/aliyun-ga/consts.go
Normal file
@ -0,0 +1,10 @@
|
||||
package aliyunga
|
||||
|
||||
type ResourceType string
|
||||
|
||||
const (
|
||||
// 资源类型:部署到指定全球加速器。
|
||||
RESOURCE_TYPE_ACCELERATOR = ResourceType("accelerator")
|
||||
// 资源类型:部署到指定监听器。
|
||||
RESOURCE_TYPE_LISTENER = ResourceType("listener")
|
||||
)
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
@ -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)
|
||||
})
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
})
|
||||
}
|
8
internal/pkg/core/deployer/providers/flexcdn/consts.go
Normal file
8
internal/pkg/core/deployer/providers/flexcdn/consts.go
Normal file
@ -0,0 +1,8 @@
|
||||
package flexcdn
|
||||
|
||||
type ResourceType string
|
||||
|
||||
const (
|
||||
// 资源类型:替换指定证书。
|
||||
RESOURCE_TYPE_CERTIFICATE = ResourceType("certificate")
|
||||
)
|
145
internal/pkg/core/deployer/providers/flexcdn/flexcdn.go
Normal file
145
internal/pkg/core/deployer/providers/flexcdn/flexcdn.go
Normal file
@ -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
|
||||
}
|
83
internal/pkg/core/deployer/providers/flexcdn/flexcdn_test.go
Normal file
83
internal/pkg/core/deployer/providers/flexcdn/flexcdn_test.go
Normal file
@ -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)
|
||||
})
|
||||
}
|
@ -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"`
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
8
internal/pkg/core/deployer/providers/lecdn/consts.go
Normal file
8
internal/pkg/core/deployer/providers/lecdn/consts.go
Normal file
@ -0,0 +1,8 @@
|
||||
package lecdn
|
||||
|
||||
type ResourceType string
|
||||
|
||||
const (
|
||||
// 资源类型:替换指定证书。
|
||||
RESOURCE_TYPE_CERTIFICATE = ResourceType("certificate")
|
||||
)
|
176
internal/pkg/core/deployer/providers/lecdn/lecdn.go
Normal file
176
internal/pkg/core/deployer/providers/lecdn/lecdn.go
Normal file
@ -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")
|
||||
}
|
87
internal/pkg/core/deployer/providers/lecdn/lecdn_test.go
Normal file
87
internal/pkg/core/deployer/providers/lecdn/lecdn_test.go
Normal file
@ -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)
|
||||
})
|
||||
}
|
@ -16,7 +16,7 @@ type DeployerConfig struct {
|
||||
// 耗子面板地址。
|
||||
ApiUrl string `json:"apiUrl"`
|
||||
// 耗子面板访问令牌 ID。
|
||||
AccessTokenId uint `json:"accessTokenId"`
|
||||
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")
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -16,13 +16,13 @@ type DeployerConfig struct {
|
||||
// 耗子面板地址。
|
||||
ApiUrl string `json:"apiUrl"`
|
||||
// 耗子面板访问令牌 ID。
|
||||
AccessTokenId uint `json:"accessTokenId"`
|
||||
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")
|
||||
}
|
||||
|
@ -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,
|
||||
})
|
||||
|
@ -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, "")
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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", "", "")
|
||||
|
104
internal/pkg/core/deployer/providers/wangsu-cdn/wangsu_cdn.go
Normal file
104
internal/pkg/core/deployer/providers/wangsu-cdn/wangsu_cdn.go
Normal file
@ -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
|
||||
}
|
@ -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)
|
||||
})
|
||||
}
|
@ -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" {
|
||||
|
@ -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
|
||||
}
|
@ -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)
|
||||
})
|
||||
}
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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", "", "")
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,11 +49,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"))
|
||||
|
||||
uploader, err := provider.NewUploader(&provider.UploaderConfig{
|
||||
ApiUrl: fApiUrl,
|
||||
ApiVersion: fApiVersion,
|
||||
ApiKey: fApiKey,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -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
|
||||
}
|
@ -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))
|
||||
})
|
||||
}
|
@ -14,18 +14,29 @@ import (
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
apiHost 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,
|
||||
}
|
||||
}
|
||||
@ -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() {
|
||||
|
@ -13,16 +13,15 @@ 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,
|
||||
}
|
||||
}
|
||||
@ -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() {
|
||||
|
@ -14,17 +14,16 @@ import (
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
apiHost 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,
|
||||
}
|
||||
@ -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() {
|
||||
|
19
internal/pkg/sdk3rd/btwaf/api.go
Normal file
19
internal/pkg/sdk3rd/btwaf/api.go
Normal file
@ -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
|
||||
}
|
77
internal/pkg/sdk3rd/btwaf/client.go
Normal file
77
internal/pkg/sdk3rd/btwaf/client.go
Normal file
@ -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
|
||||
}
|
67
internal/pkg/sdk3rd/btwaf/models.go
Normal file
67
internal/pkg/sdk3rd/btwaf/models.go
Normal file
@ -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
|
||||
}
|
@ -11,16 +11,15 @@ 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,
|
||||
}
|
||||
}
|
||||
@ -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() {
|
||||
|
@ -11,16 +11,15 @@ 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,
|
||||
}
|
||||
}
|
||||
@ -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() {
|
||||
|
@ -12,20 +12,16 @@ 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,
|
||||
}
|
||||
}
|
||||
@ -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() {
|
||||
|
@ -11,18 +11,15 @@ 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,
|
||||
}
|
||||
}
|
||||
@ -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() {
|
||||
|
@ -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
|
||||
}
|
||||
|
48
internal/pkg/sdk3rd/flexcdn/api.go
Normal file
48
internal/pkg/sdk3rd/flexcdn/api.go
Normal file
@ -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
|
||||
}
|
102
internal/pkg/sdk3rd/flexcdn/client.go
Normal file
102
internal/pkg/sdk3rd/flexcdn/client.go
Normal file
@ -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
|
||||
}
|
52
internal/pkg/sdk3rd/flexcdn/models.go
Normal file
52
internal/pkg/sdk3rd/flexcdn/models.go
Normal file
@ -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
|
||||
}
|
@ -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,23 +28,19 @@ 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 {
|
||||
if err := c.ensureAccessTokenExists(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
resp := &UpdateSSLCertResponse{}
|
||||
err := c.sendRequestWithResult(http.MethodPost, "/SSLCertService/updateSSLCert", req, resp)
|
||||
|
@ -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() {
|
||||
|
50
internal/pkg/sdk3rd/lecdn/v3/client/api.go
Normal file
50
internal/pkg/sdk3rd/lecdn/v3/client/api.go
Normal file
@ -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
|
||||
}
|
99
internal/pkg/sdk3rd/lecdn/v3/client/client.go
Normal file
99
internal/pkg/sdk3rd/lecdn/v3/client/client.go
Normal file
@ -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
|
||||
}
|
47
internal/pkg/sdk3rd/lecdn/v3/client/models.go
Normal file
47
internal/pkg/sdk3rd/lecdn/v3/client/models.go
Normal file
@ -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
|
||||
}
|
49
internal/pkg/sdk3rd/lecdn/v3/master/api.go
Normal file
49
internal/pkg/sdk3rd/lecdn/v3/master/api.go
Normal file
@ -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
|
||||
}
|
99
internal/pkg/sdk3rd/lecdn/v3/master/client.go
Normal file
99
internal/pkg/sdk3rd/lecdn/v3/master/client.go
Normal file
@ -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
|
||||
}
|
47
internal/pkg/sdk3rd/lecdn/v3/master/models.go
Normal file
47
internal/pkg/sdk3rd/lecdn/v3/master/models.go
Normal file
@ -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
|
||||
}
|
@ -11,16 +11,15 @@ 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,
|
||||
}
|
||||
}
|
||||
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -11,18 +11,15 @@ 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,
|
||||
}
|
||||
}
|
||||
@ -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() {
|
||||
|
@ -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,11 +31,9 @@ func (c *Client) getCookie() error {
|
||||
}
|
||||
|
||||
func (c *Client) UploadHttpsCertificate(req *UploadHttpsCertificateRequest) (*UploadHttpsCertificateResponse, error) {
|
||||
if c.loginCookie == "" {
|
||||
if err := c.getCookie(); err != nil {
|
||||
if err := c.ensureCookieExists(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
resp := &UploadHttpsCertificateResponse{}
|
||||
err := c.sendRequestWithResult(http.MethodPost, "/api/https/certificate/", req, resp)
|
||||
@ -39,11 +41,9 @@ func (c *Client) UploadHttpsCertificate(req *UploadHttpsCertificateRequest) (*Up
|
||||
}
|
||||
|
||||
func (c *Client) GetHttpsCertificateManager(certificateId string) (*GetHttpsCertificateManagerResponse, error) {
|
||||
if c.loginCookie == "" {
|
||||
if err := c.getCookie(); err != nil {
|
||||
if err := c.ensureCookieExists(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
req := &GetHttpsCertificateManagerRequest{CertificateId: certificateId}
|
||||
resp := &GetHttpsCertificateManagerResponse{}
|
||||
@ -52,11 +52,9 @@ func (c *Client) GetHttpsCertificateManager(certificateId string) (*GetHttpsCert
|
||||
}
|
||||
|
||||
func (c *Client) UpdateHttpsCertificateManager(req *UpdateHttpsCertificateManagerRequest) (*UpdateHttpsCertificateManagerResponse, error) {
|
||||
if c.loginCookie == "" {
|
||||
if err := c.getCookie(); err != nil {
|
||||
if err := c.ensureCookieExists(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
resp := &UpdateHttpsCertificateManagerResponse{}
|
||||
err := c.sendRequestWithResult(http.MethodPost, "/api/https/certificate/manager", req, resp)
|
||||
@ -64,11 +62,9 @@ func (c *Client) UpdateHttpsCertificateManager(req *UpdateHttpsCertificateManage
|
||||
}
|
||||
|
||||
func (c *Client) GetHttpsServiceManager(domain string) (*GetHttpsServiceManagerResponse, error) {
|
||||
if c.loginCookie == "" {
|
||||
if err := c.getCookie(); err != nil {
|
||||
if err := c.ensureCookieExists(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
req := &GetHttpsServiceManagerRequest{Domain: domain}
|
||||
resp := &GetHttpsServiceManagerResponse{}
|
||||
@ -77,11 +73,9 @@ func (c *Client) GetHttpsServiceManager(domain string) (*GetHttpsServiceManagerR
|
||||
}
|
||||
|
||||
func (c *Client) MigrateHttpsDomain(req *MigrateHttpsDomainRequest) (*MigrateHttpsDomainResponse, error) {
|
||||
if c.loginCookie == "" {
|
||||
if err := c.getCookie(); err != nil {
|
||||
if err := c.ensureCookieExists(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
resp := &MigrateHttpsDomainResponse{}
|
||||
err := c.sendRequestWithResult(http.MethodPost, "/api/https/migrate/domain", req, resp)
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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"`
|
||||
}
|
||||
|
70
internal/pkg/sdk3rd/wangsu/cdnpro/api.go
Normal file
70
internal/pkg/sdk3rd/wangsu/cdnpro/api.go
Normal file
@ -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
|
||||
}
|
20
internal/pkg/sdk3rd/wangsu/cdnpro/client.go
Normal file
20
internal/pkg/sdk3rd/wangsu/cdnpro/client.go
Normal file
@ -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
|
||||
}
|
108
internal/pkg/sdk3rd/wangsu/cdnpro/models.go
Normal file
108
internal/pkg/sdk3rd/wangsu/cdnpro/models.go
Normal file
@ -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"`
|
||||
}
|
42
internal/pkg/sdk3rd/wangsu/certificate/api.go
Normal file
42
internal/pkg/sdk3rd/wangsu/certificate/api.go
Normal file
@ -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
|
||||
}
|
20
internal/pkg/sdk3rd/wangsu/certificate/client.go
Normal file
20
internal/pkg/sdk3rd/wangsu/certificate/client.go
Normal file
@ -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
|
||||
}
|
52
internal/pkg/sdk3rd/wangsu/certificate/models.go
Normal file
52
internal/pkg/sdk3rd/wangsu/certificate/models.go
Normal file
@ -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"`
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user