From 0a14ef3725873d4f885cbef79964581cfffa88b3 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Thu, 13 Nov 2025 20:27:04 +0800 Subject: [PATCH] feat(provider): supoprt wildcard and certsan match pattern in deployment to aliyun apigw --- .../certdeploy/deployers/sp_aliyun_apigw.go | 17 +- .../providers/aliyun-apigw/aliyun_apigw.go | 349 +++++++++++++++--- .../aliyun-apigw/aliyun_apigw_test.go | 15 +- .../providers/aliyun-apigw/consts.go | 9 + .../providers/aliyun-apigw/internal/client.go | 42 +++ ...loyNodeConfigFieldsProviderAliyunAPIGW.tsx | 108 ++++-- 6 files changed, 441 insertions(+), 99 deletions(-) diff --git a/internal/certdeploy/deployers/sp_aliyun_apigw.go b/internal/certdeploy/deployers/sp_aliyun_apigw.go index d7fd8881..f457361d 100644 --- a/internal/certdeploy/deployers/sp_aliyun_apigw.go +++ b/internal/certdeploy/deployers/sp_aliyun_apigw.go @@ -17,14 +17,15 @@ func init() { } provider, err := aliyunapigw.NewSSLDeployerProvider(&aliyunapigw.SSLDeployerProviderConfig{ - AccessKeyId: credentials.AccessKeyId, - AccessKeySecret: credentials.AccessKeySecret, - ResourceGroupId: credentials.ResourceGroupId, - Region: xmaps.GetString(options.ProviderExtendedConfig, "region"), - ServiceType: xmaps.GetString(options.ProviderExtendedConfig, "serviceType"), - GatewayId: xmaps.GetString(options.ProviderExtendedConfig, "gatewayId"), - GroupId: xmaps.GetString(options.ProviderExtendedConfig, "groupId"), - Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"), + AccessKeyId: credentials.AccessKeyId, + AccessKeySecret: credentials.AccessKeySecret, + ResourceGroupId: credentials.ResourceGroupId, + Region: xmaps.GetString(options.ProviderExtendedConfig, "region"), + ServiceType: xmaps.GetString(options.ProviderExtendedConfig, "serviceType"), + GatewayId: xmaps.GetString(options.ProviderExtendedConfig, "gatewayId"), + GroupId: xmaps.GetString(options.ProviderExtendedConfig, "groupId"), + DomainMatchPattern: xmaps.GetString(options.ProviderExtendedConfig, "domainMatchPattern"), + Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"), }) return provider, err }) diff --git a/pkg/core/ssl-deployer/providers/aliyun-apigw/aliyun_apigw.go b/pkg/core/ssl-deployer/providers/aliyun-apigw/aliyun_apigw.go index 97e18189..62386898 100644 --- a/pkg/core/ssl-deployer/providers/aliyun-apigw/aliyun_apigw.go +++ b/pkg/core/ssl-deployer/providers/aliyun-apigw/aliyun_apigw.go @@ -18,6 +18,8 @@ import ( "github.com/certimate-go/certimate/pkg/core" "github.com/certimate-go/certimate/pkg/core/ssl-deployer/providers/aliyun-apigw/internal" sslmgrsp "github.com/certimate-go/certimate/pkg/core/ssl-manager/providers/aliyun-cas" + xcert "github.com/certimate-go/certimate/pkg/utils/cert" + xcerthostname "github.com/certimate-go/certimate/pkg/utils/cert/hostname" ) type SSLDeployerProviderConfig struct { @@ -37,6 +39,9 @@ type SSLDeployerProviderConfig struct { // API 分组 ID。 // 服务类型为 [SERVICE_TYPE_TRADITIONAL] 时必填。 GroupId string `json:"groupId,omitempty"` + // 域名匹配模式。 + // 零值时默认值 [DOMAIN_MATCH_PATTERN_EXACT]。 + DomainMatchPattern string `json:"domainMatchPattern,omitempty"` // 自定义域名(支持泛域名)。 Domain string `json:"domain"` } @@ -116,15 +121,220 @@ func (d *SSLDeployerProvider) deployToTraditional(ctx context.Context, certPEM s if d.config.GroupId == "" { return errors.New("config `groupId` is required") } - if d.config.Domain == "" { - return errors.New("config `domain` is required") + + // 获取待部署的域名列表 + var domains []string + switch d.config.DomainMatchPattern { + case "", DOMAIN_MATCH_PATTERN_EXACT: + { + if d.config.Domain == "" { + return errors.New("config `domain` is required") + } + + domains = []string{d.config.Domain} + } + + case DOMAIN_MATCH_PATTERN_WILDCARD: + { + if d.config.Domain == "" { + return errors.New("config `domain` is required") + } + + if strings.HasPrefix(d.config.Domain, "*.") { + domainCandidates, err := d.getTraditionalAllDomains(ctx, d.config.GroupId) + if err != nil { + return err + } + + domains = lo.Filter(domainCandidates, func(domain string, _ int) bool { + return xcerthostname.IsMatch(d.config.Domain, domain) + }) + if len(domains) == 0 { + return errors.New("could not find any domains matched by wildcard") + } + } else { + domains = []string{d.config.Domain} + } + } + + case DOMAIN_MATCH_PATTERN_CERTSAN: + { + certX509, err := xcert.ParseCertificateFromPEM(certPEM) + if err != nil { + return err + } + + domainCandidates, err := d.getTraditionalAllDomains(ctx, d.config.GroupId) + if err != nil { + return err + } + + domains = lo.Filter(domainCandidates, func(domain string, _ int) bool { + return certX509.VerifyHostname(domain) == nil + }) + if len(domains) == 0 { + return errors.New("could not find any domains matched by certificate") + } + } + + default: + return fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern) } + // 遍历更新域名证书 + if len(domains) == 0 { + d.logger.Info("no apigw domains to deploy") + } else { + d.logger.Info("found apigw domains to deploy", slog.Any("domains", domains)) + var errs []error + + for _, domain := range domains { + select { + case <-ctx.Done(): + return ctx.Err() + default: + if err := d.updateTraditionalDomainCertificate(ctx, d.config.GroupId, domain, certPEM, privkeyPEM); err != nil { + errs = append(errs, err) + } + } + } + + if len(errs) > 0 { + return errors.Join(errs...) + } + } + + return nil +} + +func (d *SSLDeployerProvider) deployToCloudNative(ctx context.Context, certPEM string, privkeyPEM string) error { + if d.config.GatewayId == "" { + return errors.New("config `gatewayId` is required") + } + + // 上传证书 + upres, err := d.sslManager.Upload(ctx, certPEM, privkeyPEM) + if err != nil { + return 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 errors.New("config `domain` is required") + } + + domains = []string{d.config.Domain} + } + + case DOMAIN_MATCH_PATTERN_WILDCARD: + { + if d.config.Domain == "" { + return errors.New("config `domain` is required") + } + + if strings.HasPrefix(d.config.Domain, "*.") { + domainCandidates, err := d.getCloudNativeAllDomains(ctx, d.config.GatewayId) + if err != nil { + return err + } + + domains = lo.Filter(domainCandidates, func(domain string, _ int) bool { + return xcerthostname.IsMatch(d.config.Domain, domain) + }) + if len(domains) == 0 { + return errors.New("could not find any domains matched by wildcard") + } + } else { + domains = []string{d.config.Domain} + } + } + + case DOMAIN_MATCH_PATTERN_CERTSAN: + { + certX509, err := xcert.ParseCertificateFromPEM(certPEM) + if err != nil { + return err + } + + domainCandidates, err := d.getCloudNativeAllDomains(ctx, d.config.GatewayId) + if err != nil { + return err + } + + domains = lo.Filter(domainCandidates, func(domain string, _ int) bool { + return certX509.VerifyHostname(domain) == nil + }) + if len(domains) == 0 { + return errors.New("could not find any domains matched by certificate") + } + } + + default: + return fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern) + } + + // 遍历更新域名证书 + if len(domains) == 0 { + d.logger.Info("no apigw domains to deploy") + } else { + d.logger.Info("found apigw domains to deploy", slog.Any("domains", domains)) + var errs []error + + for _, domain := range domains { + select { + case <-ctx.Done(): + return ctx.Err() + default: + certId := upres.ExtendedData["CertIdentifier"].(string) + if err := d.updateCloudNativeDomainCertificate(ctx, d.config.GatewayId, domain, certId); err != nil { + errs = append(errs, err) + } + } + } + + if len(errs) > 0 { + return errors.Join(errs...) + } + } + + return nil +} + +func (d *SSLDeployerProvider) getTraditionalAllDomains(ctx context.Context, cloudGroupId string) ([]string, error) { + domains := make([]string, 0) + + // 查询 API 分组详情 + // REF: https://help.aliyun.com/zh/api-gateway/traditional-api-gateway/developer-reference/api-cloudapi-2016-07-14-describeapigroup + describeApiGroupReq := &alicloudapi.DescribeApiGroupRequest{ + GroupId: tea.String(cloudGroupId), + } + describeApiGroupResp, err := d.sdkClients.TraditionalAPIGateway.DescribeApiGroupWithContext(ctx, describeApiGroupReq, &dara.RuntimeOptions{}) + d.logger.Debug("sdk request 'apigateway.DescribeApiGroup'", slog.Any("request", describeApiGroupReq), slog.Any("response", describeApiGroupResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'apigateway.DescribeApiGroup': %w", err) + } + + for _, domainItem := range describeApiGroupResp.Body.CustomDomains.DomainItem { + if strings.EqualFold(tea.StringValue(domainItem.DomainBindingStatus), "BINDING") { + domains = append(domains, tea.StringValue(domainItem.DomainName)) + } + } + + return domains, nil +} + +func (d *SSLDeployerProvider) updateTraditionalDomainCertificate(ctx context.Context, cloudGroupId string, domain string, certPEM, privkeyPEM string) error { // 为自定义域名添加 SSL 证书 // REF: https://help.aliyun.com/zh/api-gateway/traditional-api-gateway/developer-reference/api-cloudapi-2016-07-14-setdomaincertificate setDomainCertificateReq := &alicloudapi.SetDomainCertificateRequest{ - GroupId: tea.String(d.config.GroupId), - DomainName: tea.String(d.config.Domain), + GroupId: tea.String(cloudGroupId), + DomainName: tea.String(domain), CertificateName: tea.String(fmt.Sprintf("certimate_%d", time.Now().UnixMilli())), CertificateBody: tea.String(certPEM), CertificatePrivateKey: tea.String(privkeyPEM), @@ -138,59 +348,53 @@ func (d *SSLDeployerProvider) deployToTraditional(ctx context.Context, certPEM s return nil } -func (d *SSLDeployerProvider) deployToCloudNative(ctx context.Context, certPEM string, privkeyPEM string) error { - if d.config.GatewayId == "" { - return errors.New("config `gatewayId` is required") - } - if d.config.Domain == "" { - return errors.New("config `domain` is required") +func (d *SSLDeployerProvider) getCloudNativeAllDomains(ctx context.Context, cloudGatewayId string) ([]string, error) { + domains := make([]string, 0) + + // 查询域名列表 + // REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-listdomains + listDomainsPageNumber := 1 + listDomainsPageSize := 10 + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + + listDomainsReq := &aliapig.ListDomainsRequest{ + ResourceGroupId: lo.EmptyableToPtr(d.config.ResourceGroupId), + GatewayId: tea.String(cloudGatewayId), + PageNumber: tea.Int32(int32(listDomainsPageNumber)), + PageSize: tea.Int32(int32(listDomainsPageSize)), + } + listDomainsResp, err := d.sdkClients.CloudNativeAPIGateway.ListDomainsWithContext(ctx, listDomainsReq, make(map[string]*string), &dara.RuntimeOptions{}) + d.logger.Debug("sdk request 'apig.ListDomains'", slog.Any("request", listDomainsReq), slog.Any("response", listDomainsResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'apig.ListDomains': %w", err) + } + + if listDomainsResp.Body == nil || listDomainsResp.Body.Data == nil { + break + } + + for _, domainItem := range listDomainsResp.Body.Data.Items { + if strings.EqualFold(tea.StringValue(domainItem.Status), "Published") { + domains = append(domains, tea.StringValue(domainItem.Name)) + } + } + + if len(listDomainsResp.Body.Data.Items) < listDomainsPageSize { + break + } + + listDomainsPageNumber++ } - // 获取域名 ID - domainId, err := d.findCloudNativeDomainIdByDomain(ctx, d.config.Domain) - if err != nil { - return err - } - - // 查询域名 - // REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-getdomain - getDomainReq := &aliapig.GetDomainRequest{} - getDomainResp, err := d.sdkClients.CloudNativeAPIGateway.GetDomainWithContext(ctx, tea.String(domainId), getDomainReq, make(map[string]*string), &dara.RuntimeOptions{}) - d.logger.Debug("sdk request 'apig.GetDomain'", slog.String("domainId", domainId), slog.Any("request", getDomainReq), slog.Any("response", getDomainResp)) - if err != nil { - return fmt.Errorf("failed to execute sdk request 'apig.GetDomain': %w", err) - } - - // 上传证书 - upres, err := d.sslManager.Upload(ctx, certPEM, privkeyPEM) - if err != nil { - return fmt.Errorf("failed to upload certificate file: %w", err) - } else { - d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) - } - - // 更新域名 - // REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-updatedomain - updateDomainReq := &aliapig.UpdateDomainRequest{ - Protocol: tea.String("HTTPS"), - ForceHttps: getDomainResp.Body.Data.ForceHttps, - MTLSEnabled: getDomainResp.Body.Data.MTLSEnabled, - Http2Option: getDomainResp.Body.Data.Http2Option, - TlsMin: getDomainResp.Body.Data.TlsMin, - TlsMax: getDomainResp.Body.Data.TlsMax, - TlsCipherSuitesConfig: getDomainResp.Body.Data.TlsCipherSuitesConfig, - CertIdentifier: tea.String(upres.ExtendedData["CertIdentifier"].(string)), - } - updateDomainResp, err := d.sdkClients.CloudNativeAPIGateway.UpdateDomainWithContext(ctx, tea.String(domainId), updateDomainReq, make(map[string]*string), &dara.RuntimeOptions{}) - d.logger.Debug("sdk request 'apig.UpdateDomain'", slog.String("domainId", domainId), slog.Any("request", updateDomainReq), slog.Any("response", updateDomainResp)) - if err != nil { - return fmt.Errorf("failed to execute sdk request 'apig.UpdateDomain': %w", err) - } - - return nil + return domains, nil } -func (d *SSLDeployerProvider) findCloudNativeDomainIdByDomain(ctx context.Context, domain string) (string, error) { +func (d *SSLDeployerProvider) findCloudNativeDomainIdByDomain(ctx context.Context, cloudGatewayId string, domain string) (string, error) { // 查询域名列表 // REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-listdomains listDomainsPageNumber := 1 @@ -204,7 +408,7 @@ func (d *SSLDeployerProvider) findCloudNativeDomainIdByDomain(ctx context.Contex listDomainsReq := &aliapig.ListDomainsRequest{ ResourceGroupId: lo.EmptyableToPtr(d.config.ResourceGroupId), - GatewayId: tea.String(d.config.GatewayId), + GatewayId: tea.String(cloudGatewayId), NameLike: tea.String(domain), PageNumber: tea.Int32(int32(listDomainsPageNumber)), PageSize: tea.Int32(int32(listDomainsPageSize)), @@ -235,6 +439,43 @@ func (d *SSLDeployerProvider) findCloudNativeDomainIdByDomain(ctx context.Contex return "", fmt.Errorf("could not find domain '%s'", domain) } +func (d *SSLDeployerProvider) updateCloudNativeDomainCertificate(ctx context.Context, cloudGatewayId string, domain string, cloudCertId string) error { + // 获取域名 ID + domainId, err := d.findCloudNativeDomainIdByDomain(ctx, cloudGatewayId, domain) + if err != nil { + return err + } + + // 查询域名 + // REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-getdomain + getDomainReq := &aliapig.GetDomainRequest{} + getDomainResp, err := d.sdkClients.CloudNativeAPIGateway.GetDomainWithContext(ctx, tea.String(domainId), getDomainReq, make(map[string]*string), &dara.RuntimeOptions{}) + d.logger.Debug("sdk request 'apig.GetDomain'", slog.String("domainId", domainId), slog.Any("request", getDomainReq), slog.Any("response", getDomainResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'apig.GetDomain': %w", err) + } + + // 更新域名 + // REF: https://help.aliyun.com/zh/api-gateway/cloud-native-api-gateway/developer-reference/api-apig-2024-03-27-updatedomain + updateDomainReq := &aliapig.UpdateDomainRequest{ + Protocol: tea.String("HTTPS"), + ForceHttps: getDomainResp.Body.Data.ForceHttps, + MTLSEnabled: getDomainResp.Body.Data.MTLSEnabled, + Http2Option: getDomainResp.Body.Data.Http2Option, + TlsMin: getDomainResp.Body.Data.TlsMin, + TlsMax: getDomainResp.Body.Data.TlsMax, + TlsCipherSuitesConfig: getDomainResp.Body.Data.TlsCipherSuitesConfig, + CertIdentifier: tea.String(cloudCertId), + } + updateDomainResp, err := d.sdkClients.CloudNativeAPIGateway.UpdateDomainWithContext(ctx, tea.String(domainId), updateDomainReq, make(map[string]*string), &dara.RuntimeOptions{}) + d.logger.Debug("sdk request 'apig.UpdateDomain'", slog.String("domainId", domainId), slog.Any("request", updateDomainReq), slog.Any("response", updateDomainResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'apig.UpdateDomain': %w", err) + } + + return nil +} + func createSDKClients(accessKeyId, accessKeySecret, region string) (*wSDKClients, error) { // 接入点一览 https://api.aliyun.com/product/APIG var cloudNativeAPIGEndpoint string diff --git a/pkg/core/ssl-deployer/providers/aliyun-apigw/aliyun_apigw_test.go b/pkg/core/ssl-deployer/providers/aliyun-apigw/aliyun_apigw_test.go index ef522b00..2ed1b052 100644 --- a/pkg/core/ssl-deployer/providers/aliyun-apigw/aliyun_apigw_test.go +++ b/pkg/core/ssl-deployer/providers/aliyun-apigw/aliyun_apigw_test.go @@ -69,13 +69,14 @@ func TestDeploy(t *testing.T) { }, "\n")) deployer, err := provider.NewSSLDeployerProvider(&provider.SSLDeployerProviderConfig{ - AccessKeyId: fAccessKeyId, - AccessKeySecret: fAccessKeySecret, - Region: fRegion, - ServiceType: fServiceType, - GatewayId: fGatewayId, - GroupId: fGroupId, - Domain: fDomain, + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + Region: fRegion, + ServiceType: fServiceType, + GatewayId: fGatewayId, + GroupId: fGroupId, + DomainMatchPattern: provider.DOMAIN_MATCH_PATTERN_EXACT, + Domain: fDomain, }) if err != nil { t.Errorf("err: %+v", err) diff --git a/pkg/core/ssl-deployer/providers/aliyun-apigw/consts.go b/pkg/core/ssl-deployer/providers/aliyun-apigw/consts.go index 7a767b08..a37be784 100644 --- a/pkg/core/ssl-deployer/providers/aliyun-apigw/consts.go +++ b/pkg/core/ssl-deployer/providers/aliyun-apigw/consts.go @@ -6,3 +6,12 @@ const ( // 服务类型:云原生 API 网关。 SERVICE_TYPE_CLOUDNATIVE = "cloudnative" ) + +const ( + // 匹配模式:精确匹配。 + DOMAIN_MATCH_PATTERN_EXACT = "exact" + // 匹配模式:通配符匹配。 + DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard" + // 匹配模式:证书 SAN 匹配。 + DOMAIN_MATCH_PATTERN_CERTSAN = "certsan" +) diff --git a/pkg/core/ssl-deployer/providers/aliyun-apigw/internal/client.go b/pkg/core/ssl-deployer/providers/aliyun-apigw/internal/client.go index b737a525..b5d80a4e 100644 --- a/pkg/core/ssl-deployer/providers/aliyun-apigw/internal/client.go +++ b/pkg/core/ssl-deployer/providers/aliyun-apigw/internal/client.go @@ -197,6 +197,48 @@ func NewCloudapiClient(config *openapiutil.Config) (*CloudapiClient, error) { return client, err } +func (client *CloudapiClient) DescribeApiGroupWithContext(ctx context.Context, request *alicloudapi.DescribeApiGroupRequest, runtime *dara.RuntimeOptions) (_result *alicloudapi.DescribeApiGroupResponse, _err error) { + _err = request.Validate() + if _err != nil { + return _result, _err + } + query := map[string]interface{}{} + + if !dara.IsNil(request.GroupId) { + query["GroupId"] = request.GroupId + } + + if !dara.IsNil(request.SecurityToken) { + query["SecurityToken"] = request.SecurityToken + } + + if !dara.IsNil(request.Tag) { + query["Tag"] = request.Tag + } + + req := &openapiutil.OpenApiRequest{ + Query: openapiutil.Query(query), + } + params := &openapiutil.Params{ + Action: dara.String("DescribeApiGroup"), + Version: dara.String("2016-07-14"), + Protocol: dara.String("HTTPS"), + Pathname: dara.String("/"), + Method: dara.String("POST"), + AuthType: dara.String("AK"), + Style: dara.String("RPC"), + ReqBodyType: dara.String("formData"), + BodyType: dara.String("json"), + } + _result = &alicloudapi.DescribeApiGroupResponse{} + _body, _err := client.CallApiWithCtx(ctx, params, req, runtime) + if _err != nil { + return _result, _err + } + _err = dara.Convert(_body, &_result) + return _result, _err +} + func (client *CloudapiClient) SetDomainCertificateWithContext(ctx context.Context, request *alicloudapi.SetDomainCertificateRequest, runtime *dara.RuntimeOptions) (_result *alicloudapi.SetDomainCertificateResponse, _err error) { _err = request.Validate() if _err != nil { diff --git a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderAliyunAPIGW.tsx b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderAliyunAPIGW.tsx index 9abd5ce5..78e63827 100644 --- a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderAliyunAPIGW.tsx +++ b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderAliyunAPIGW.tsx @@ -1,5 +1,5 @@ import { getI18n, useTranslation } from "react-i18next"; -import { Form, Input, Select } from "antd"; +import { Form, Input, Radio, Select } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; @@ -11,6 +11,10 @@ import { useFormNestedFieldsContext } from "./_context"; const SERVICE_TYPE_CLOUDNATIVE = "cloudnative" as const; const SERVICE_TYPE_TRADITIONAL = "traditional" as const; +const DOMAIN_MATCH_PATTERN_EXACT = "exact" as const; +const DOMAIN_MATCH_PATTERN_WILDCARD = "wildcard" as const; +const DOMAIN_MATCH_PATTERN_CERTSAN = "certsan" as const; + const BizDeployNodeConfigFieldsProviderAliyunAPIGW = () => { const { i18n, t } = useTranslation(); @@ -23,6 +27,7 @@ const BizDeployNodeConfigFieldsProviderAliyunAPIGW = () => { const initialValues = getInitialValues(); const fieldServiceType = Form.useWatch([parentNamePath, "serviceType"], formInst); + const fieldDomainMatchPattern = Form.useWatch([parentNamePath, "domainMatchPattern"], { form: formInst, preserve: true }); return ( <> @@ -77,13 +82,37 @@ const BizDeployNodeConfigFieldsProviderAliyunAPIGW = () => { + ) : ( + void 0 + ) + } rules={[formRule]} > - + ({ + key: s, + label: t(`workflow_node.deploy.form.shared_domain_match_pattern.option.${s}.label`), + value: s, + }))} + /> + + + + + + ); }; @@ -91,6 +120,8 @@ const BizDeployNodeConfigFieldsProviderAliyunAPIGW = () => { const getInitialValues = (): Nullish>> => { return { region: "", + domainMatchPattern: DOMAIN_MATCH_PATTERN_EXACT, + domain: "", }; }; @@ -103,36 +134,53 @@ const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) region: z.string().nonempty(t("workflow_node.deploy.form.aliyun_apigw_region.placeholder")), gatewayId: z.string().nullish(), groupId: z.string().nullish(), - domain: z - .string() - .nonempty(t("workflow_node.deploy.form.aliyun_apigw_domain.placeholder")) - .refine((v) => validDomainName(v!, { allowWildcard: true }), t("common.errmsg.domain_invalid")), + domainMatchPattern: z.string().nonempty(t("workflow_node.deploy.form.shared_domain_match_pattern.placeholder")).default(DOMAIN_MATCH_PATTERN_EXACT), + domain: z.string().nullish(), }) .superRefine((values, ctx) => { - switch (values.serviceType) { - case SERVICE_TYPE_CLOUDNATIVE: - { - if (!values.gatewayId?.trim()) { - ctx.addIssue({ - code: "custom", - message: t("workflow_node.deploy.form.aliyun_apigw_gateway_id.placeholder"), - path: ["gatewayId"], - }); + if (values.serviceType) { + switch (values.serviceType) { + case SERVICE_TYPE_CLOUDNATIVE: + { + if (!values.gatewayId?.trim()) { + ctx.addIssue({ + code: "custom", + message: t("workflow_node.deploy.form.aliyun_apigw_gateway_id.placeholder"), + path: ["gatewayId"], + }); + } } - } - break; + break; - case SERVICE_TYPE_TRADITIONAL: - { - if (!values.groupId?.trim()) { - ctx.addIssue({ - code: "custom", - message: t("workflow_node.deploy.form.aliyun_apigw_group_id.placeholder"), - path: ["groupId"], - }); + case SERVICE_TYPE_TRADITIONAL: + { + if (!values.groupId?.trim()) { + ctx.addIssue({ + code: "custom", + message: t("workflow_node.deploy.form.aliyun_apigw_group_id.placeholder"), + path: ["groupId"], + }); + } } - } - break; + break; + } + } + + if (values.domainMatchPattern) { + switch (values.domainMatchPattern) { + case DOMAIN_MATCH_PATTERN_EXACT: + case DOMAIN_MATCH_PATTERN_WILDCARD: + { + if (!validDomainName(values.domain!, { allowWildcard: true })) { + ctx.addIssue({ + code: "custom", + message: t("common.errmsg.domain_invalid"), + path: ["domain"], + }); + } + } + break; + } } }); };