Initial commit (code only without large binaries)
This commit is contained in:
413
EdgeAPI/internal/dnsclients/provider_godaddy_plus.go
Normal file
413
EdgeAPI/internal/dnsclients/provider_godaddy_plus.go
Normal file
@@ -0,0 +1,413 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
//go:build plus
|
||||
|
||||
package dnsclients
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
teaconst "github.com/TeaOSLab/EdgeAPI/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/dnstypes"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/godaddy"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
GoDaddyAPIEndpoint = "https://api.godaddy.com/v1"
|
||||
GoDaddyDefaultRoute = "default"
|
||||
GoDaddyIdDelim = "$"
|
||||
GoDaddyDefaultTTL = 600
|
||||
)
|
||||
|
||||
var goDaddyHTTPClient = &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
/**Proxy: func(req *http.Request) (*url.URL, error) {
|
||||
return url.Parse("socks5://127.0.0.1:7890")
|
||||
},**/
|
||||
},
|
||||
}
|
||||
|
||||
// GoDaddyProvider
|
||||
//
|
||||
// 参考文档:https://developer.godaddy.com/doc/endpoint/domains
|
||||
type GoDaddyProvider struct {
|
||||
BaseProvider
|
||||
|
||||
ProviderId int64
|
||||
|
||||
key string
|
||||
secret string
|
||||
}
|
||||
|
||||
// Auth 认证
|
||||
func (this *GoDaddyProvider) Auth(params maps.Map) error {
|
||||
this.key = params.GetString("key")
|
||||
if len(this.key) == 0 {
|
||||
return errors.New("'key' should not be empty")
|
||||
}
|
||||
|
||||
this.secret = params.GetString("secret")
|
||||
if len(this.secret) == 0 {
|
||||
return errors.New("'secret' should not be empty")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MaskParams 对参数进行掩码
|
||||
func (this *GoDaddyProvider) MaskParams(params maps.Map) {
|
||||
if params == nil {
|
||||
return
|
||||
}
|
||||
params["secret"] = MaskString(params.GetString("secret"))
|
||||
}
|
||||
|
||||
// GetDomains 获取所有域名列表
|
||||
func (this *GoDaddyProvider) GetDomains() (domains []string, err error) {
|
||||
var respDomains = godaddy.DomainsResponse{}
|
||||
err = this.doAPI(http.MethodGet, "/domains", nil, &respDomains)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, domain := range respDomains {
|
||||
if domain.Status == "ACTIVE" {
|
||||
domains = append(domains, domain.Domain)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetRecords 获取域名解析记录列表
|
||||
func (this *GoDaddyProvider) GetRecords(domain string) (records []*dnstypes.Record, err error) {
|
||||
var respRecords = godaddy.RecordsResponse{}
|
||||
err = this.doAPI(http.MethodGet, "/domains/"+domain+"/records", nil, &respRecords)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, record := range respRecords {
|
||||
// 修正Record
|
||||
if record.Type == dnstypes.RecordTypeCNAME && !strings.HasSuffix(record.Data, ".") {
|
||||
record.Data += "."
|
||||
}
|
||||
|
||||
var recordObj = &dnstypes.Record{
|
||||
Name: record.Name,
|
||||
Type: record.Type,
|
||||
Value: record.Data,
|
||||
Route: GoDaddyDefaultRoute,
|
||||
TTL: record.TTL,
|
||||
}
|
||||
this.addRecordId(recordObj)
|
||||
records = append(records, recordObj)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetRoutes 读取域名支持的线路数据
|
||||
func (this *GoDaddyProvider) GetRoutes(domain string) (routes []*dnstypes.Route, err error) {
|
||||
routes = []*dnstypes.Route{
|
||||
{Name: "默认", Code: GoDaddyDefaultRoute},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// QueryRecord 查询单个记录
|
||||
func (this *GoDaddyProvider) QueryRecord(domain string, name string, recordType dnstypes.RecordType) (*dnstypes.Record, error) {
|
||||
var respRecords = godaddy.RecordsResponse{}
|
||||
err := this.doAPI(http.MethodGet, "/domains/"+domain+"/records/"+recordType+"/"+name, nil, &respRecords)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, record := range respRecords {
|
||||
// 再次检查名称
|
||||
if record.Name != name {
|
||||
continue
|
||||
}
|
||||
|
||||
// 修正Record
|
||||
if record.Type == dnstypes.RecordTypeCNAME && !strings.HasSuffix(record.Data, ".") {
|
||||
record.Data += "."
|
||||
}
|
||||
|
||||
return &dnstypes.Record{
|
||||
Id: record.Name + GoDaddyIdDelim + record.Type + GoDaddyIdDelim + stringutil.Md5(record.Data),
|
||||
Name: record.Name,
|
||||
Type: record.Type,
|
||||
Value: record.Data,
|
||||
Route: GoDaddyDefaultRoute,
|
||||
TTL: record.TTL,
|
||||
}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// QueryRecords 查询多个记录
|
||||
func (this *GoDaddyProvider) QueryRecords(domain string, name string, recordType dnstypes.RecordType) ([]*dnstypes.Record, error) {
|
||||
var respRecords = godaddy.RecordsResponse{}
|
||||
err := this.doAPI(http.MethodGet, "/domains/"+domain+"/records/"+recordType+"/"+name, nil, &respRecords)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result = []*dnstypes.Record{}
|
||||
for _, record := range respRecords {
|
||||
// 再次检查名称
|
||||
if record.Name != name {
|
||||
continue
|
||||
}
|
||||
|
||||
// 修正Record
|
||||
if record.Type == dnstypes.RecordTypeCNAME && !strings.HasSuffix(record.Data, ".") {
|
||||
record.Data += "."
|
||||
}
|
||||
|
||||
result = append(result, &dnstypes.Record{
|
||||
Id: record.Name + GoDaddyIdDelim + record.Type + GoDaddyIdDelim + stringutil.Md5(record.Data),
|
||||
Name: record.Name,
|
||||
Type: record.Type,
|
||||
Value: record.Data,
|
||||
Route: GoDaddyDefaultRoute,
|
||||
TTL: record.TTL,
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// AddRecord 设置记录
|
||||
func (this *GoDaddyProvider) AddRecord(domain string, newRecord *dnstypes.Record) error {
|
||||
if newRecord.TTL <= 0 {
|
||||
newRecord.TTL = GoDaddyDefaultTTL
|
||||
}
|
||||
if newRecord.Type == dnstypes.RecordTypeCNAME {
|
||||
if !strings.HasSuffix(newRecord.Value, ".") {
|
||||
newRecord.Value += "."
|
||||
}
|
||||
}
|
||||
var recordMaps = []maps.Map{
|
||||
{
|
||||
"data": newRecord.Value,
|
||||
"name": newRecord.Name,
|
||||
"ttl": newRecord.TTL,
|
||||
"type": newRecord.Type,
|
||||
"priority": 0,
|
||||
"weight": 0,
|
||||
"port": 65535,
|
||||
},
|
||||
}
|
||||
recordMapsJSON, err := json.Marshal(recordMaps)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encode records failed: %w", err)
|
||||
}
|
||||
|
||||
err = this.doAPI(http.MethodPatch, "/domains/"+domain+"/records", recordMapsJSON, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
this.addRecordId(newRecord)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateRecord 修改记录
|
||||
func (this *GoDaddyProvider) UpdateRecord(domain string, record *dnstypes.Record, newRecord *dnstypes.Record) error {
|
||||
var recordType = record.Type
|
||||
var recordName = record.Name
|
||||
var recordValueMd5 = stringutil.Md5(record.Value)
|
||||
|
||||
if len(recordType) == 0 || len(recordName) == 0 {
|
||||
if len(record.Id) == 0 {
|
||||
return errors.New("invalid record to delete")
|
||||
}
|
||||
recordName, recordType, recordValueMd5 = this.splitRecordId(record.Id)
|
||||
if len(recordType) == 0 || len(recordName) == 0 {
|
||||
return errors.New("invalid record to delete")
|
||||
}
|
||||
}
|
||||
|
||||
var respRecords = godaddy.RecordsResponse{}
|
||||
err := this.doAPI(http.MethodGet, "/domains/"+domain+"/records/"+recordType+"/"+recordName, nil, &respRecords)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
this.addRecordId(newRecord)
|
||||
|
||||
var found = false
|
||||
for index, gRecord := range respRecords {
|
||||
var gRecordValue = gRecord.Data
|
||||
if gRecord.Type == dnstypes.RecordTypeCNAME {
|
||||
if !strings.HasSuffix(gRecordValue, ".") {
|
||||
gRecordValue += "."
|
||||
}
|
||||
}
|
||||
if gRecord.Name == recordName && gRecord.Type == recordType && stringutil.Md5(gRecordValue) == recordValueMd5 {
|
||||
gRecord.Name = newRecord.Name
|
||||
|
||||
if newRecord.Type == dnstypes.RecordTypeCNAME && !strings.HasSuffix(newRecord.Value, ".") {
|
||||
newRecord.Value += "."
|
||||
}
|
||||
gRecord.Data = newRecord.Value
|
||||
|
||||
gRecord.Type = newRecord.Type
|
||||
gRecord.TTL = newRecord.TTL
|
||||
|
||||
if newRecord.TTL <= 0 {
|
||||
gRecord.TTL = GoDaddyDefaultTTL
|
||||
}
|
||||
|
||||
respRecords[index] = gRecord
|
||||
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
newRecordsJSON, err := json.Marshal(respRecords)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = this.doAPI(http.MethodPut, "/domains/"+domain+"/records/"+recordType+"/"+recordName, newRecordsJSON, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteRecord 删除记录
|
||||
func (this *GoDaddyProvider) DeleteRecord(domain string, record *dnstypes.Record) error {
|
||||
var recordType = record.Type
|
||||
var recordName = record.Name
|
||||
var recordValueMd5 = stringutil.Md5(record.Value)
|
||||
|
||||
if len(recordType) == 0 || len(recordName) == 0 {
|
||||
if len(record.Id) == 0 {
|
||||
return errors.New("invalid record to delete")
|
||||
}
|
||||
recordName, recordType, recordValueMd5 = this.splitRecordId(record.Id)
|
||||
if len(recordType) == 0 || len(recordName) == 0 {
|
||||
return errors.New("invalid record to delete")
|
||||
}
|
||||
}
|
||||
|
||||
var respRecords = godaddy.RecordsResponse{}
|
||||
err := this.doAPI(http.MethodGet, "/domains/"+domain+"/records/"+recordType+"/"+recordName, nil, &respRecords)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var newRecords = godaddy.RecordsResponse{}
|
||||
for _, gRecord := range respRecords {
|
||||
var gRecordValue = gRecord.Data
|
||||
if gRecord.Type == dnstypes.RecordTypeCNAME {
|
||||
if !strings.HasSuffix(gRecordValue, ".") {
|
||||
gRecordValue += "."
|
||||
}
|
||||
}
|
||||
if gRecord.Name == recordName && gRecord.Type == recordType && stringutil.Md5(gRecordValue) == recordValueMd5 {
|
||||
continue
|
||||
}
|
||||
newRecords = append(newRecords, gRecord)
|
||||
}
|
||||
|
||||
if len(newRecords) > 0 {
|
||||
newRecordsJSON, err := json.Marshal(newRecords)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = this.doAPI(http.MethodPut, "/domains/"+domain+"/records/"+recordType+"/"+recordName, newRecordsJSON, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = this.doAPI(http.MethodDelete, "/domains/"+domain+"/records/"+recordType+"/"+recordName, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DefaultRoute 默认线路
|
||||
func (this *GoDaddyProvider) DefaultRoute() string {
|
||||
return GoDaddyDefaultRoute
|
||||
}
|
||||
|
||||
func (this *GoDaddyProvider) addRecordId(record *dnstypes.Record) {
|
||||
record.Id = record.Name + GoDaddyIdDelim + record.Type + GoDaddyIdDelim + stringutil.Md5(record.Value)
|
||||
}
|
||||
|
||||
func (this *GoDaddyProvider) splitRecordId(recordId string) (recordName string, recordType string, valueMd5 string) {
|
||||
var pieces = strings.Split(recordId, GoDaddyIdDelim)
|
||||
if len(pieces) < 3 {
|
||||
return
|
||||
}
|
||||
return pieces[0], pieces[1], pieces[2]
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
func (this *GoDaddyProvider) doAPI(method string, apiPath string, bodyJSON []byte, respPtr interface{}) error {
|
||||
apiURL := GoDaddyAPIEndpoint + apiPath
|
||||
method = strings.ToUpper(method)
|
||||
|
||||
var bodyReader io.Reader = nil
|
||||
if len(bodyJSON) > 0 {
|
||||
bodyReader = bytes.NewReader(bodyJSON)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, apiURL, bodyReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("User-Agent", teaconst.ProductName+"/"+teaconst.Version)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "sso-key "+this.key+":"+this.secret)
|
||||
resp, err := goDaddyHTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode == 0 {
|
||||
return errors.New("invalid response status '" + strconv.Itoa(resp.StatusCode) + "', response '" + string(data) + "'")
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return errors.New("response error: " + string(data))
|
||||
}
|
||||
|
||||
if respPtr != nil {
|
||||
err = json.Unmarshal(data, respPtr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decode json failed: %w, response text: %s", err, string(data))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user