feat: support configuring huaweicloud enterprise project id

This commit is contained in:
Fu Diwei 2025-05-27 17:28:06 +08:00
parent a4f736e0f3
commit b8b94dfd77
14 changed files with 137 additions and 59 deletions

View File

@ -676,40 +676,44 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
switch options.Provider {
case domain.DeploymentProviderTypeHuaweiCloudCDN:
deployer, err := pHuaweiCloudCDN.NewDeployer(&pHuaweiCloudCDN.DeployerConfig{
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
Region: maputil.GetString(options.ProviderServiceConfig, "region"),
Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
EnterpriseProjectId: access.EnterpriseProjectId,
Region: maputil.GetString(options.ProviderServiceConfig, "region"),
Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err
case domain.DeploymentProviderTypeHuaweiCloudELB:
deployer, err := pHuaweiCloudELB.NewDeployer(&pHuaweiCloudELB.DeployerConfig{
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
Region: maputil.GetString(options.ProviderServiceConfig, "region"),
ResourceType: pHuaweiCloudELB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
CertificateId: maputil.GetString(options.ProviderServiceConfig, "certificateId"),
LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"),
ListenerId: maputil.GetString(options.ProviderServiceConfig, "listenerId"),
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
EnterpriseProjectId: access.EnterpriseProjectId,
Region: maputil.GetString(options.ProviderServiceConfig, "region"),
ResourceType: pHuaweiCloudELB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
CertificateId: maputil.GetString(options.ProviderServiceConfig, "certificateId"),
LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"),
ListenerId: maputil.GetString(options.ProviderServiceConfig, "listenerId"),
})
return deployer, err
case domain.DeploymentProviderTypeHuaweiCloudSCM:
deployer, err := pHuaweiCloudSCM.NewDeployer(&pHuaweiCloudSCM.DeployerConfig{
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
EnterpriseProjectId: access.EnterpriseProjectId,
})
return deployer, err
case domain.DeploymentProviderTypeHuaweiCloudWAF:
deployer, err := pHuaweiCloudWAF.NewDeployer(&pHuaweiCloudWAF.DeployerConfig{
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
Region: maputil.GetString(options.ProviderServiceConfig, "region"),
ResourceType: pHuaweiCloudWAF.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
CertificateId: maputil.GetString(options.ProviderServiceConfig, "certificateId"),
Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
EnterpriseProjectId: access.EnterpriseProjectId,
Region: maputil.GetString(options.ProviderServiceConfig, "region"),
ResourceType: pHuaweiCloudWAF.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
CertificateId: maputil.GetString(options.ProviderServiceConfig, "certificateId"),
Domain: maputil.GetString(options.ProviderServiceConfig, "domain"),
})
return deployer, err

View File

@ -199,8 +199,9 @@ type AccessConfigForHetzner struct {
}
type AccessConfigForHuaweiCloud struct {
AccessKeyId string `json:"accessKeyId"`
SecretAccessKey string `json:"secretAccessKey"`
AccessKeyId string `json:"accessKeyId"`
SecretAccessKey string `json:"secretAccessKey"`
EnterpriseProjectId string `json:"enterpriseProjectId,omitempty"`
}
type AccessConfigForJDCloud struct {

View File

@ -21,6 +21,8 @@ type DeployerConfig struct {
AccessKeyId string `json:"accessKeyId"`
// 华为云 SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
// 华为云企业项目 ID。
EnterpriseProjectId string `json:"enterpriseProjectId,omitempty"`
// 华为云区域。
Region string `json:"region"`
// 加速域名(不支持泛域名)。
@ -51,8 +53,9 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
}
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
EnterpriseProjectId: config.EnterpriseProjectId,
})
if err != nil {
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
@ -88,7 +91,8 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
// 查询加速域名配置
// REF: https://support.huaweicloud.com/api-cdn/ShowDomainFullConfig.html
showDomainFullConfigReq := &hccdnmodel.ShowDomainFullConfigRequest{
DomainName: d.config.Domain,
EnterpriseProjectId: typeutil.ToPtrOrZeroNil(d.config.EnterpriseProjectId),
DomainName: d.config.Domain,
}
showDomainFullConfigResp, err := d.sdkClient.ShowDomainFullConfig(showDomainFullConfigReq)
d.logger.Debug("sdk request 'cdn.ShowDomainFullConfig'", slog.Any("request", showDomainFullConfigReq), slog.Any("response", showDomainFullConfigResp))
@ -107,6 +111,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
updateDomainMultiCertificatesReqBodyContent.CertName = typeutil.ToPtr(upres.CertName)
updateDomainMultiCertificatesReqBodyContent = assign(updateDomainMultiCertificatesReqBodyContent, showDomainFullConfigResp.Configs)
updateDomainMultiCertificatesReq := &hccdnmodel.UpdateDomainMultiCertificatesRequest{
EnterpriseProjectId: typeutil.ToPtrOrZeroNil(d.config.EnterpriseProjectId),
Body: &hccdnmodel.UpdateDomainMultiCertificatesRequestBody{
Https: updateDomainMultiCertificatesReqBodyContent,
},

View File

@ -27,6 +27,8 @@ type DeployerConfig struct {
AccessKeyId string `json:"accessKeyId"`
// 华为云 SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
// 华为云企业项目 ID。
EnterpriseProjectId string `json:"enterpriseProjectId,omitempty"`
// 华为云区域。
Region string `json:"region"`
// 部署资源类型。
@ -62,9 +64,10 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
}
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
Region: config.Region,
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
EnterpriseProjectId: config.EnterpriseProjectId,
Region: config.Region,
})
if err != nil {
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
@ -172,6 +175,9 @@ func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, certPEM str
Protocol: &[]string{"HTTPS", "TERMINATED_HTTPS"},
LoadbalancerId: &[]string{showLoadBalancerResp.Loadbalancer.Id},
}
if d.config.EnterpriseProjectId != "" {
listListenersReq.EnterpriseProjectId = typeutil.ToPtr([]string{d.config.EnterpriseProjectId})
}
listListenersResp, err := d.sdkClient.ListListeners(listListenersReq)
d.logger.Debug("sdk request 'elb.ListListeners'", slog.Any("request", listListenersReq), slog.Any("response", listListenersResp))
if err != nil {

View File

@ -15,6 +15,8 @@ type DeployerConfig struct {
AccessKeyId string `json:"accessKeyId"`
// 华为云 SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
// 华为云企业项目 ID。
EnterpriseProjectId string `json:"enterpriseProjectId,omitempty"`
}
type DeployerProvider struct {
@ -31,8 +33,9 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
}
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
EnterpriseProjectId: config.EnterpriseProjectId,
})
if err != nil {
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)

View File

@ -27,6 +27,8 @@ type DeployerConfig struct {
AccessKeyId string `json:"accessKeyId"`
// 华为云 SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
// 华为云企业项目 ID。
EnterpriseProjectId string `json:"enterpriseProjectId,omitempty"`
// 华为云区域。
Region string `json:"region"`
// 部署资源类型。
@ -59,9 +61,10 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
}
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
Region: config.Region,
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
EnterpriseProjectId: config.EnterpriseProjectId,
Region: config.Region,
})
if err != nil {
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
@ -126,7 +129,8 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri
// 查询证书
// REF: https://support.huaweicloud.com/api-waf/ShowCertificate.html
showCertificateReq := &hcwafmodel.ShowCertificateRequest{
CertificateId: d.config.CertificateId,
EnterpriseProjectId: typeutil.ToPtrOrZeroNil(d.config.EnterpriseProjectId),
CertificateId: d.config.CertificateId,
}
showCertificateResp, err := d.sdkClient.ShowCertificate(showCertificateReq)
d.logger.Debug("sdk request 'waf.ShowCertificate'", slog.Any("request", showCertificateReq), slog.Any("response", showCertificateResp))
@ -137,7 +141,8 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri
// 更新证书
// REF: https://support.huaweicloud.com/api-waf/UpdateCertificate.html
updateCertificateReq := &hcwafmodel.UpdateCertificateRequest{
CertificateId: d.config.CertificateId,
EnterpriseProjectId: typeutil.ToPtrOrZeroNil(d.config.EnterpriseProjectId),
CertificateId: d.config.CertificateId,
Body: &hcwafmodel.UpdateCertificateRequestBody{
Name: *showCertificateResp.Name,
Content: typeutil.ToPtr(certPEM),
@ -179,9 +184,10 @@ func (d *DeployerProvider) deployToCloudServer(ctx context.Context, certPEM stri
}
listHostReq := &hcwafmodel.ListHostRequest{
Hostname: typeutil.ToPtr(strings.TrimPrefix(d.config.Domain, "*")),
Page: typeutil.ToPtr(listHostPage),
Pagesize: typeutil.ToPtr(listHostPageSize),
EnterpriseProjectId: typeutil.ToPtrOrZeroNil(d.config.EnterpriseProjectId),
Hostname: typeutil.ToPtr(strings.TrimPrefix(d.config.Domain, "*")),
Page: typeutil.ToPtr(listHostPage),
Pagesize: typeutil.ToPtr(listHostPageSize),
}
listHostResp, err := d.sdkClient.ListHost(listHostReq)
d.logger.Debug("sdk request 'waf.ListHost'", slog.Any("request", listHostReq), slog.Any("response", listHostResp))
@ -211,7 +217,8 @@ func (d *DeployerProvider) deployToCloudServer(ctx context.Context, certPEM stri
// 更新云模式防护域名的配置
// REF: https://support.huaweicloud.com/api-waf/UpdateHost.html
updateHostReq := &hcwafmodel.UpdateHostRequest{
InstanceId: hostId,
EnterpriseProjectId: typeutil.ToPtrOrZeroNil(d.config.EnterpriseProjectId),
InstanceId: hostId,
Body: &hcwafmodel.UpdateHostRequestBody{
Certificateid: typeutil.ToPtr(upres.CertId),
Certificatename: typeutil.ToPtr(upres.CertName),
@ -252,9 +259,10 @@ func (d *DeployerProvider) deployToPremiumHost(ctx context.Context, certPEM stri
}
listPremiumHostReq := &hcwafmodel.ListPremiumHostRequest{
Hostname: typeutil.ToPtr(strings.TrimPrefix(d.config.Domain, "*")),
Page: typeutil.ToPtr(fmt.Sprintf("%d", listPremiumHostPage)),
Pagesize: typeutil.ToPtr(fmt.Sprintf("%d", listPremiumHostPageSize)),
EnterpriseProjectId: typeutil.ToPtrOrZeroNil(d.config.EnterpriseProjectId),
Hostname: typeutil.ToPtr(strings.TrimPrefix(d.config.Domain, "*")),
Page: typeutil.ToPtr(fmt.Sprintf("%d", listPremiumHostPage)),
Pagesize: typeutil.ToPtr(fmt.Sprintf("%d", listPremiumHostPageSize)),
}
listPremiumHostResp, err := d.sdkClient.ListPremiumHost(listPremiumHostReq)
d.logger.Debug("sdk request 'waf.ListPremiumHost'", slog.Any("request", listPremiumHostReq), slog.Any("response", listPremiumHostResp))
@ -284,7 +292,8 @@ func (d *DeployerProvider) deployToPremiumHost(ctx context.Context, certPEM stri
// 修改独享模式域名配置
// REF: https://support.huaweicloud.com/api-waf/UpdatePremiumHost.html
updatePremiumHostReq := &hcwafmodel.UpdatePremiumHostRequest{
HostId: hostId,
EnterpriseProjectId: typeutil.ToPtrOrZeroNil(d.config.EnterpriseProjectId),
HostId: hostId,
Body: &hcwafmodel.UpdatePremiumHostRequestBody{
Certificateid: typeutil.ToPtr(upres.CertId),
Certificatename: typeutil.ToPtr(upres.CertName),

View File

@ -26,6 +26,8 @@ type UploaderConfig struct {
AccessKeyId string `json:"accessKeyId"`
// 华为云 SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
// 华为云企业项目 ID。
EnterpriseProjectId string `json:"enterpriseProjectId,omitempty"`
// 华为云区域。
Region string `json:"region"`
}
@ -141,10 +143,11 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
createCertificateReq := &hcelbmodel.CreateCertificateRequest{
Body: &hcelbmodel.CreateCertificateRequestBody{
Certificate: &hcelbmodel.CreateCertificateOption{
ProjectId: typeutil.ToPtr(projectId),
Name: typeutil.ToPtr(certName),
Certificate: typeutil.ToPtr(certPEM),
PrivateKey: typeutil.ToPtr(privkeyPEM),
EnterpriseProjectId: typeutil.ToPtrOrZeroNil(u.config.EnterpriseProjectId),
ProjectId: typeutil.ToPtr(projectId),
Name: typeutil.ToPtr(certName),
Certificate: typeutil.ToPtr(certPEM),
PrivateKey: typeutil.ToPtr(privkeyPEM),
},
},
}

View File

@ -21,6 +21,8 @@ type UploaderConfig struct {
AccessKeyId string `json:"accessKeyId"`
// 华为云 SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
// 华为云企业项目 ID。
EnterpriseProjectId string `json:"enterpriseProjectId,omitempty"`
// 华为云区域。
Region string `json:"region"`
}
@ -79,10 +81,11 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
}
listCertificatesReq := &hcscmmodel.ListCertificatesRequest{
Limit: typeutil.ToPtr(listCertificatesLimit),
Offset: typeutil.ToPtr(listCertificatesOffset),
SortDir: typeutil.ToPtr("DESC"),
SortKey: typeutil.ToPtr("certExpiredTime"),
EnterpriseProjectId: typeutil.ToPtrOrZeroNil(u.config.EnterpriseProjectId),
Limit: typeutil.ToPtr(listCertificatesLimit),
Offset: typeutil.ToPtr(listCertificatesOffset),
SortDir: typeutil.ToPtr("DESC"),
SortKey: typeutil.ToPtr("certExpiredTime"),
}
listCertificatesResp, err := u.sdkClient.ListCertificates(listCertificatesReq)
u.logger.Debug("sdk request 'scm.ListCertificates'", slog.Any("request", listCertificatesReq), slog.Any("response", listCertificatesResp))
@ -142,9 +145,10 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
// REF: https://support.huaweicloud.com/api-ccm/ImportCertificate.html
importCertificateReq := &hcscmmodel.ImportCertificateRequest{
Body: &hcscmmodel.ImportCertificateRequestBody{
Name: certName,
Certificate: certPEM,
PrivateKey: privkeyPEM,
EnterpriseProjectId: typeutil.ToPtrOrZeroNil(u.config.EnterpriseProjectId),
Name: certName,
Certificate: certPEM,
PrivateKey: privkeyPEM,
},
}
importCertificateResp, err := u.sdkClient.ImportCertificate(importCertificateReq)

View File

@ -26,6 +26,8 @@ type UploaderConfig struct {
AccessKeyId string `json:"accessKeyId"`
// 华为云 SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
// 华为云企业项目 ID。
EnterpriseProjectId string `json:"enterpriseProjectId,omitempty"`
// 华为云区域。
Region string `json:"region"`
}
@ -84,8 +86,9 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
}
listCertificatesReq := &hcwafmodel.ListCertificatesRequest{
Page: typeutil.ToPtr(listCertificatesPage),
Pagesize: typeutil.ToPtr(listCertificatesPageSize),
EnterpriseProjectId: typeutil.ToPtrOrZeroNil(u.config.EnterpriseProjectId),
Page: typeutil.ToPtr(listCertificatesPage),
Pagesize: typeutil.ToPtr(listCertificatesPageSize),
}
listCertificatesResp, err := u.sdkClient.ListCertificates(listCertificatesReq)
u.logger.Debug("sdk request 'waf.ShowCertificate'", slog.Any("request", listCertificatesReq), slog.Any("response", listCertificatesResp))
@ -96,7 +99,8 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
if listCertificatesResp.Items != nil {
for _, certItem := range *listCertificatesResp.Items {
showCertificateReq := &hcwafmodel.ShowCertificateRequest{
CertificateId: certItem.Id,
EnterpriseProjectId: typeutil.ToPtrOrZeroNil(u.config.EnterpriseProjectId),
CertificateId: certItem.Id,
}
showCertificateResp, err := u.sdkClient.ShowCertificate(showCertificateReq)
u.logger.Debug("sdk request 'waf.ShowCertificate'", slog.Any("request", showCertificateReq), slog.Any("response", showCertificateResp))
@ -141,6 +145,7 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPE
// 创建证书
// REF: https://support.huaweicloud.com/api-waf/CreateCertificate.html
createCertificateReq := &hcwafmodel.CreateCertificateRequest{
EnterpriseProjectId: typeutil.ToPtrOrZeroNil(u.config.EnterpriseProjectId),
Body: &hcwafmodel.CreateCertificateRequestBody{
Name: certName,
Content: certPEM,

View File

@ -1,5 +1,7 @@
package typeutil
import "reflect"
// 将对象转换为指针。
//
// 入参:
@ -11,6 +13,21 @@ func ToPtr[T any](v T) (p *T) {
return &v
}
// 将非零值的对象转换为指针。
// 与 [ToPtr] 不同的是,如果对象的值为零值,则返回 nil。
//
// 入参:
// - 待转换的对象。
//
// 出参:
// - 返回对象的指针。
func ToPtrOrZeroNil[T any](v T) (p *T) {
if !reflect.ValueOf(v).IsZero() {
return &v
}
return nil
}
// 将指针转换为对象。
//
// 入参:

View File

@ -28,14 +28,19 @@ const AccessFormHuaweiCloudConfig = ({ form: formInst, formName, disabled, initi
const formSchema = z.object({
accessKeyId: z
.string()
.trim()
.min(1, t("access.form.huaweicloud_access_key_id.placeholder"))
.max(64, t("common.errmsg.string_max", { max: 64 }))
.trim(),
.max(64, t("common.errmsg.string_max", { max: 64 })),
secretAccessKey: z
.string()
.trim()
.min(1, t("access.form.huaweicloud_secret_access_key.placeholder"))
.max(64, t("common.errmsg.string_max", { max: 64 })),
enterpriseProjectId: z
.string()
.trim()
.max(64, t("common.errmsg.string_max", { max: 64 }))
.trim(),
.nullish(),
});
const formRule = createSchemaFieldRule(formSchema);
@ -69,6 +74,15 @@ const AccessFormHuaweiCloudConfig = ({ form: formInst, formName, disabled, initi
>
<Input.Password autoComplete="new-password" placeholder={t("access.form.huaweicloud_secret_access_key.placeholder")} />
</Form.Item>
<Form.Item
name="enterpriseProjectId"
label={t("access.form.huaweicloud_enterprise_project_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.huaweicloud_enterprise_project_id.tooltip") }}></span>}
>
<Input allowClear autoComplete="new-password" placeholder={t("access.form.huaweicloud_enterprise_project_id.placeholder")} />
</Form.Item>
</Form>
);
};

View File

@ -264,6 +264,7 @@ export type AccessConfigForHetzner = {
export type AccessConfigForHuaweiCloud = {
accessKeyId: string;
secretAccessKey: string;
enterpriseProjectId?: string;
};
export type AccessConfigForJDCloud = {

View File

@ -252,6 +252,9 @@
"access.form.huaweicloud_secret_access_key.label": "Huawei Cloud SecretAccessKey",
"access.form.huaweicloud_secret_access_key.placeholder": "Please enter Huawei Cloud SecretAccessKey",
"access.form.huaweicloud_secret_access_key.tooltip": "For more information, see <a href=\"https://support.huaweicloud.com/intl/en-us/usermanual-ca/ca_01_0003.html\" target=\"_blank\">https://support.huaweicloud.com/intl/en-us/usermanual-ca/ca_01_0003.html</a>",
"access.form.huaweicloud_enterprise_project_id.label": "Huawei Cloud enterprise project ID (Optional)",
"access.form.huaweicloud_enterprise_project_id.placeholder": "Please enter Huawei Cloud enterprise project ID",
"access.form.huaweicloud_enterprise_project_id.tooltip": "For more information, see <a href=\"https://support.huaweicloud.com/intl/en-us/usermanual-em/em_03_0000.html\" target=\"_blank\">https://support.huaweicloud.com/intl/en-us/usermanual-em/em_03_0000.html</a>",
"access.form.jdcloud_access_key_id.label": "JD Cloud AccessKeyId",
"access.form.jdcloud_access_key_id.placeholder": "Please enter JD Cloud AccessKeyId",
"access.form.jdcloud_access_key_id.tooltip": "For more information, see <a href=\"https://docs.jdcloud.com/en/account-management/accesskey-management\" target=\"_blank\">https://docs.jdcloud.com/en/account-management/accesskey-management</a>",

View File

@ -252,6 +252,9 @@
"access.form.huaweicloud_secret_access_key.label": "华为云 SecretAccessKey",
"access.form.huaweicloud_secret_access_key.placeholder": "请输入华为云 SecretAccessKey",
"access.form.huaweicloud_secret_access_key.tooltip": "这是什么?请参阅 <a href=\"https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html\" target=\"_blank\">https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html</a>",
"access.form.huaweicloud_enterprise_project_id.label": "华为云企业项目 ID可选",
"access.form.huaweicloud_enterprise_project_id.placeholder": "请输入华为云企业项目 ID",
"access.form.huaweicloud_enterprise_project_id.tooltip": "这是什么?请参阅 <a href=\"https://support.huaweicloud.com/usermanual-em/zh-cn_topic_0126101490.html\" target=\"_blank\">https://support.huaweicloud.com/usermanual-em/zh-cn_topic_0126101490.html</a>",
"access.form.jdcloud_access_key_id.label": "京东云 AccessKeyId",
"access.form.jdcloud_access_key_id.placeholder": "请输入京东云 AccessKeyId",
"access.form.jdcloud_access_key_id.tooltip": "这是什么?请参阅 <a href=\"https://docs.jdcloud.com/cn/account-management/accesskey-management\" target=\"_blank\">https://docs.jdcloud.com/cn/account-management/accesskey-management</a>",