diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go
index 1fec85e0..71874c90 100644
--- a/internal/deployer/providers.go
+++ b/internal/deployer/providers.go
@@ -1048,6 +1048,7 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) {
deployer, err := pWangsuCDNPro.NewDeployer(&pWangsuCDNPro.DeployerConfig{
AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret,
+ ApiKey: access.ApiKey,
Environment: maputil.GetOrDefaultString(options.ProviderDeployConfig, "environment", "production"),
Domain: maputil.GetString(options.ProviderDeployConfig, "domain"),
CertificateId: maputil.GetString(options.ProviderDeployConfig, "certificateId"),
diff --git a/internal/domain/access.go b/internal/domain/access.go
index 0571eb84..fdceae48 100644
--- a/internal/domain/access.go
+++ b/internal/domain/access.go
@@ -235,6 +235,7 @@ type AccessConfigForVolcEngine struct {
type AccessConfigForWangsu struct {
AccessKeyId string `json:"accessKeyId"`
AccessKeySecret string `json:"accessKeySecret"`
+ ApiKey string `json:"apiKey"`
}
type AccessConfigForWebhook struct {
diff --git a/internal/pkg/core/deployer/providers/ssh/ssh.go b/internal/pkg/core/deployer/providers/ssh/ssh.go
index 8fba9490..3093ab42 100644
--- a/internal/pkg/core/deployer/providers/ssh/ssh.go
+++ b/internal/pkg/core/deployer/providers/ssh/ssh.go
@@ -243,7 +243,6 @@ func writeFileWithSCP(sshCli *ssh.Client, path string, data []byte) error {
if err != nil {
return xerrors.Wrap(err, "failed to create scp client")
}
- defer scpCli.Close()
reader := bytes.NewReader(data)
err = scpCli.CopyToRemote(reader, path, &scp.FileTransferOption{})
diff --git a/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro.go b/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro.go
index c5ac15b9..a0c1e586 100644
--- a/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro.go
+++ b/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro.go
@@ -13,6 +13,7 @@ import (
"fmt"
"log/slog"
"regexp"
+ "strconv"
"time"
"github.com/alibabacloud-go/tea/tea"
@@ -28,6 +29,8 @@ type DeployerConfig struct {
AccessKeyId string `json:"accessKeyId"`
// 网宿云 AccessKeySecret。
AccessKeySecret string `json:"accessKeySecret"`
+ // 网宿云 API Key。
+ ApiKey string `json:"apiKey"`
// 网宿云环境。
Environment string `json:"environment"`
// 加速域名(支持泛域名)。
@@ -93,7 +96,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
}
// 生成网宿云证书参数
- encryptedPrivateKey, err := encryptPrivateKey(privkeyPem, d.config.AccessKeySecret, time.Now().Unix())
+ encryptedPrivateKey, err := encryptPrivateKey(privkeyPem, d.config.ApiKey, time.Now().Unix())
if err != nil {
return nil, xerrors.Wrap(err, "failed to encrypt private key")
}
@@ -111,7 +114,8 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
// http://open.chinanetcenter.com/cdn/certificates/5dca2205f9e9cc0001df7b33
// http://open.chinanetcenter.com/cdn/certificates/329f12c1fe6708c23c31e91f/versions/5
var wangsuCertUrl string
- var wangsuCertId, wangsuCertVer string
+ var wangsuCertId string
+ var wangsuCertVer int32
// 如果原证书 ID 为空,则创建证书;否则更新证书。
timestamp := time.Now().Unix()
@@ -137,7 +141,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
wangsuCertId = wangsuCertIdMatches[1]
}
- wangsuCertVer = "1"
+ wangsuCertVer = 1
} else {
// 更新证书
updateCertificateReq := &wangsucdn.UpdateCertificateRequest{
@@ -162,7 +166,8 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
wangsuCertVerMatches := regexp.MustCompile(`/versions/(\d+)`).FindStringSubmatch(wangsuCertUrl)
if len(wangsuCertVerMatches) > 1 {
- wangsuCertVer = wangsuCertVerMatches[1]
+ n, _ := strconv.ParseInt(wangsuCertVerMatches[1], 10, 32)
+ wangsuCertVer = int32(n)
}
}
@@ -175,7 +180,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
{
Action: tea.String("deploy_cert"),
CertificateId: tea.String(wangsuCertId),
- Version: tea.String(wangsuCertVer),
+ Version: tea.Int32(wangsuCertVer),
},
},
}
@@ -191,7 +196,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
// 循环获取部署任务详细信息,等待任务状态变更
// REF: https://www.wangsu.com/document/api-doc/27038
var wangsuTaskId string
- wangsuTaskMatches := regexp.MustCompile(`/deploymentTasks/([a-zA-Z0-9-]+)`).FindStringSubmatch(wangsuCertUrl)
+ wangsuTaskMatches := regexp.MustCompile(`/deploymentTasks/([a-zA-Z0-9-]+)`).FindStringSubmatch(createDeploymentTaskResp.DeploymentTaskUrl)
if len(wangsuTaskMatches) > 1 {
wangsuTaskId = wangsuTaskMatches[1]
}
@@ -201,19 +206,19 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
}
getDeploymentTaskDetailResp, err := d.sdkClient.GetDeploymentTaskDetail(wangsuTaskId)
- d.logger.Debug("sdk request 'cdn.GetDeploymentTaskDetail'", slog.Any("taskId", wangsuTaskId), slog.Any("response", getDeploymentTaskDetailResp))
+ d.logger.Info("sdk request 'cdn.GetDeploymentTaskDetail'", slog.Any("taskId", wangsuTaskId), slog.Any("response", getDeploymentTaskDetailResp))
if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.GetDeploymentTaskDetail'")
}
if getDeploymentTaskDetailResp.Status == "failed" {
return nil, errors.New("unexpected deployment task status")
- } else if getDeploymentTaskDetailResp.Status == "succeeded" {
+ } else if getDeploymentTaskDetailResp.Status == "succeeded" || getDeploymentTaskDetailResp.FinishTime != "" {
break
}
- d.logger.Info("waiting for deployment task completion ...")
- time.Sleep(time.Second * 15)
+ d.logger.Info(fmt.Sprintf("waiting for deployment task completion (current status: %s) ...", getDeploymentTaskDetailResp.Status))
+ time.Sleep(time.Second * 5)
}
return &deployer.DeployResult{}, nil
@@ -231,11 +236,11 @@ func createSdkClient(accessKeyId, accessKeySecret string) (*wangsucdn.Client, er
return wangsucdn.NewClient(accessKeyId, accessKeySecret), nil
}
-func encryptPrivateKey(privkeyPem string, secretKey string, timestamp int64) (string, error) {
+func encryptPrivateKey(privkeyPem string, apiKey string, timestamp int64) (string, error) {
date := time.Unix(timestamp, 0).UTC()
dateStr := date.Format("Mon, 02 Jan 2006 15:04:05 GMT")
- mac := hmac.New(sha256.New, []byte(secretKey))
+ mac := hmac.New(sha256.New, []byte(apiKey))
mac.Write([]byte(dateStr))
aesivkey := mac.Sum(nil)
aesivkeyHex := hex.EncodeToString(aesivkey)
diff --git a/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro_test.go b/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro_test.go
index 25dd7b1e..2a868fab 100644
--- a/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro_test.go
+++ b/internal/pkg/core/deployer/providers/wangsu-cdnpro/wangsu_cdnpro_test.go
@@ -16,6 +16,7 @@ var (
fInputKeyPath string
fAccessKeyId string
fAccessKeySecret string
+ fApiKey string
fEnvironment string
fDomain string
fCertificateId string
@@ -29,6 +30,7 @@ func init() {
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
+ flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
flag.StringVar(&fEnvironment, argsPrefix+"ENVIRONMENT", "production", "")
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
flag.StringVar(&fCertificateId, argsPrefix+"CERTIFICATEID", "", "")
@@ -43,9 +45,10 @@ Shell command to run this test:
--CERTIMATE_DEPLOYER_WANGSUCDNPRO_INPUTKEYPATH="/path/to/your-input-key.pem" \
--CERTIMATE_DEPLOYER_WANGSUCDNPRO_ACCESSKEYID="your-access-key-id" \
--CERTIMATE_DEPLOYER_WANGSUCDNPRO_ACCESSKEYSECRET="your-access-key-secret" \
+ --CERTIMATE_DEPLOYER_WANGSUCDNPRO_APIKEY="your-api-key" \
--CERTIMATE_DEPLOYER_WANGSUCDNPRO_ENVIRONMENT="production" \
--CERTIMATE_DEPLOYER_WANGSUCDNPRO_DOMAIN="example.com" \
- --CERTIMATE_DEPLOYER_WANGSUCDNPRO_CERTIFICATEID="your-certificate-id"\
+ --CERTIMATE_DEPLOYER_WANGSUCDNPRO_CERTIFICATEID="your-certificate-id" \
--CERTIMATE_DEPLOYER_WANGSUCDNPRO_WEBHOOKID="your-webhook-id"
*/
func TestDeploy(t *testing.T) {
@@ -58,6 +61,7 @@ func TestDeploy(t *testing.T) {
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
+ fmt.Sprintf("APIKEY: %v", fApiKey),
fmt.Sprintf("ENVIRONMENT: %v", fEnvironment),
fmt.Sprintf("DOMAIN: %v", fDomain),
fmt.Sprintf("CERTIFICATEID: %v", fCertificateId),
@@ -67,6 +71,7 @@ func TestDeploy(t *testing.T) {
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
AccessKeyId: fAccessKeyId,
AccessKeySecret: fAccessKeySecret,
+ ApiKey: fApiKey,
Environment: fEnvironment,
Domain: fDomain,
CertificateId: fCertificateId,
diff --git a/internal/pkg/vendors/cdnfly-sdk/models.go b/internal/pkg/vendors/cdnfly-sdk/models.go
index 07497cd3..9622627a 100644
--- a/internal/pkg/vendors/cdnfly-sdk/models.go
+++ b/internal/pkg/vendors/cdnfly-sdk/models.go
@@ -1,6 +1,6 @@
package cdnflysdk
-import "encoding/json"
+import "fmt"
type BaseResponse interface {
GetCode() string
@@ -8,12 +8,24 @@ type BaseResponse interface {
}
type baseResponse struct {
- Code json.Number `json:"code"`
- Message string `json:"msg"`
+ Code any `json:"code"`
+ Message string `json:"msg"`
}
func (r *baseResponse) GetCode() string {
- return r.Code.String()
+ if r.Code == nil {
+ return ""
+ }
+
+ if code, ok := r.Code.(int); ok {
+ return fmt.Sprintf("%d", code)
+ }
+
+ if code, ok := r.Code.(string); ok {
+ return code
+ }
+
+ return ""
}
func (r *baseResponse) GetMessage() string {
diff --git a/internal/pkg/vendors/wangsu-sdk/cdn/api.go b/internal/pkg/vendors/wangsu-sdk/cdn/api.go
index dff719f1..92a05cfa 100644
--- a/internal/pkg/vendors/wangsu-sdk/cdn/api.go
+++ b/internal/pkg/vendors/wangsu-sdk/cdn/api.go
@@ -22,6 +22,10 @@ func (c *Client) CreateCertificate(req *CreateCertificateRequest) (*CreateCertif
}
func (c *Client) UpdateCertificate(certificateId string, req *UpdateCertificateRequest) (*UpdateCertificateResponse, error) {
+ if certificateId == "" {
+ return nil, fmt.Errorf("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))
@@ -35,6 +39,10 @@ func (c *Client) UpdateCertificate(certificateId string, req *UpdateCertificateR
}
func (c *Client) GetHostnameDetail(hostname string) (*GetHostnameDetailResponse, error) {
+ if hostname == "" {
+ return nil, fmt.Errorf("invalid parameter: hostname")
+ }
+
resp := &GetHostnameDetailResponse{}
_, err := c.client.SendRequestWithResult(http.MethodGet, fmt.Sprintf("/cdn/hostnames/%s", url.PathEscape(hostname)), nil, resp)
return resp, err
@@ -52,7 +60,11 @@ func (c *Client) CreateDeploymentTask(req *CreateDeploymentTaskRequest) (*Create
}
func (c *Client) GetDeploymentTaskDetail(deploymentTaskId string) (*GetDeploymentTaskDetailResponse, error) {
+ if deploymentTaskId == "" {
+ return nil, fmt.Errorf("invalid parameter: deploymentTaskId")
+ }
+
resp := &GetDeploymentTaskDetailResponse{}
- _, err := c.client.SendRequestWithResult(http.MethodGet, fmt.Sprintf("/cdn/deploymentTasks/%s", deploymentTaskId), nil, resp)
+ _, err := c.client.SendRequestWithResult(http.MethodGet, fmt.Sprintf("/cdn/deploymentTasks/%s", url.PathEscape(deploymentTaskId)), nil, resp)
return resp, err
}
diff --git a/internal/pkg/vendors/wangsu-sdk/cdn/models.go b/internal/pkg/vendors/wangsu-sdk/cdn/models.go
index 0126418a..2bebced2 100644
--- a/internal/pkg/vendors/wangsu-sdk/cdn/models.go
+++ b/internal/pkg/vendors/wangsu-sdk/cdn/models.go
@@ -80,7 +80,7 @@ type DeploymentTaskAction struct {
Action *string `json:"action,omitempty" required:"true"`
PropertyId *string `json:"propertyId,omitempty"`
CertificateId *string `json:"certificateId,omitempty"`
- Version *string `json:"version,omitempty"`
+ Version *int32 `json:"version,omitempty"`
}
type CreateDeploymentTaskRequest struct {
@@ -97,6 +97,7 @@ type CreateDeploymentTaskResponse struct {
type GetDeploymentTaskDetailResponse struct {
baseResponse
+ Name string `json:"name"`
Target string `json:"target"`
Actions []DeploymentTaskAction `json:"actions"`
Status string `json:"status"`
diff --git a/internal/pkg/vendors/wangsu-sdk/openapi/client.go b/internal/pkg/vendors/wangsu-sdk/openapi/client.go
index 6492aba8..d63c20c7 100644
--- a/internal/pkg/vendors/wangsu-sdk/openapi/client.go
+++ b/internal/pkg/vendors/wangsu-sdk/openapi/client.go
@@ -111,7 +111,7 @@ func NewClient(accessKey, secretKey string) *Client {
signHex := strings.ToLower(hex.EncodeToString(sign))
// Step 9: Add headers to request
- req.Header.Set("x-cnc-accessKey", accessKey)
+ req.Header.Set("x-cnc-accesskey", accessKey)
req.Header.Set("x-cnc-timestamp", timestampString)
req.Header.Set("x-cnc-auth-method", "AKSK")
req.Header.Set("Authorization", fmt.Sprintf("%s Credential=%s, SignedHeaders=%s, Signature=%s", SignAlgorithmHeader, accessKey, signedHeaders, signHex))
@@ -178,8 +178,11 @@ func (c *Client) SendRequestWithResult(method string, path string, params interf
return resp, err
}
- if err := json.Unmarshal(resp.Body(), &result); err != nil {
- return resp, fmt.Errorf("wangsu api error: failed to parse response: %w", err)
+ respBody := resp.Body()
+ if len(respBody) != 0 {
+ if err := json.Unmarshal(respBody, &result); err != nil {
+ return resp, fmt.Errorf("wangsu api error: failed to parse response: %w", err)
+ }
}
result.SetRequestId(resp.Header().Get("x-cnc-request-id"))
diff --git a/ui/src/components/access/AccessFormWangsuConfig.tsx b/ui/src/components/access/AccessFormWangsuConfig.tsx
index f9676829..bb4f699c 100644
--- a/ui/src/components/access/AccessFormWangsuConfig.tsx
+++ b/ui/src/components/access/AccessFormWangsuConfig.tsx
@@ -19,6 +19,7 @@ const initFormModel = (): AccessFormWangsuConfigFieldValues => {
return {
accessKeyId: "",
accessKeySecret: "",
+ apiKey: "",
};
};
@@ -36,6 +37,11 @@ const AccessFormWangsuConfig = ({ form: formInst, formName, disabled, initialVal
.min(1, t("access.form.wangsu_access_key_secret.placeholder"))
.max(64, t("common.errmsg.string_max", { max: 64 }))
.trim(),
+ apiKey: z
+ .string()
+ .min(1, t("access.form.wangsu_api_key.placeholder"))
+ .max(256, t("common.errmsg.string_max", { max: 256 }))
+ .trim(),
});
const formRule = createSchemaFieldRule(formSchema);
@@ -69,6 +75,15 @@ const AccessFormWangsuConfig = ({ form: formInst, formName, disabled, initialVal
>