Compare commits

...

9 Commits

Author SHA1 Message Date
Yoan.liu
cf98e789d8
Merge pull request #627 from fudiwei/bugfix
bugfix
2025-04-21 17:50:02 +08:00
Fu Diwei
ff58b9a317 fix: wangsu api error 2025-04-21 09:17:52 +08:00
Fu Diwei
54ae378e30 fix: handle deployment task status check on deployment to wangsu cdnpro 2025-04-19 19:37:31 +08:00
Fu Diwei
32ff658e84 fix: #626 2025-04-18 18:20:01 +08:00
Fu Diwei
c10ceed753 feat: improve log 2025-04-18 18:04:52 +08:00
Fu Diwei
eb45b56a87 fix: ignore wangsu api responses without content 2025-04-18 17:54:51 +08:00
Fu Diwei
7329a22132 chore(ui): improve i18n 2025-04-17 22:11:45 +08:00
Fu Diwei
50b48d956f fix: #617 2025-04-17 22:08:53 +08:00
Fu Diwei
55d7a05af8 fix: #615 2025-04-17 21:44:40 +08:00
13 changed files with 87 additions and 26 deletions

View File

@ -1048,6 +1048,7 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) {
deployer, err := pWangsuCDNPro.NewDeployer(&pWangsuCDNPro.DeployerConfig{ deployer, err := pWangsuCDNPro.NewDeployer(&pWangsuCDNPro.DeployerConfig{
AccessKeyId: access.AccessKeyId, AccessKeyId: access.AccessKeyId,
AccessKeySecret: access.AccessKeySecret, AccessKeySecret: access.AccessKeySecret,
ApiKey: access.ApiKey,
Environment: maputil.GetOrDefaultString(options.ProviderDeployConfig, "environment", "production"), Environment: maputil.GetOrDefaultString(options.ProviderDeployConfig, "environment", "production"),
Domain: maputil.GetString(options.ProviderDeployConfig, "domain"), Domain: maputil.GetString(options.ProviderDeployConfig, "domain"),
CertificateId: maputil.GetString(options.ProviderDeployConfig, "certificateId"), CertificateId: maputil.GetString(options.ProviderDeployConfig, "certificateId"),

View File

@ -235,6 +235,7 @@ type AccessConfigForVolcEngine struct {
type AccessConfigForWangsu struct { type AccessConfigForWangsu struct {
AccessKeyId string `json:"accessKeyId"` AccessKeyId string `json:"accessKeyId"`
AccessKeySecret string `json:"accessKeySecret"` AccessKeySecret string `json:"accessKeySecret"`
ApiKey string `json:"apiKey"`
} }
type AccessConfigForWebhook struct { type AccessConfigForWebhook struct {

View File

@ -243,7 +243,6 @@ func writeFileWithSCP(sshCli *ssh.Client, path string, data []byte) error {
if err != nil { if err != nil {
return xerrors.Wrap(err, "failed to create scp client") return xerrors.Wrap(err, "failed to create scp client")
} }
defer scpCli.Close()
reader := bytes.NewReader(data) reader := bytes.NewReader(data)
err = scpCli.CopyToRemote(reader, path, &scp.FileTransferOption{}) err = scpCli.CopyToRemote(reader, path, &scp.FileTransferOption{})

View File

@ -13,6 +13,7 @@ import (
"fmt" "fmt"
"log/slog" "log/slog"
"regexp" "regexp"
"strconv"
"time" "time"
"github.com/alibabacloud-go/tea/tea" "github.com/alibabacloud-go/tea/tea"
@ -28,6 +29,8 @@ type DeployerConfig struct {
AccessKeyId string `json:"accessKeyId"` AccessKeyId string `json:"accessKeyId"`
// 网宿云 AccessKeySecret。 // 网宿云 AccessKeySecret。
AccessKeySecret string `json:"accessKeySecret"` AccessKeySecret string `json:"accessKeySecret"`
// 网宿云 API Key。
ApiKey string `json:"apiKey"`
// 网宿云环境。 // 网宿云环境。
Environment string `json:"environment"` 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 { if err != nil {
return nil, xerrors.Wrap(err, "failed to encrypt private key") 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/5dca2205f9e9cc0001df7b33
// http://open.chinanetcenter.com/cdn/certificates/329f12c1fe6708c23c31e91f/versions/5 // http://open.chinanetcenter.com/cdn/certificates/329f12c1fe6708c23c31e91f/versions/5
var wangsuCertUrl string var wangsuCertUrl string
var wangsuCertId, wangsuCertVer string var wangsuCertId string
var wangsuCertVer int32
// 如果原证书 ID 为空,则创建证书;否则更新证书。 // 如果原证书 ID 为空,则创建证书;否则更新证书。
timestamp := time.Now().Unix() timestamp := time.Now().Unix()
@ -137,7 +141,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
wangsuCertId = wangsuCertIdMatches[1] wangsuCertId = wangsuCertIdMatches[1]
} }
wangsuCertVer = "1" wangsuCertVer = 1
} else { } else {
// 更新证书 // 更新证书
updateCertificateReq := &wangsucdn.UpdateCertificateRequest{ updateCertificateReq := &wangsucdn.UpdateCertificateRequest{
@ -162,7 +166,8 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPe
wangsuCertVerMatches := regexp.MustCompile(`/versions/(\d+)`).FindStringSubmatch(wangsuCertUrl) wangsuCertVerMatches := regexp.MustCompile(`/versions/(\d+)`).FindStringSubmatch(wangsuCertUrl)
if len(wangsuCertVerMatches) > 1 { 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"), Action: tea.String("deploy_cert"),
CertificateId: tea.String(wangsuCertId), 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 // REF: https://www.wangsu.com/document/api-doc/27038
var wangsuTaskId string 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 { if len(wangsuTaskMatches) > 1 {
wangsuTaskId = 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) 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 { if err != nil {
return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.GetDeploymentTaskDetail'") return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.GetDeploymentTaskDetail'")
} }
if getDeploymentTaskDetailResp.Status == "failed" { if getDeploymentTaskDetailResp.Status == "failed" {
return nil, errors.New("unexpected deployment task status") return nil, errors.New("unexpected deployment task status")
} else if getDeploymentTaskDetailResp.Status == "succeeded" { } else if getDeploymentTaskDetailResp.Status == "succeeded" || getDeploymentTaskDetailResp.FinishTime != "" {
break break
} }
d.logger.Info("waiting for deployment task completion ...") d.logger.Info(fmt.Sprintf("waiting for deployment task completion (current status: %s) ...", getDeploymentTaskDetailResp.Status))
time.Sleep(time.Second * 15) time.Sleep(time.Second * 5)
} }
return &deployer.DeployResult{}, nil return &deployer.DeployResult{}, nil
@ -231,11 +236,11 @@ func createSdkClient(accessKeyId, accessKeySecret string) (*wangsucdn.Client, er
return wangsucdn.NewClient(accessKeyId, accessKeySecret), nil 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() date := time.Unix(timestamp, 0).UTC()
dateStr := date.Format("Mon, 02 Jan 2006 15:04:05 GMT") 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)) mac.Write([]byte(dateStr))
aesivkey := mac.Sum(nil) aesivkey := mac.Sum(nil)
aesivkeyHex := hex.EncodeToString(aesivkey) aesivkeyHex := hex.EncodeToString(aesivkey)

View File

@ -16,6 +16,7 @@ var (
fInputKeyPath string fInputKeyPath string
fAccessKeyId string fAccessKeyId string
fAccessKeySecret string fAccessKeySecret string
fApiKey string
fEnvironment string fEnvironment string
fDomain string fDomain string
fCertificateId string fCertificateId string
@ -29,6 +30,7 @@ func init() {
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "") flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "")
flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
flag.StringVar(&fEnvironment, argsPrefix+"ENVIRONMENT", "production", "") flag.StringVar(&fEnvironment, argsPrefix+"ENVIRONMENT", "production", "")
flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "")
flag.StringVar(&fCertificateId, argsPrefix+"CERTIFICATEID", "", "") flag.StringVar(&fCertificateId, argsPrefix+"CERTIFICATEID", "", "")
@ -43,6 +45,7 @@ Shell command to run this test:
--CERTIMATE_DEPLOYER_WANGSUCDNPRO_INPUTKEYPATH="/path/to/your-input-key.pem" \ --CERTIMATE_DEPLOYER_WANGSUCDNPRO_INPUTKEYPATH="/path/to/your-input-key.pem" \
--CERTIMATE_DEPLOYER_WANGSUCDNPRO_ACCESSKEYID="your-access-key-id" \ --CERTIMATE_DEPLOYER_WANGSUCDNPRO_ACCESSKEYID="your-access-key-id" \
--CERTIMATE_DEPLOYER_WANGSUCDNPRO_ACCESSKEYSECRET="your-access-key-secret" \ --CERTIMATE_DEPLOYER_WANGSUCDNPRO_ACCESSKEYSECRET="your-access-key-secret" \
--CERTIMATE_DEPLOYER_WANGSUCDNPRO_APIKEY="your-api-key" \
--CERTIMATE_DEPLOYER_WANGSUCDNPRO_ENVIRONMENT="production" \ --CERTIMATE_DEPLOYER_WANGSUCDNPRO_ENVIRONMENT="production" \
--CERTIMATE_DEPLOYER_WANGSUCDNPRO_DOMAIN="example.com" \ --CERTIMATE_DEPLOYER_WANGSUCDNPRO_DOMAIN="example.com" \
--CERTIMATE_DEPLOYER_WANGSUCDNPRO_CERTIFICATEID="your-certificate-id" \ --CERTIMATE_DEPLOYER_WANGSUCDNPRO_CERTIFICATEID="your-certificate-id" \
@ -58,6 +61,7 @@ func TestDeploy(t *testing.T) {
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret), fmt.Sprintf("ACCESSKEYSECRET: %v", fAccessKeySecret),
fmt.Sprintf("APIKEY: %v", fApiKey),
fmt.Sprintf("ENVIRONMENT: %v", fEnvironment), fmt.Sprintf("ENVIRONMENT: %v", fEnvironment),
fmt.Sprintf("DOMAIN: %v", fDomain), fmt.Sprintf("DOMAIN: %v", fDomain),
fmt.Sprintf("CERTIFICATEID: %v", fCertificateId), fmt.Sprintf("CERTIFICATEID: %v", fCertificateId),
@ -67,6 +71,7 @@ func TestDeploy(t *testing.T) {
deployer, err := provider.NewDeployer(&provider.DeployerConfig{ deployer, err := provider.NewDeployer(&provider.DeployerConfig{
AccessKeyId: fAccessKeyId, AccessKeyId: fAccessKeyId,
AccessKeySecret: fAccessKeySecret, AccessKeySecret: fAccessKeySecret,
ApiKey: fApiKey,
Environment: fEnvironment, Environment: fEnvironment,
Domain: fDomain, Domain: fDomain,
CertificateId: fCertificateId, CertificateId: fCertificateId,

View File

@ -1,6 +1,6 @@
package cdnflysdk package cdnflysdk
import "encoding/json" import "fmt"
type BaseResponse interface { type BaseResponse interface {
GetCode() string GetCode() string
@ -8,12 +8,24 @@ type BaseResponse interface {
} }
type baseResponse struct { type baseResponse struct {
Code json.Number `json:"code"` Code any `json:"code"`
Message string `json:"msg"` Message string `json:"msg"`
} }
func (r *baseResponse) GetCode() string { 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 { func (r *baseResponse) GetMessage() string {

View File

@ -22,6 +22,10 @@ func (c *Client) CreateCertificate(req *CreateCertificateRequest) (*CreateCertif
} }
func (c *Client) UpdateCertificate(certificateId string, req *UpdateCertificateRequest) (*UpdateCertificateResponse, error) { func (c *Client) UpdateCertificate(certificateId string, req *UpdateCertificateRequest) (*UpdateCertificateResponse, error) {
if certificateId == "" {
return nil, fmt.Errorf("invalid parameter: certificateId")
}
resp := &UpdateCertificateResponse{} resp := &UpdateCertificateResponse{}
r, err := c.client.SendRequestWithResult(http.MethodPatch, fmt.Sprintf("/cdn/certificates/%s", url.PathEscape(certificateId)), req, resp, func(r *resty.Request) { 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)) 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) { func (c *Client) GetHostnameDetail(hostname string) (*GetHostnameDetailResponse, error) {
if hostname == "" {
return nil, fmt.Errorf("invalid parameter: hostname")
}
resp := &GetHostnameDetailResponse{} resp := &GetHostnameDetailResponse{}
_, err := c.client.SendRequestWithResult(http.MethodGet, fmt.Sprintf("/cdn/hostnames/%s", url.PathEscape(hostname)), nil, resp) _, err := c.client.SendRequestWithResult(http.MethodGet, fmt.Sprintf("/cdn/hostnames/%s", url.PathEscape(hostname)), nil, resp)
return resp, err return resp, err
@ -52,7 +60,11 @@ func (c *Client) CreateDeploymentTask(req *CreateDeploymentTaskRequest) (*Create
} }
func (c *Client) GetDeploymentTaskDetail(deploymentTaskId string) (*GetDeploymentTaskDetailResponse, error) { func (c *Client) GetDeploymentTaskDetail(deploymentTaskId string) (*GetDeploymentTaskDetailResponse, error) {
if deploymentTaskId == "" {
return nil, fmt.Errorf("invalid parameter: deploymentTaskId")
}
resp := &GetDeploymentTaskDetailResponse{} 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 return resp, err
} }

View File

@ -80,7 +80,7 @@ type DeploymentTaskAction struct {
Action *string `json:"action,omitempty" required:"true"` Action *string `json:"action,omitempty" required:"true"`
PropertyId *string `json:"propertyId,omitempty"` PropertyId *string `json:"propertyId,omitempty"`
CertificateId *string `json:"certificateId,omitempty"` CertificateId *string `json:"certificateId,omitempty"`
Version *string `json:"version,omitempty"` Version *int32 `json:"version,omitempty"`
} }
type CreateDeploymentTaskRequest struct { type CreateDeploymentTaskRequest struct {
@ -97,6 +97,7 @@ type CreateDeploymentTaskResponse struct {
type GetDeploymentTaskDetailResponse struct { type GetDeploymentTaskDetailResponse struct {
baseResponse baseResponse
Name string `json:"name"`
Target string `json:"target"` Target string `json:"target"`
Actions []DeploymentTaskAction `json:"actions"` Actions []DeploymentTaskAction `json:"actions"`
Status string `json:"status"` Status string `json:"status"`

View File

@ -111,7 +111,7 @@ func NewClient(accessKey, secretKey string) *Client {
signHex := strings.ToLower(hex.EncodeToString(sign)) signHex := strings.ToLower(hex.EncodeToString(sign))
// Step 9: Add headers to request // 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-timestamp", timestampString)
req.Header.Set("x-cnc-auth-method", "AKSK") 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)) req.Header.Set("Authorization", fmt.Sprintf("%s Credential=%s, SignedHeaders=%s, Signature=%s", SignAlgorithmHeader, accessKey, signedHeaders, signHex))
@ -178,9 +178,12 @@ func (c *Client) SendRequestWithResult(method string, path string, params interf
return resp, err return resp, err
} }
if err := json.Unmarshal(resp.Body(), &result); err != nil { 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) return resp, fmt.Errorf("wangsu api error: failed to parse response: %w", err)
} }
}
result.SetRequestId(resp.Header().Get("x-cnc-request-id")) result.SetRequestId(resp.Header().Get("x-cnc-request-id"))
return resp, nil return resp, nil

View File

@ -19,6 +19,7 @@ const initFormModel = (): AccessFormWangsuConfigFieldValues => {
return { return {
accessKeyId: "", accessKeyId: "",
accessKeySecret: "", accessKeySecret: "",
apiKey: "",
}; };
}; };
@ -36,6 +37,11 @@ const AccessFormWangsuConfig = ({ form: formInst, formName, disabled, initialVal
.min(1, t("access.form.wangsu_access_key_secret.placeholder")) .min(1, t("access.form.wangsu_access_key_secret.placeholder"))
.max(64, t("common.errmsg.string_max", { max: 64 })) .max(64, t("common.errmsg.string_max", { max: 64 }))
.trim(), .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); const formRule = createSchemaFieldRule(formSchema);
@ -69,6 +75,15 @@ const AccessFormWangsuConfig = ({ form: formInst, formName, disabled, initialVal
> >
<Input.Password autoComplete="new-password" placeholder={t("access.form.wangsu_access_key_secret.placeholder")} /> <Input.Password autoComplete="new-password" placeholder={t("access.form.wangsu_access_key_secret.placeholder")} />
</Form.Item> </Form.Item>
<Form.Item
name="apiKey"
label={t("access.form.wangsu_api_key.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.wangsu_api_key.tooltip") }}></span>}
>
<Input.Password autoComplete="new-password" placeholder={t("access.form.wangsu_api_key.placeholder")} />
</Form.Item>
</Form> </Form>
); );
}; };

View File

@ -277,6 +277,7 @@ export type AccessConfigForVolcEngine = {
export type AccessConfigForWangsu = { export type AccessConfigForWangsu = {
accessKeyId: string; accessKeyId: string;
accessKeySecret: string; accessKeySecret: string;
apiKey: string;
}; };
export type AccessConfigForWebhook = { export type AccessConfigForWebhook = {

View File

@ -307,6 +307,9 @@
"access.form.wangsu_access_key_secret.label": "Wangsu Cloud AccessKeySecret", "access.form.wangsu_access_key_secret.label": "Wangsu Cloud AccessKeySecret",
"access.form.wangsu_access_key_secret.placeholder": "Please enter Wangsu Cloud AccessKeySecret", "access.form.wangsu_access_key_secret.placeholder": "Please enter Wangsu Cloud AccessKeySecret",
"access.form.wangsu_access_key_secret.tooltip": "For more information, see <a href=\"https://en.wangsu.com/document/account-manage/15775\" target=\"_blank\">https://en.wangsu.com/document/account-manage/15775</a>", "access.form.wangsu_access_key_secret.tooltip": "For more information, see <a href=\"https://en.wangsu.com/document/account-manage/15775\" target=\"_blank\">https://en.wangsu.com/document/account-manage/15775</a>",
"access.form.wangsu_api_key.label": "Wangsu Cloud API key",
"access.form.wangsu_api_key.placeholder": "Please enter Wangsu Cloud API key",
"access.form.wangsu_api_key.tooltip": "For more information, see <a href=\"https://en.wangsu.com/document/account-manage/15776\" target=\"_blank\">https://en.wangsu.com/document/account-manage/15776</a>",
"access.form.webhook_url.label": "Webhook URL", "access.form.webhook_url.label": "Webhook URL",
"access.form.webhook_url.placeholder": "Please enter Webhook URL", "access.form.webhook_url.placeholder": "Please enter Webhook URL",
"access.form.webhook_allow_insecure_conns.label": "Insecure SSL/TLS connections", "access.form.webhook_allow_insecure_conns.label": "Insecure SSL/TLS connections",

View File

@ -302,11 +302,14 @@
"access.form.volcengine_secret_access_key.placeholder": "请输入火山引擎 SecretAccessKey", "access.form.volcengine_secret_access_key.placeholder": "请输入火山引擎 SecretAccessKey",
"access.form.volcengine_secret_access_key.tooltip": "这是什么?请参阅 <a href=\"https://www.volcengine.com/docs/6291/216571\" target=\"_blank\">https://www.volcengine.com/docs/6291/216571</a>", "access.form.volcengine_secret_access_key.tooltip": "这是什么?请参阅 <a href=\"https://www.volcengine.com/docs/6291/216571\" target=\"_blank\">https://www.volcengine.com/docs/6291/216571</a>",
"access.form.wangsu_access_key_id.label": "网宿云 AccessKeyId", "access.form.wangsu_access_key_id.label": "网宿云 AccessKeyId",
"access.form.wangsu_access_key_id.placeholder": "请输入网宿科技 AccessKeyId", "access.form.wangsu_access_key_id.placeholder": "请输入网宿 AccessKeyId",
"access.form.wangsu_access_key_id.tooltip": "这是什么?请参阅 <a href=\"https://www.wangsu.com/document/account-manage/15775\" target=\"_blank\">https://www.wangsu.com/document/account-manage/15775</a>", "access.form.wangsu_access_key_id.tooltip": "这是什么?请参阅 <a href=\"https://www.wangsu.com/document/account-manage/15775\" target=\"_blank\">https://www.wangsu.com/document/account-manage/15775</a>",
"access.form.wangsu_access_key_secret.label": "网宿科技 AccessKeySecret", "access.form.wangsu_access_key_secret.label": "网宿 AccessKeySecret",
"access.form.wangsu_access_key_secret.placeholder": "请输入网宿科技 AccessKeySecret", "access.form.wangsu_access_key_secret.placeholder": "请输入网宿 AccessKeySecret",
"access.form.wangsu_access_key_secret.tooltip": "这是什么?请参阅 <a href=\"https://www.wangsu.com/document/account-manage/15775\" target=\"_blank\">https://www.wangsu.com/document/account-manage/15775</a>", "access.form.wangsu_access_key_secret.tooltip": "这是什么?请参阅 <a href=\"https://www.wangsu.com/document/account-manage/15775\" target=\"_blank\">https://www.wangsu.com/document/account-manage/15775</a>",
"access.form.wangsu_api_key.label": "网宿云 API 接口密码",
"access.form.wangsu_api_key.placeholder": "请输入网宿云 API 接口密码",
"access.form.wangsu_api_key.tooltip": "这是什么?请参阅 <a href=\"https://www.wangsu.com/document/account-manage/15776\" target=\"_blank\">https://www.wangsu.com/document/account-manage/15776</a>",
"access.form.webhook_url.label": "Webhook 回调地址", "access.form.webhook_url.label": "Webhook 回调地址",
"access.form.webhook_url.placeholder": "请输入 Webhook 回调地址", "access.form.webhook_url.placeholder": "请输入 Webhook 回调地址",
"access.form.webhook_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", "access.form.webhook_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误",