diff --git a/README.md b/README.md
index f9d4df00..fcc0ad27 100644
--- a/README.md
+++ b/README.md
@@ -93,6 +93,7 @@ make local.run
| [百度智能云](https://cloud.baidu.com/) | |
| [华为云](https://www.huaweicloud.com/) | |
| [火山引擎](https://www.volcengine.com/) | |
+| [京东云](https://www.jdcloud.com/) | |
| [AWS Route53](https://aws.amazon.com/route53/) | |
| [Azure](https://azure.microsoft.com/) | |
| [CloudFlare](https://www.cloudflare.com/) | |
diff --git a/README_EN.md b/README_EN.md
index a6d13c26..cae7e0f7 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -92,6 +92,7 @@ The following DNS providers are supported:
| [Baidu AI Cloud](https://intl.cloud.baidu.com/) | |
| [Huawei Cloud](https://www.huaweicloud.com/) | |
| [Volcengine](https://www.volcengine.com/) | |
+| [JD Cloud](https://www.jdcloud.com/) | |
| [AWS Route53](https://aws.amazon.com/route53/) | |
| [Azure DNS](https://azure.microsoft.com/) | |
| [CloudFlare](https://www.cloudflare.com/) | |
diff --git a/go.mod b/go.mod
index c07f2a5b..5a1949f5 100644
--- a/go.mod
+++ b/go.mod
@@ -77,12 +77,14 @@ require (
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.16.0 // indirect
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect
+ github.com/gofrs/uuid v4.4.0+incompatible // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
+ github.com/jdcloud-api/jdcloud-sdk-go v1.62.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
diff --git a/go.sum b/go.sum
index f127aeb4..1929c808 100644
--- a/go.sum
+++ b/go.sum
@@ -427,6 +427,8 @@ github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
+github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
+github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
@@ -576,6 +578,8 @@ github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/U
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
+github.com/jdcloud-api/jdcloud-sdk-go v1.62.0 h1:uPfyOSY16mBrhggriDNeySFB4ZkzMMXpNac2P0fbDRw=
+github.com/jdcloud-api/jdcloud-sdk-go v1.62.0/go.mod h1:UrKjuULIWLjHFlG6aSPunArE5QX57LftMmStAZJBEX8=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go
index 10466389..d1e80083 100644
--- a/internal/applicant/providers.go
+++ b/internal/applicant/providers.go
@@ -17,6 +17,7 @@ import (
pGname "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname"
pGoDaddy "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/godaddy"
pHuaweiCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/huaweicloud"
+ pJDCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud"
pNameDotCom "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namedotcom"
pNameSilo "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namesilo"
pNS1 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/ns1"
@@ -85,7 +86,7 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
- case domain.ApplyDNSProviderTypeAzureDNS:
+ case domain.ApplyDNSProviderTypeAzure, domain.ApplyDNSProviderTypeAzureDNS:
{
access := domain.AccessConfigForAzure{}
if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
@@ -214,6 +215,23 @@ func createApplicant(options *applicantOptions) (challenge.Provider, error) {
return applicant, err
}
+ case domain.ApplyDNSProviderTypeJDCloud, domain.ApplyDNSProviderTypeJDCloudDNS:
+ {
+ access := domain.AccessConfigForJDCloud{}
+ if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
+ return nil, fmt.Errorf("failed to populate provider access config: %w", err)
+ }
+
+ applicant, err := pJDCloud.NewChallengeProvider(&pJDCloud.ChallengeProviderConfig{
+ AccessKeyId: access.AccessKeyId,
+ AccessKeySecret: access.AccessKeySecret,
+ RegionId: maps.GetValueAsString(options.ProviderApplyConfig, "region_id"),
+ DnsPropagationTimeout: options.DnsPropagationTimeout,
+ DnsTTL: options.DnsTTL,
+ })
+ return applicant, err
+ }
+
case domain.ApplyDNSProviderTypeNameDotCom:
{
access := domain.AccessConfigForNameDotCom{}
diff --git a/internal/domain/access.go b/internal/domain/access.go
index eaa3340e..ddd00b1c 100644
--- a/internal/domain/access.go
+++ b/internal/domain/access.go
@@ -115,12 +115,17 @@ type AccessConfigForHuaweiCloud struct {
SecretAccessKey string `json:"secretAccessKey"`
}
-type AccessConfigForLocal struct{}
+type AccessConfigForJDCloud struct {
+ AccessKeyId string `json:"accessKeyId"`
+ AccessKeySecret string `json:"accessKeySecret"`
+}
type AccessConfigForKubernetes struct {
KubeConfig string `json:"kubeConfig,omitempty"`
}
+type AccessConfigForLocal struct{}
+
type AccessConfigForNameDotCom struct {
Username string `json:"username"`
ApiToken string `json:"apiToken"`
diff --git a/internal/domain/provider.go b/internal/domain/provider.go
index 08527fdd..5f36117e 100644
--- a/internal/domain/provider.go
+++ b/internal/domain/provider.go
@@ -34,7 +34,7 @@ const (
AccessProviderTypeGoDaddy = AccessProviderType("godaddy")
AccessProviderTypeGoEdge = AccessProviderType("goedge") // GoEdge(预留)
AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud")
- AccessProviderTypeJDCloud = AccessProviderType("jdcloud") // 京东云(预留)
+ AccessProviderTypeJDCloud = AccessProviderType("jdcloud")
AccessProviderTypeKubernetes = AccessProviderType("k8s")
AccessProviderTypeLocal = AccessProviderType("local")
AccessProviderTypeNameDotCom = AccessProviderType("namedotcom")
@@ -68,6 +68,7 @@ const (
ApplyDNSProviderTypeAliyunDNS = ApplyDNSProviderType("aliyun-dns")
ApplyDNSProviderTypeAWS = ApplyDNSProviderType("aws") // 兼容旧值,等同于 [ApplyDNSProviderTypeAWSRoute53]
ApplyDNSProviderTypeAWSRoute53 = ApplyDNSProviderType("aws-route53")
+ ApplyDNSProviderTypeAzure = ApplyDNSProviderType("azure") // 兼容旧值,等同于 [ApplyDNSProviderTypeAzure]
ApplyDNSProviderTypeAzureDNS = ApplyDNSProviderType("azure-dns")
ApplyDNSProviderTypeBaiduCloud = ApplyDNSProviderType("baiducloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeBaiduCloudDNS]
ApplyDNSProviderTypeBaiduCloudDNS = ApplyDNSProviderType("baiducloud-dns")
@@ -78,6 +79,8 @@ const (
ApplyDNSProviderTypeGoDaddy = ApplyDNSProviderType("godaddy")
ApplyDNSProviderTypeHuaweiCloud = ApplyDNSProviderType("huaweicloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeHuaweiCloudDNS]
ApplyDNSProviderTypeHuaweiCloudDNS = ApplyDNSProviderType("huaweicloud-dns")
+ ApplyDNSProviderTypeJDCloud = ApplyDNSProviderType("jdcloud") // 兼容旧值,等同于 [ApplyDNSProviderTypeJDCloudDNS]
+ ApplyDNSProviderTypeJDCloudDNS = ApplyDNSProviderType("jdcloud-dns")
ApplyDNSProviderTypeNameDotCom = ApplyDNSProviderType("namedotcom")
ApplyDNSProviderTypeNameSilo = ApplyDNSProviderType("namesilo")
ApplyDNSProviderTypeNS1 = ApplyDNSProviderType("ns1")
diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/baiducloud/internal/lego.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/baiducloud/internal/lego.go
index fd138c6d..558ad5eb 100644
--- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/baiducloud/internal/lego.go
+++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/baiducloud/internal/lego.go
@@ -184,10 +184,10 @@ func (d *DNSProvider) removeDNSRecord(domain, subDomain, value string) error {
if record == nil {
return nil
+ } else {
+ err = d.client.DeleteRecord(domain, record.Id, d.generateClientToken())
+ return err
}
-
- err = d.client.DeleteRecord(domain, record.Id, d.generateClientToken())
- return err
}
func (d *DNSProvider) generateClientToken() string {
diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud/internal/lego.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud/internal/lego.go
new file mode 100644
index 00000000..a85fa79c
--- /dev/null
+++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud/internal/lego.go
@@ -0,0 +1,229 @@
+package lego_jdcloud
+
+import (
+ "errors"
+ "fmt"
+ "time"
+
+ "github.com/go-acme/lego/v4/challenge"
+ "github.com/go-acme/lego/v4/challenge/dns01"
+ "github.com/go-acme/lego/v4/platform/config/env"
+ jdCore "github.com/jdcloud-api/jdcloud-sdk-go/core"
+ jdDnsApi "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/apis"
+ jdDnsClient "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/client"
+ jdDnsModel "github.com/jdcloud-api/jdcloud-sdk-go/services/domainservice/models"
+)
+
+const (
+ envNamespace = "JDCLOUD_"
+
+ EnvAccessKeyID = envNamespace + "ACCESS_KEY_ID"
+ EnvAccessKeySecret = envNamespace + "ACCESS_KEY_SECRET"
+ EnvRegionId = envNamespace + "REGION_ID"
+
+ EnvTTL = envNamespace + "TTL"
+ EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
+ EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
+ EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
+)
+
+var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
+
+type Config struct {
+ AccessKeyID string
+ AccessKeySecret string
+ RegionId string
+
+ PropagationTimeout time.Duration
+ PollingInterval time.Duration
+ TTL int32
+ HTTPTimeout time.Duration
+}
+
+type DNSProvider struct {
+ client *jdDnsClient.DomainserviceClient
+ config *Config
+}
+
+func NewDefaultConfig() *Config {
+ return &Config{
+ TTL: int32(env.GetOrDefaultInt(EnvTTL, 300)),
+ PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
+ PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
+ HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
+ }
+}
+
+func NewDNSProvider() (*DNSProvider, error) {
+ values, err := env.Get(EnvAccessKeyID, EnvAccessKeySecret)
+ if err != nil {
+ return nil, fmt.Errorf("jdcloud: %w", err)
+ }
+
+ config := NewDefaultConfig()
+ config.AccessKeyID = values[EnvAccessKeyID]
+ config.AccessKeySecret = values[EnvAccessKeySecret]
+ config.RegionId = values[EnvRegionId]
+
+ return NewDNSProviderConfig(config)
+}
+
+func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
+ if config == nil {
+ return nil, errors.New("jdcloud: the configuration of the DNS provider is nil")
+ }
+
+ clientCredentials := jdCore.NewCredentials(config.AccessKeyID, config.AccessKeySecret)
+ clientConfig := jdCore.NewConfig()
+ clientConfig.SetTimeout(config.HTTPTimeout)
+ client := jdDnsClient.NewDomainserviceClient(clientCredentials)
+ client.SetConfig(clientConfig)
+
+ return &DNSProvider{
+ client: client,
+ config: config,
+ }, nil
+}
+
+func (d *DNSProvider) Present(domain, token, keyAuth string) error {
+ info := dns01.GetChallengeInfo(domain, keyAuth)
+
+ zoneName, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
+ if err != nil {
+ return fmt.Errorf("jdcloud: %w", err)
+ }
+
+ subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zoneName)
+ if err != nil {
+ return fmt.Errorf("jdcloud: %w", err)
+ }
+
+ if err := d.addOrUpdateDNSRecord(domain, subDomain, info.Value); err != nil {
+ return fmt.Errorf("jdcloud: %w", err)
+ }
+
+ return nil
+}
+
+func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
+ fqdn, value := dns01.GetRecord(domain, keyAuth)
+ subDomain := dns01.UnFqdn(fqdn)
+
+ if err := d.removeDNSRecord(domain, subDomain, value); err != nil {
+ return fmt.Errorf("jdcloud: %w", err)
+ }
+
+ return nil
+}
+
+func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
+ return d.config.PropagationTimeout, d.config.PollingInterval
+}
+
+func (d *DNSProvider) getDNSZone(domain string) (*jdDnsModel.DomainInfo, error) {
+ pageNumber := 1
+ pageSize := 100
+ for {
+ request := &jdDnsApi.DescribeDomainsRequest{}
+ request.RegionId = d.config.RegionId
+ request.DomainName = &domain
+ request.PageNumber = pageNumber
+ request.PageSize = pageSize
+
+ response, err := d.client.DescribeDomains(request)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, item := range response.Result.DataList {
+ if item.DomainName == domain {
+ return &item, nil
+ }
+ }
+
+ if len(response.Result.DataList) < pageSize {
+ break
+ }
+
+ pageNumber++
+ }
+
+ return nil, fmt.Errorf("jdcloud: zone %s not found", domain)
+}
+
+func (d *DNSProvider) getDNSZoneAndRecord(domain, subDomain string) (*jdDnsModel.DomainInfo, *jdDnsModel.RRInfo, error) {
+ zone, err := d.getDNSZone(domain)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ pageNumber := 1
+ pageSize := 100
+ for {
+ request := jdDnsApi.NewDescribeResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", &zone.Id))
+ request.Search = &subDomain
+ request.PageNumber = &pageNumber
+ request.PageSize = &pageSize
+
+ response, err := d.client.DescribeResourceRecord(request)
+ if err != nil {
+ return zone, nil, err
+ }
+
+ for _, record := range response.Result.DataList {
+ if record.Type == "TXT" && record.HostRecord == subDomain {
+ return zone, &record, nil
+ }
+ }
+
+ if len(response.Result.DataList) < pageSize {
+ break
+ }
+
+ pageNumber++
+ }
+
+ return nil, nil, nil
+}
+
+func (d *DNSProvider) addOrUpdateDNSRecord(domain, subDomain, value string) error {
+ zone, record, err := d.getDNSZoneAndRecord(domain, subDomain)
+ if err != nil {
+ return err
+ }
+
+ if record == nil {
+ request := jdDnsApi.NewCreateResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", &zone.Id), &jdDnsModel.AddRR{
+ Type: "TXT",
+ HostRecord: subDomain,
+ HostValue: value,
+ Ttl: int(d.config.TTL),
+ })
+ _, err := d.client.CreateResourceRecord(request)
+ return err
+ } else {
+ request := jdDnsApi.NewModifyResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", &zone.Id), fmt.Sprintf("%d", &record.Id), &jdDnsModel.UpdateRR{
+ Type: "TXT",
+ HostRecord: subDomain,
+ HostValue: value,
+ Ttl: int(d.config.TTL),
+ })
+ _, err := d.client.ModifyResourceRecord(request)
+ return err
+ }
+}
+
+func (d *DNSProvider) removeDNSRecord(domain, subDomain, value string) error {
+ zone, record, err := d.getDNSZoneAndRecord(domain, subDomain)
+ if err != nil {
+ return err
+ }
+
+ if record == nil {
+ return nil
+ } else {
+ req := jdDnsApi.NewDeleteResourceRecordRequest(d.config.RegionId, fmt.Sprintf("%d", &zone.Id), fmt.Sprintf("%d", &record.Id))
+ _, err = d.client.DeleteResourceRecord(req)
+ return err
+ }
+}
diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud/jdcloud.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud/jdcloud.go
new file mode 100644
index 00000000..5729d932
--- /dev/null
+++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud/jdcloud.go
@@ -0,0 +1,47 @@
+package jdcloud
+
+import (
+ "time"
+
+ "github.com/go-acme/lego/v4/challenge"
+
+ internal "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud/internal"
+)
+
+type ChallengeProviderConfig struct {
+ AccessKeyId string `json:"accessKeyId"`
+ AccessKeySecret string `json:"accessKeySecret"`
+ RegionId string `json:"regionId"`
+ DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
+ DnsTTL int32 `json:"dnsTTL,omitempty"`
+}
+
+func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) {
+ if config == nil {
+ panic("config is nil")
+ }
+
+ regionId := config.RegionId
+ if regionId == "" {
+ // 京东云的 SDK 要求必须传一个区域,实际上 DNS-01 流程里用不到,但不传会报错
+ regionId = "cn-north-1"
+ }
+
+ providerConfig := internal.NewDefaultConfig()
+ providerConfig.AccessKeyID = config.AccessKeyId
+ providerConfig.AccessKeySecret = config.AccessKeySecret
+ providerConfig.RegionId = regionId
+ if config.DnsPropagationTimeout != 0 {
+ providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
+ }
+ if config.DnsTTL != 0 {
+ providerConfig.TTL = config.DnsTTL
+ }
+
+ provider, err := internal.NewDNSProviderConfig(providerConfig)
+ if err != nil {
+ return nil, err
+ }
+
+ return provider, nil
+}
diff --git a/ui/public/imgs/providers/jdcloud.svg b/ui/public/imgs/providers/jdcloud.svg
new file mode 100644
index 00000000..720dbf4d
--- /dev/null
+++ b/ui/public/imgs/providers/jdcloud.svg
@@ -0,0 +1 @@
+
diff --git a/ui/src/components/Version.tsx b/ui/src/components/Version.tsx
index fcdf1170..3d736271 100644
--- a/ui/src/components/Version.tsx
+++ b/ui/src/components/Version.tsx
@@ -1,4 +1,4 @@
-import { memo, useEffect, useState } from "react";
+import { memo } from "react";
import { useTranslation } from "react-i18next";
import { ReadOutlined as ReadOutlinedIcon } from "@ant-design/icons";
import { Badge, Divider, Space, Typography } from "antd";
diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx
index f552e594..44d6ae0c 100644
--- a/ui/src/components/access/AccessForm.tsx
+++ b/ui/src/components/access/AccessForm.tsx
@@ -27,6 +27,7 @@ import AccessFormGcoreConfig from "./AccessFormGcoreConfig";
import AccessFormGnameConfig from "./AccessFormGnameConfig";
import AccessFormGoDaddyConfig from "./AccessFormGoDaddyConfig";
import AccessFormHuaweiCloudConfig from "./AccessFormHuaweiCloudConfig";
+import AccessFormJDCloudConfig from "./AccessFormJDCloudConfig";
import AccessFormKubernetesConfig from "./AccessFormKubernetesConfig";
import AccessFormLocalConfig from "./AccessFormLocalConfig";
import AccessFormNameDotComConfig from "./AccessFormNameDotComConfig";
@@ -131,6 +132,8 @@ const AccessForm = forwardRef(({ className,
return ;
case ACCESS_PROVIDERS.HUAWEICLOUD:
return ;
+ case ACCESS_PROVIDERS.JDCLOUD:
+ return ;
case ACCESS_PROVIDERS.KUBERNETES:
return ;
case ACCESS_PROVIDERS.LOCAL:
diff --git a/ui/src/components/access/AccessFormJDCloudConfig.tsx b/ui/src/components/access/AccessFormJDCloudConfig.tsx
new file mode 100644
index 00000000..7ab6b167
--- /dev/null
+++ b/ui/src/components/access/AccessFormJDCloudConfig.tsx
@@ -0,0 +1,76 @@
+import { useTranslation } from "react-i18next";
+import { Form, type FormInstance, Input } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+import { type AccessConfigForJDCloud } from "@/domain/access";
+
+type AccessFormJDCloudConfigFieldValues = Nullish;
+
+export type AccessFormJDCloudConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: AccessFormJDCloudConfigFieldValues;
+ onValuesChange?: (values: AccessFormJDCloudConfigFieldValues) => void;
+};
+
+const initFormModel = (): AccessFormJDCloudConfigFieldValues => {
+ return {
+ accessKeyId: "",
+ accessKeySecret: "",
+ };
+};
+
+const AccessFormJDCloudConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange: onValuesChange }: AccessFormJDCloudConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ accessKeyId: z
+ .string()
+ .min(1, t("access.form.jdcloud_access_key_id.placeholder"))
+ .max(64, t("common.errmsg.string_max", { max: 64 }))
+ .trim(),
+ accessKeySecret: z
+ .string()
+ .min(1, t("access.form.jdcloud_access_key_secret.placeholder"))
+ .max(64, t("common.errmsg.string_max", { max: 64 }))
+ .trim(),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ );
+};
+
+export default AccessFormJDCloudConfig;
diff --git a/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx b/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx
index 7b43cad2..c714e197 100644
--- a/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx
+++ b/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx
@@ -35,6 +35,7 @@ import { validDomainName, validIPv4Address, validIPv6Address } from "@/utils/val
import ApplyNodeConfigFormAWSRoute53Config from "./ApplyNodeConfigFormAWSRoute53Config";
import ApplyNodeConfigFormHuaweiCloudDNSConfig from "./ApplyNodeConfigFormHuaweiCloudDNSConfig";
+import ApplyNodeConfigFormJDCloudDNSConfig from "./ApplyNodeConfigFormJDCloudDNSConfig";
type ApplyNodeConfigFormFieldValues = Partial;
@@ -145,6 +146,9 @@ const ApplyNodeConfigForm = forwardRef;
+ case APPLY_DNS_PROVIDERS.JDCLOUD:
+ case APPLY_DNS_PROVIDERS.JDCLOUD_DNS:
+ return ;
}
}, [disabled, initialValues?.providerConfig, fieldProvider, nestedFormInst, nestedFormName]);
diff --git a/ui/src/components/workflow/node/ApplyNodeConfigFormJDCloudDNSConfig.tsx b/ui/src/components/workflow/node/ApplyNodeConfigFormJDCloudDNSConfig.tsx
new file mode 100644
index 00000000..f9533972
--- /dev/null
+++ b/ui/src/components/workflow/node/ApplyNodeConfigFormJDCloudDNSConfig.tsx
@@ -0,0 +1,61 @@
+import { useTranslation } from "react-i18next";
+import { Form, type FormInstance, Input } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+type ApplyNodeConfigFormJDCloudDNSConfigFieldValues = Nullish<{
+ regionId: string;
+}>;
+
+export type ApplyNodeConfigFormJDCloudDNSConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: ApplyNodeConfigFormJDCloudDNSConfigFieldValues;
+ onValuesChange?: (values: ApplyNodeConfigFormJDCloudDNSConfigFieldValues) => void;
+};
+
+const initFormModel = (): ApplyNodeConfigFormJDCloudDNSConfigFieldValues => {
+ return {
+ regionId: "cn-north-1",
+ };
+};
+
+const ApplyNodeConfigFormJDCloudDNSConfig = ({
+ form: formInst,
+ formName,
+ disabled,
+ initialValues,
+ onValuesChange,
+}: ApplyNodeConfigFormJDCloudDNSConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ regionId: z
+ .string({ message: t("workflow_node.apply.form.jdcloud_dns_region_id.placeholder") })
+ .nonempty(t("workflow_node.apply.form.jdcloud_dns_region_id.placeholder"))
+ .trim(),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+
+
+
+
+ );
+};
+
+export default ApplyNodeConfigFormJDCloudDNSConfig;
diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts
index 0ece4826..c91e647f 100644
--- a/ui/src/domain/access.ts
+++ b/ui/src/domain/access.ts
@@ -24,6 +24,7 @@ export interface AccessModel extends BaseModel {
| AccessConfigForGname
| AccessConfigForGoDaddy
| AccessConfigForHuaweiCloud
+ | AccessConfigForJDCloud
| AccessConfigForKubernetes
| AccessConfigForLocal
| AccessConfigForNameDotCom
@@ -133,6 +134,11 @@ export type AccessConfigForHuaweiCloud = {
secretAccessKey: string;
};
+export type AccessConfigForJDCloud = {
+ accessKeyId: string;
+ accessKeySecret: string;
+};
+
export type AccessConfigForKubernetes = {
kubeConfig?: string;
};
diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts
index b5bcaca7..39a5424e 100644
--- a/ui/src/domain/provider.ts
+++ b/ui/src/domain/provider.ts
@@ -22,6 +22,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
GODADDY: "godaddy",
EDGIO: "edgio",
HUAWEICLOUD: "huaweicloud",
+ JDCLOUD: "jdcloud",
KUBERNETES: "k8s",
LOCAL: "local",
NAMEDOTCOM: "namedotcom",
@@ -70,6 +71,7 @@ export const accessProvidersMap: Maphttps://www.alibabacloud.com/help/en/acr/create-and-obtain-an-accesskey-pair",
- "access.form.aliyun_access_key_secret.label": "Aliyun AccessKey Secret",
- "access.form.aliyun_access_key_secret.placeholder": "Please enter Aliyun AccessKey Secret",
+ "access.form.aliyun_access_key_secret.label": "Aliyun AccessKeySecret",
+ "access.form.aliyun_access_key_secret.placeholder": "Please enter Aliyun AccessKeySecret",
"access.form.aliyun_access_key_secret.tooltip": "For more information, see https://www.alibabacloud.com/help/en/acr/create-and-obtain-an-accesskey-pair",
"access.form.aws_access_key_id.label": "AWS AccessKeyId",
"access.form.aws_access_key_id.placeholder": "Please enter AWS AccessKeyId",
@@ -133,6 +133,12 @@
"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 https://support.huaweicloud.com/intl/en-us/usermanual-ca/ca_01_0003.html",
+ "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 https://docs.jdcloud.com/en/account-management/accesskey-management",
+ "access.form.jdcloud_access_key_secret.label": "JD Cloud AccessKeySecret",
+ "access.form.jdcloud_access_key_secret.placeholder": "Please enter JD Cloud AccessKeySecret",
+ "access.form.jdcloud_access_key_secret.tooltip": "For more information, see https://docs.jdcloud.com/en/account-management/accesskey-management",
"access.form.k8s_kubeconfig.label": "KubeConfig",
"access.form.k8s_kubeconfig.placeholder": "Please enter KubeConfig file",
"access.form.k8s_kubeconfig.upload": "Choose File ...",
diff --git a/ui/src/i18n/locales/en/nls.common.json b/ui/src/i18n/locales/en/nls.common.json
index d5e5508b..eb288ce5 100644
--- a/ui/src/i18n/locales/en/nls.common.json
+++ b/ui/src/i18n/locales/en/nls.common.json
@@ -42,7 +42,7 @@
"provider.aliyun.cdn": "Alibaba Cloud - CDN (Content Delivery Network)",
"provider.aliyun.clb": "Alibaba Cloud - CLB (Classic Load Balancer)",
"provider.aliyun.dcdn": "Alibaba Cloud - DCDN (Dynamic Route for Content Delivery Network)",
- "provider.aliyun.dns": "Alibaba Cloud - DNS (Domain Name Service)",
+ "provider.aliyun.dns": "Alibaba Cloud - DNS",
"provider.aliyun.esa": "Alibaba Cloud - ESA (Edge Security Acceleration)",
"provider.aliyun.live": "Alibaba Cloud - ApsaraVideo Live",
"provider.aliyun.nlb": "Alibaba Cloud - NLB (Network Load Balancer)",
@@ -69,8 +69,10 @@
"provider.godaddy": "GoDaddy",
"provider.huaweicloud": "Huawei Cloud",
"provider.huaweicloud.cdn": "Huawei Cloud - CDN (Content Delivery Network)",
- "provider.huaweicloud.dns": "Huawei Cloud - DNS (Domain Name Service)",
+ "provider.huaweicloud.dns": "Huawei Cloud - DNS",
"provider.huaweicloud.elb": "Huawei Cloud - ELB (Elastic Load Balance)",
+ "provider.jdcloud": "JD Cloud",
+ "provider.jdcloud.dns": "JD Cloud - DNS",
"provider.kubernetes": "Kubernetes",
"provider.kubernetes.secret": "Kubernetes - Secret",
"provider.local": "Local deployment",
@@ -88,7 +90,7 @@
"provider.tencentcloud.clb": "Tencent Cloud - CLB (Cloud Load Balancer)",
"provider.tencentcloud.cos": "Tencent Cloud - COS (Cloud Object Storage)",
"provider.tencentcloud.css": "Tencent Cloud - CSS (Cloud Streaming Service)",
- "provider.tencentcloud.dns": "Tencent Cloud - DNS (Domain Name Service)",
+ "provider.tencentcloud.dns": "Tencent Cloud - DNS",
"provider.tencentcloud.ecdn": "Tencent Cloud - ECDN (Enterprise Content Delivery Network)",
"provider.tencentcloud.eo": "Tencent Cloud - EdgeOne",
"provider.tencentcloud.ssl_deploy": "Tencent Cloud - via SSL Certificate Service Deployment Job",
@@ -99,7 +101,7 @@
"provider.volcengine.cdn": "Volcengine - CDN (Content Delivery Network)",
"provider.volcengine.clb": "Volcengine - CLB (Cloud Load Balancer)",
"provider.volcengine.dcdn": "Volcengine - DCDN (Dynamic Content Delivery Network)",
- "provider.volcengine.dns": "Volcengine - DNS (Domain Name Service)",
+ "provider.volcengine.dns": "Volcengine - DNS",
"provider.volcengine.live": "Volcengine - Live",
"provider.volcengine.tos": "Volcengine - TOS (Tinder Object Storage)",
"provider.webhook": "Webhook",
diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json
index 6bf12328..d0ae4173 100644
--- a/ui/src/i18n/locales/en/nls.workflow.nodes.json
+++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json
@@ -46,6 +46,8 @@
"workflow_node.apply.form.huaweicloud_dns_region.label": "Huawei Cloud DNS region",
"workflow_node.apply.form.huaweicloud_dns_region.placeholder": "Please enter Huawei Cloud DNS region (e.g. cn-north-1)",
"workflow_node.apply.form.huaweicloud_dns_region.tooltip": "For more information, see https://console-intl.huaweicloud.com/apiexplorer/#/endpoint",
+ "workflow_node.apply.form.jdcloud_dns_region_id.label": "JD Cloud DNS region ID",
+ "workflow_node.apply.form.jdcloud_dns_region_id.placeholder": "Please enter JD Cloud DNS region ID (e.g. cn-north-1)",
"workflow_node.apply.form.advanced_config.label": "Advanced settings",
"workflow_node.apply.form.key_algorithm.label": "Certificate key algorithm",
"workflow_node.apply.form.key_algorithm.placeholder": "Please select certificate key algorithm",
diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json
index ee7fea3a..c35d65d3 100644
--- a/ui/src/i18n/locales/zh/nls.access.json
+++ b/ui/src/i18n/locales/zh/nls.access.json
@@ -38,8 +38,8 @@
"access.form.aliyun_access_key_id.label": "阿里云 AccessKeyId",
"access.form.aliyun_access_key_id.placeholder": "请输入阿里云 AccessKeyId",
"access.form.aliyun_access_key_id.tooltip": "这是什么?请参阅 https://help.aliyun.com/zh/ram/user-guide/create-an-accesskey-pair",
- "access.form.aliyun_access_key_secret.label": "阿里云 AccessKey Secret",
- "access.form.aliyun_access_key_secret.placeholder": "请输入阿里云 AccessKey Secret",
+ "access.form.aliyun_access_key_secret.label": "阿里云 AccessKeySecret",
+ "access.form.aliyun_access_key_secret.placeholder": "请输入阿里云 AccessKeySecret",
"access.form.aliyun_access_key_secret.tooltip": "这是什么?请参阅 https://help.aliyun.com/zh/ram/user-guide/create-an-accesskey-pair",
"access.form.aws_access_key_id.label": "AWS AccessKeyId",
"access.form.aws_access_key_id.placeholder": "请输入 AWS AccessKeyId",
@@ -133,6 +133,12 @@
"access.form.huaweicloud_secret_access_key.label": "华为云 SecretAccessKey",
"access.form.huaweicloud_secret_access_key.placeholder": "请输入华为云 SecretAccessKey",
"access.form.huaweicloud_secret_access_key.tooltip": "这是什么?请参阅 https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html",
+ "access.form.jdcloud_access_key_id.label": "京东云 AccessKeyId",
+ "access.form.jdcloud_access_key_id.placeholder": "请输入京东云 AccessKeyId",
+ "access.form.jdcloud_access_key_id.tooltip": "这是什么?请参阅 https://docs.jdcloud.com/cn/account-management/accesskey-management",
+ "access.form.jdcloud_access_key_secret.label": "京东云 AccessKeySecret",
+ "access.form.jdcloud_access_key_secret.placeholder": "请输入京东云 AccessKeySecret",
+ "access.form.jdcloud_access_key_secret.tooltip": "这是什么?请参阅 https://docs.jdcloud.com/cn/account-management/accesskey-management",
"access.form.k8s_kubeconfig.label": "KubeConfig",
"access.form.k8s_kubeconfig.placeholder": "请选择 KubeConfig 文件",
"access.form.k8s_kubeconfig.upload": "选择文件",
diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json
index e120b343..958f54a3 100644
--- a/ui/src/i18n/locales/zh/nls.provider.json
+++ b/ui/src/i18n/locales/zh/nls.provider.json
@@ -53,6 +53,8 @@
"provider.huaweicloud.dns": "华为云 - 云解析 DNS",
"provider.huaweicloud.elb": "华为云 - 弹性负载均衡 ELB",
"provider.huaweicloud.waf": "华为云 - Web 应用防火墙 WAF",
+ "provider.jdcloud": "京东云",
+ "provider.jdcloud.dns": "京东云 - 云解析 DNS",
"provider.kubernetes": "Kubernetes",
"provider.kubernetes.secret": "Kubernetes - Secret",
"provider.local": "本地部署",
diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json
index d5ac3aca..5860ba08 100644
--- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json
+++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json
@@ -46,6 +46,8 @@
"workflow_node.apply.form.huaweicloud_dns_region.label": "华为云 DNS 服务区域",
"workflow_node.apply.form.huaweicloud_dns_region.placeholder": "请输入华为云 DNS 服务区域(例如:cn-north-1)",
"workflow_node.apply.form.huaweicloud_dns_region.tooltip": "这是什么?请参阅 https://console.huaweicloud.com/apiexplorer/#/endpoint",
+ "workflow_node.apply.form.jdcloud_dns_region_id.label": "京东云 DNS 服务地域 ID",
+ "workflow_node.apply.form.jdcloud_dns_region_id.placeholder": "请输入京东云 DNS 服务地域 ID(例如:cn-north-1)",
"workflow_node.apply.form.advanced_config.label": "高级设置",
"workflow_node.apply.form.key_algorithm.label": "数字证书算法",
"workflow_node.apply.form.key_algorithm.placeholder": "请选择数字证书算法",