lucky/ddns/cloudflare.go

227 lines
5.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package ddns
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"github.com/gdy666/lucky/ddnscore.go"
"github.com/gdy666/lucky/thirdlib/gdylib/httputils"
)
const (
zonesAPI string = "https://api.cloudflare.com/client/v4/zones"
)
// Cloudflare Cloudflare实现
type Cloudflare struct {
DNSCommon
TTL int
}
// CloudflareZonesResp cloudflare zones返回结果
type CloudflareZonesResp struct {
CloudflareStatus
Result []struct {
ID string
Name string
Status string
Paused bool
}
}
// CloudflareRecordsResp records
type CloudflareRecordsResp struct {
CloudflareStatus
Result []CloudflareRecord
}
// CloudflareRecord 记录实体
type CloudflareRecord struct {
ID string `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Content string `json:"content"`
Proxied bool `json:"proxied"`
TTL int `json:"ttl"`
}
// CloudflareStatus 公共状态
type CloudflareStatus struct {
Success bool
Messages []string
}
// Init 初始化
func (cf *Cloudflare) Init(task *ddnscore.DDNSTaskInfo) {
cf.DNSCommon.Init(task)
if task.TTL == "" {
// 默认1 auto ttl
cf.TTL = 1
} else {
ttl, err := strconv.Atoi(task.TTL)
if err != nil {
cf.TTL = 1
} else {
cf.TTL = ttl
}
}
cf.SetCreateUpdateDomainFunc(cf.createUpdateDomain)
}
func (cf *Cloudflare) createUpdateDomain(recordType, ipAddr string, domain *ddnscore.Domain) {
result, err := cf.getZones(domain)
if err != nil || len(result.Result) != 1 {
errMsg := "更新失败[001]:\n"
if err != nil {
errMsg += err.Error()
} else {
errMsg += fmt.Sprintf("%v", result)
}
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, errMsg)
return
}
zoneID := result.Result[0].ID
var records CloudflareRecordsResp
// getDomains 最多更新前50条
err = cf.request(
"GET",
fmt.Sprintf(zonesAPI+"/%s/dns_records?type=%s&name=%s&per_page=50", zoneID, recordType, domain),
nil,
&records,
)
if err != nil || !records.Success {
errMsg := "更新失败[002]:\n"
if err != nil {
errMsg += err.Error()
}
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, errMsg)
return
}
if len(records.Result) > 0 {
// 更新
cf.modify(records, zoneID, domain, recordType, ipAddr)
} else {
// 新增
cf.create(zoneID, domain, recordType, ipAddr)
}
}
// 创建
func (cf *Cloudflare) create(zoneID string, domain *ddnscore.Domain, recordType string, ipAddr string) {
record := &CloudflareRecord{
Type: recordType,
Name: domain.String(),
Content: ipAddr,
Proxied: false,
TTL: cf.TTL,
}
var status CloudflareStatus
err := cf.request(
"POST",
fmt.Sprintf(zonesAPI+"/%s/dns_records", zoneID),
record,
&status,
)
if err == nil && status.Success {
//log.Printf("新增域名解析 %s 成功IP: %s", domain, ipAddr)
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
errMsg := fmt.Sprintf("创建域名失败:\n%v\n", status)
if err != nil {
errMsg += fmt.Sprintf(":%s", err.Error())
}
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, errMsg)
}
}
// 修改
func (cf *Cloudflare) modify(result CloudflareRecordsResp, zoneID string, domain *ddnscore.Domain, recordType string, ipAddr string) {
for _, record := range result.Result {
// 相同不修改
if record.Content == ipAddr {
if domain.UpdateStatus == ddnscore.UpdatedFailed {
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
domain.SetDomainUpdateStatus(ddnscore.UpdatedNothing, "")
}
continue
}
var status CloudflareStatus
record.Content = ipAddr
record.TTL = cf.TTL
err := cf.request(
"PUT",
fmt.Sprintf(zonesAPI+"/%s/dns_records/%s", zoneID, record.ID),
record,
&status,
)
if err == nil && status.Success {
//log.Printf("更新域名解析 %s 成功IP: %s", domain, ipAddr)
domain.SetDomainUpdateStatus(ddnscore.UpdatedSuccess, "")
} else {
//log.Printf("更新域名解析 %s 失败Messages: %s", domain, status.Messages)
errMsg := fmt.Sprintf("更新域名解析失败:%v\n", status)
if err != nil {
errMsg += err.Error()
}
domain.SetDomainUpdateStatus(ddnscore.UpdatedFailed, errMsg)
}
}
}
// 获得域名记录列表
func (cf *Cloudflare) getZones(domain *ddnscore.Domain) (result CloudflareZonesResp, err error) {
err = cf.request(
"GET",
fmt.Sprintf(zonesAPI+"?name=%s&status=%s&per_page=%s", domain.DomainName, "active", "50"),
nil,
&result,
)
return
}
// request 统一请求接口
func (cf *Cloudflare) request(method string, url string, data interface{}, result interface{}) (err error) {
jsonStr := make([]byte, 0)
if data != nil {
jsonStr, _ = json.Marshal(data)
}
req, err := http.NewRequest(
method,
url,
bytes.NewBuffer(jsonStr),
)
if err != nil {
log.Println("http.NewRequest失败. Error: ", err)
return
}
req.Header.Set("Authorization", "Bearer "+cf.task.DNS.Secret)
req.Header.Set("Content-Type", "application/json")
client, err := cf.CreateHTTPClient()
if err != nil {
return err
}
resp, err := client.Do(req)
if err != nil {
return err
}
return httputils.GetAndParseJSONResponseFromHttpResponse(resp, result)
}