diff --git a/README.md b/README.md
index 59b62a8de..66ed2e4d6 100644
--- a/README.md
+++ b/README.md
@@ -221,37 +221,37 @@ Detailed documentation is available [here](https://go-acme.github.io/lego/dns).
Stackpath |
Technitium |
Tencent Cloud DNS |
- Timeweb Cloud |
+ Tencent EdgeOne |
+ | Timeweb Cloud |
TransIP |
UKFast SafeDNS |
Ultradns |
- Variomedia |
+ | Variomedia |
VegaDNS |
Vercel |
Versio.[nl|eu|uk] |
- VinylDNS |
+ | VinylDNS |
VK Cloud |
Volcano Engine/火山引擎 |
Vscale |
- Vultr |
+ | Vultr |
Webnames |
Websupport |
WEDOS |
- West.cn/西部数码 |
+ | West.cn/西部数码 |
Yandex 360 |
Yandex Cloud |
Yandex PDD |
- Zone.ee |
+ | Zone.ee |
ZoneEdit |
Zonomi |
|
- |
diff --git a/cmd/zz_gen_cmd_dnshelp.go b/cmd/zz_gen_cmd_dnshelp.go
index 73c162b21..1aacdc7af 100644
--- a/cmd/zz_gen_cmd_dnshelp.go
+++ b/cmd/zz_gen_cmd_dnshelp.go
@@ -60,6 +60,7 @@ func allDNSCodes() string {
"dynu",
"easydns",
"edgedns",
+ "edgeone",
"efficientip",
"epik",
"exec",
@@ -1206,6 +1207,29 @@ func displayDNSHelp(w io.Writer, name string) error {
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/edgedns`)
+ case "edgeone":
+ // generated from: providers/dns/edgeone/edgeone.toml
+ ew.writeln(`Configuration for Tencent EdgeOne.`)
+ ew.writeln(`Code: 'edgeone'`)
+ ew.writeln(`Since: 'v4.26.0'`)
+ ew.writeln()
+
+ ew.writeln(`Credentials:`)
+ ew.writeln(` - "EDGEONE_SECRET_ID": Access key ID`)
+ ew.writeln(` - "EDGEONE_SECRET_KEY": Access Key secret`)
+ ew.writeln()
+
+ ew.writeln(`Additional Configuration:`)
+ ew.writeln(` - "EDGEONE_HTTP_TIMEOUT": API request timeout in seconds (Default: 30)`)
+ ew.writeln(` - "EDGEONE_POLLING_INTERVAL": Time between DNS propagation check in seconds (Default: 30)`)
+ ew.writeln(` - "EDGEONE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation in seconds (Default: 1200)`)
+ ew.writeln(` - "EDGEONE_REGION": Region`)
+ ew.writeln(` - "EDGEONE_SESSION_TOKEN": Access Key token`)
+ ew.writeln(` - "EDGEONE_TTL": The TTL of the TXT record used for the DNS challenge in seconds (Default: 60)`)
+
+ ew.writeln()
+ ew.writeln(`More information: https://go-acme.github.io/lego/dns/edgeone`)
+
case "efficientip":
// generated from: providers/dns/efficientip/efficientip.toml
ew.writeln(`Configuration for Efficient IP.`)
diff --git a/docs/content/dns/zz_gen_edgeone.md b/docs/content/dns/zz_gen_edgeone.md
new file mode 100644
index 000000000..b7b5b1eec
--- /dev/null
+++ b/docs/content/dns/zz_gen_edgeone.md
@@ -0,0 +1,72 @@
+---
+title: "Tencent EdgeOne"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: edgeone
+dnsprovider:
+ since: "v4.26.0"
+ code: "edgeone"
+ url: "https://edgeone.ai"
+---
+
+
+
+
+
+
+Configuration for [Tencent EdgeOne](https://edgeone.ai).
+
+
+
+
+- Code: `edgeone`
+- Since: v4.26.0
+
+
+Here is an example bash command using the Tencent EdgeOne provider:
+
+```bash
+EDGEONE_SECRET_ID=abcdefghijklmnopqrstuvwx \
+EDGEONE_SECRET_KEY=your-secret-key \
+lego --email you@example.com --dns edgeone -d '*.example.com' -d example.com run
+```
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `EDGEONE_SECRET_ID` | Access key ID |
+| `EDGEONE_SECRET_KEY` | Access Key secret |
+
+The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
+More information [here]({{% ref "dns#configuration-and-credentials" %}}).
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `EDGEONE_HTTP_TIMEOUT` | API request timeout in seconds (Default: 30) |
+| `EDGEONE_POLLING_INTERVAL` | Time between DNS propagation check in seconds (Default: 30) |
+| `EDGEONE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation in seconds (Default: 1200) |
+| `EDGEONE_REGION` | Region |
+| `EDGEONE_SESSION_TOKEN` | Access Key token |
+| `EDGEONE_TTL` | The TTL of the TXT record used for the DNS challenge in seconds (Default: 60) |
+
+The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
+More information [here]({{% ref "dns#configuration-and-credentials" %}}).
+
+
+
+
+## More information
+
+- [API documentation](https://edgeone.ai/document/50454#dns-record-apis)
+- [Go client](https://github.com/tencentcloud/tencentcloud-sdk-go)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_tencentcloud.md b/docs/content/dns/zz_gen_tencentcloud.md
index bc08c43ce..ef1e6cdf8 100644
--- a/docs/content/dns/zz_gen_tencentcloud.md
+++ b/docs/content/dns/zz_gen_tencentcloud.md
@@ -6,7 +6,7 @@ slug: tencentcloud
dnsprovider:
since: "v4.6.0"
code: "tencentcloud"
- url: "https://cloud.tencent.com/product/cns"
+ url: "https://cloud.tencent.com/product/dns"
---
@@ -14,7 +14,7 @@ dnsprovider:
-Configuration for [Tencent Cloud DNS](https://cloud.tencent.com/product/cns).
+Configuration for [Tencent Cloud DNS](https://cloud.tencent.com/product/dns).
diff --git a/docs/data/zz_cli_help.toml b/docs/data/zz_cli_help.toml
index 35f641cd4..62e422217 100644
--- a/docs/data/zz_cli_help.toml
+++ b/docs/data/zz_cli_help.toml
@@ -152,7 +152,7 @@ To display the documentation for a specific DNS provider, run:
$ lego dnshelp -c code
Supported DNS providers:
- acme-dns, active24, alidns, allinkl, arvancloud, auroradns, autodns, axelname, azion, azure, azuredns, baiducloud, bindman, bluecat, bookmyname, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, conoha, conohav3, constellix, corenetworks, cpanel, derak, desec, designate, digitalocean, directadmin, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dyndnsfree, dynu, easydns, edgedns, efficientip, epik, exec, exoscale, f5xc, freemyip, gandi, gandiv5, gcloud, gcore, glesys, godaddy, googledomains, hetzner, hostingde, hosttech, httpnet, httpreq, huaweicloud, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ipv64, iwantmyname, joker, liara, lightsail, limacity, linode, liquidweb, loopia, luadns, mailinabox, manageengine, manual, metaname, metaregistrar, mijnhost, mittwald, myaddr, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, netcup, netlify, nicmanager, nicru, nifcloud, njalla, nodion, ns1, oraclecloud, otc, ovh, pdns, plesk, porkbun, rackspace, rainyun, rcodezero, regfish, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, selectelv2, selfhostde, servercow, shellrent, simply, sonic, spaceship, stackpath, technitium, tencentcloud, timewebcloud, transip, ultradns, variomedia, vegadns, vercel, versio, vinyldns, vkcloud, volcengine, vscale, vultr, webnames, websupport, wedos, westcn, yandex, yandex360, yandexcloud, zoneedit, zoneee, zonomi
+ acme-dns, active24, alidns, allinkl, arvancloud, auroradns, autodns, axelname, azion, azure, azuredns, baiducloud, bindman, bluecat, bookmyname, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, conoha, conohav3, constellix, corenetworks, cpanel, derak, desec, designate, digitalocean, directadmin, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dyndnsfree, dynu, easydns, edgedns, edgeone, efficientip, epik, exec, exoscale, f5xc, freemyip, gandi, gandiv5, gcloud, gcore, glesys, godaddy, googledomains, hetzner, hostingde, hosttech, httpnet, httpreq, huaweicloud, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ipv64, iwantmyname, joker, liara, lightsail, limacity, linode, liquidweb, loopia, luadns, mailinabox, manageengine, manual, metaname, metaregistrar, mijnhost, mittwald, myaddr, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, netcup, netlify, nicmanager, nicru, nifcloud, njalla, nodion, ns1, oraclecloud, otc, ovh, pdns, plesk, porkbun, rackspace, rainyun, rcodezero, regfish, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, selectelv2, selfhostde, servercow, shellrent, simply, sonic, spaceship, stackpath, technitium, tencentcloud, timewebcloud, transip, ultradns, variomedia, vegadns, vercel, versio, vinyldns, vkcloud, volcengine, vscale, vultr, webnames, websupport, wedos, westcn, yandex, yandex360, yandexcloud, zoneedit, zoneee, zonomi
More information: https://go-acme.github.io/lego/dns
"""
diff --git a/go.mod b/go.mod
index c5c328d41..47ce27ebe 100644
--- a/go.mod
+++ b/go.mod
@@ -32,6 +32,7 @@ require (
github.com/exoscale/egoscale/v3 v3.1.24
github.com/go-acme/alidns-20150109/v4 v4.5.10
github.com/go-acme/tencentclouddnspod v1.0.1208
+ github.com/go-acme/tencentedgdeone v1.0.1212
github.com/go-jose/go-jose/v4 v4.1.1
github.com/go-viper/mapstructure/v2 v2.4.0
github.com/google/go-cmp v0.7.0
@@ -76,7 +77,7 @@ require (
github.com/selectel/go-selvpcclient/v4 v4.1.0
github.com/softlayer/softlayer-go v1.1.7
github.com/stretchr/testify v1.10.0
- github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1210
+ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1212
github.com/transip/gotransip/v6 v6.26.0
github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec
github.com/urfave/cli/v2 v2.27.7
diff --git a/go.sum b/go.sum
index f6b2aceee..f99d60e97 100644
--- a/go.sum
+++ b/go.sum
@@ -321,6 +321,8 @@ github.com/go-acme/alidns-20150109/v4 v4.5.10 h1:epLD0VaHR5XUpiM6mjm4MzQFICrk+zp
github.com/go-acme/alidns-20150109/v4 v4.5.10/go.mod h1:qGRq8kD0xVgn82qRSQmhHwh/oWxKRjF4Db5OI4ScV5g=
github.com/go-acme/tencentclouddnspod v1.0.1208 h1:xAVy1lmg2KcKKeYmFSBQUttwc1o1S++9QTjAotGC+BM=
github.com/go-acme/tencentclouddnspod v1.0.1208/go.mod h1:yxG02mkbbVd7lTb97nOn7oj09djhm7hAwxNQw4B9dpQ=
+github.com/go-acme/tencentedgdeone v1.0.1212 h1:H0HaYrkyX4htOXgusb6Mf8mKDIr/+CIqq5hbOdQB2EY=
+github.com/go-acme/tencentedgdeone v1.0.1212/go.mod h1:IADOuBpaFM1IIKFedlJj7EfMqstThcm1g1g1uVejgxo=
github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
@@ -895,8 +897,8 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1208/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1210 h1:waSk2KyI2VvXtR+XQJm0v1lWfgbJg51iSWJh4hWnyeo=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1210/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1212 h1:61myGZ5h8YgmFEziBGuKPSnPNs75KlqDQwEeGC2dwyk=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1212/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
diff --git a/providers/dns/edgeone/edgeone.go b/providers/dns/edgeone/edgeone.go
new file mode 100644
index 000000000..88a795c69
--- /dev/null
+++ b/providers/dns/edgeone/edgeone.go
@@ -0,0 +1,188 @@
+// Package edgeone implements a DNS provider for solving the DNS-01 challenge using Tencent EdgeOne.
+package edgeone
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "math"
+ "sync"
+ "time"
+
+ "github.com/go-acme/lego/v4/challenge/dns01"
+ "github.com/go-acme/lego/v4/platform/config/env"
+ "github.com/go-acme/lego/v4/providers/dns/internal/ptr"
+ teo "github.com/go-acme/tencentedgdeone/v20220901"
+ "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
+ "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
+ "golang.org/x/net/idna"
+)
+
+// Environment variables names.
+const (
+ envNamespace = "EDGEONE_"
+
+ EnvSecretID = envNamespace + "SECRET_ID"
+ EnvSecretKey = envNamespace + "SECRET_KEY"
+ EnvRegion = envNamespace + "REGION"
+ EnvSessionToken = envNamespace + "SESSION_TOKEN"
+
+ EnvTTL = envNamespace + "TTL"
+ EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
+ EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
+ EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
+)
+
+// Config is used to configure the creation of the DNSProvider.
+type Config struct {
+ SecretID string
+ SecretKey string
+ Region string
+ SessionToken string
+
+ PropagationTimeout time.Duration
+ PollingInterval time.Duration
+ TTL int
+ HTTPTimeout time.Duration
+}
+
+// NewDefaultConfig returns a default configuration for the DNSProvider.
+func NewDefaultConfig() *Config {
+ return &Config{
+ TTL: env.GetOrDefaultInt(EnvTTL, 60),
+ PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 20*time.Minute),
+ PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, 30*time.Second),
+ HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
+ }
+}
+
+// DNSProvider implements the challenge.Provider interface.
+type DNSProvider struct {
+ config *Config
+ client *teo.Client
+
+ recordIDs map[string]*string
+ recordIDsMu sync.Mutex
+}
+
+// NewDNSProvider returns a DNSProvider instance configured for Tencent EdgeOne.
+func NewDNSProvider() (*DNSProvider, error) {
+ values, err := env.Get(EnvSecretID, EnvSecretKey)
+ if err != nil {
+ return nil, fmt.Errorf("edgeone: %w", err)
+ }
+
+ config := NewDefaultConfig()
+ config.SecretID = values[EnvSecretID]
+ config.SecretKey = values[EnvSecretKey]
+ config.Region = env.GetOrDefaultString(EnvRegion, "")
+ config.SessionToken = env.GetOrDefaultString(EnvSessionToken, "")
+
+ return NewDNSProviderConfig(config)
+}
+
+// NewDNSProviderConfig return a DNSProvider instance configured for Tencent EdgeOne.
+func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
+ if config == nil {
+ return nil, errors.New("edgeone: the configuration of the DNS provider is nil")
+ }
+
+ var credential *common.Credential
+
+ switch {
+ case config.SecretID != "" && config.SecretKey != "" && config.SessionToken != "":
+ credential = common.NewTokenCredential(config.SecretID, config.SecretKey, config.SessionToken)
+ case config.SecretID != "" && config.SecretKey != "":
+ credential = common.NewCredential(config.SecretID, config.SecretKey)
+ default:
+ return nil, errors.New("edgeone: credentials missing")
+ }
+
+ cpf := profile.NewClientProfile()
+ cpf.HttpProfile.Endpoint = "teo.intl.tencentcloudapi.com"
+ cpf.HttpProfile.ReqTimeout = int(math.Round(config.HTTPTimeout.Seconds()))
+
+ client, err := teo.NewClient(credential, config.Region, cpf)
+ if err != nil {
+ return nil, fmt.Errorf("edgeone: %w", err)
+ }
+
+ return &DNSProvider{
+ config: config,
+ client: client,
+ recordIDs: map[string]*string{},
+ recordIDsMu: sync.Mutex{},
+ }, nil
+}
+
+// Present creates a TXT record using the specified parameters.
+func (d *DNSProvider) Present(domain, token, keyAuth string) error {
+ info := dns01.GetChallengeInfo(domain, keyAuth)
+
+ ctx := context.Background()
+
+ zone, err := d.getHostedZone(ctx, info.EffectiveFQDN)
+ if err != nil {
+ return fmt.Errorf("edgeone: failed to get hosted zone: %w", err)
+ }
+
+ punnyCoded, err := idna.ToASCII(dns01.UnFqdn(info.EffectiveFQDN))
+ if err != nil {
+ return fmt.Errorf("edgeone: fail to convert punycode: %w", err)
+ }
+
+ request := teo.NewCreateDnsRecordRequest()
+ request.Name = ptr.Pointer(punnyCoded)
+ request.ZoneId = zone.ZoneId
+ request.Type = ptr.Pointer("TXT")
+ request.Content = ptr.Pointer(info.Value)
+ request.TTL = ptr.Pointer(int64(d.config.TTL))
+
+ nr, err := teo.CreateDnsRecordWithContext(ctx, d.client, request)
+ if err != nil {
+ return fmt.Errorf("edgeone: API call failed: %w", err)
+ }
+
+ d.recordIDsMu.Lock()
+ d.recordIDs[token] = nr.Response.RecordId
+ d.recordIDsMu.Unlock()
+
+ return nil
+}
+
+// CleanUp removes the TXT record matching the specified parameters.
+func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
+ info := dns01.GetChallengeInfo(domain, keyAuth)
+
+ ctx := context.Background()
+
+ zone, err := d.getHostedZone(ctx, info.EffectiveFQDN)
+ if err != nil {
+ return fmt.Errorf("edgeone: failed to get hosted zone: %w", err)
+ }
+
+ // get the record's unique ID from when we created it
+ d.recordIDsMu.Lock()
+ recordID, ok := d.recordIDs[token]
+ d.recordIDsMu.Unlock()
+ if !ok {
+ return fmt.Errorf("edgeone: unknown record ID for '%s'", info.EffectiveFQDN)
+ }
+
+ request := teo.NewDeleteDnsRecordsRequest()
+ request.ZoneId = zone.ZoneId
+ request.RecordIds = []*string{recordID}
+
+ _, err = teo.DeleteDnsRecordsWithContext(ctx, d.client, request)
+ if err != nil {
+ return fmt.Errorf("edgeone: delete record failed: %w", err)
+ }
+
+ return nil
+}
+
+// Timeout returns the timeout and interval to use when checking for DNS propagation.
+// Adjusting here to cope with spikes in propagation times.
+func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
+ return d.config.PropagationTimeout, d.config.PollingInterval
+}
diff --git a/providers/dns/edgeone/edgeone.toml b/providers/dns/edgeone/edgeone.toml
new file mode 100644
index 000000000..120756da6
--- /dev/null
+++ b/providers/dns/edgeone/edgeone.toml
@@ -0,0 +1,27 @@
+Name = "Tencent EdgeOne"
+Description = ''''''
+URL = "https://edgeone.ai"
+Code = "edgeone"
+Since = "v4.26.0"
+
+Example = '''
+EDGEONE_SECRET_ID=abcdefghijklmnopqrstuvwx \
+EDGEONE_SECRET_KEY=your-secret-key \
+lego --email you@example.com --dns edgeone -d '*.example.com' -d example.com run
+'''
+
+[Configuration]
+ [Configuration.Credentials]
+ EDGEONE_SECRET_ID = "Access key ID"
+ EDGEONE_SECRET_KEY = "Access Key secret"
+ [Configuration.Additional]
+ EDGEONE_SESSION_TOKEN = "Access Key token"
+ EDGEONE_REGION = "Region"
+ EDGEONE_POLLING_INTERVAL = "Time between DNS propagation check in seconds (Default: 30)"
+ EDGEONE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation in seconds (Default: 1200)"
+ EDGEONE_TTL = "The TTL of the TXT record used for the DNS challenge in seconds (Default: 60)"
+ EDGEONE_HTTP_TIMEOUT = "API request timeout in seconds (Default: 30)"
+
+[Links]
+ API = "https://edgeone.ai/document/50454#dns-record-apis"
+ GoClient = "https://github.com/tencentcloud/tencentcloud-sdk-go"
diff --git a/providers/dns/edgeone/edgeone_test.go b/providers/dns/edgeone/edgeone_test.go
new file mode 100644
index 000000000..e1c004d67
--- /dev/null
+++ b/providers/dns/edgeone/edgeone_test.go
@@ -0,0 +1,147 @@
+package edgeone
+
+import (
+ "testing"
+
+ "github.com/go-acme/lego/v4/platform/tester"
+ "github.com/stretchr/testify/require"
+)
+
+const envDomain = envNamespace + "DOMAIN"
+
+var envTest = tester.NewEnvTest(EnvSecretID, EnvSecretKey).
+ WithDomain(envDomain)
+
+func TestNewDNSProvider(t *testing.T) {
+ testCases := []struct {
+ desc string
+ envVars map[string]string
+ expected string
+ }{
+ {
+ desc: "success",
+ envVars: map[string]string{
+ EnvSecretID: "123",
+ EnvSecretKey: "456",
+ },
+ },
+ {
+ desc: "missing credentials",
+ envVars: map[string]string{
+ EnvSecretID: "",
+ EnvSecretKey: "",
+ },
+ expected: "edgeone: some credentials information are missing: EDGEONE_SECRET_ID,EDGEONE_SECRET_KEY",
+ },
+ {
+ desc: "missing access id",
+ envVars: map[string]string{
+ EnvSecretID: "",
+ EnvSecretKey: "456",
+ },
+ expected: "edgeone: some credentials information are missing: EDGEONE_SECRET_ID",
+ },
+ {
+ desc: "missing secret key",
+ envVars: map[string]string{
+ EnvSecretID: "123",
+ EnvSecretKey: "",
+ },
+ expected: "edgeone: some credentials information are missing: EDGEONE_SECRET_KEY",
+ },
+ }
+
+ for _, test := range testCases {
+ t.Run(test.desc, func(t *testing.T) {
+ defer envTest.RestoreEnv()
+ envTest.ClearEnv()
+
+ envTest.Apply(test.envVars)
+
+ p, err := NewDNSProvider()
+
+ if test.expected == "" {
+ require.NoError(t, err)
+ require.NotNil(t, p)
+ require.NotNil(t, p.config)
+ require.NotNil(t, p.client)
+ } else {
+ require.EqualError(t, err, test.expected)
+ }
+ })
+ }
+}
+
+func TestNewDNSProviderConfig(t *testing.T) {
+ testCases := []struct {
+ desc string
+ secretID string
+ secretKey string
+ expected string
+ }{
+ {
+ desc: "success",
+ secretID: "123",
+ secretKey: "456",
+ },
+ {
+ desc: "missing credentials",
+ expected: "edgeone: credentials missing",
+ },
+ {
+ desc: "missing secret id",
+ secretKey: "456",
+ expected: "edgeone: credentials missing",
+ },
+ {
+ desc: "missing secret key",
+ secretID: "123",
+ expected: "edgeone: credentials missing",
+ },
+ }
+
+ for _, test := range testCases {
+ t.Run(test.desc, func(t *testing.T) {
+ config := NewDefaultConfig()
+ config.SecretID = test.secretID
+ config.SecretKey = test.secretKey
+
+ p, err := NewDNSProviderConfig(config)
+
+ if test.expected == "" {
+ require.NoError(t, err)
+ require.NotNil(t, p)
+ require.NotNil(t, p.config)
+ require.NotNil(t, p.client)
+ } else {
+ require.EqualError(t, err, test.expected)
+ }
+ })
+ }
+}
+
+func TestLivePresent(t *testing.T) {
+ if !envTest.IsLiveTest() {
+ t.Skip("skipping live test")
+ }
+
+ envTest.RestoreEnv()
+ provider, err := NewDNSProvider()
+ require.NoError(t, err)
+
+ err = provider.Present(envTest.GetDomain(), "", "123d==")
+ require.NoError(t, err)
+}
+
+func TestLiveCleanUp(t *testing.T) {
+ if !envTest.IsLiveTest() {
+ t.Skip("skipping live test")
+ }
+
+ envTest.RestoreEnv()
+ provider, err := NewDNSProvider()
+ require.NoError(t, err)
+
+ err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
+ require.NoError(t, err)
+}
diff --git a/providers/dns/edgeone/wrapper.go b/providers/dns/edgeone/wrapper.go
new file mode 100644
index 000000000..0d207f030
--- /dev/null
+++ b/providers/dns/edgeone/wrapper.go
@@ -0,0 +1,50 @@
+package edgeone
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/go-acme/lego/v4/challenge/dns01"
+ "github.com/go-acme/lego/v4/providers/dns/internal/ptr"
+ teo "github.com/go-acme/tencentedgdeone/v20220901"
+)
+
+func (d *DNSProvider) getHostedZone(ctx context.Context, domain string) (*teo.Zone, error) {
+ request := teo.NewDescribeZonesRequest()
+
+ var domains []*teo.Zone
+
+ for {
+ response, err := teo.DescribeZonesWithContext(ctx, d.client, request)
+ if err != nil {
+ return nil, fmt.Errorf("API call failed: %w", err)
+ }
+
+ domains = append(domains, response.Response.Zones...)
+
+ if int64(len(domains)) >= ptr.Deref(response.Response.TotalCount) {
+ break
+ }
+
+ request.Offset = ptr.Pointer(int64(len(domains)))
+ }
+
+ authZone, err := dns01.FindZoneByFqdn(domain)
+ if err != nil {
+ return nil, fmt.Errorf("could not find zone: %w", err)
+ }
+
+ var hostedZone *teo.Zone
+ for _, zone := range domains {
+ unfqdn := dns01.UnFqdn(authZone)
+ if ptr.Deref(zone.ZoneName) == unfqdn {
+ hostedZone = zone
+ }
+ }
+
+ if hostedZone == nil {
+ return nil, fmt.Errorf("zone %s not found in dnspod for domain %s", authZone, domain)
+ }
+
+ return hostedZone, nil
+}
diff --git a/providers/dns/tencentcloud/tencentcloud.toml b/providers/dns/tencentcloud/tencentcloud.toml
index 75a950a49..7f06d9386 100644
--- a/providers/dns/tencentcloud/tencentcloud.toml
+++ b/providers/dns/tencentcloud/tencentcloud.toml
@@ -1,6 +1,6 @@
Name = "Tencent Cloud DNS"
Description = ''''''
-URL = "https://cloud.tencent.com/product/cns"
+URL = "https://cloud.tencent.com/product/dns"
Code = "tencentcloud"
Since = "v4.6.0"
diff --git a/providers/dns/zz_gen_dns_providers.go b/providers/dns/zz_gen_dns_providers.go
index 59205f7f6..f3598b7b5 100644
--- a/providers/dns/zz_gen_dns_providers.go
+++ b/providers/dns/zz_gen_dns_providers.go
@@ -54,6 +54,7 @@ import (
"github.com/go-acme/lego/v4/providers/dns/dynu"
"github.com/go-acme/lego/v4/providers/dns/easydns"
"github.com/go-acme/lego/v4/providers/dns/edgedns"
+ "github.com/go-acme/lego/v4/providers/dns/edgeone"
"github.com/go-acme/lego/v4/providers/dns/efficientip"
"github.com/go-acme/lego/v4/providers/dns/epik"
"github.com/go-acme/lego/v4/providers/dns/exec"
@@ -265,6 +266,8 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) {
return easydns.NewDNSProvider()
case "edgedns", "fastdns":
return edgedns.NewDNSProvider()
+ case "edgeone":
+ return edgeone.NewDNSProvider()
case "efficientip":
return efficientip.NewDNSProvider()
case "epik":