diff --git a/internal/certapply/applicators/sp_hostingde.go b/internal/certapply/applicators/sp_hostingde.go
new file mode 100644
index 000000000..9c3716a60
--- /dev/null
+++ b/internal/certapply/applicators/sp_hostingde.go
@@ -0,0 +1,29 @@
+package applicators
+
+import (
+ "fmt"
+
+ "github.com/go-acme/lego/v4/challenge"
+
+ "github.com/certimate-go/certimate/internal/domain"
+ "github.com/certimate-go/certimate/pkg/core/ssl-applicator/acme-dns01/providers/hostingde"
+ xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
+)
+
+func init() {
+ if err := ACMEDns01Registries.Register(domain.ACMEDns01ProviderTypeHostingde, func(options *ProviderFactoryOptions) (challenge.Provider, error) {
+ credentials := domain.AccessConfigForHostingde{}
+ if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
+ return nil, fmt.Errorf("failed to populate provider access config: %w", err)
+ }
+
+ provider, err := hostingde.NewChallengeProvider(&hostingde.ChallengeProviderConfig{
+ ApiKey: credentials.ApiKey,
+ DnsPropagationTimeout: options.DnsPropagationTimeout,
+ DnsTTL: options.DnsTTL,
+ })
+ return provider, err
+ }); err != nil {
+ panic(err)
+ }
+}
diff --git a/internal/domain/access.go b/internal/domain/access.go
index 991e55ab1..59e28e6db 100644
--- a/internal/domain/access.go
+++ b/internal/domain/access.go
@@ -247,6 +247,10 @@ type AccessConfigForHetzner struct {
ApiToken string `json:"apiToken"`
}
+type AccessConfigForHostingde struct {
+ ApiKey string `json:"apiKey"`
+}
+
type AccessConfigForHostinger struct {
ApiToken string `json:"apiToken"`
}
diff --git a/internal/domain/provider.go b/internal/domain/provider.go
index d2f2c5f10..4980be0c8 100644
--- a/internal/domain/provider.go
+++ b/internal/domain/provider.go
@@ -54,6 +54,7 @@ const (
AccessProviderTypeGoEdge = AccessProviderType("goedge")
AccessProviderTypeGoogleTrustServices = AccessProviderType("googletrustservices")
AccessProviderTypeHetzner = AccessProviderType("hetzner")
+ AccessProviderTypeHostingde = AccessProviderType("hostingde")
AccessProviderTypeHostinger = AccessProviderType("hostinger")
AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud")
AccessProviderTypeIONOS = AccessProviderType("ionos")
@@ -165,6 +166,7 @@ const (
ACMEDns01ProviderTypeGname = ACMEDns01ProviderType(AccessProviderTypeGname)
ACMEDns01ProviderTypeGoDaddy = ACMEDns01ProviderType(AccessProviderTypeGoDaddy)
ACMEDns01ProviderTypeHetzner = ACMEDns01ProviderType(AccessProviderTypeHetzner)
+ ACMEDns01ProviderTypeHostingde = ACMEDns01ProviderType(AccessProviderTypeHostingde)
ACMEDns01ProviderTypeHostinger = ACMEDns01ProviderType(AccessProviderTypeHostinger)
ACMEDns01ProviderTypeHuaweiCloud = ACMEDns01ProviderType(AccessProviderTypeHuaweiCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeHuaweiCloudDNS]
ACMEDns01ProviderTypeHuaweiCloudDNS = ACMEDns01ProviderType(AccessProviderTypeHuaweiCloud + "-dns")
diff --git a/pkg/core/ssl-applicator/acme-dns01/providers/hostingde/hostingde.go b/pkg/core/ssl-applicator/acme-dns01/providers/hostingde/hostingde.go
new file mode 100644
index 000000000..4ff4216d3
--- /dev/null
+++ b/pkg/core/ssl-applicator/acme-dns01/providers/hostingde/hostingde.go
@@ -0,0 +1,38 @@
+package hostingde
+
+import (
+ "errors"
+ "time"
+
+ "github.com/go-acme/lego/v4/providers/dns/hostingde"
+
+ "github.com/certimate-go/certimate/pkg/core"
+)
+
+type ChallengeProviderConfig struct {
+ ApiKey string `json:"apiKey"`
+ DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"`
+ DnsTTL int32 `json:"dnsTTL,omitempty"`
+}
+
+func NewChallengeProvider(config *ChallengeProviderConfig) (core.ACMEChallenger, error) {
+ if config == nil {
+ return nil, errors.New("the configuration of the acme challenge provider is nil")
+ }
+
+ providerConfig := hostingde.NewDefaultConfig()
+ providerConfig.APIKey = config.ApiKey
+ if config.DnsPropagationTimeout != 0 {
+ providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second
+ }
+ if config.DnsTTL != 0 {
+ providerConfig.TTL = int(config.DnsTTL)
+ }
+
+ provider, err := hostingde.NewDNSProviderConfig(providerConfig)
+ if err != nil {
+ return nil, err
+ }
+
+ return provider, nil
+}
diff --git a/ui/public/imgs/providers/baiducloud.svg b/ui/public/imgs/providers/baiducloud.svg
index e448fd3ef..f92dd1bf7 100644
--- a/ui/public/imgs/providers/baiducloud.svg
+++ b/ui/public/imgs/providers/baiducloud.svg
@@ -1 +1 @@
-
+
diff --git a/ui/public/imgs/providers/hostingde.svg b/ui/public/imgs/providers/hostingde.svg
new file mode 100644
index 000000000..2929b09d0
--- /dev/null
+++ b/ui/public/imgs/providers/hostingde.svg
@@ -0,0 +1 @@
+
diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx
index a10a82187..0ee333907 100644
--- a/ui/src/components/access/AccessForm.tsx
+++ b/ui/src/components/access/AccessForm.tsx
@@ -53,6 +53,7 @@ import AccessConfigFieldsProviderGoDaddy from "./forms/AccessConfigFieldsProvide
import AccessConfigFieldsProviderGoEdge from "./forms/AccessConfigFieldsProviderGoEdge";
import AccessConfigFieldsProviderGoogleTrustServices from "./forms/AccessConfigFieldsProviderGoogleTrustServices";
import AccessConfigFieldsProviderHetzner from "./forms/AccessConfigFieldsProviderHetzner";
+import AccessConfigFieldsProviderHostingde from "./forms/AccessConfigFieldsProviderHostingde";
import AccessConfigFieldsProviderHostinger from "./forms/AccessConfigFieldsProviderHostinger";
import AccessConfigFieldsProviderHuaweiCloud from "./forms/AccessConfigFieldsProviderHuaweiCloud";
import AccessConfigFieldsProviderIONOS from "./forms/AccessConfigFieldsProviderIONOS";
@@ -266,6 +267,9 @@ const AccessForm = ({ className, style, disabled, initialValues, mode, usage, on
case ACCESS_PROVIDERS.HETZNER: {
return