From 7fce3c7c3e9d7a2bab6dc99459f39a15893e2ade Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Wed, 27 May 2026 23:47:14 +0800 Subject: [PATCH] refactor(provider): use lego to implement dns-01 challenger of dnsla --- .../challengers/dns01/dnsla/dnsla.go | 9 +- .../challengers/dns01/dnsla/internal/lego.go | 184 ------------------ pkg/sdk3rd/dnsla/api_create_record.go | 46 ----- pkg/sdk3rd/dnsla/api_delete_record.go | 36 ---- pkg/sdk3rd/dnsla/api_list_domains.go | 48 ----- pkg/sdk3rd/dnsla/api_list_records.go | 53 ----- pkg/sdk3rd/dnsla/api_update_record.go | 43 ---- pkg/sdk3rd/dnsla/client.go | 96 --------- pkg/sdk3rd/dnsla/types.go | 59 ------ 9 files changed, 5 insertions(+), 569 deletions(-) delete mode 100644 pkg/core/certifier/challengers/dns01/dnsla/internal/lego.go delete mode 100644 pkg/sdk3rd/dnsla/api_create_record.go delete mode 100644 pkg/sdk3rd/dnsla/api_delete_record.go delete mode 100644 pkg/sdk3rd/dnsla/api_list_domains.go delete mode 100644 pkg/sdk3rd/dnsla/api_list_records.go delete mode 100644 pkg/sdk3rd/dnsla/api_update_record.go delete mode 100644 pkg/sdk3rd/dnsla/client.go delete mode 100644 pkg/sdk3rd/dnsla/types.go diff --git a/pkg/core/certifier/challengers/dns01/dnsla/dnsla.go b/pkg/core/certifier/challengers/dns01/dnsla/dnsla.go index 450730bb6..0bf6b1598 100644 --- a/pkg/core/certifier/challengers/dns01/dnsla/dnsla.go +++ b/pkg/core/certifier/challengers/dns01/dnsla/dnsla.go @@ -4,8 +4,9 @@ import ( "fmt" "time" + "github.com/go-acme/lego/v5/providers/dns/dnsla" + "github.com/certimate-go/certimate/pkg/core/certifier" - "github.com/certimate-go/certimate/pkg/core/certifier/challengers/dns01/dnsla/internal" ) type ChallengerConfig struct { @@ -20,8 +21,8 @@ func NewChallenger(config *ChallengerConfig) (certifier.ACMEChallenger, error) { return nil, fmt.Errorf("the configuration of the acme challenge provider is nil") } - providerConfig := internal.NewDefaultConfig() - providerConfig.APIId = config.ApiId + providerConfig := dnsla.NewDefaultConfig() + providerConfig.APIID = config.ApiId providerConfig.APISecret = config.ApiSecret if config.DnsPropagationTimeout != 0 { providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second @@ -30,7 +31,7 @@ func NewChallenger(config *ChallengerConfig) (certifier.ACMEChallenger, error) { providerConfig.TTL = config.DnsTTL } - provider, err := internal.NewDNSProviderConfig(providerConfig) + provider, err := dnsla.NewDNSProviderConfig(providerConfig) if err != nil { return nil, err } diff --git a/pkg/core/certifier/challengers/dns01/dnsla/internal/lego.go b/pkg/core/certifier/challengers/dns01/dnsla/internal/lego.go deleted file mode 100644 index 0e6247a0d..000000000 --- a/pkg/core/certifier/challengers/dns01/dnsla/internal/lego.go +++ /dev/null @@ -1,184 +0,0 @@ -package internal - -import ( - "context" - "fmt" - "strings" - "sync" - "time" - - "github.com/go-acme/lego/v5/challenge" - "github.com/go-acme/lego/v5/challenge/dns01" - "github.com/go-acme/lego/v5/platform/env" - "github.com/samber/lo" - - dnslasdk "github.com/certimate-go/certimate/pkg/sdk3rd/dnsla" -) - -const ( - envNamespace = "DNSLA_" - - EnvAPIId = envNamespace + "API_ID" - EnvAPISecret = envNamespace + "API_SECRET" - - EnvTTL = envNamespace + "TTL" - EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT" - EnvPollingInterval = envNamespace + "POLLING_INTERVAL" - EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT" -) - -var _ challenge.ProviderTimeout = (*DNSProvider)(nil) - -type Config struct { - APIId string - APISecret string - - PropagationTimeout time.Duration - PollingInterval time.Duration - TTL int - HTTPTimeout time.Duration -} - -type DNSProvider struct { - config *Config - client *dnslasdk.Client - - recordIDs map[string]string // Key: ChallengeToken; Value: RecordID - recordIDsMu sync.Mutex -} - -func NewDefaultConfig() *Config { - return &Config{ - TTL: env.GetOrDefaultInt(EnvTTL, 300), - PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 5*time.Minute), - PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval), - HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second), - } -} - -func NewDNSProvider() (*DNSProvider, error) { - values, err := env.Get(EnvAPIId, EnvAPISecret) - if err != nil { - return nil, fmt.Errorf("dnsla: %w", err) - } - - config := NewDefaultConfig() - config.APIId = values[EnvAPIId] - config.APISecret = values[EnvAPISecret] - - return NewDNSProviderConfig(config) -} - -func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { - if config == nil { - return nil, fmt.Errorf("dnsla: the configuration of the DNS provider is nil") - } - - client, err := dnslasdk.NewClient(config.APIId, config.APISecret) - if err != nil { - return nil, fmt.Errorf("dnsla: %w", err) - } else { - client.SetTimeout(config.HTTPTimeout) - } - - return &DNSProvider{ - config: config, - client: client, - recordIDs: make(map[string]string), - recordIDsMu: sync.Mutex{}, - }, nil -} - -func (d *DNSProvider) Present(ctx context.Context, domain, token, keyAuth string) error { - info := dns01.GetChallengeInfo(ctx, domain, keyAuth) - - authZone, err := dns01.DefaultClient().FindZoneByFqdn(ctx, info.EffectiveFQDN) - if err != nil { - return fmt.Errorf("dnsla: could not find zone for domain %q: %w", domain, err) - } - - subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) - if err != nil { - return fmt.Errorf("dnsla: %w", err) - } - - zone, err := d.findZone(ctx, dns01.UnFqdn(authZone)) - if err != nil { - return fmt.Errorf("dnsla: error when list zones: %w", err) - } - - // REF: https://www.dnsla.cn/docs/ApiDoc - request := &dnslasdk.CreateRecordRequest{ - DomainId: lo.ToPtr(zone.Id), - Type: lo.ToPtr(int32(16)), - Host: lo.ToPtr(subDomain), - Data: lo.ToPtr(info.Value), - Ttl: lo.ToPtr(int32(d.config.TTL)), - } - response, err := d.client.CreateRecordWithContext(ctx, request) - if err != nil { - return fmt.Errorf("dnsla: error when create record: %w", err) - } - - d.recordIDsMu.Lock() - d.recordIDs[token] = response.Data.Id - d.recordIDsMu.Unlock() - - return nil -} - -func (d *DNSProvider) CleanUp(ctx context.Context, domain, token, keyAuth string) error { - info := dns01.GetChallengeInfo(ctx, domain, keyAuth) - - d.recordIDsMu.Lock() - recordID, ok := d.recordIDs[token] - d.recordIDsMu.Unlock() - if !ok { - return fmt.Errorf("dnsla: unknown record ID for '%s'", info.EffectiveFQDN) - } - - // REF: https://www.dnsla.cn/docs/ApiDoc - if _, err := d.client.DeleteRecordWithContext(ctx, recordID); err != nil { - return fmt.Errorf("dnsla: error when delete record: %w", err) - } - - return nil -} - -func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { - return d.config.PropagationTimeout, d.config.PollingInterval -} - -func (d *DNSProvider) findZone(ctx context.Context, zoneName string) (*dnslasdk.DomainRecord, error) { - pageIndex := 1 - pageSize := 100 - for { - // REF: https://www.dnsla.cn/docs/ApiDoc - request := &dnslasdk.ListDomainsRequest{ - PageIndex: lo.ToPtr(int32(pageIndex)), - PageSize: lo.ToPtr(int32(pageSize)), - } - response, err := d.client.ListDomainsWithContext(ctx, request) - if err != nil { - return nil, err - } - - if response.Data == nil { - break - } - - for _, domainItem := range response.Data.Results { - if strings.TrimSuffix(domainItem.Domain, ".") == zoneName || strings.TrimSuffix(domainItem.DisplayDomain, ".") == zoneName { - return domainItem, nil - } - } - - if len(response.Data.Results) < pageSize { - break - } - - pageIndex++ - } - - return nil, fmt.Errorf("could not find zone '%s'", zoneName) -} diff --git a/pkg/sdk3rd/dnsla/api_create_record.go b/pkg/sdk3rd/dnsla/api_create_record.go deleted file mode 100644 index 7bcc56845..000000000 --- a/pkg/sdk3rd/dnsla/api_create_record.go +++ /dev/null @@ -1,46 +0,0 @@ -package dnsla - -import ( - "context" - "net/http" -) - -type CreateRecordRequest struct { - DomainId *string `json:"domainId"` - GroupId *string `json:"groupId,omitempty"` - LineId *string `json:"lineId,omitempty"` - Type *int32 `json:"type"` - Host *string `json:"host"` - Data *string `json:"data"` - Ttl *int32 `json:"ttl"` - Weight *int32 `json:"weight,omitempty"` - Preference *int32 `json:"preference,omitempty"` -} - -type CreateRecordResponse struct { - sdkResponseBase - Data *struct { - Id string `json:"id"` - } `json:"data,omitempty"` -} - -func (c *Client) CreateRecord(req *CreateRecordRequest) (*CreateRecordResponse, error) { - return c.CreateRecordWithContext(context.Background(), req) -} - -func (c *Client) CreateRecordWithContext(ctx context.Context, req *CreateRecordRequest) (*CreateRecordResponse, error) { - httpreq, err := c.newRequest(http.MethodPost, "/record") - if err != nil { - return nil, err - } else { - httpreq.SetBody(req) - httpreq.SetContext(ctx) - } - - result := &CreateRecordResponse{} - if _, err := c.doRequestWithResult(httpreq, result); err != nil { - return result, err - } - - return result, nil -} diff --git a/pkg/sdk3rd/dnsla/api_delete_record.go b/pkg/sdk3rd/dnsla/api_delete_record.go deleted file mode 100644 index 32fe8f50e..000000000 --- a/pkg/sdk3rd/dnsla/api_delete_record.go +++ /dev/null @@ -1,36 +0,0 @@ -package dnsla - -import ( - "context" - "fmt" - "net/http" -) - -type DeleteRecordResponse struct { - sdkResponseBase -} - -func (c *Client) DeleteRecord(recordId string) (*DeleteRecordResponse, error) { - return c.DeleteRecordWithContext(context.Background(), recordId) -} - -func (c *Client) DeleteRecordWithContext(ctx context.Context, recordId string) (*DeleteRecordResponse, error) { - if recordId == "" { - return nil, fmt.Errorf("sdkerr: unset recordId") - } - - httpreq, err := c.newRequest(http.MethodDelete, "/record") - if err != nil { - return nil, err - } else { - httpreq.SetQueryParam("id", recordId) - httpreq.SetContext(ctx) - } - - result := &DeleteRecordResponse{} - if _, err := c.doRequestWithResult(httpreq, result); err != nil { - return result, err - } - - return result, nil -} diff --git a/pkg/sdk3rd/dnsla/api_list_domains.go b/pkg/sdk3rd/dnsla/api_list_domains.go deleted file mode 100644 index ad354daea..000000000 --- a/pkg/sdk3rd/dnsla/api_list_domains.go +++ /dev/null @@ -1,48 +0,0 @@ -package dnsla - -import ( - "context" - "net/http" - - qs "github.com/google/go-querystring/query" -) - -type ListDomainsRequest struct { - GroupId *string `json:"groupId,omitempty" url:"groupId,omitempty"` - PageIndex *int32 `json:"pageIndex,omitempty" url:"pageIndex,omitempty"` - PageSize *int32 `json:"pageSize,omitempty" url:"pageSize,omitempty"` -} - -type ListDomainsResponse struct { - sdkResponseBase - Data *struct { - Total int32 `json:"total"` - Results []*DomainRecord `json:"results"` - } `json:"data,omitempty"` -} - -func (c *Client) ListDomains(req *ListDomainsRequest) (*ListDomainsResponse, error) { - return c.ListDomainsWithContext(context.Background(), req) -} - -func (c *Client) ListDomainsWithContext(ctx context.Context, req *ListDomainsRequest) (*ListDomainsResponse, error) { - httpreq, err := c.newRequest(http.MethodGet, "/domainList") - if err != nil { - return nil, err - } else { - values, err := qs.Values(req) - if err != nil { - return nil, err - } - - httpreq.SetQueryParamsFromValues(values) - httpreq.SetContext(ctx) - } - - result := &ListDomainsResponse{} - if _, err := c.doRequestWithResult(httpreq, result); err != nil { - return result, err - } - - return result, nil -} diff --git a/pkg/sdk3rd/dnsla/api_list_records.go b/pkg/sdk3rd/dnsla/api_list_records.go deleted file mode 100644 index 3e5c54daa..000000000 --- a/pkg/sdk3rd/dnsla/api_list_records.go +++ /dev/null @@ -1,53 +0,0 @@ -package dnsla - -import ( - "context" - "net/http" - - qs "github.com/google/go-querystring/query" -) - -type ListRecordsRequest struct { - DomainId *string `json:"domainId,omitempty" url:"domainId,omitempty"` - GroupId *string `json:"groupId,omitempty" url:"groupId,omitempty"` - LineId *string `json:"lineId,omitempty" url:"lineId,omitempty"` - Type *int32 `json:"type,omitempty" url:"type,omitempty"` - Host *string `json:"host,omitempty" url:"host,omitempty"` - Data *string `json:"data,omitempty" url:"data,omitempty"` - PageIndex *int32 `json:"pageIndex,omitempty" url:"pageIndex,omitempty"` - PageSize *int32 `json:"pageSize,omitempty" url:"pageSize,omitempty"` -} - -type ListRecordsResponse struct { - sdkResponseBase - Data *struct { - Total int32 `json:"total"` - Results []*DnsRecord `json:"results"` - } `json:"data,omitempty"` -} - -func (c *Client) ListRecords(req *ListRecordsRequest) (*ListRecordsResponse, error) { - return c.ListRecordsWithContext(context.Background(), req) -} - -func (c *Client) ListRecordsWithContext(ctx context.Context, req *ListRecordsRequest) (*ListRecordsResponse, error) { - httpreq, err := c.newRequest(http.MethodGet, "/recordList") - if err != nil { - return nil, err - } else { - values, err := qs.Values(req) - if err != nil { - return nil, err - } - - httpreq.SetQueryParamsFromValues(values) - httpreq.SetContext(ctx) - } - - result := &ListRecordsResponse{} - if _, err := c.doRequestWithResult(httpreq, result); err != nil { - return result, err - } - - return result, nil -} diff --git a/pkg/sdk3rd/dnsla/api_update_record.go b/pkg/sdk3rd/dnsla/api_update_record.go deleted file mode 100644 index dfd905230..000000000 --- a/pkg/sdk3rd/dnsla/api_update_record.go +++ /dev/null @@ -1,43 +0,0 @@ -package dnsla - -import ( - "context" - "net/http" -) - -type UpdateRecordRequest struct { - Id *string `json:"id"` - GroupId *string `json:"groupId,omitempty"` - LineId *string `json:"lineId,omitempty"` - Type *int32 `json:"type,omitempty"` - Host *string `json:"host,omitempty"` - Data *string `json:"data,omitempty"` - Ttl *int32 `json:"ttl,omitempty"` - Weight *int32 `json:"weight,omitempty"` - Preference *int32 `json:"preference,omitempty"` -} - -type UpdateRecordResponse struct { - sdkResponseBase -} - -func (c *Client) UpdateRecord(req *UpdateRecordRequest) (*UpdateRecordResponse, error) { - return c.UpdateRecordWithContext(context.Background(), req) -} - -func (c *Client) UpdateRecordWithContext(ctx context.Context, req *UpdateRecordRequest) (*UpdateRecordResponse, error) { - httpreq, err := c.newRequest(http.MethodPut, "/record") - if err != nil { - return nil, err - } else { - httpreq.SetBody(req) - httpreq.SetContext(ctx) - } - - result := &UpdateRecordResponse{} - if _, err := c.doRequestWithResult(httpreq, result); err != nil { - return result, err - } - - return result, nil -} diff --git a/pkg/sdk3rd/dnsla/client.go b/pkg/sdk3rd/dnsla/client.go deleted file mode 100644 index 930e5fb3e..000000000 --- a/pkg/sdk3rd/dnsla/client.go +++ /dev/null @@ -1,96 +0,0 @@ -package dnsla - -import ( - "encoding/json" - "fmt" - "time" - - "github.com/go-resty/resty/v2" - - "github.com/certimate-go/certimate/internal/app" -) - -type Client struct { - client *resty.Client -} - -func NewClient(apiId, apiSecret string) (*Client, error) { - if apiId == "" { - return nil, fmt.Errorf("sdkerr: unset apiId") - } - if apiSecret == "" { - return nil, fmt.Errorf("sdkerr: unset apiSecret") - } - - client := resty.New(). - SetBaseURL("https://api.dns.la/api"). - SetBasicAuth(apiId, apiSecret). - SetHeader("Accept", "application/json"). - SetHeader("Content-Type", "application/json"). - SetHeader("User-Agent", app.AppUserAgent) - - return &Client{client}, nil -} - -func (c *Client) SetTimeout(timeout time.Duration) *Client { - c.client.SetTimeout(timeout) - return c -} - -func (c *Client) newRequest(method string, path string) (*resty.Request, error) { - if method == "" { - return nil, fmt.Errorf("sdkerr: unset method") - } - if path == "" { - return nil, fmt.Errorf("sdkerr: unset path") - } - - req := c.client.R() - req.Method = method - req.URL = path - return req, nil -} - -func (c *Client) doRequest(req *resty.Request) (*resty.Response, error) { - if req == nil { - return nil, fmt.Errorf("sdkerr: nil request") - } - - // WARN: - // PLEASE DO NOT USE `req.SetResult` or `req.SetError` HERE! USE `doRequestWithResult` INSTEAD. - - resp, err := req.Send() - if err != nil { - return resp, fmt.Errorf("sdkerr: failed to send request: %w", err) - } else if resp.IsError() { - return resp, fmt.Errorf("sdkerr: unexpected status code: %d (resp: %s)", resp.StatusCode(), resp.String()) - } - - return resp, nil -} - -func (c *Client) doRequestWithResult(req *resty.Request, res sdkResponse) (*resty.Response, error) { - if req == nil { - return nil, fmt.Errorf("sdkerr: nil request") - } - - resp, err := c.doRequest(req) - if err != nil { - if resp != nil { - json.Unmarshal(resp.Body(), &res) - } - return resp, err - } - - if len(resp.Body()) != 0 { - if err := json.Unmarshal(resp.Body(), &res); err != nil { - return resp, fmt.Errorf("sdkerr: failed to unmarshal response: %w (resp: %s)", err, resp.String()) - } else { - if tcode := res.GetCode(); tcode/100 != 2 { - return resp, fmt.Errorf("sdkerr: code='%d', message='%s'", tcode, res.GetMessage()) - } - } - } - - return resp, nil -} diff --git a/pkg/sdk3rd/dnsla/types.go b/pkg/sdk3rd/dnsla/types.go deleted file mode 100644 index fa8a75687..000000000 --- a/pkg/sdk3rd/dnsla/types.go +++ /dev/null @@ -1,59 +0,0 @@ -package dnsla - -type sdkResponse interface { - GetCode() int - GetMessage() string -} - -type sdkResponseBase struct { - Code *int `json:"code,omitempty"` - Message *string `json:"message,omitempty"` -} - -func (r *sdkResponseBase) GetCode() int { - if r.Code == nil { - return 0 - } - - return *r.Code -} - -func (r *sdkResponseBase) GetMessage() string { - if r.Message == nil { - return "" - } - - return *r.Message -} - -var _ sdkResponse = (*sdkResponseBase)(nil) - -type DomainRecord struct { - Id string `json:"id"` - GroupId string `json:"groupId"` - GroupName string `json:"groupName"` - Domain string `json:"domain"` - DisplayDomain string `json:"displayDomain"` - CreatedAt int64 `json:"createdAt"` - UpdatedAt int64 `json:"updatedAt"` -} - -type DnsRecord struct { - Id string `json:"id"` - DomainId string `json:"domainId"` - GroupId string `json:"groupId"` - GroupName string `json:"groupName"` - LineId string `json:"lineId"` - LineCode string `json:"lineCode"` - LineName string `json:"lineName"` - Type int32 `json:"type"` - Host string `json:"host"` - DisplayHost string `json:"displayHost"` - Data string `json:"data"` - DisplayData string `json:"displayData"` - Ttl int32 `json:"ttl"` - Weight int32 `json:"weight"` - Preference int32 `json:"preference"` - CreatedAt int64 `json:"createdAt"` - UpdatedAt int64 `json:"updatedAt"` -}