feat(provider): supoprt wildcard and certsan match pattern in deployment to aliyun apigw

This commit is contained in:
Fu Diwei 2025-11-13 20:27:04 +08:00
parent 3722ab0e01
commit 0a14ef3725
6 changed files with 441 additions and 99 deletions

View File

@ -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
})

View File

@ -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

View File

@ -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)

View File

@ -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"
)

View File

@ -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 {

View File

@ -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 = () => {
</Show>
<Form.Item
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.aliyun_apigw_domain.label")}
name={[parentNamePath, "domainMatchPattern"]}
initialValue={initialValues.domainMatchPattern}
label={t("workflow_node.deploy.form.shared_domain_match_pattern.label")}
extra={
fieldDomainMatchPattern === DOMAIN_MATCH_PATTERN_EXACT ? (
<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.shared_domain_match_pattern.help_wildcard") }}></span>
) : (
void 0
)
}
rules={[formRule]}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_apigw_domain.placeholder")} />
<Radio.Group
options={[DOMAIN_MATCH_PATTERN_EXACT, DOMAIN_MATCH_PATTERN_WILDCARD, DOMAIN_MATCH_PATTERN_CERTSAN].map((s) => ({
key: s,
label: t(`workflow_node.deploy.form.shared_domain_match_pattern.option.${s}.label`),
value: s,
}))}
/>
</Form.Item>
<Show when={fieldDomainMatchPattern !== DOMAIN_MATCH_PATTERN_CERTSAN}>
<Form.Item
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.aliyun_apigw_domain.label")}
rules={[formRule]}
>
<Input placeholder={t("workflow_node.deploy.form.aliyun_apigw_domain.placeholder")} />
</Form.Item>
</Show>
</>
);
};
@ -91,6 +120,8 @@ const BizDeployNodeConfigFieldsProviderAliyunAPIGW = () => {
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
domainMatchPattern: DOMAIN_MATCH_PATTERN_EXACT,
domain: "",
};
};
@ -103,36 +134,53 @@ const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> })
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;
}
}
});
};