mirror of
https://github.com/TeaOSLab/EdgeAPI.git
synced 2025-11-03 23:20:26 +08:00
域名解析增加CloudFlare DNS支持
This commit is contained in:
22
internal/dnsclients/cloudflare/response_base.go
Normal file
22
internal/dnsclients/cloudflare/response_base.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
type BaseResponse struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Errors []struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
} `json:"errors"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *BaseResponse) IsOk() bool {
|
||||||
|
return this.Success
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *BaseResponse) LastError() (code int, message string) {
|
||||||
|
if len(this.Errors) == 0 {
|
||||||
|
return 0, ""
|
||||||
|
}
|
||||||
|
return this.Errors[0].Code, this.Errors[0].Message
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
type CreateDNSRecordResponse struct {
|
||||||
|
BaseResponse
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
type DeleteDNSRecordResponse struct {
|
||||||
|
BaseResponse
|
||||||
|
}
|
||||||
17
internal/dnsclients/cloudflare/response_get_dns_records.go
Normal file
17
internal/dnsclients/cloudflare/response_get_dns_records.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
type GetDNSRecordsResponse struct {
|
||||||
|
BaseResponse
|
||||||
|
|
||||||
|
Result []struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
Ttl int `json:"ttl"`
|
||||||
|
ZoneId string `json:"zoneId"`
|
||||||
|
ZoneName string `json:"zoneName"`
|
||||||
|
} `json:"result"`
|
||||||
|
}
|
||||||
8
internal/dnsclients/cloudflare/response_interface.go
Normal file
8
internal/dnsclients/cloudflare/response_interface.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
type ResponseInterface interface {
|
||||||
|
IsOk() bool
|
||||||
|
LastError() (code int, message string)
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
type UpdateDNSRecordResponse struct {
|
||||||
|
BaseResponse
|
||||||
|
}
|
||||||
12
internal/dnsclients/cloudflare/response_zones.go
Normal file
12
internal/dnsclients/cloudflare/response_zones.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
type ZonesResponse struct {
|
||||||
|
BaseResponse
|
||||||
|
|
||||||
|
Result []struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
} `json:"result"`
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 阿里云服务商
|
// AliDNSProvider 阿里云服务商
|
||||||
type AliDNSProvider struct {
|
type AliDNSProvider struct {
|
||||||
BaseProvider
|
BaseProvider
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ type AliDNSProvider struct {
|
|||||||
accessKeySecret string
|
accessKeySecret string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 认证
|
// Auth 认证
|
||||||
func (this *AliDNSProvider) Auth(params maps.Map) error {
|
func (this *AliDNSProvider) Auth(params maps.Map) error {
|
||||||
this.accessKeyId = params.GetString("accessKeyId")
|
this.accessKeyId = params.GetString("accessKeyId")
|
||||||
this.accessKeySecret = params.GetString("accessKeySecret")
|
this.accessKeySecret = params.GetString("accessKeySecret")
|
||||||
@@ -30,7 +30,7 @@ func (this *AliDNSProvider) Auth(params maps.Map) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取域名列表
|
// GetRecords 获取域名列表
|
||||||
func (this *AliDNSProvider) GetRecords(domain string) (records []*Record, err error) {
|
func (this *AliDNSProvider) GetRecords(domain string) (records []*Record, err error) {
|
||||||
pageNumber := 1
|
pageNumber := 1
|
||||||
size := 100
|
size := 100
|
||||||
@@ -70,7 +70,7 @@ func (this *AliDNSProvider) GetRecords(domain string) (records []*Record, err er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取域名支持的线路数据
|
// GetRoutes 读取域名支持的线路数据
|
||||||
func (this *AliDNSProvider) GetRoutes(domain string) (routes []*Route, err error) {
|
func (this *AliDNSProvider) GetRoutes(domain string) (routes []*Route, err error) {
|
||||||
req := alidns.CreateDescribeSupportLinesRequest()
|
req := alidns.CreateDescribeSupportLinesRequest()
|
||||||
req.DomainName = domain
|
req.DomainName = domain
|
||||||
@@ -89,7 +89,7 @@ func (this *AliDNSProvider) GetRoutes(domain string) (routes []*Route, err error
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询单个记录
|
// QueryRecord 查询单个记录
|
||||||
func (this *AliDNSProvider) QueryRecord(domain string, name string, recordType RecordType) (*Record, error) {
|
func (this *AliDNSProvider) QueryRecord(domain string, name string, recordType RecordType) (*Record, error) {
|
||||||
records, err := this.GetRecords(domain)
|
records, err := this.GetRecords(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -103,7 +103,7 @@ func (this *AliDNSProvider) QueryRecord(domain string, name string, recordType R
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置记录
|
// AddRecord 设置记录
|
||||||
func (this *AliDNSProvider) AddRecord(domain string, newRecord *Record) error {
|
func (this *AliDNSProvider) AddRecord(domain string, newRecord *Record) error {
|
||||||
req := alidns.CreateAddDomainRecordRequest()
|
req := alidns.CreateAddDomainRecordRequest()
|
||||||
req.RR = newRecord.Name
|
req.RR = newRecord.Name
|
||||||
@@ -124,7 +124,7 @@ func (this *AliDNSProvider) AddRecord(domain string, newRecord *Record) error {
|
|||||||
return errors.New(resp.GetHttpContentString())
|
return errors.New(resp.GetHttpContentString())
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改记录
|
// UpdateRecord 修改记录
|
||||||
func (this *AliDNSProvider) UpdateRecord(domain string, record *Record, newRecord *Record) error {
|
func (this *AliDNSProvider) UpdateRecord(domain string, record *Record, newRecord *Record) error {
|
||||||
req := alidns.CreateUpdateDomainRecordRequest()
|
req := alidns.CreateUpdateDomainRecordRequest()
|
||||||
req.RecordId = record.Id
|
req.RecordId = record.Id
|
||||||
@@ -138,7 +138,7 @@ func (this *AliDNSProvider) UpdateRecord(domain string, record *Record, newRecor
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除记录
|
// DeleteRecord 删除记录
|
||||||
func (this *AliDNSProvider) DeleteRecord(domain string, record *Record) error {
|
func (this *AliDNSProvider) DeleteRecord(domain string, record *Record) error {
|
||||||
req := alidns.CreateDeleteDomainRecordRequest()
|
req := alidns.CreateDeleteDomainRecordRequest()
|
||||||
req.RecordId = record.Id
|
req.RecordId = record.Id
|
||||||
@@ -148,7 +148,7 @@ func (this *AliDNSProvider) DeleteRecord(domain string, record *Record) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 默认线路
|
// DefaultRoute 默认线路
|
||||||
func (this *AliDNSProvider) DefaultRoute() string {
|
func (this *AliDNSProvider) DefaultRoute() string {
|
||||||
return "default"
|
return "default"
|
||||||
}
|
}
|
||||||
|
|||||||
288
internal/dnsclients/provider_cloud_flare.go
Normal file
288
internal/dnsclients/provider_cloud_flare.go
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package dnsclients
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/cloudflare"
|
||||||
|
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const CloudFlareAPIEndpoint = "https://api.cloudflare.com/client/v4/"
|
||||||
|
const CloudFlareDefaultRoute = "default"
|
||||||
|
|
||||||
|
var cloudFlareHTTPClient = &http.Client{
|
||||||
|
Timeout: 10 * time.Second,
|
||||||
|
Transport: &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloudFlareProvider struct {
|
||||||
|
BaseProvider
|
||||||
|
|
||||||
|
apiKey string // API密钥
|
||||||
|
email string // 账号邮箱
|
||||||
|
|
||||||
|
zoneMap map[string]string // domain => zoneId
|
||||||
|
zoneLocker sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auth 认证
|
||||||
|
func (this *CloudFlareProvider) Auth(params maps.Map) error {
|
||||||
|
this.apiKey = params.GetString("apiKey")
|
||||||
|
if len(this.apiKey) == 0 {
|
||||||
|
return errors.New("'apiKey' should not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
this.email = params.GetString("email")
|
||||||
|
if len(this.email) == 0 {
|
||||||
|
return errors.New("'email' should not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
this.zoneMap = map[string]string{}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRecords 获取域名解析记录列表
|
||||||
|
func (this *CloudFlareProvider) GetRecords(domain string) (records []*Record, err error) {
|
||||||
|
zoneId, err := this.findZoneIdWithDomain(domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 这个页数限制预示着每次最多只能获取 500 * 100 即5万个数据
|
||||||
|
for page := 1; page <= 500; page++ {
|
||||||
|
resp := new(cloudflare.GetDNSRecordsResponse)
|
||||||
|
err = this.doAPI(http.MethodGet, "zones/"+zoneId+"/dns_records", map[string]string{
|
||||||
|
"per_page": "100",
|
||||||
|
"page": strconv.Itoa(page),
|
||||||
|
}, nil, resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(resp.Result) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, record := range resp.Result {
|
||||||
|
// 修正Record
|
||||||
|
if record.Type == RecordTypeCName && !strings.HasSuffix(record.Content, ".") {
|
||||||
|
record.Content += "."
|
||||||
|
}
|
||||||
|
|
||||||
|
record.Name = strings.TrimSuffix(record.Name, "."+domain)
|
||||||
|
|
||||||
|
records = append(records, &Record{
|
||||||
|
Id: record.Id,
|
||||||
|
Name: record.Name,
|
||||||
|
Type: record.Type,
|
||||||
|
Value: record.Content,
|
||||||
|
Route: CloudFlareDefaultRoute,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRoutes 读取域名支持的线路数据
|
||||||
|
func (this *CloudFlareProvider) GetRoutes(domain string) (routes []*Route, err error) {
|
||||||
|
routes = []*Route{
|
||||||
|
{Name: "默认", Code: CloudFlareDefaultRoute},
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRecord 查询单个记录
|
||||||
|
func (this *CloudFlareProvider) QueryRecord(domain string, name string, recordType RecordType) (*Record, error) {
|
||||||
|
zoneId, err := this.findZoneIdWithDomain(domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := new(cloudflare.GetDNSRecordsResponse)
|
||||||
|
err = this.doAPI(http.MethodGet, "zones/"+zoneId+"/dns_records", map[string]string{
|
||||||
|
"per_page": "100",
|
||||||
|
"name": name + "." + domain,
|
||||||
|
"type": recordType,
|
||||||
|
}, nil, resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(resp.Result) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
record := resp.Result[0]
|
||||||
|
|
||||||
|
// 修正Record
|
||||||
|
if record.Type == RecordTypeCName && !strings.HasSuffix(record.Content, ".") {
|
||||||
|
record.Content += "."
|
||||||
|
}
|
||||||
|
|
||||||
|
record.Name = strings.TrimSuffix(record.Name, "."+domain)
|
||||||
|
|
||||||
|
return &Record{
|
||||||
|
Id: record.Id,
|
||||||
|
Name: record.Name,
|
||||||
|
Type: record.Type,
|
||||||
|
Value: record.Content,
|
||||||
|
Route: CloudFlareDefaultRoute,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRecord 设置记录
|
||||||
|
func (this *CloudFlareProvider) AddRecord(domain string, newRecord *Record) error {
|
||||||
|
zoneId, err := this.findZoneIdWithDomain(domain)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := new(cloudflare.CreateDNSRecordResponse)
|
||||||
|
err = this.doAPI(http.MethodPost, "zones/"+zoneId+"/dns_records", nil, maps.Map{
|
||||||
|
"type": newRecord.Type,
|
||||||
|
"name": newRecord.Name + "." + domain,
|
||||||
|
"content": newRecord.Value,
|
||||||
|
"ttl": 1,
|
||||||
|
}, resp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateRecord 修改记录
|
||||||
|
func (this *CloudFlareProvider) UpdateRecord(domain string, record *Record, newRecord *Record) error {
|
||||||
|
zoneId, err := this.findZoneIdWithDomain(domain)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := new(cloudflare.UpdateDNSRecordResponse)
|
||||||
|
return this.doAPI(http.MethodPut, "zones/"+zoneId+"/dns_records/"+record.Id, nil, maps.Map{
|
||||||
|
"type": newRecord.Type,
|
||||||
|
"name": newRecord.Name + "." + domain,
|
||||||
|
"content": newRecord.Value,
|
||||||
|
"ttl": 1,
|
||||||
|
}, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRecord 删除记录
|
||||||
|
func (this *CloudFlareProvider) DeleteRecord(domain string, record *Record) error {
|
||||||
|
zoneId, err := this.findZoneIdWithDomain(domain)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := new(cloudflare.DeleteDNSRecordResponse)
|
||||||
|
err = this.doAPI(http.MethodDelete, "zones/"+zoneId+"/dns_records/"+record.Id, map[string]string{}, nil, resp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultRoute 默认线路
|
||||||
|
func (this *CloudFlareProvider) DefaultRoute() string {
|
||||||
|
return CloudFlareDefaultRoute
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行API
|
||||||
|
func (this *CloudFlareProvider) doAPI(method string, apiPath string, args map[string]string, bodyMap maps.Map, respPtr cloudflare.ResponseInterface) error {
|
||||||
|
apiURL := CloudFlareAPIEndpoint + strings.TrimLeft(apiPath, "/")
|
||||||
|
if len(args) > 0 {
|
||||||
|
apiURL += "?"
|
||||||
|
argStrings := []string{}
|
||||||
|
for k, v := range args {
|
||||||
|
argStrings = append(argStrings, k+"="+url.QueryEscape(v))
|
||||||
|
}
|
||||||
|
apiURL += strings.Join(argStrings, "&")
|
||||||
|
}
|
||||||
|
method = strings.ToUpper(method)
|
||||||
|
|
||||||
|
var bodyReader io.Reader = nil
|
||||||
|
if bodyMap != nil {
|
||||||
|
bodyData, err := json.Marshal(bodyMap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bodyReader = bytes.NewReader(bodyData)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(method, apiURL, bodyReader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("X-Auth-Key", this.apiKey)
|
||||||
|
req.Header.Set("x-Auth-Email", this.email)
|
||||||
|
resp, err := cloudFlareHTTPClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
data, err := ioutil.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) + "'")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(data, respPtr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return errors.New("response error: " + string(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列出一个域名对应的区域
|
||||||
|
func (this *CloudFlareProvider) findZoneIdWithDomain(domain string) (zoneId string, err error) {
|
||||||
|
this.zoneLocker.Lock()
|
||||||
|
cacheZonedId, ok := this.zoneMap[domain]
|
||||||
|
if ok {
|
||||||
|
this.zoneLocker.Unlock()
|
||||||
|
return cacheZonedId, nil
|
||||||
|
}
|
||||||
|
this.zoneLocker.Unlock()
|
||||||
|
|
||||||
|
resp := new(cloudflare.ZonesResponse)
|
||||||
|
err = this.doAPI(http.MethodGet, "zones", map[string]string{
|
||||||
|
"name": domain,
|
||||||
|
}, nil, resp)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if len(resp.Result) == 0 {
|
||||||
|
return "", errors.New("can not found zone for domain '" + domain + "'")
|
||||||
|
}
|
||||||
|
zoneId = resp.Result[0].Id
|
||||||
|
this.zoneLocker.Lock()
|
||||||
|
this.zoneMap[domain] = zoneId
|
||||||
|
this.zoneLocker.Unlock()
|
||||||
|
return zoneId, nil
|
||||||
|
}
|
||||||
167
internal/dnsclients/provider_cloud_flare_test.go
Normal file
167
internal/dnsclients/provider_cloud_flare_test.go
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package dnsclients
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||||
|
"github.com/iwind/TeaGo/dbs"
|
||||||
|
"github.com/iwind/TeaGo/logs"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCloudFlareProvider_GetRecords(t *testing.T) {
|
||||||
|
provider, err := testCloudFlareProvider()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("===meloy.cn===")
|
||||||
|
{
|
||||||
|
records, err := provider.GetRecords("meloy.cn")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(records) > 0 {
|
||||||
|
t.Log(len(records), "records")
|
||||||
|
}
|
||||||
|
logs.PrintAsJSON(records, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("===teaos.cn===")
|
||||||
|
{
|
||||||
|
records, err := provider.GetRecords("teaos.cn")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
logs.PrintAsJSON(records, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloudFlareProvider_QueryRecord(t *testing.T) {
|
||||||
|
provider, err := testCloudFlareProvider()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
t.Log("== www.meloy.cn/A ==")
|
||||||
|
record, err := provider.QueryRecord("meloy.cn", "www", RecordTypeA)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
logs.PrintAsJSON(record, t)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
t.Log("== www.meloy.cn/CNAME ==")
|
||||||
|
record, err := provider.QueryRecord("meloy.cn", "www", RecordTypeCName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
logs.PrintAsJSON(record, t)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
t.Log("== hello.meloy.cn ==")
|
||||||
|
record, err := provider.QueryRecord("meloy.cn", "hello", RecordTypeA)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
logs.PrintAsJSON(record, t)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
t.Log("== test.meloy.cn ==")
|
||||||
|
record, err := provider.QueryRecord("meloy.cn", "test", RecordTypeCName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
logs.PrintAsJSON(record, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloudFlareProvider_AddRecord(t *testing.T) {
|
||||||
|
provider, err := testCloudFlareProvider()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
err = provider.AddRecord("meloy.cn", &Record{
|
||||||
|
Id: "",
|
||||||
|
Name: "test",
|
||||||
|
Type: RecordTypeA,
|
||||||
|
Value: "182.92.212.46",
|
||||||
|
Route: "",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
err = provider.AddRecord("meloy.cn", &Record{
|
||||||
|
Id: "",
|
||||||
|
Name: "test1",
|
||||||
|
Type: RecordTypeCName,
|
||||||
|
Value: "cdn.meloy.cn.",
|
||||||
|
Route: "",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Log("ok")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloudFlareProvider_UpdateRecord(t *testing.T) {
|
||||||
|
provider, err := testCloudFlareProvider()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = provider.UpdateRecord("meloy.cn", &Record{Id: "b4da7ad9f90173ec37c80ba6bb70641a"}, &Record{
|
||||||
|
Id: "",
|
||||||
|
Name: "test1",
|
||||||
|
Type: RecordTypeCName,
|
||||||
|
Value: "cdn123.meloy.cn.",
|
||||||
|
Route: "",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("ok")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloudFlareProvider_DeleteRecord(t *testing.T) {
|
||||||
|
provider, err := testCloudFlareProvider()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = provider.DeleteRecord("meloy.cn", &Record{
|
||||||
|
Id: "86282d89bbd1f66a69ca409da84f34b1",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("ok")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCloudFlareProvider() (ProviderInterface, error) {
|
||||||
|
db, err := dbs.Default()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
one, err := db.FindOne("SELECT * FROM edgeDNSProviders WHERE type='cloudFlare' ORDER BY id DESC")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if one == nil {
|
||||||
|
return nil, errors.New("can not find providers with type 'cloudFlare'")
|
||||||
|
}
|
||||||
|
apiParams := maps.Map{}
|
||||||
|
err = json.Unmarshal([]byte(one.GetString("apiParams")), &apiParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
provider := &CloudFlareProvider{}
|
||||||
|
err = provider.Auth(apiParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return provider, nil
|
||||||
|
}
|
||||||
@@ -24,13 +24,13 @@ var customHTTPClient = &http.Client{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP自定义DNS
|
// CustomHTTPProvider HTTP自定义DNS
|
||||||
type CustomHTTPProvider struct {
|
type CustomHTTPProvider struct {
|
||||||
url string
|
url string
|
||||||
secret string
|
secret string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 认证
|
// Auth 认证
|
||||||
// 参数:
|
// 参数:
|
||||||
// - url
|
// - url
|
||||||
// - secret
|
// - secret
|
||||||
@@ -48,7 +48,7 @@ func (this *CustomHTTPProvider) Auth(params maps.Map) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取域名解析记录列表
|
// GetRecords 获取域名解析记录列表
|
||||||
func (this *CustomHTTPProvider) GetRecords(domain string) (records []*Record, err error) {
|
func (this *CustomHTTPProvider) GetRecords(domain string) (records []*Record, err error) {
|
||||||
resp, err := this.post(maps.Map{
|
resp, err := this.post(maps.Map{
|
||||||
"action": "GetRecords",
|
"action": "GetRecords",
|
||||||
@@ -61,7 +61,7 @@ func (this *CustomHTTPProvider) GetRecords(domain string) (records []*Record, er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取域名支持的线路数据
|
// GetRoutes 读取域名支持的线路数据
|
||||||
func (this *CustomHTTPProvider) GetRoutes(domain string) (routes []*Route, err error) {
|
func (this *CustomHTTPProvider) GetRoutes(domain string) (routes []*Route, err error) {
|
||||||
resp, err := this.post(maps.Map{
|
resp, err := this.post(maps.Map{
|
||||||
"action": "GetRoutes",
|
"action": "GetRoutes",
|
||||||
@@ -74,7 +74,7 @@ func (this *CustomHTTPProvider) GetRoutes(domain string) (routes []*Route, err e
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询单个记录
|
// QueryRecord 查询单个记录
|
||||||
func (this *CustomHTTPProvider) QueryRecord(domain string, name string, recordType RecordType) (*Record, error) {
|
func (this *CustomHTTPProvider) QueryRecord(domain string, name string, recordType RecordType) (*Record, error) {
|
||||||
resp, err := this.post(maps.Map{
|
resp, err := this.post(maps.Map{
|
||||||
"action": "QueryRecord",
|
"action": "QueryRecord",
|
||||||
@@ -99,7 +99,7 @@ func (this *CustomHTTPProvider) QueryRecord(domain string, name string, recordTy
|
|||||||
return record, nil
|
return record, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置记录
|
// AddRecord 设置记录
|
||||||
func (this *CustomHTTPProvider) AddRecord(domain string, newRecord *Record) error {
|
func (this *CustomHTTPProvider) AddRecord(domain string, newRecord *Record) error {
|
||||||
_, err := this.post(maps.Map{
|
_, err := this.post(maps.Map{
|
||||||
"action": "AddRecord",
|
"action": "AddRecord",
|
||||||
@@ -109,7 +109,7 @@ func (this *CustomHTTPProvider) AddRecord(domain string, newRecord *Record) erro
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改记录
|
// UpdateRecord 修改记录
|
||||||
func (this *CustomHTTPProvider) UpdateRecord(domain string, record *Record, newRecord *Record) error {
|
func (this *CustomHTTPProvider) UpdateRecord(domain string, record *Record, newRecord *Record) error {
|
||||||
_, err := this.post(maps.Map{
|
_, err := this.post(maps.Map{
|
||||||
"action": "UpdateRecord",
|
"action": "UpdateRecord",
|
||||||
@@ -120,7 +120,7 @@ func (this *CustomHTTPProvider) UpdateRecord(domain string, record *Record, newR
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除记录
|
// DeleteRecord 删除记录
|
||||||
func (this *CustomHTTPProvider) DeleteRecord(domain string, record *Record) error {
|
func (this *CustomHTTPProvider) DeleteRecord(domain string, record *Record) error {
|
||||||
_, err := this.post(maps.Map{
|
_, err := this.post(maps.Map{
|
||||||
"action": "DeleteRecord",
|
"action": "DeleteRecord",
|
||||||
@@ -130,7 +130,7 @@ func (this *CustomHTTPProvider) DeleteRecord(domain string, record *Record) erro
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 默认线路
|
// DefaultRoute 默认线路
|
||||||
func (this *CustomHTTPProvider) DefaultRoute() string {
|
func (this *CustomHTTPProvider) DefaultRoute() string {
|
||||||
resp, err := this.post(maps.Map{
|
resp, err := this.post(maps.Map{
|
||||||
"action": "DefaultRoute",
|
"action": "DefaultRoute",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DNSPod服务商
|
// DNSPodProvider DNSPod服务商
|
||||||
type DNSPodProvider struct {
|
type DNSPodProvider struct {
|
||||||
BaseProvider
|
BaseProvider
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ type DNSPodProvider struct {
|
|||||||
apiToken string
|
apiToken string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 认证
|
// Auth 认证
|
||||||
func (this *DNSPodProvider) Auth(params maps.Map) error {
|
func (this *DNSPodProvider) Auth(params maps.Map) error {
|
||||||
this.apiId = params.GetString("id")
|
this.apiId = params.GetString("id")
|
||||||
this.apiToken = params.GetString("token")
|
this.apiToken = params.GetString("token")
|
||||||
@@ -34,7 +34,7 @@ func (this *DNSPodProvider) Auth(params maps.Map) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取域名列表
|
// GetRecords 获取域名列表
|
||||||
func (this *DNSPodProvider) GetRecords(domain string) (records []*Record, err error) {
|
func (this *DNSPodProvider) GetRecords(domain string) (records []*Record, err error) {
|
||||||
offset := 0
|
offset := 0
|
||||||
size := 100
|
size := 100
|
||||||
@@ -72,7 +72,7 @@ func (this *DNSPodProvider) GetRecords(domain string) (records []*Record, err er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取线路数据
|
// GetRoutes 读取线路数据
|
||||||
func (this *DNSPodProvider) GetRoutes(domain string) (routes []*Route, err error) {
|
func (this *DNSPodProvider) GetRoutes(domain string) (routes []*Route, err error) {
|
||||||
infoResp, err := this.post("/Domain.info", map[string]string{
|
infoResp, err := this.post("/Domain.info", map[string]string{
|
||||||
"domain": domain,
|
"domain": domain,
|
||||||
@@ -106,7 +106,7 @@ func (this *DNSPodProvider) GetRoutes(domain string) (routes []*Route, err error
|
|||||||
return routes, nil
|
return routes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询单个记录
|
// QueryRecord 查询单个记录
|
||||||
func (this *DNSPodProvider) QueryRecord(domain string, name string, recordType RecordType) (*Record, error) {
|
func (this *DNSPodProvider) QueryRecord(domain string, name string, recordType RecordType) (*Record, error) {
|
||||||
records, err := this.GetRecords(domain)
|
records, err := this.GetRecords(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -120,7 +120,7 @@ func (this *DNSPodProvider) QueryRecord(domain string, name string, recordType R
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置记录
|
// AddRecord 设置记录
|
||||||
func (this *DNSPodProvider) AddRecord(domain string, newRecord *Record) error {
|
func (this *DNSPodProvider) AddRecord(domain string, newRecord *Record) error {
|
||||||
if newRecord == nil {
|
if newRecord == nil {
|
||||||
return errors.New("invalid new record")
|
return errors.New("invalid new record")
|
||||||
@@ -140,7 +140,7 @@ func (this *DNSPodProvider) AddRecord(domain string, newRecord *Record) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改记录
|
// UpdateRecord 修改记录
|
||||||
func (this *DNSPodProvider) UpdateRecord(domain string, record *Record, newRecord *Record) error {
|
func (this *DNSPodProvider) UpdateRecord(domain string, record *Record, newRecord *Record) error {
|
||||||
if record == nil {
|
if record == nil {
|
||||||
return errors.New("invalid record")
|
return errors.New("invalid record")
|
||||||
@@ -164,7 +164,7 @@ func (this *DNSPodProvider) UpdateRecord(domain string, record *Record, newRecor
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除记录
|
// DeleteRecord 删除记录
|
||||||
func (this *DNSPodProvider) DeleteRecord(domain string, record *Record) error {
|
func (this *DNSPodProvider) DeleteRecord(domain string, record *Record) error {
|
||||||
if record == nil {
|
if record == nil {
|
||||||
return errors.New("invalid record to delete")
|
return errors.New("invalid record to delete")
|
||||||
@@ -223,7 +223,7 @@ func (this *DNSPodProvider) post(path string, params map[string]string) (maps.Ma
|
|||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 默认线路
|
// DefaultRoute 默认线路
|
||||||
func (this *DNSPodProvider) DefaultRoute() string {
|
func (this *DNSPodProvider) DefaultRoute() string {
|
||||||
return "默认"
|
return "默认"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,29 +2,29 @@ package dnsclients
|
|||||||
|
|
||||||
import "github.com/iwind/TeaGo/maps"
|
import "github.com/iwind/TeaGo/maps"
|
||||||
|
|
||||||
// DNS操作接口
|
// ProviderInterface DNS操作接口
|
||||||
type ProviderInterface interface {
|
type ProviderInterface interface {
|
||||||
// 认证
|
// Auth 认证
|
||||||
Auth(params maps.Map) error
|
Auth(params maps.Map) error
|
||||||
|
|
||||||
// 获取域名解析记录列表
|
// GetRecords 获取域名解析记录列表
|
||||||
GetRecords(domain string) (records []*Record, err error)
|
GetRecords(domain string) (records []*Record, err error)
|
||||||
|
|
||||||
// 读取域名支持的线路数据
|
// GetRoutes 读取域名支持的线路数据
|
||||||
GetRoutes(domain string) (routes []*Route, err error)
|
GetRoutes(domain string) (routes []*Route, err error)
|
||||||
|
|
||||||
// 查询单个记录
|
// QueryRecord 查询单个记录
|
||||||
QueryRecord(domain string, name string, recordType RecordType) (*Record, error)
|
QueryRecord(domain string, name string, recordType RecordType) (*Record, error)
|
||||||
|
|
||||||
// 设置记录
|
// AddRecord 设置记录
|
||||||
AddRecord(domain string, newRecord *Record) error
|
AddRecord(domain string, newRecord *Record) error
|
||||||
|
|
||||||
// 修改记录
|
// UpdateRecord 修改记录
|
||||||
UpdateRecord(domain string, record *Record, newRecord *Record) error
|
UpdateRecord(domain string, record *Record, newRecord *Record) error
|
||||||
|
|
||||||
// 删除记录
|
// DeleteRecord 删除记录
|
||||||
DeleteRecord(domain string, record *Record) error
|
DeleteRecord(domain string, record *Record) error
|
||||||
|
|
||||||
// 默认线路
|
// DefaultRoute 默认线路
|
||||||
DefaultRoute() string
|
DefaultRoute() string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package dnsclients
|
package dnsclients
|
||||||
|
|
||||||
// 线路描述
|
// Route 线路描述
|
||||||
type Route struct {
|
type Route struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ const (
|
|||||||
ProviderTypeDNSPod ProviderType = "dnspod"
|
ProviderTypeDNSPod ProviderType = "dnspod"
|
||||||
ProviderTypeAliDNS ProviderType = "alidns"
|
ProviderTypeAliDNS ProviderType = "alidns"
|
||||||
ProviderTypeDNSCom ProviderType = "dnscom"
|
ProviderTypeDNSCom ProviderType = "dnscom"
|
||||||
|
ProviderTypeCloudFlare ProviderType = "cloudFlare"
|
||||||
ProviderTypeCustomHTTP ProviderType = "customHTTP"
|
ProviderTypeCustomHTTP ProviderType = "customHTTP"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 所有的服务商类型
|
// AllProviderTypes 所有的服务商类型
|
||||||
var AllProviderTypes = []maps.Map{
|
var AllProviderTypes = []maps.Map{
|
||||||
{
|
{
|
||||||
"name": "阿里云DNS",
|
"name": "阿里云DNS",
|
||||||
@@ -26,26 +27,32 @@ var AllProviderTypes = []maps.Map{
|
|||||||
"name": "帝恩思DNS.COM",
|
"name": "帝恩思DNS.COM",
|
||||||
"code": ProviderTypeDNSCom,
|
"code": ProviderTypeDNSCom,
|
||||||
},**/
|
},**/
|
||||||
|
{
|
||||||
|
"name": "CloudFlare DNS",
|
||||||
|
"code": ProviderTypeCloudFlare,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "自定义HTTP DNS",
|
"name": "自定义HTTP DNS",
|
||||||
"code": ProviderTypeCustomHTTP,
|
"code": ProviderTypeCustomHTTP,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查找服务商实例
|
// FindProvider 查找服务商实例
|
||||||
func FindProvider(providerType ProviderType) ProviderInterface {
|
func FindProvider(providerType ProviderType) ProviderInterface {
|
||||||
switch providerType {
|
switch providerType {
|
||||||
case ProviderTypeDNSPod:
|
case ProviderTypeDNSPod:
|
||||||
return &DNSPodProvider{}
|
return &DNSPodProvider{}
|
||||||
case ProviderTypeAliDNS:
|
case ProviderTypeAliDNS:
|
||||||
return &AliDNSProvider{}
|
return &AliDNSProvider{}
|
||||||
|
case ProviderTypeCloudFlare:
|
||||||
|
return &CloudFlareProvider{}
|
||||||
case ProviderTypeCustomHTTP:
|
case ProviderTypeCustomHTTP:
|
||||||
return &CustomHTTPProvider{}
|
return &CustomHTTPProvider{}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查找服务商名称
|
// FindProviderTypeName 查找服务商名称
|
||||||
func FindProviderTypeName(providerType ProviderType) string {
|
func FindProviderTypeName(providerType ProviderType) string {
|
||||||
for _, t := range AllProviderTypes {
|
for _, t := range AllProviderTypes {
|
||||||
if t.GetString("code") == providerType {
|
if t.GetString("code") == providerType {
|
||||||
|
|||||||
Reference in New Issue
Block a user