mirror of
https://github.com/jeessy2/ddns-go.git
synced 2025-10-20 15:33:46 +08:00
* feat: init the i18n * fix: porkbun * refactor: http_util GetHTTPResponse * fix: save * fix: add more i18n * feat: i18n at writing.html * feat: README EN * fix: default api * fix: pic
218 lines
5.1 KiB
Go
218 lines
5.1 KiB
Go
package dns
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/jeessy2/ddns-go/v5/config"
|
|
"github.com/jeessy2/ddns-go/v5/util"
|
|
)
|
|
|
|
const (
|
|
zonesAPI string = "https://api.cloudflare.com/client/v4/zones"
|
|
)
|
|
|
|
// Cloudflare Cloudflare实现
|
|
type Cloudflare struct {
|
|
DNS config.DNS
|
|
Domains config.Domains
|
|
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(dnsConf *config.DnsConfig, ipv4cache *util.IpCache, ipv6cache *util.IpCache) {
|
|
cf.Domains.Ipv4Cache = ipv4cache
|
|
cf.Domains.Ipv6Cache = ipv6cache
|
|
cf.DNS = dnsConf.DNS
|
|
cf.Domains.GetNewIp(dnsConf)
|
|
if dnsConf.TTL == "" {
|
|
// 默认1 auto ttl
|
|
cf.TTL = 1
|
|
} else {
|
|
ttl, err := strconv.Atoi(dnsConf.TTL)
|
|
if err != nil {
|
|
cf.TTL = 1
|
|
} else {
|
|
cf.TTL = ttl
|
|
}
|
|
}
|
|
}
|
|
|
|
// AddUpdateDomainRecords 添加或更新IPv4/IPv6记录
|
|
func (cf *Cloudflare) AddUpdateDomainRecords() config.Domains {
|
|
cf.addUpdateDomainRecords("A")
|
|
cf.addUpdateDomainRecords("AAAA")
|
|
return cf.Domains
|
|
}
|
|
|
|
func (cf *Cloudflare) addUpdateDomainRecords(recordType string) {
|
|
ipAddr, domains := cf.Domains.GetNewIpResult(recordType)
|
|
|
|
if ipAddr == "" {
|
|
return
|
|
}
|
|
|
|
for _, domain := range domains {
|
|
// get zone
|
|
result, err := cf.getZones(domain)
|
|
if err != nil || len(result.Result) != 1 {
|
|
util.Log("查询域名信息发生异常! %s", err)
|
|
domain.UpdateStatus = config.UpdatedFailed
|
|
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 {
|
|
util.Log("查询域名信息发生异常! %s", err)
|
|
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 *config.Domain, recordType string, ipAddr string) {
|
|
record := &CloudflareRecord{
|
|
Type: recordType,
|
|
Name: domain.String(),
|
|
Content: ipAddr,
|
|
Proxied: false,
|
|
TTL: cf.TTL,
|
|
}
|
|
record.Proxied = domain.GetCustomParams().Get("proxied") == "true"
|
|
var status CloudflareStatus
|
|
err := cf.request(
|
|
"POST",
|
|
fmt.Sprintf(zonesAPI+"/%s/dns_records", zoneID),
|
|
record,
|
|
&status,
|
|
)
|
|
if err == nil && status.Success {
|
|
util.Log("新增域名解析 %s 成功! IP: %s", domain, ipAddr)
|
|
domain.UpdateStatus = config.UpdatedSuccess
|
|
} else {
|
|
util.Log("新增域名解析 %s 失败! 异常信息: %s", domain, err)
|
|
domain.UpdateStatus = config.UpdatedFailed
|
|
}
|
|
}
|
|
|
|
// 修改
|
|
func (cf *Cloudflare) modify(result CloudflareRecordsResp, zoneID string, domain *config.Domain, recordType string, ipAddr string) {
|
|
for _, record := range result.Result {
|
|
// 相同不修改
|
|
if record.Content == ipAddr {
|
|
util.Log("你的IP %s 没有变化, 域名 %s", ipAddr, domain)
|
|
continue
|
|
}
|
|
var status CloudflareStatus
|
|
record.Content = ipAddr
|
|
record.TTL = cf.TTL
|
|
// 存在参数才修改proxied
|
|
if domain.GetCustomParams().Has("proxied") {
|
|
record.Proxied = domain.GetCustomParams().Get("proxied") == "true"
|
|
}
|
|
err := cf.request(
|
|
"PUT",
|
|
fmt.Sprintf(zonesAPI+"/%s/dns_records/%s", zoneID, record.ID),
|
|
record,
|
|
&status,
|
|
)
|
|
if err == nil && status.Success {
|
|
util.Log("更新域名解析 %s 成功! IP: %s", domain, ipAddr)
|
|
domain.UpdateStatus = config.UpdatedSuccess
|
|
} else {
|
|
util.Log("更新域名解析 %s 失败! 异常信息: %s", domain, err)
|
|
domain.UpdateStatus = config.UpdatedFailed
|
|
}
|
|
}
|
|
}
|
|
|
|
// 获得域名记录列表
|
|
func (cf *Cloudflare) getZones(domain *config.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 {
|
|
util.Log("异常信息: %s", err)
|
|
return
|
|
}
|
|
req.Header.Set("Authorization", "Bearer "+cf.DNS.Secret)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
client := util.CreateHTTPClient()
|
|
resp, err := client.Do(req)
|
|
err = util.GetHTTPResponse(resp, err, result)
|
|
|
|
return
|
|
}
|