feat(provider): new deployment provider: huaweicloud vod

This commit is contained in:
Fu Diwei 2026-07-13 15:06:37 +08:00 committed by RHQYZ
parent 78a61e256c
commit 757b299654
21 changed files with 575 additions and 15 deletions

View File

@ -20,6 +20,7 @@ func init() {
AccessKeyId: credentials.AccessKeyId,
SecretAccessKey: credentials.SecretAccessKey,
EnterpriseProjectId: credentials.EnterpriseProjectId,
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
InstanceId: xmaps.GetString(options.ProviderExtendedConfig, "instanceId"),
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),

View File

@ -0,0 +1,29 @@
package deployers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
dplyimpl "github.com/certimate-go/certimate/pkg/core/deployer/providers/huaweicloud-vod"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
Registries.MustRegister(domain.DeploymentProviderTypeHuaweiCloudVOD, func(options *ProviderFactoryOptions) (core.Deployer, error) {
credentials := domain.AccessConfigForHuaweiCloud{}
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := dplyimpl.NewDeployer(&dplyimpl.DeployerConfig{
AccessKeyId: credentials.AccessKeyId,
SecretAccessKey: credentials.SecretAccessKey,
EnterpriseProjectId: credentials.EnterpriseProjectId,
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"),
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
})
return provider, err
})
}

View File

@ -388,6 +388,7 @@ const (
DeploymentProviderTypeHuaweiCloudLive = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-live")
DeploymentProviderTypeHuaweiCloudOBS = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-obs")
DeploymentProviderTypeHuaweiCloudSCM = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-scm")
DeploymentProviderTypeHuaweiCloudVOD = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-vod")
DeploymentProviderTypeHuaweiCloudWAF = DeploymentProviderType(AccessProviderTypeHuaweiCloud + "-waf")
DeploymentProviderTypeJDCloudALB = DeploymentProviderType(AccessProviderTypeJDCloud + "-alb")
DeploymentProviderTypeJDCloudCDN = DeploymentProviderType(AccessProviderTypeJDCloud + "-cdn")

View File

@ -32,6 +32,8 @@ type DeployerConfig struct {
SecretAccessKey string `json:"secretAccessKey"`
// 华为云企业项目 ID。
EnterpriseProjectId string `json:"enterpriseProjectId,omitempty"`
// 华为云区域。
Region string `json:"region"`
// DDoS 高防实例 ID。
InstanceId string `json:"instanceId"`
// 域名匹配模式。
@ -59,7 +61,7 @@ func NewDeployer(config *DeployerConfig) (*Deployer, error) {
return nil, fmt.Errorf("the configuration of the deployer provider is nil")
}
clients, err := createSDKClients(config.AccessKeyId, config.SecretAccessKey)
clients, err := createSDKClients(config.AccessKeyId, config.SecretAccessKey, config.Region)
if err != nil {
return nil, fmt.Errorf("could not create client: %w", err)
}
@ -251,12 +253,14 @@ func (d *Deployer) updateDomainCertificate(ctx context.Context, cloudDomainId st
return nil
}
func createSDKClients(accessKeyId, secretAccessKey string) (*wSDKClients, error) {
func createSDKClients(accessKeyId, secretAccessKey, region string) (*wSDKClients, error) {
wsdk := &wSDKClients{}
{
region := "cn-north-4" // AAD 服务默认区域:华北北京四
if region == "" {
region = "cn-north-4" // AAD 服务默认区域:华北北京四
}
{
auth, err := global.NewCredentialsBuilder().
WithAk(accessKeyId).
WithSk(secretAccessKey).
@ -283,8 +287,6 @@ func createSDKClients(accessKeyId, secretAccessKey string) (*wSDKClients, error)
}
{
region := "cn-north-4" // AAD 服务默认区域:华北北京四
auth, err := global.NewCredentialsBuilder().
WithAk(accessKeyId).
WithSk(secretAccessKey).

View File

@ -11,7 +11,7 @@ import (
hwapigmodel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/apig/v2/model"
hwapigregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/apig/v2/region"
hwiam "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3"
hwiamModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/model"
hwiammodel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/model"
hwiamregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/region"
"github.com/samber/lo"
@ -188,7 +188,7 @@ func getSDKProjectId(accessKeyId, secretAccessKey, region string) (string, error
client := hwiam.NewIamClient(hcClient)
request := &hwiamModel.KeystoneListProjectsRequest{
request := &hwiammodel.KeystoneListProjectsRequest{
Name: &region,
}
response, err := client.KeystoneListProjects(request)

View File

@ -9,7 +9,7 @@ import (
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/global"
hwiam "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3"
hwiamModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/model"
hwiammodel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/model"
hwiamregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/region"
hwlivemodel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/live/v1/model"
hwliveregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/live/v1/region"
@ -262,7 +262,7 @@ func getSDKProjectId(accessKeyId, secretAccessKey, region string) (string, error
client := hwiam.NewIamClient(hcClient)
request := &hwiamModel.KeystoneListProjectsRequest{
request := &hwiammodel.KeystoneListProjectsRequest{
Name: &region,
}
response, err := client.KeystoneListProjects(request)

View File

@ -0,0 +1,6 @@
package huaweicloudvod
const (
// 匹配模式:精确匹配。
DOMAIN_MATCH_PATTERN_EXACT = "exact"
)

View File

@ -0,0 +1,249 @@
package huaweicloudvod
import (
"context"
"errors"
"fmt"
"log/slog"
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/global"
hwiam "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3"
hwiammodel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/model"
hwiamregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/region"
hwvodmodel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/vod/v1/model"
hwvodregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/vod/v1/region"
"github.com/samber/lo"
hwvod "github.com/certimate-go/certimate/pkg/sdk3rd-trimmed/github.com/huaweicloud/huaweicloud-sdk-go-v3/services/vod/v1"
"github.com/certimate-go/certimate/pkg/core"
cmgrimpl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/huaweicloud-scm"
)
type (
Provider = core.Deployer
DeployResult = core.DeployerDeployResult
)
type DeployerConfig struct {
// 华为云 AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// 华为云 SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
// 华为云企业项目 ID。
EnterpriseProjectId string `json:"enterpriseProjectId,omitempty"`
// 华为云区域。
Region string `json:"region"`
// 域名匹配模式。
// 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。
DomainMatchPattern string `json:"domainMatchPattern,omitempty"`
// 点播加速域名(不支持泛域名)。
Domain string `json:"domain"`
}
type Deployer struct {
config *DeployerConfig
logger *slog.Logger
sdkClient *hwvod.VodClient
sdkCertmgr core.Certmgr
}
var _ Provider = (*Deployer)(nil)
func NewDeployer(config *DeployerConfig) (*Deployer, error) {
if config == nil {
return nil, fmt.Errorf("the configuration of the deployer provider is nil")
}
client, err := createSDKClient(
config.AccessKeyId,
config.SecretAccessKey,
config.Region,
)
if err != nil {
return nil, fmt.Errorf("could not create client: %w", err)
}
pcertmgr, err := cmgrimpl.NewCertmgr(&cmgrimpl.CertmgrConfig{
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
EnterpriseProjectId: config.EnterpriseProjectId,
})
if err != nil {
return nil, fmt.Errorf("could not create certmgr: %w", err)
}
return &Deployer{
config: config,
logger: slog.Default(),
sdkClient: client,
sdkCertmgr: pcertmgr,
}, nil
}
func (d *Deployer) SetLogger(logger *slog.Logger) {
if logger == nil {
d.logger = slog.New(slog.DiscardHandler)
} else {
d.logger = logger
}
d.sdkCertmgr.SetLogger(logger)
}
func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*DeployResult, error) {
// 上传证书
upres, err := d.sdkCertmgr.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))
}
// 获取待部署的域名列表
var domains []string
switch d.config.DomainMatchPattern {
case "", DOMAIN_MATCH_PATTERN_EXACT:
{
if d.config.Domain == "" {
return nil, fmt.Errorf("config `domain` is required")
}
domains = []string{d.config.Domain}
}
default:
return nil, fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern)
}
// 遍历更新域名证书
if len(domains) == 0 {
d.logger.Info("no vod domains to deploy")
} else {
d.logger.Info("found vod domains to deploy", slog.Any("domains", domains))
var errs []error
for _, domain := range domains {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
if err := d.updateDomainsCertificate(ctx, domain, upres.CertId); err != nil {
errs = append(errs, err)
}
}
}
if len(errs) > 0 {
return nil, errors.Join(errs...)
}
}
return &DeployResult{}, nil
}
func (d *Deployer) updateDomainsCertificate(ctx context.Context, domain string, cloudCertId string) error {
// 查询 HTTPS 配置
// REF: https://support.huaweicloud.com/api-vod/ShowHttpsConfig.html
showHttpsConfigReq := &hwvodmodel.ShowHttpsConfigRequest{
Domain: domain,
}
showHttpsConfigResp, err := d.sdkClient.ShowHttpsConfig(showHttpsConfigReq)
d.logger.Debug("sdk request 'vod.ShowHttpsConfig'", slog.Any("request", showHttpsConfigReq), slog.Any("response", showHttpsConfigResp))
if err != nil {
return fmt.Errorf("failed to execute sdk request 'vod.ShowHttpsConfig': %w", err)
} else if lo.FromPtr(showHttpsConfigResp.CertId) == cloudCertId {
// 已部署过,直接返回
return nil
}
// 配置 HTTPS
// REF: https://support.huaweicloud.com/api-vod/UpdateHttpsConfig.html
updateHttpsConfigReq := &hwvodmodel.UpdateHttpsConfigRequest{
Body: &hwvodmodel.ConfigCdnHttpsReq{
Domain: domain,
Source: lo.ToPtr("scm"),
CertId: lo.ToPtr(cloudCertId),
HttpsStatus: lo.ToPtr(int32(1)),
Http2: showHttpsConfigResp.Http2,
ForceRedirectHttps: showHttpsConfigResp.ForceRedirectHttps,
},
}
updateHttpsConfigResp, err := d.sdkClient.UpdateHttpsConfig(updateHttpsConfigReq)
d.logger.Debug("sdk request 'vod.UpdateHttpsConfig'", slog.Any("request", updateHttpsConfigReq), slog.Any("response", updateHttpsConfigResp))
if err != nil {
return fmt.Errorf("failed to execute sdk request 'vod.UpdateHttpsConfig': %w", err)
}
return nil
}
func createSDKClient(accessKeyId, secretAccessKey, region string) (*hwvod.VodClient, error) {
projectId, err := getSDKProjectId(accessKeyId, secretAccessKey, region)
if err != nil {
return nil, err
}
auth, err := basic.NewCredentialsBuilder().
WithAk(accessKeyId).
WithSk(secretAccessKey).
WithProjectId(projectId).
SafeBuild()
if err != nil {
return nil, err
}
hcRegion, err := hwvodregion.SafeValueOf(region)
if err != nil {
return nil, err
}
hcClient, err := hwvod.VodClientBuilder().
WithRegion(hcRegion).
WithCredential(auth).
SafeBuild()
if err != nil {
return nil, err
}
client := hwvod.NewVodClient(hcClient)
return client, nil
}
func getSDKProjectId(accessKeyId, secretAccessKey, region string) (string, error) {
auth, err := global.NewCredentialsBuilder().
WithAk(accessKeyId).
WithSk(secretAccessKey).
SafeBuild()
if err != nil {
return "", err
}
hcRegion, err := hwiamregion.SafeValueOf(region)
if err != nil {
return "", err
}
hcClient, err := hwiam.IamClientBuilder().
WithRegion(hcRegion).
WithCredential(auth).
SafeBuild()
if err != nil {
return "", err
}
client := hwiam.NewIamClient(hcClient)
request := &hwiammodel.KeystoneListProjectsRequest{
Name: &region,
}
response, err := client.KeystoneListProjects(request)
if err != nil {
return "", err
} else if response.Projects == nil || len(*response.Projects) == 0 {
return "", fmt.Errorf("huaweicloud: no project found")
}
return (*response.Projects)[0].Id, nil
}

View File

@ -0,0 +1,58 @@
package huaweicloudvod_test
import (
"testing"
"github.com/certimate-go/certimate/pkg/core/deployer/internal/tester"
impl "github.com/certimate-go/certimate/pkg/core/deployer/providers/huaweicloud-vod"
)
var (
fp = tester.Args("HUAWEICLOUDVOD_")
fTestCertPath string
fTestKeyPath string
fAccessKeyId string
fSecretAccessKey string
fRegion string
fDomain string
)
func init() {
fp.DefineString(&fTestCertPath, "TESTCERTPATH")
fp.DefineString(&fTestKeyPath, "TESTKEYPATH")
fp.DefineString(&fAccessKeyId, "ACCESSKEYID")
fp.DefineString(&fSecretAccessKey, "SECRETACCESSKEY")
fp.DefineString(&fRegion, "REGION")
fp.DefineString(&fDomain, "DOMAIN")
}
/*
Shell command to run this test:
go test -v ./huaweicloud_vod_test.go -args \
--HUAWEICLOUDVOD_TESTCERTPATH="/path/to/your-test-cert.pem" \
--HUAWEICLOUDVOD_TESTKEYPATH="/path/to/your-test-key.pem" \
--HUAWEICLOUDVOD_ACCESSKEYID="your-access-key-id" \
--HUAWEICLOUDVOD_SECRETACCESSKEY="your-secret-access-key" \
--HUAWEICLOUDVOD_REGION="cn-north-1" \
--HUAWEICLOUDVOD_DOMAIN="example.com"
*/
func TestProvider(t *testing.T) {
fp.Parse()
t.Run("Deploy", func(t *testing.T) {
provider, err := impl.NewDeployer(&impl.DeployerConfig{
AccessKeyId: fAccessKeyId,
SecretAccessKey: fSecretAccessKey,
Region: fRegion,
DomainMatchPattern: impl.DOMAIN_MATCH_PATTERN_EXACT,
Domain: fDomain,
})
if err != nil {
t.Errorf("err: %+v", err)
return
}
tester.TestDeploy(t, provider, tester.TestDeployArgs{CertPath: fTestCertPath, KeyPath: fTestKeyPath})
})
}

View File

@ -9,7 +9,7 @@ import (
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/global"
hwiam "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3"
hwiamModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/model"
hwiammodel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/model"
hwiamregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/region"
hwwafmodel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1/model"
hwwafregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1/region"
@ -372,7 +372,7 @@ func getSDKProjectId(accessKeyId, secretAccessKey, region string) (string, error
client := hwiam.NewIamClient(hcClient)
request := &hwiamModel.KeystoneListProjectsRequest{
request := &hwiammodel.KeystoneListProjectsRequest{
Name: &region,
}
response, err := client.KeystoneListProjects(request)

View File

@ -0,0 +1,39 @@
package v1
import (
httpclient "github.com/huaweicloud/huaweicloud-sdk-go-v3/core"
vod "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/vod/v1"
"github.com/huaweicloud/huaweicloud-sdk-go-v3/services/vod/v1/model"
)
type VodClient struct {
HcClient *httpclient.HcHttpClient
}
func NewVodClient(hcClient *httpclient.HcHttpClient) *VodClient {
return &VodClient{HcClient: hcClient}
}
func VodClientBuilder() *httpclient.HcHttpClientBuilder {
return vod.VodClientBuilder()
}
func (c *VodClient) ShowHttpsConfig(request *model.ShowHttpsConfigRequest) (*model.ShowHttpsConfigResponse, error) {
requestDef := GenReqDefForShowHttpsConfig()
if resp, err := c.HcClient.Sync(request, requestDef); err != nil {
return nil, err
} else {
return resp.(*model.ShowHttpsConfigResponse), nil
}
}
func (c *VodClient) UpdateHttpsConfig(request *model.UpdateHttpsConfigRequest) (*model.UpdateHttpsConfigResponse, error) {
requestDef := GenReqDefForUpdateHttpsConfig()
if resp, err := c.HcClient.Sync(request, requestDef); err != nil {
return nil, err
} else {
return resp.(*model.UpdateHttpsConfigResponse), nil
}
}

View File

@ -0,0 +1,14 @@
package v1
import (
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/def"
vod "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/vod/v1"
)
func GenReqDefForShowHttpsConfig() *def.HttpRequestDef {
return vod.GenReqDefForShowHttpsConfig()
}
func GenReqDefForUpdateHttpsConfig() *def.HttpRequestDef {
return vod.GenReqDefForUpdateHttpsConfig()
}

View File

@ -72,6 +72,7 @@ import BizDeployNodeConfigFieldsProviderHuaweiCloudCDN from "./BizDeployNodeConf
import BizDeployNodeConfigFieldsProviderHuaweiCloudELB from "./BizDeployNodeConfigFieldsProviderHuaweiCloudELB";
import BizDeployNodeConfigFieldsProviderHuaweiCloudLive from "./BizDeployNodeConfigFieldsProviderHuaweiCloudLive";
import BizDeployNodeConfigFieldsProviderHuaweiCloudOBS from "./BizDeployNodeConfigFieldsProviderHuaweiCloudOBS";
import BizDeployNodeConfigFieldsProviderHuaweiCloudVOD from "./BizDeployNodeConfigFieldsProviderHuaweiCloudVOD";
import BizDeployNodeConfigFieldsProviderHuaweiCloudWAF from "./BizDeployNodeConfigFieldsProviderHuaweiCloudWAF";
import BizDeployNodeConfigFieldsProviderJDCloudALB from "./BizDeployNodeConfigFieldsProviderJDCloudALB";
import BizDeployNodeConfigFieldsProviderJDCloudCDN from "./BizDeployNodeConfigFieldsProviderJDCloudCDN";
@ -219,6 +220,7 @@ const providerComponentMap: Partial<Record<DeploymentProviderType, React.Compone
[DEPLOYMENT_PROVIDERS.HUAWEICLOUD_ELB]: BizDeployNodeConfigFieldsProviderHuaweiCloudELB,
[DEPLOYMENT_PROVIDERS.HUAWEICLOUD_LIVE]: BizDeployNodeConfigFieldsProviderHuaweiCloudLive,
[DEPLOYMENT_PROVIDERS.HUAWEICLOUD_OBS]: BizDeployNodeConfigFieldsProviderHuaweiCloudOBS,
[DEPLOYMENT_PROVIDERS.HUAWEICLOUD_VOD]: BizDeployNodeConfigFieldsProviderHuaweiCloudVOD,
[DEPLOYMENT_PROVIDERS.HUAWEICLOUD_WAF]: BizDeployNodeConfigFieldsProviderHuaweiCloudWAF,
[DEPLOYMENT_PROVIDERS.JDCLOUD_ALB]: BizDeployNodeConfigFieldsProviderJDCloudALB,
[DEPLOYMENT_PROVIDERS.JDCLOUD_CDN]: BizDeployNodeConfigFieldsProviderJDCloudCDN,

View File

@ -27,6 +27,16 @@ const BizDeployNodeConfigFieldsProviderHuaweiCloudAAD = () => {
return (
<>
<Form.Item
name={[parentNamePath, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.huaweicloud_aad_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.huaweicloud_aad_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.huaweicloud_aad_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "instanceId"]}
initialValue={initialValues.instanceId}
@ -74,6 +84,7 @@ const BizDeployNodeConfigFieldsProviderHuaweiCloudAAD = () => {
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
instanceId: "",
domainMatchPattern: DOMAIN_MATCH_PATTERN_EXACT,
domain: "",
@ -85,6 +96,7 @@ const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> })
return z
.object({
region: z.string().nonempty(),
instanceId: z.string().nonempty(),
domainMatchPattern: z.string().nonempty().default(DOMAIN_MATCH_PATTERN_EXACT),
domain: z.string().nullish(),

View File

@ -26,6 +26,16 @@ const BizDeployNodeConfigFieldsProviderHuaweiCloudLive = () => {
return (
<>
<Form.Item
name={[parentNamePath, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.huaweicloud_live_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.huaweicloud_live_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.huaweicloud_live_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "domainMatchPattern"]}
initialValue={initialValues.domainMatchPattern}
@ -56,6 +66,7 @@ const BizDeployNodeConfigFieldsProviderHuaweiCloudLive = () => {
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
domainMatchPattern: DOMAIN_MATCH_PATTERN_EXACT,
domain: "",
};
@ -66,6 +77,7 @@ const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> })
return z
.object({
region: z.string().nonempty(),
domainMatchPattern: z.string().nonempty().default(DOMAIN_MATCH_PATTERN_EXACT),
domain: z.string().nullish(),
})

View File

@ -0,0 +1,103 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Radio } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { isDomain } from "@/utils/validator";
import { useFormNestedFieldsContext } from "./_context";
const DOMAIN_MATCH_PATTERN_EXACT = "exact" as const;
const BizDeployNodeConfigFieldsProviderHuaweiCloudVOD = () => {
const { i18n, t } = useTranslation();
const { parentNamePath } = useFormNestedFieldsContext();
const formSchema = z.object({
[parentNamePath]: getSchema({ i18n }),
});
const formRule = createSchemaFieldRule(formSchema);
const initialValues = getInitialValues();
return (
<>
<Form.Item
name={[parentNamePath, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.huaweicloud_vod_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.huaweicloud_vod_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.huaweicloud_vod_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "domainMatchPattern"]}
initialValue={initialValues.domainMatchPattern}
label={t("workflow_node.deploy.form.shared_domain_match_pattern.label")}
rules={[formRule]}
>
<Radio.Group
options={[DOMAIN_MATCH_PATTERN_EXACT].map((s) => ({
label: t(`workflow_node.deploy.form.shared_domain_match_pattern.option.${s}.label`),
value: s,
}))}
/>
</Form.Item>
<Form.Item
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.huaweicloud_vod_domain.label")}
rules={[formRule]}
>
<Input placeholder={t("workflow_node.deploy.form.huaweicloud_vod_domain.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
domainMatchPattern: DOMAIN_MATCH_PATTERN_EXACT,
domain: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z
.object({
region: z.string().nonempty(),
domainMatchPattern: z.string().nonempty().default(DOMAIN_MATCH_PATTERN_EXACT),
domain: z.string().nullish(),
})
.superRefine((values, ctx) => {
if (values.domainMatchPattern) {
switch (values.domainMatchPattern) {
case DOMAIN_MATCH_PATTERN_EXACT:
{
const scDomain = z.string().refine((v) => isDomain(v), t("common.errmsg.domain_invalid"));
const spDomain = scDomain.safeParse(values.domain);
if (!spDomain.success) {
ctx.addIssue({
code: "custom",
message: z.treeifyError(spDomain.error).errors.join(),
path: ["domain"],
});
}
}
break;
}
}
});
};
const _default = Object.assign(BizDeployNodeConfigFieldsProviderHuaweiCloudVOD, {
getInitialValues,
getSchema,
});
export default _default;

View File

@ -678,6 +678,7 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
HUAWEICLOUD_LIVE: `${ACCESS_PROVIDERS.HUAWEICLOUD}-live`,
HUAWEICLOUD_OBS: `${ACCESS_PROVIDERS.HUAWEICLOUD}-obs`,
HUAWEICLOUD_SCM: `${ACCESS_PROVIDERS.HUAWEICLOUD}-scm`,
HUAWEICLOUD_VOD: `${ACCESS_PROVIDERS.HUAWEICLOUD}-vod`,
HUAWEICLOUD_WAF: `${ACCESS_PROVIDERS.HUAWEICLOUD}-waf`,
JDCLOUD_ALB: `${ACCESS_PROVIDERS.JDCLOUD}-alb`,
JDCLOUD_CDN: `${ACCESS_PROVIDERS.JDCLOUD}-cdn`,
@ -829,6 +830,7 @@ export const deploymentProvidersMap: Map<DeploymentProvider["type"] | string, De
[DEPLOYMENT_PROVIDERS.HUAWEICLOUD_WAF, "provider.huaweicloud_waf", DEPLOYMENT_CATEGORIES.FIREWALL],
[DEPLOYMENT_PROVIDERS.HUAWEICLOUD_AAD, "provider.huaweicloud_aad", DEPLOYMENT_CATEGORIES.FIREWALL],
[DEPLOYMENT_PROVIDERS.HUAWEICLOUD_LIVE, "provider.huaweicloud_live", DEPLOYMENT_CATEGORIES.AV],
[DEPLOYMENT_PROVIDERS.HUAWEICLOUD_VOD, "provider.huaweicloud_vod", DEPLOYMENT_CATEGORIES.AV],
[DEPLOYMENT_PROVIDERS.HUAWEICLOUD_APIG, "provider.huaweicloud_apig", DEPLOYMENT_CATEGORIES.APIGATEWAY],
[DEPLOYMENT_PROVIDERS.HUAWEICLOUD_SCM, "provider.huaweicloud_scm_upload", DEPLOYMENT_CATEGORIES.SSL],
[DEPLOYMENT_PROVIDERS.VOLCENGINE_TOS, "provider.volcengine_tos", DEPLOYMENT_CATEGORIES.STORAGE],

View File

@ -168,6 +168,7 @@
"huaweicloud_live": "Huawei Cloud - Cloud Live",
"huaweicloud_obs": "Huawei Cloud - OBS (Object Storage Service)",
"huaweicloud_scm_upload": "Huawei Cloud - Upload to SCM (SSL Certificate Manager)",
"huaweicloud_vod": "Huawei Cloud - VOD (Video on Demand)",
"huaweicloud_waf": "Huawei Cloud - WAF (Web Application Firewall)",
"infomaniak": "Infomaniak",
"ionos": "IONOS",

View File

@ -1581,15 +1581,20 @@
"placeholder": "Please enter GoEdge certificate ID",
"tooltip": "You can find it on GoEdge dashboard. <br><br>(Please open the browser devtools, and visit the \"网站列表 -> 证书管理 -> 证书\" page to capture network requests, then you can get the certificate ID.)"
},
"huaweicloud_aad_domain": {
"label": "Huawei Cloud AAD domain",
"placeholder": "Please enter Huawei Cloud AAD domain name"
"huaweicloud_aad_region": {
"label": "Huawei Cloud region",
"placeholder": "Please enter Huawei Cloud AAD region (e.g. cn-north-4)",
"tooltip": "For more information, see <a href=\"https://console-intl.huaweicloud.com/apiexplorer/#/endpoint?locale=en-us\" target=\"_blank\">https://console-intl.huaweicloud.com/apiexplorer/#/endpoint</a>"
},
"huaweicloud_aad_instance_id": {
"label": "Huawei Cloud AAD instance ID",
"placeholder": "Please enter Huawei Cloud AAD instance ID",
"tooltip": "For more information, see <a href=\"https://console-intl.huaweicloud.com/cnad/#/cnad/cad/list\" target=\"_blank\">https://console-intl.huaweicloud.com/cnad/#/cnad/cad/list</a>"
},
"huaweicloud_aad_domain": {
"label": "Huawei Cloud AAD domain",
"placeholder": "Please enter Huawei Cloud AAD domain name"
},
"huaweicloud_apig_region": {
"label": "Huawei Cloud region",
"placeholder": "Please enter Huawei Cloud API gateway region (e.g. cn-north-1)",
@ -1671,6 +1676,15 @@
"label": "Huawei Cloud Live streaming domain",
"placeholder": "Please enter Huawei Cloud Live streaming domain name"
},
"huaweicloud_vod_region": {
"label": "Huawei Cloud region",
"placeholder": "Please enter Huawei Cloud VOD region (e.g. cn-north-1)",
"tooltip": "For more information, see <a href=\"https://console-intl.huaweicloud.com/apiexplorer/#/endpoint?locale=en-us\" target=\"_blank\">https://console-intl.huaweicloud.com/apiexplorer/#/endpoint</a>"
},
"huaweicloud_vod_domain": {
"label": "Huawei Cloud VOD domain",
"placeholder": "Please enter Huawei Cloud VOD domain name"
},
"huaweicloud_waf_region": {
"label": "Huawei Cloud region",
"placeholder": "Please enter Huawei Cloud WAF region (e.g. cn-north-1)",

View File

@ -168,6 +168,7 @@
"huaweicloud_live": "华为云 - 视频直播 Live",
"huaweicloud_obs": "华为云 - 对象储存服务 OBS",
"huaweicloud_scm_upload": "华为云 - 上传到云证书管理服务 SCM",
"huaweicloud_vod": "华为云 - 视频点播 VOD",
"huaweicloud_waf": "华为云 - Web 应用防火墙 WAF",
"infomaniak": "Infomaniak",
"ionos": "IONOS",

View File

@ -1580,6 +1580,11 @@
"placeholder": "请输入 GoEdge 证书 ID",
"tooltip": "请登录 GoEdge 控制台查看<br><br>(目前需要在「网站列表 -> 证书管理 -> 证书」页面,并打开浏览器开发者工具抓取网络请求后得到证书 ID"
},
"huaweicloud_aad_region": {
"label": "华为云服务区域",
"placeholder": "请输入华为云 DDoS 高防服务区域例如cn-north-4",
"tooltip": "这是什么?请参阅 <a href=\"https://console.huaweicloud.com/apiexplorer/#/endpoint\" target=\"_blank\">https://console.huaweicloud.com/apiexplorer/#/endpoint</a>"
},
"huaweicloud_aad_instance_id": {
"label": "华为云 DDoS 高防实例 ID",
"placeholder": "请输入华为云 DDoS 高防实例 ID",
@ -1670,6 +1675,15 @@
"label": "华为云视频直播流域名",
"placeholder": "请输入华为云视频直播流域名"
},
"huaweicloud_vod_region": {
"label": "华为云服务区域",
"placeholder": "请输入华为云视频点播服务区域例如cn-north-1",
"tooltip": "这是什么?请参阅 <a href=\"https://console.huaweicloud.com/apiexplorer/#/endpoint\" target=\"_blank\">https://console.huaweicloud.com/apiexplorer/#/endpoint</a>"
},
"huaweicloud_vod_domain": {
"label": "华为云视频点播加速域名",
"placeholder": "请输入华为云视频点播加速域名"
},
"huaweicloud_waf_region": {
"label": "华为云服务区域",
"placeholder": "请输入华为云 WAF 服务区域例如cn-north-1",