mirror of
				https://github.com/TeaOSLab/EdgeAPI.git
				synced 2025-11-04 16:00:24 +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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 阿里云服务商
 | 
			
		||||
// AliDNSProvider 阿里云服务商
 | 
			
		||||
type AliDNSProvider struct {
 | 
			
		||||
	BaseProvider
 | 
			
		||||
 | 
			
		||||
@@ -17,7 +17,7 @@ type AliDNSProvider struct {
 | 
			
		||||
	accessKeySecret string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 认证
 | 
			
		||||
// Auth 认证
 | 
			
		||||
func (this *AliDNSProvider) Auth(params maps.Map) error {
 | 
			
		||||
	this.accessKeyId = params.GetString("accessKeyId")
 | 
			
		||||
	this.accessKeySecret = params.GetString("accessKeySecret")
 | 
			
		||||
@@ -30,7 +30,7 @@ func (this *AliDNSProvider) Auth(params maps.Map) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 获取域名列表
 | 
			
		||||
// GetRecords 获取域名列表
 | 
			
		||||
func (this *AliDNSProvider) GetRecords(domain string) (records []*Record, err error) {
 | 
			
		||||
	pageNumber := 1
 | 
			
		||||
	size := 100
 | 
			
		||||
@@ -70,7 +70,7 @@ func (this *AliDNSProvider) GetRecords(domain string) (records []*Record, err er
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 读取域名支持的线路数据
 | 
			
		||||
// GetRoutes 读取域名支持的线路数据
 | 
			
		||||
func (this *AliDNSProvider) GetRoutes(domain string) (routes []*Route, err error) {
 | 
			
		||||
	req := alidns.CreateDescribeSupportLinesRequest()
 | 
			
		||||
	req.DomainName = domain
 | 
			
		||||
@@ -89,7 +89,7 @@ func (this *AliDNSProvider) GetRoutes(domain string) (routes []*Route, err error
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 查询单个记录
 | 
			
		||||
// QueryRecord 查询单个记录
 | 
			
		||||
func (this *AliDNSProvider) QueryRecord(domain string, name string, recordType RecordType) (*Record, error) {
 | 
			
		||||
	records, err := this.GetRecords(domain)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -103,7 +103,7 @@ func (this *AliDNSProvider) QueryRecord(domain string, name string, recordType R
 | 
			
		||||
	return nil, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 设置记录
 | 
			
		||||
// AddRecord 设置记录
 | 
			
		||||
func (this *AliDNSProvider) AddRecord(domain string, newRecord *Record) error {
 | 
			
		||||
	req := alidns.CreateAddDomainRecordRequest()
 | 
			
		||||
	req.RR = newRecord.Name
 | 
			
		||||
@@ -124,7 +124,7 @@ func (this *AliDNSProvider) AddRecord(domain string, newRecord *Record) error {
 | 
			
		||||
	return errors.New(resp.GetHttpContentString())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 修改记录
 | 
			
		||||
// UpdateRecord 修改记录
 | 
			
		||||
func (this *AliDNSProvider) UpdateRecord(domain string, record *Record, newRecord *Record) error {
 | 
			
		||||
	req := alidns.CreateUpdateDomainRecordRequest()
 | 
			
		||||
	req.RecordId = record.Id
 | 
			
		||||
@@ -138,7 +138,7 @@ func (this *AliDNSProvider) UpdateRecord(domain string, record *Record, newRecor
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 删除记录
 | 
			
		||||
// DeleteRecord 删除记录
 | 
			
		||||
func (this *AliDNSProvider) DeleteRecord(domain string, record *Record) error {
 | 
			
		||||
	req := alidns.CreateDeleteDomainRecordRequest()
 | 
			
		||||
	req.RecordId = record.Id
 | 
			
		||||
@@ -148,7 +148,7 @@ func (this *AliDNSProvider) DeleteRecord(domain string, record *Record) error {
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 默认线路
 | 
			
		||||
// DefaultRoute 默认线路
 | 
			
		||||
func (this *AliDNSProvider) DefaultRoute() string {
 | 
			
		||||
	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 {
 | 
			
		||||
	url    string
 | 
			
		||||
	secret string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 认证
 | 
			
		||||
// Auth 认证
 | 
			
		||||
// 参数:
 | 
			
		||||
//   - url
 | 
			
		||||
//   - secret
 | 
			
		||||
@@ -48,7 +48,7 @@ func (this *CustomHTTPProvider) Auth(params maps.Map) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 获取域名解析记录列表
 | 
			
		||||
// GetRecords 获取域名解析记录列表
 | 
			
		||||
func (this *CustomHTTPProvider) GetRecords(domain string) (records []*Record, err error) {
 | 
			
		||||
	resp, err := this.post(maps.Map{
 | 
			
		||||
		"action": "GetRecords",
 | 
			
		||||
@@ -61,7 +61,7 @@ func (this *CustomHTTPProvider) GetRecords(domain string) (records []*Record, er
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 读取域名支持的线路数据
 | 
			
		||||
// GetRoutes 读取域名支持的线路数据
 | 
			
		||||
func (this *CustomHTTPProvider) GetRoutes(domain string) (routes []*Route, err error) {
 | 
			
		||||
	resp, err := this.post(maps.Map{
 | 
			
		||||
		"action": "GetRoutes",
 | 
			
		||||
@@ -74,7 +74,7 @@ func (this *CustomHTTPProvider) GetRoutes(domain string) (routes []*Route, err e
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 查询单个记录
 | 
			
		||||
// QueryRecord 查询单个记录
 | 
			
		||||
func (this *CustomHTTPProvider) QueryRecord(domain string, name string, recordType RecordType) (*Record, error) {
 | 
			
		||||
	resp, err := this.post(maps.Map{
 | 
			
		||||
		"action":     "QueryRecord",
 | 
			
		||||
@@ -99,7 +99,7 @@ func (this *CustomHTTPProvider) QueryRecord(domain string, name string, recordTy
 | 
			
		||||
	return record, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 设置记录
 | 
			
		||||
// AddRecord 设置记录
 | 
			
		||||
func (this *CustomHTTPProvider) AddRecord(domain string, newRecord *Record) error {
 | 
			
		||||
	_, err := this.post(maps.Map{
 | 
			
		||||
		"action":    "AddRecord",
 | 
			
		||||
@@ -109,7 +109,7 @@ func (this *CustomHTTPProvider) AddRecord(domain string, newRecord *Record) erro
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 修改记录
 | 
			
		||||
// UpdateRecord 修改记录
 | 
			
		||||
func (this *CustomHTTPProvider) UpdateRecord(domain string, record *Record, newRecord *Record) error {
 | 
			
		||||
	_, err := this.post(maps.Map{
 | 
			
		||||
		"action":    "UpdateRecord",
 | 
			
		||||
@@ -120,7 +120,7 @@ func (this *CustomHTTPProvider) UpdateRecord(domain string, record *Record, newR
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 删除记录
 | 
			
		||||
// DeleteRecord 删除记录
 | 
			
		||||
func (this *CustomHTTPProvider) DeleteRecord(domain string, record *Record) error {
 | 
			
		||||
	_, err := this.post(maps.Map{
 | 
			
		||||
		"action": "DeleteRecord",
 | 
			
		||||
@@ -130,7 +130,7 @@ func (this *CustomHTTPProvider) DeleteRecord(domain string, record *Record) erro
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 默认线路
 | 
			
		||||
// DefaultRoute 默认线路
 | 
			
		||||
func (this *CustomHTTPProvider) DefaultRoute() string {
 | 
			
		||||
	resp, err := this.post(maps.Map{
 | 
			
		||||
		"action": "DefaultRoute",
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DNSPod服务商
 | 
			
		||||
// DNSPodProvider DNSPod服务商
 | 
			
		||||
type DNSPodProvider struct {
 | 
			
		||||
	BaseProvider
 | 
			
		||||
 | 
			
		||||
@@ -20,7 +20,7 @@ type DNSPodProvider struct {
 | 
			
		||||
	apiToken string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 认证
 | 
			
		||||
// Auth 认证
 | 
			
		||||
func (this *DNSPodProvider) Auth(params maps.Map) error {
 | 
			
		||||
	this.apiId = params.GetString("id")
 | 
			
		||||
	this.apiToken = params.GetString("token")
 | 
			
		||||
@@ -34,7 +34,7 @@ func (this *DNSPodProvider) Auth(params maps.Map) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 获取域名列表
 | 
			
		||||
// GetRecords 获取域名列表
 | 
			
		||||
func (this *DNSPodProvider) GetRecords(domain string) (records []*Record, err error) {
 | 
			
		||||
	offset := 0
 | 
			
		||||
	size := 100
 | 
			
		||||
@@ -72,7 +72,7 @@ func (this *DNSPodProvider) GetRecords(domain string) (records []*Record, err er
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 读取线路数据
 | 
			
		||||
// GetRoutes 读取线路数据
 | 
			
		||||
func (this *DNSPodProvider) GetRoutes(domain string) (routes []*Route, err error) {
 | 
			
		||||
	infoResp, err := this.post("/Domain.info", map[string]string{
 | 
			
		||||
		"domain": domain,
 | 
			
		||||
@@ -106,7 +106,7 @@ func (this *DNSPodProvider) GetRoutes(domain string) (routes []*Route, err error
 | 
			
		||||
	return routes, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 查询单个记录
 | 
			
		||||
// QueryRecord 查询单个记录
 | 
			
		||||
func (this *DNSPodProvider) QueryRecord(domain string, name string, recordType RecordType) (*Record, error) {
 | 
			
		||||
	records, err := this.GetRecords(domain)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -120,7 +120,7 @@ func (this *DNSPodProvider) QueryRecord(domain string, name string, recordType R
 | 
			
		||||
	return nil, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 设置记录
 | 
			
		||||
// AddRecord 设置记录
 | 
			
		||||
func (this *DNSPodProvider) AddRecord(domain string, newRecord *Record) error {
 | 
			
		||||
	if newRecord == nil {
 | 
			
		||||
		return errors.New("invalid new record")
 | 
			
		||||
@@ -140,7 +140,7 @@ func (this *DNSPodProvider) AddRecord(domain string, newRecord *Record) error {
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 修改记录
 | 
			
		||||
// UpdateRecord 修改记录
 | 
			
		||||
func (this *DNSPodProvider) UpdateRecord(domain string, record *Record, newRecord *Record) error {
 | 
			
		||||
	if record == nil {
 | 
			
		||||
		return errors.New("invalid record")
 | 
			
		||||
@@ -164,7 +164,7 @@ func (this *DNSPodProvider) UpdateRecord(domain string, record *Record, newRecor
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 删除记录
 | 
			
		||||
// DeleteRecord 删除记录
 | 
			
		||||
func (this *DNSPodProvider) DeleteRecord(domain string, record *Record) error {
 | 
			
		||||
	if record == nil {
 | 
			
		||||
		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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 默认线路
 | 
			
		||||
// DefaultRoute 默认线路
 | 
			
		||||
func (this *DNSPodProvider) DefaultRoute() string {
 | 
			
		||||
	return "默认"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,29 +2,29 @@ package dnsclients
 | 
			
		||||
 | 
			
		||||
import "github.com/iwind/TeaGo/maps"
 | 
			
		||||
 | 
			
		||||
// DNS操作接口
 | 
			
		||||
// ProviderInterface DNS操作接口
 | 
			
		||||
type ProviderInterface interface {
 | 
			
		||||
	// 认证
 | 
			
		||||
	// Auth 认证
 | 
			
		||||
	Auth(params maps.Map) error
 | 
			
		||||
 | 
			
		||||
	// 获取域名解析记录列表
 | 
			
		||||
	// GetRecords 获取域名解析记录列表
 | 
			
		||||
	GetRecords(domain string) (records []*Record, err error)
 | 
			
		||||
 | 
			
		||||
	// 读取域名支持的线路数据
 | 
			
		||||
	// GetRoutes 读取域名支持的线路数据
 | 
			
		||||
	GetRoutes(domain string) (routes []*Route, err error)
 | 
			
		||||
 | 
			
		||||
	// 查询单个记录
 | 
			
		||||
	// QueryRecord 查询单个记录
 | 
			
		||||
	QueryRecord(domain string, name string, recordType RecordType) (*Record, error)
 | 
			
		||||
 | 
			
		||||
	// 设置记录
 | 
			
		||||
	// AddRecord 设置记录
 | 
			
		||||
	AddRecord(domain string, newRecord *Record) error
 | 
			
		||||
 | 
			
		||||
	// 修改记录
 | 
			
		||||
	// UpdateRecord 修改记录
 | 
			
		||||
	UpdateRecord(domain string, record *Record, newRecord *Record) error
 | 
			
		||||
 | 
			
		||||
	// 删除记录
 | 
			
		||||
	// DeleteRecord 删除记录
 | 
			
		||||
	DeleteRecord(domain string, record *Record) error
 | 
			
		||||
 | 
			
		||||
	// 默认线路
 | 
			
		||||
	// DefaultRoute 默认线路
 | 
			
		||||
	DefaultRoute() string
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
package dnsclients
 | 
			
		||||
 | 
			
		||||
// 线路描述
 | 
			
		||||
// Route 线路描述
 | 
			
		||||
type Route struct {
 | 
			
		||||
	Name string `json:"name"`
 | 
			
		||||
	Code string `json:"code"`
 | 
			
		||||
 
 | 
			
		||||
@@ -9,10 +9,11 @@ const (
 | 
			
		||||
	ProviderTypeDNSPod     ProviderType = "dnspod"
 | 
			
		||||
	ProviderTypeAliDNS     ProviderType = "alidns"
 | 
			
		||||
	ProviderTypeDNSCom     ProviderType = "dnscom"
 | 
			
		||||
	ProviderTypeCloudFlare ProviderType = "cloudFlare"
 | 
			
		||||
	ProviderTypeCustomHTTP ProviderType = "customHTTP"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 所有的服务商类型
 | 
			
		||||
// AllProviderTypes 所有的服务商类型
 | 
			
		||||
var AllProviderTypes = []maps.Map{
 | 
			
		||||
	{
 | 
			
		||||
		"name": "阿里云DNS",
 | 
			
		||||
@@ -26,26 +27,32 @@ var AllProviderTypes = []maps.Map{
 | 
			
		||||
		"name": "帝恩思DNS.COM",
 | 
			
		||||
		"code": ProviderTypeDNSCom,
 | 
			
		||||
	},**/
 | 
			
		||||
	{
 | 
			
		||||
		"name": "CloudFlare DNS",
 | 
			
		||||
		"code": ProviderTypeCloudFlare,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"name": "自定义HTTP DNS",
 | 
			
		||||
		"code": ProviderTypeCustomHTTP,
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 查找服务商实例
 | 
			
		||||
// FindProvider 查找服务商实例
 | 
			
		||||
func FindProvider(providerType ProviderType) ProviderInterface {
 | 
			
		||||
	switch providerType {
 | 
			
		||||
	case ProviderTypeDNSPod:
 | 
			
		||||
		return &DNSPodProvider{}
 | 
			
		||||
	case ProviderTypeAliDNS:
 | 
			
		||||
		return &AliDNSProvider{}
 | 
			
		||||
	case ProviderTypeCloudFlare:
 | 
			
		||||
		return &CloudFlareProvider{}
 | 
			
		||||
	case ProviderTypeCustomHTTP:
 | 
			
		||||
		return &CustomHTTPProvider{}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 查找服务商名称
 | 
			
		||||
// FindProviderTypeName 查找服务商名称
 | 
			
		||||
func FindProviderTypeName(providerType ProviderType) string {
 | 
			
		||||
	for _, t := range AllProviderTypes {
 | 
			
		||||
		if t.GetString("code") == providerType {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user