mirror of
https://github.com/usual2970/certimate.git
synced 2025-10-05 05:54:53 +00:00
Merge branch 'main' into feat-ssh-jumpserver
This commit is contained in:
@@ -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,14 +49,15 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
|
||||
panic("config is nil")
|
||||
}
|
||||
|
||||
client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections)
|
||||
client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create sdk client: %w", err)
|
||||
}
|
||||
|
||||
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
|
||||
ApiUrl: config.ApiUrl,
|
||||
ApiKey: config.ApiKey,
|
||||
ApiUrl: config.ApiUrl,
|
||||
ApiVersion: config.ApiVersion,
|
||||
ApiKey: config.ApiKey,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
|
||||
@@ -103,7 +107,7 @@ func (d *DeployerProvider) deployToWebsite(ctx context.Context, certPEM string,
|
||||
}
|
||||
|
||||
// 获取网站 HTTPS 配置
|
||||
getHttpsConfReq := &opsdk.GetHttpsConfRequest{
|
||||
getHttpsConfReq := &onepanelsdk.GetHttpsConfRequest{
|
||||
WebsiteID: d.config.WebsiteId,
|
||||
}
|
||||
getHttpsConfResp, err := d.sdkClient.GetHttpsConf(getHttpsConfReq)
|
||||
@@ -122,7 +126,7 @@ func (d *DeployerProvider) deployToWebsite(ctx context.Context, certPEM string,
|
||||
|
||||
// 修改网站 HTTPS 配置
|
||||
certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
|
||||
updateHttpsConfReq := &opsdk.UpdateHttpsConfRequest{
|
||||
updateHttpsConfReq := &onepanelsdk.UpdateHttpsConfRequest{
|
||||
WebsiteID: d.config.WebsiteId,
|
||||
Type: "existed",
|
||||
WebsiteSSLID: certId,
|
||||
@@ -147,7 +151,7 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri
|
||||
}
|
||||
|
||||
// 获取证书详情
|
||||
getWebsiteSSLReq := &opsdk.GetWebsiteSSLRequest{
|
||||
getWebsiteSSLReq := &onepanelsdk.GetWebsiteSSLRequest{
|
||||
SSLID: d.config.CertificateId,
|
||||
}
|
||||
getWebsiteSSLResp, err := d.sdkClient.GetWebsiteSSL(getWebsiteSSLReq)
|
||||
@@ -157,7 +161,7 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri
|
||||
}
|
||||
|
||||
// 更新证书
|
||||
uploadWebsiteSSLReq := &opsdk.UploadWebsiteSSLRequest{
|
||||
uploadWebsiteSSLReq := &onepanelsdk.UploadWebsiteSSLRequest{
|
||||
Type: "paste",
|
||||
SSLID: d.config.CertificateId,
|
||||
Description: getWebsiteSSLResp.Data.Description,
|
||||
@@ -173,16 +177,20 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*opsdk.Client, error) {
|
||||
func createSdkClient(apiUrl, apiVersion, apiKey string, skipTlsVerify bool) (*onepanelsdk.Client, error) {
|
||||
if _, err := url.Parse(apiUrl); err != nil {
|
||||
return nil, errors.New("invalid 1panel api url")
|
||||
}
|
||||
|
||||
if apiVersion == "" {
|
||||
return nil, errors.New("invalid 1panel api version")
|
||||
}
|
||||
|
||||
if apiKey == "" {
|
||||
return nil, errors.New("invalid 1panel api key")
|
||||
}
|
||||
|
||||
client := opsdk.NewClient(apiUrl, apiKey)
|
||||
client := onepanelsdk.NewClient(apiUrl, apiVersion, apiKey)
|
||||
if skipTlsVerify {
|
||||
client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
||||
}
|
||||
|
@@ -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,8 +211,13 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId
|
||||
d.logger.Info("found https/quic listeners to deploy", slog.Any("listenerIds", listenerIds))
|
||||
|
||||
for _, listenerId := range listenerIds {
|
||||
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
||||
errs = append(errs, err)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
})
|
||||
}
|
@@ -15,8 +15,8 @@ import (
|
||||
type DeployerConfig struct {
|
||||
// 耗子面板地址。
|
||||
ApiUrl string `json:"apiUrl"`
|
||||
// 耗子面板访问令牌ID。
|
||||
AccessTokenId uint `json:"accessTokenId"`
|
||||
// 耗子面板访问令牌 ID。
|
||||
AccessTokenId int32 `json:"accessTokenId"`
|
||||
// 耗子面板访问令牌。
|
||||
AccessToken string `json:"accessToken"`
|
||||
// 是否允许不安全的连接。
|
||||
@@ -64,15 +64,15 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
||||
PrivateKey: privkeyPEM,
|
||||
}
|
||||
settingCertResp, err := d.sdkClient.SettingCert(settingCertReq)
|
||||
d.logger.Debug("sdk request 'ratpanel.SettingCertRequest'", slog.Any("request", settingCertReq), slog.Any("response", settingCertResp))
|
||||
d.logger.Debug("sdk request 'ratpanel.SettingCert'", slog.Any("request", settingCertReq), slog.Any("response", settingCertResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'ratpanel.SettingCertRequest': %w", err)
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'ratpanel.SettingCert': %w", err)
|
||||
}
|
||||
|
||||
return &deployer.DeployResult{}, nil
|
||||
}
|
||||
|
||||
func createSdkClient(apiUrl string, accessTokenId uint, accessToken string, skipTlsVerify bool) (*rpsdk.Client, error) {
|
||||
func createSdkClient(apiUrl string, accessTokenId int32, accessToken string, skipTlsVerify bool) (*rpsdk.Client, error) {
|
||||
if _, err := url.Parse(apiUrl); err != nil {
|
||||
return nil, errors.New("invalid ratpanel api url")
|
||||
}
|
||||
@@ -80,6 +80,7 @@ func createSdkClient(apiUrl string, accessTokenId uint, accessToken string, skip
|
||||
if accessTokenId == 0 {
|
||||
return nil, errors.New("invalid ratpanel access token id")
|
||||
}
|
||||
|
||||
if accessToken == "" {
|
||||
return nil, errors.New("invalid ratpanel access token")
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -15,14 +15,14 @@ import (
|
||||
type DeployerConfig struct {
|
||||
// 耗子面板地址。
|
||||
ApiUrl string `json:"apiUrl"`
|
||||
// 耗子面板访问令牌ID。
|
||||
AccessTokenId uint `json:"accessTokenId"`
|
||||
// 耗子面板访问令牌 ID。
|
||||
AccessTokenId int32 `json:"accessTokenId"`
|
||||
// 耗子面板访问令牌。
|
||||
AccessToken string `json:"accessToken"`
|
||||
// 是否允许不安全的连接。
|
||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||
// 网站名称。
|
||||
SiteName string `json:"siteName,omitempty"`
|
||||
SiteName string `json:"siteName"`
|
||||
}
|
||||
|
||||
type DeployerProvider struct {
|
||||
@@ -71,15 +71,15 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
||||
PrivateKey: privkeyPEM,
|
||||
}
|
||||
websiteCertResp, err := d.sdkClient.WebsiteCert(websiteCertReq)
|
||||
d.logger.Debug("sdk request 'ratpanel.WebsiteCertRequest'", slog.Any("request", websiteCertReq), slog.Any("response", websiteCertResp))
|
||||
d.logger.Debug("sdk request 'ratpanel.WebsiteCert'", slog.Any("request", websiteCertReq), slog.Any("response", websiteCertResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'ratpanel.WebsiteCertRequest': %w", err)
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'ratpanel.WebsiteCert': %w", err)
|
||||
}
|
||||
|
||||
return &deployer.DeployResult{}, nil
|
||||
}
|
||||
|
||||
func createSdkClient(apiUrl string, accessTokenId uint, accessToken string, skipTlsVerify bool) (*rpsdk.Client, error) {
|
||||
func createSdkClient(apiUrl string, accessTokenId int32, accessToken string, skipTlsVerify bool) (*rpsdk.Client, error) {
|
||||
if _, err := url.Parse(apiUrl); err != nil {
|
||||
return nil, errors.New("invalid ratpanel api url")
|
||||
}
|
||||
@@ -87,6 +87,7 @@ func createSdkClient(apiUrl string, accessTokenId uint, accessToken string, skip
|
||||
if accessTokenId == 0 {
|
||||
return nil, errors.New("invalid ratpanel access token id")
|
||||
}
|
||||
|
||||
if accessToken == "" {
|
||||
return nil, errors.New("invalid ratpanel access token")
|
||||
}
|
||||
|
@@ -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,12 +49,14 @@ func TestDeploy(t *testing.T) {
|
||||
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
|
||||
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
|
||||
fmt.Sprintf("APIURL: %v", fApiUrl),
|
||||
fmt.Sprintf("APIVERSION: %v", fApiVersion),
|
||||
fmt.Sprintf("APIKEY: %v", fApiKey),
|
||||
}, "\n"))
|
||||
|
||||
uploader, err := provider.NewUploader(&provider.UploaderConfig{
|
||||
ApiUrl: fApiUrl,
|
||||
ApiKey: fApiKey,
|
||||
ApiUrl: fApiUrl,
|
||||
ApiVersion: fApiVersion,
|
||||
ApiKey: fApiKey,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
|
@@ -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,19 +14,30 @@ import (
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
apiHost string
|
||||
apiKey string
|
||||
apiKey string
|
||||
|
||||
client *resty.Client
|
||||
}
|
||||
|
||||
func NewClient(apiHost, apiKey string) *Client {
|
||||
client := resty.New()
|
||||
func NewClient(apiHost, apiVersion, apiKey string) *Client {
|
||||
if apiVersion == "" {
|
||||
apiVersion = "v1"
|
||||
}
|
||||
|
||||
client := resty.New().
|
||||
SetBaseURL(strings.TrimRight(apiHost, "/") + "/api/" + apiVersion).
|
||||
SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
|
||||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||||
tokenMd5 := md5.Sum([]byte("1panel" + apiKey + timestamp))
|
||||
tokenMd5Hex := hex.EncodeToString(tokenMd5[:])
|
||||
req.Header.Set("1Panel-Timestamp", timestamp)
|
||||
req.Header.Set("1Panel-Token", tokenMd5Hex)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return &Client{
|
||||
apiHost: strings.TrimRight(apiHost, "/"),
|
||||
apiKey: apiKey,
|
||||
client: client,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,16 +51,8 @@ func (c *Client) WithTLSConfig(config *tls.Config) *Client {
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Client) generateToken(timestamp string) string {
|
||||
tokenMd5 := md5.Sum([]byte("1panel" + c.apiKey + timestamp))
|
||||
tokenMd5Hex := hex.EncodeToString(tokenMd5[:])
|
||||
return tokenMd5Hex
|
||||
}
|
||||
|
||||
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
|
||||
req := c.client.R()
|
||||
req.Method = method
|
||||
req.URL = c.apiHost + "/api/v1" + path
|
||||
if strings.EqualFold(method, http.MethodGet) {
|
||||
qs := make(map[string]string)
|
||||
if params != nil {
|
||||
@@ -65,17 +68,10 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r
|
||||
|
||||
req = req.SetQueryParams(qs)
|
||||
} else {
|
||||
req = req.
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(params)
|
||||
req = req.SetHeader("Content-Type", "application/json").SetBody(params)
|
||||
}
|
||||
|
||||
timestamp := fmt.Sprintf("%d", time.Now().Unix())
|
||||
token := c.generateToken(timestamp)
|
||||
req.SetHeader("1Panel-Timestamp", timestamp)
|
||||
req.SetHeader("1Panel-Token", token)
|
||||
|
||||
resp, err := req.Send()
|
||||
resp, err := req.Execute(method, path)
|
||||
if err != nil {
|
||||
return resp, fmt.Errorf("1panel api error: failed to send request: %w", err)
|
||||
} else if resp.IsError() {
|
||||
|
@@ -13,17 +13,16 @@ import (
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
apiToken string
|
||||
|
||||
client *resty.Client
|
||||
}
|
||||
|
||||
func NewClient(apiToken string) *Client {
|
||||
client := resty.New()
|
||||
client := resty.New().
|
||||
SetBaseURL("https://cdn.api.baishan.com").
|
||||
SetHeader("token", apiToken)
|
||||
|
||||
return &Client{
|
||||
apiToken: apiToken,
|
||||
client: client,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,8 +33,6 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client {
|
||||
|
||||
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
|
||||
req := c.client.R()
|
||||
req.Method = method
|
||||
req.URL = "https://cdn.api.baishan.com" + path
|
||||
if strings.EqualFold(method, http.MethodGet) {
|
||||
qs := url.Values{}
|
||||
if params != nil {
|
||||
@@ -61,17 +58,12 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r
|
||||
}
|
||||
}
|
||||
|
||||
req = req.
|
||||
SetQueryParam("token", c.apiToken).
|
||||
SetQueryParamsFromValues(qs)
|
||||
req = req.SetQueryParamsFromValues(qs)
|
||||
} else {
|
||||
req = req.
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetQueryParam("token", c.apiToken).
|
||||
SetBody(params)
|
||||
req = req.SetHeader("Content-Type", "application/json").SetBody(params)
|
||||
}
|
||||
|
||||
resp, err := req.Send()
|
||||
resp, err := req.Execute(method, path)
|
||||
if err != nil {
|
||||
return resp, fmt.Errorf("baishan api error: failed to send request: %w", err)
|
||||
} else if resp.IsError() {
|
||||
|
@@ -14,19 +14,18 @@ import (
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
apiHost string
|
||||
apiKey string
|
||||
apiKey string
|
||||
|
||||
client *resty.Client
|
||||
}
|
||||
|
||||
func NewClient(apiHost, apiKey string) *Client {
|
||||
client := resty.New()
|
||||
client := resty.New().
|
||||
SetBaseURL(strings.TrimRight(apiHost, "/"))
|
||||
|
||||
return &Client{
|
||||
apiHost: strings.TrimRight(apiHost, "/"),
|
||||
apiKey: apiKey,
|
||||
client: client,
|
||||
apiKey: apiKey,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,11 +77,10 @@ func (c *Client) sendRequest(path string, params interface{}) (*resty.Response,
|
||||
data["request_time"] = fmt.Sprintf("%d", timestamp)
|
||||
data["request_token"] = c.generateSignature(fmt.Sprintf("%d", timestamp))
|
||||
|
||||
url := c.apiHost + path
|
||||
req := c.client.R().
|
||||
SetHeader("Content-Type", "application/x-www-form-urlencoded").
|
||||
SetFormData(data)
|
||||
resp, err := req.Post(url)
|
||||
resp, err := req.Post(path)
|
||||
if err != nil {
|
||||
return resp, fmt.Errorf("baota api error: failed to send request: %w", err)
|
||||
} else if resp.IsError() {
|
||||
|
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,17 +11,16 @@ import (
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
apiToken string
|
||||
|
||||
client *resty.Client
|
||||
}
|
||||
|
||||
func NewClient(apiToken string) *Client {
|
||||
client := resty.New()
|
||||
client := resty.New().
|
||||
SetBaseURL("https://api.bunny.net").
|
||||
SetHeader("AccessKey", apiToken)
|
||||
|
||||
return &Client{
|
||||
apiToken: apiToken,
|
||||
client: client,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,9 +31,6 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client {
|
||||
|
||||
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
|
||||
req := c.client.R()
|
||||
req.Method = method
|
||||
req.URL = "https://api.bunny.net" + path
|
||||
req = req.SetHeader("AccessKey", c.apiToken)
|
||||
if strings.EqualFold(method, http.MethodGet) {
|
||||
qs := make(map[string]string)
|
||||
if params != nil {
|
||||
@@ -50,12 +46,10 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r
|
||||
|
||||
req = req.SetQueryParams(qs)
|
||||
} else {
|
||||
req = req.
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(params)
|
||||
req = req.SetHeader("Content-Type", "application/json").SetBody(params)
|
||||
}
|
||||
|
||||
resp, err := req.Send()
|
||||
resp, err := req.Execute(method, path)
|
||||
if err != nil {
|
||||
return resp, fmt.Errorf("bunny api error: failed to send request: %w", err)
|
||||
} else if resp.IsError() {
|
||||
|
@@ -11,17 +11,16 @@ import (
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
apiToken string
|
||||
|
||||
client *resty.Client
|
||||
}
|
||||
|
||||
func NewClient(apiToken string) *Client {
|
||||
client := resty.New()
|
||||
client := resty.New().
|
||||
SetBaseURL("https://api.cachefly.com/api/2.5").
|
||||
SetHeader("x-cf-authorization", "Bearer "+apiToken)
|
||||
|
||||
return &Client{
|
||||
apiToken: apiToken,
|
||||
client: client,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,9 +31,6 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client {
|
||||
|
||||
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
|
||||
req := c.client.R()
|
||||
req.Method = method
|
||||
req.URL = "https://api.cachefly.com/api/2.5" + path
|
||||
req = req.SetHeader("x-cf-authorization", "Bearer "+c.apiToken)
|
||||
if strings.EqualFold(method, http.MethodGet) {
|
||||
qs := make(map[string]string)
|
||||
if params != nil {
|
||||
@@ -50,12 +46,10 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r
|
||||
|
||||
req = req.SetQueryParams(qs)
|
||||
} else {
|
||||
req = req.
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(params)
|
||||
req = req.SetHeader("Content-Type", "application/json").SetBody(params)
|
||||
}
|
||||
|
||||
resp, err := req.Send()
|
||||
resp, err := req.Execute(method, path)
|
||||
if err != nil {
|
||||
return resp, fmt.Errorf("cachefly api error: failed to send request: %w", err)
|
||||
} else if resp.IsError() {
|
||||
|
@@ -12,21 +12,17 @@ import (
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
apiHost string
|
||||
apiKey string
|
||||
apiSecret string
|
||||
|
||||
client *resty.Client
|
||||
}
|
||||
|
||||
func NewClient(apiHost, apiKey, apiSecret string) *Client {
|
||||
client := resty.New()
|
||||
client := resty.New().
|
||||
SetBaseURL(strings.TrimRight(apiHost, "/")).
|
||||
SetHeader("api-key", apiKey).
|
||||
SetHeader("api-secret", apiSecret)
|
||||
|
||||
return &Client{
|
||||
apiHost: strings.TrimRight(apiHost, "/"),
|
||||
apiKey: apiKey,
|
||||
apiSecret: apiSecret,
|
||||
client: client,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,11 +38,6 @@ func (c *Client) WithTLSConfig(config *tls.Config) *Client {
|
||||
|
||||
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
|
||||
req := c.client.R()
|
||||
req.Method = method
|
||||
req.URL = c.apiHost + path
|
||||
req = req.
|
||||
SetHeader("api-key", c.apiKey).
|
||||
SetHeader("api-secret", c.apiSecret)
|
||||
if strings.EqualFold(method, http.MethodGet) {
|
||||
qs := make(map[string]string)
|
||||
if params != nil {
|
||||
@@ -62,12 +53,10 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r
|
||||
|
||||
req = req.SetQueryParams(qs)
|
||||
} else {
|
||||
req = req.
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(params)
|
||||
req = req.SetHeader("Content-Type", "application/json").SetBody(params)
|
||||
}
|
||||
|
||||
resp, err := req.Send()
|
||||
resp, err := req.Execute(method, path)
|
||||
if err != nil {
|
||||
return resp, fmt.Errorf("cdnfly api error: failed to send request: %w", err)
|
||||
} else if resp.IsError() {
|
||||
|
@@ -11,19 +11,16 @@ import (
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
apiId string
|
||||
apiSecret string
|
||||
|
||||
client *resty.Client
|
||||
}
|
||||
|
||||
func NewClient(apiId, apiSecret string) *Client {
|
||||
client := resty.New()
|
||||
client := resty.New().
|
||||
SetBaseURL("https://api.dns.la/api").
|
||||
SetBasicAuth(apiId, apiSecret)
|
||||
|
||||
return &Client{
|
||||
apiId: apiId,
|
||||
apiSecret: apiSecret,
|
||||
client: client,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,9 +30,7 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client {
|
||||
}
|
||||
|
||||
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
|
||||
req := c.client.R().SetBasicAuth(c.apiId, c.apiSecret)
|
||||
req.Method = method
|
||||
req.URL = "https://api.dns.la/api" + path
|
||||
req := c.client.R()
|
||||
if strings.EqualFold(method, http.MethodGet) {
|
||||
qs := make(map[string]string)
|
||||
if params != nil {
|
||||
@@ -51,12 +46,10 @@ func (c *Client) sendRequest(method string, path string, params interface{}) (*r
|
||||
|
||||
req = req.SetQueryParams(qs)
|
||||
} else {
|
||||
req = req.
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(params)
|
||||
req = req.SetHeader("Content-Type", "application/json").SetBody(params)
|
||||
}
|
||||
|
||||
resp, err := req.Send()
|
||||
resp, err := req.Execute(method, path)
|
||||
if err != nil {
|
||||
return resp, fmt.Errorf("dnsla api error: failed to send request: %w", err)
|
||||
} else if resp.IsError() {
|
||||
|
@@ -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,22 +28,18 @@ func (c *Client) getAccessToken() error {
|
||||
if err := json.Unmarshal(res.Body(), &resp); err != nil {
|
||||
return fmt.Errorf("goedge api error: failed to unmarshal response: %w", err)
|
||||
} else if resp.GetCode() != 200 {
|
||||
return fmt.Errorf("goedge get access token failed: code: %d, message: %s", resp.GetCode(), resp.GetMessage())
|
||||
return fmt.Errorf("goedge get access token failed: code='%d', message='%s'", resp.GetCode(), resp.GetMessage())
|
||||
}
|
||||
|
||||
c.accessTokenMtx.Lock()
|
||||
c.accessToken = resp.Data.Token
|
||||
c.accessTokenExp = time.Unix(resp.Data.ExpiresAt, 0)
|
||||
c.accessTokenMtx.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) UpdateSSLCert(req *UpdateSSLCertRequest) (*UpdateSSLCertResponse, error) {
|
||||
if c.accessToken == "" || c.accessTokenExp.Before(time.Now()) {
|
||||
if err := c.getAccessToken(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.ensureAccessTokenExists(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &UpdateSSLCertResponse{}
|
||||
|
@@ -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,17 +11,16 @@ import (
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
apiToken string
|
||||
|
||||
client *resty.Client
|
||||
}
|
||||
|
||||
func NewClient(apiToken string) *Client {
|
||||
client := resty.New()
|
||||
client := resty.New().
|
||||
SetBaseURL("https://api.netlify.com/api/v1").
|
||||
SetHeader("Authorization", "Bearer "+apiToken)
|
||||
|
||||
return &Client{
|
||||
apiToken: apiToken,
|
||||
client: client,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +30,7 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client {
|
||||
}
|
||||
|
||||
func (c *Client) sendRequest(method string, path string, queryParams interface{}, payloadParams interface{}) (*resty.Response, error) {
|
||||
req := c.client.R().SetHeader("Authorization", "Bearer "+c.apiToken)
|
||||
req.Method = method
|
||||
req.URL = "https://api.netlify.com/api/v1" + path
|
||||
req := c.client.R()
|
||||
|
||||
if queryParams != nil {
|
||||
qs := make(map[string]string)
|
||||
@@ -63,12 +60,10 @@ func (c *Client) sendRequest(method string, path string, queryParams interface{}
|
||||
|
||||
req = req.SetQueryParams(qs)
|
||||
} else {
|
||||
req = req.
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(payloadParams)
|
||||
req = req.SetHeader("Content-Type", "application/json").SetBody(payloadParams)
|
||||
}
|
||||
|
||||
resp, err := req.Send()
|
||||
resp, err := req.Execute(method, path)
|
||||
if err != nil {
|
||||
return resp, fmt.Errorf("netlify api error: failed to send request: %w", err)
|
||||
} else if resp.IsError() {
|
||||
|
@@ -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,19 +11,16 @@ import (
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
apiHost string
|
||||
apiToken string
|
||||
|
||||
client *resty.Client
|
||||
}
|
||||
|
||||
func NewClient(apiHost, apiToken string) *Client {
|
||||
client := resty.New()
|
||||
client := resty.New().
|
||||
SetBaseURL(strings.TrimRight(apiHost, "/")).
|
||||
SetHeader("X-SLCE-API-TOKEN", apiToken)
|
||||
|
||||
return &Client{
|
||||
apiHost: strings.TrimRight(apiHost, "/"),
|
||||
apiToken: apiToken,
|
||||
client: client,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,12 +35,10 @@ func (c *Client) WithTLSConfig(config *tls.Config) *Client {
|
||||
}
|
||||
|
||||
func (c *Client) sendRequest(path string, params interface{}) (*resty.Response, error) {
|
||||
url := c.apiHost + path
|
||||
req := c.client.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("X-SLCE-API-TOKEN", c.apiToken).
|
||||
SetBody(params)
|
||||
resp, err := req.Post(url)
|
||||
resp, err := req.Post(path)
|
||||
if err != nil {
|
||||
return resp, fmt.Errorf("safeline api error: failed to send request: %w", err)
|
||||
} else if resp.IsError() {
|
||||
|
@@ -7,7 +7,11 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (c *Client) getCookie() error {
|
||||
func (c *Client) ensureCookieExists() error {
|
||||
if c.loginCookie != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
req := &signinRequest{Username: c.username, Password: c.password}
|
||||
res, err := c.sendRequest(http.MethodPost, "/accounts/signin/", req)
|
||||
if err != nil {
|
||||
@@ -27,10 +31,8 @@ func (c *Client) getCookie() error {
|
||||
}
|
||||
|
||||
func (c *Client) UploadHttpsCertificate(req *UploadHttpsCertificateRequest) (*UploadHttpsCertificateResponse, error) {
|
||||
if c.loginCookie == "" {
|
||||
if err := c.getCookie(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.ensureCookieExists(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &UploadHttpsCertificateResponse{}
|
||||
@@ -39,10 +41,8 @@ func (c *Client) UploadHttpsCertificate(req *UploadHttpsCertificateRequest) (*Up
|
||||
}
|
||||
|
||||
func (c *Client) GetHttpsCertificateManager(certificateId string) (*GetHttpsCertificateManagerResponse, error) {
|
||||
if c.loginCookie == "" {
|
||||
if err := c.getCookie(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.ensureCookieExists(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := &GetHttpsCertificateManagerRequest{CertificateId: certificateId}
|
||||
@@ -52,10 +52,8 @@ func (c *Client) GetHttpsCertificateManager(certificateId string) (*GetHttpsCert
|
||||
}
|
||||
|
||||
func (c *Client) UpdateHttpsCertificateManager(req *UpdateHttpsCertificateManagerRequest) (*UpdateHttpsCertificateManagerResponse, error) {
|
||||
if c.loginCookie == "" {
|
||||
if err := c.getCookie(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.ensureCookieExists(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &UpdateHttpsCertificateManagerResponse{}
|
||||
@@ -64,10 +62,8 @@ func (c *Client) UpdateHttpsCertificateManager(req *UpdateHttpsCertificateManage
|
||||
}
|
||||
|
||||
func (c *Client) GetHttpsServiceManager(domain string) (*GetHttpsServiceManagerResponse, error) {
|
||||
if c.loginCookie == "" {
|
||||
if err := c.getCookie(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.ensureCookieExists(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := &GetHttpsServiceManagerRequest{Domain: domain}
|
||||
@@ -77,10 +73,8 @@ func (c *Client) GetHttpsServiceManager(domain string) (*GetHttpsServiceManagerR
|
||||
}
|
||||
|
||||
func (c *Client) MigrateHttpsDomain(req *MigrateHttpsDomainRequest) (*MigrateHttpsDomainResponse, error) {
|
||||
if c.loginCookie == "" {
|
||||
if err := c.getCookie(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.ensureCookieExists(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &MigrateHttpsDomainResponse{}
|
||||
|
@@ -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"`
|
||||
}
|
@@ -134,8 +134,6 @@ func (c *Client) WithTimeout(timeout time.Duration) *Client {
|
||||
|
||||
func (c *Client) sendRequest(method string, path string, params interface{}, configureReq ...func(req *resty.Request)) (*resty.Response, error) {
|
||||
req := c.client.R()
|
||||
req.Method = method
|
||||
req.URL = path
|
||||
if strings.EqualFold(method, http.MethodGet) {
|
||||
qs := make(map[string]string)
|
||||
if params != nil {
|
||||
@@ -151,14 +149,16 @@ func (c *Client) sendRequest(method string, path string, params interface{}, con
|
||||
|
||||
req = req.SetQueryParams(qs)
|
||||
} else {
|
||||
req = req.SetBody(params)
|
||||
req = req.SetHeader("Content-Type", "application/json").SetBody(params)
|
||||
}
|
||||
|
||||
for _, fn := range configureReq {
|
||||
fn(req)
|
||||
if configureReq != nil {
|
||||
for _, fn := range configureReq {
|
||||
fn(req)
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := req.Send()
|
||||
resp, err := req.Execute(method, path)
|
||||
if err != nil {
|
||||
return resp, fmt.Errorf("wangsu api error: failed to send request: %w", err)
|
||||
} else if resp.IsError() {
|
||||
|
@@ -68,31 +68,42 @@ func GetOrDefaultInt32(dict map[string]any, key string, defaultValue int32) int3
|
||||
}
|
||||
|
||||
if value, ok := dict[key]; ok {
|
||||
if result, ok := value.(int32); ok {
|
||||
if result != 0 {
|
||||
return result
|
||||
var result int32
|
||||
|
||||
switch v := value.(type) {
|
||||
case int:
|
||||
result = int32(v)
|
||||
case int8:
|
||||
result = int32(v)
|
||||
case int16:
|
||||
result = int32(v)
|
||||
case int32:
|
||||
result = v
|
||||
case int64:
|
||||
result = int32(v)
|
||||
case uint:
|
||||
result = int32(v)
|
||||
case uint8:
|
||||
result = int32(v)
|
||||
case uint16:
|
||||
result = int32(v)
|
||||
case uint32:
|
||||
result = int32(v)
|
||||
case uint64:
|
||||
result = int32(v)
|
||||
case float32:
|
||||
result = int32(v)
|
||||
case float64:
|
||||
result = int32(v)
|
||||
case string:
|
||||
// 兼容字符串类型的值
|
||||
if t, err := strconv.ParseInt(v, 10, 32); err == nil {
|
||||
result = int32(t)
|
||||
}
|
||||
}
|
||||
|
||||
if result, ok := value.(int64); ok {
|
||||
if result != 0 {
|
||||
return int32(result)
|
||||
}
|
||||
}
|
||||
|
||||
if result, ok := value.(int); ok {
|
||||
if result != 0 {
|
||||
return int32(result)
|
||||
}
|
||||
}
|
||||
|
||||
// 兼容字符串类型的值
|
||||
if str, ok := value.(string); ok {
|
||||
if result, err := strconv.ParseInt(str, 10, 32); err == nil {
|
||||
if result != 0 {
|
||||
return int32(result)
|
||||
}
|
||||
}
|
||||
if result != 0 {
|
||||
return int32(result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,31 +137,42 @@ func GetOrDefaultInt64(dict map[string]any, key string, defaultValue int64) int6
|
||||
}
|
||||
|
||||
if value, ok := dict[key]; ok {
|
||||
if result, ok := value.(int64); ok {
|
||||
if result != 0 {
|
||||
return result
|
||||
var result int64
|
||||
|
||||
switch v := value.(type) {
|
||||
case int:
|
||||
result = int64(v)
|
||||
case int8:
|
||||
result = int64(v)
|
||||
case int16:
|
||||
result = int64(v)
|
||||
case int32:
|
||||
result = int64(v)
|
||||
case int64:
|
||||
result = v
|
||||
case uint:
|
||||
result = int64(v)
|
||||
case uint8:
|
||||
result = int64(v)
|
||||
case uint16:
|
||||
result = int64(v)
|
||||
case uint32:
|
||||
result = int64(v)
|
||||
case uint64:
|
||||
result = int64(v)
|
||||
case float32:
|
||||
result = int64(v)
|
||||
case float64:
|
||||
result = int64(v)
|
||||
case string:
|
||||
// 兼容字符串类型的值
|
||||
if t, err := strconv.ParseInt(v, 10, 32); err == nil {
|
||||
result = t
|
||||
}
|
||||
}
|
||||
|
||||
if result, ok := value.(int32); ok {
|
||||
if result != 0 {
|
||||
return int64(result)
|
||||
}
|
||||
}
|
||||
|
||||
if result, ok := value.(int); ok {
|
||||
if result != 0 {
|
||||
return int64(result)
|
||||
}
|
||||
}
|
||||
|
||||
// 兼容字符串类型的值
|
||||
if str, ok := value.(string); ok {
|
||||
if result, err := strconv.ParseInt(str, 10, 64); err == nil {
|
||||
if result != 0 {
|
||||
return result
|
||||
}
|
||||
}
|
||||
if result != 0 {
|
||||
return int64(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user