Files
havline-native/app-src/internal/ddns/tencentcloud.go
T

187 lines
5.4 KiB
Go

package ddns
import (
"context"
"errors"
"fmt"
"strings"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
errorsdk "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
dnspod "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod/v20210323"
)
type TencentCloud struct{}
func NewTencentCloud() *TencentCloud { return &TencentCloud{} }
func (t *TencentCloud) Name() string { return "tencentcloud" }
func (t *TencentCloud) Verify(ctx context.Context, cred Credentials) error {
client, err := t.client(cred)
if err != nil {
return err
}
req := dnspod.NewDescribeDomainListRequest()
req.Limit = common.Int64Ptr(1)
_, err = client.DescribeDomainList(req)
return wrapTencentErr(err)
}
func (t *TencentCloud) HasZone(ctx context.Context, cred Credentials, zone string) (bool, error) {
client, err := t.client(cred)
if err != nil {
return false, err
}
_, err = t.domainInfo(client, zone)
return zoneLookupOK(wrapTencentErr(err))
}
func (t *TencentCloud) GetRecordIP(ctx context.Context, cred Credentials, rootDomain, recordName, recordType string) (string, error) {
client, err := t.client(cred)
if err != nil {
return "", err
}
zone, err := t.domainInfo(client, rootDomain)
if err != nil {
return "", err
}
records, err := t.listRecords(client, zone, tencentSubdomain(recordName), recordType)
if err != nil {
return "", err
}
for _, rec := range records {
if rec.Value != nil {
return *rec.Value, nil
}
}
return "", nil
}
func (t *TencentCloud) UpdateRecord(ctx context.Context, cred Credentials, rootDomain, recordName, recordType, ip string) error {
client, err := t.client(cred)
if err != nil {
return err
}
zone, err := t.domainInfo(client, rootDomain)
if err != nil {
return err
}
subDomain := tencentSubdomain(recordName)
records, err := t.listRecords(client, zone, subDomain, recordType)
if err != nil {
return err
}
if len(records) > 0 && records[0].RecordId != nil {
req := dnspod.NewModifyRecordRequest()
req.Domain = zone.Name
req.DomainId = zone.DomainId
req.RecordId = records[0].RecordId
req.SubDomain = common.StringPtr(subDomain)
req.RecordType = common.StringPtr(recordType)
req.RecordLine = common.StringPtr(tencentRecordLine(records[0]))
req.Value = common.StringPtr(ip)
req.TTL = common.Uint64Ptr(600)
_, err = client.ModifyRecord(req)
return wrapTencentErr(err)
}
req := dnspod.NewCreateRecordRequest()
req.Domain = zone.Name
req.DomainId = zone.DomainId
req.SubDomain = common.StringPtr(subDomain)
req.RecordType = common.StringPtr(recordType)
req.RecordLine = common.StringPtr("默认")
req.Value = common.StringPtr(ip)
req.TTL = common.Uint64Ptr(600)
_, err = client.CreateRecord(req)
return wrapTencentErr(err)
}
func (t *TencentCloud) client(cred Credentials) (*dnspod.Client, error) {
if cred.Token == "" || cred.Secret == "" {
return nil, fmt.Errorf("腾讯云 DNS 凭证未配置")
}
credential := common.NewCredential(cred.Token, cred.Secret)
cpf := profile.NewClientProfile()
cpf.HttpProfile.Endpoint = "dnspod.tencentcloudapi.com"
return dnspod.NewClient(credential, "", cpf)
}
func (t *TencentCloud) domainInfo(client *dnspod.Client, rootDomain string) (*dnspod.DomainListItem, error) {
rootDomain = strings.ToLower(strings.TrimSpace(rootDomain))
req := dnspod.NewDescribeDomainListRequest()
var offset int64
for {
req.Offset = common.Int64Ptr(offset)
resp, err := client.DescribeDomainList(req)
if err != nil {
return nil, wrapTencentErr(err)
}
for _, zone := range resp.Response.DomainList {
if zone.Name != nil && strings.EqualFold(*zone.Name, rootDomain) {
return zone, nil
}
if zone.Punycode != nil && strings.EqualFold(*zone.Punycode, rootDomain) {
return zone, nil
}
}
total := int64(0)
if resp.Response.DomainCountInfo != nil && resp.Response.DomainCountInfo.AllTotal != nil {
total = int64(*resp.Response.DomainCountInfo.AllTotal)
}
offset += int64(len(resp.Response.DomainList))
if offset >= total || len(resp.Response.DomainList) == 0 {
break
}
}
return nil, fmt.Errorf("未找到域名 %s 对应的腾讯云 DNS 域名", rootDomain)
}
func (t *TencentCloud) listRecords(client *dnspod.Client, zone *dnspod.DomainListItem, subDomain, recordType string) ([]*dnspod.RecordListItem, error) {
req := dnspod.NewDescribeRecordListRequest()
req.Domain = zone.Name
req.DomainId = zone.DomainId
req.Subdomain = common.StringPtr(subDomain)
req.RecordType = common.StringPtr(recordType)
resp, err := client.DescribeRecordList(req)
if err != nil {
var sdkErr *errorsdk.TencentCloudSDKError
if errors.As(err, &sdkErr) && sdkErr.Code == dnspod.RESOURCENOTFOUND_NODATAOFRECORD {
return nil, nil
}
return nil, wrapTencentErr(err)
}
if resp.Response == nil {
return nil, nil
}
return resp.Response.RecordList, nil
}
func tencentSubdomain(recordName string) string {
sub := subDomainForRecord(recordName)
if sub == "@" {
return "@"
}
return sub
}
func tencentRecordLine(rec *dnspod.RecordListItem) string {
if rec != nil && rec.Line != nil && *rec.Line != "" {
return *rec.Line
}
return "默认"
}
func wrapTencentErr(err error) error {
if err == nil {
return nil
}
var sdkErr *errorsdk.TencentCloudSDKError
if errors.As(err, &sdkErr) {
return fmt.Errorf("腾讯云 DNS API 错误:%s", sdkErr.Message)
}
return err
}