feat: 项目新增了对 DNS.LA 的官方支持 (#1484)

This commit is contained in:
马兵
2025-06-10 14:43:36 +08:00
committed by GitHub
parent d7b4bccf28
commit 5e9736a9c1
5 changed files with 292 additions and 7 deletions

View File

@ -18,7 +18,7 @@
## 特性 ## 特性
- 支持Mac、Windows、Linux系统支持ARM、x86架构 - 支持Mac、Windows、Linux系统支持ARM、x86架构
- 支持的域名服务商 `阿里云` `腾讯云` `Dnspod` `Cloudflare` `华为云` `Callback` `百度云` `Porkbun` `GoDaddy` `Namecheap` `NameSilo` `Dynadot` - 支持的域名服务商 `阿里云` `腾讯云` `Dnspod` `Cloudflare` `华为云` `Callback` `百度云` `Porkbun` `GoDaddy` `Namecheap` `NameSilo` `Dynadot` `DNSLA`
- 支持接口/网卡/[命令](https://github.com/jeessy2/ddns-go/wiki/通过命令获取IP参考)获取IP - 支持接口/网卡/[命令](https://github.com/jeessy2/ddns-go/wiki/通过命令获取IP参考)获取IP
- 支持以服务的方式运行 - 支持以服务的方式运行
- 默认间隔5分钟同步一次 - 默认间隔5分钟同步一次
@ -157,7 +157,7 @@
} }
} }
``` ```
</details> </details>
- <details><summary>飞书</summary> - <details><summary>飞书</summary>
- 飞书电脑端 -> 群设置 -> 添加机器人 -> 自定义机器人 - 飞书电脑端 -> 群设置 -> 添加机器人 -> 自定义机器人
@ -190,7 +190,7 @@
} }
} }
``` ```
</details> </details>
- <details><summary>Telegram</summary> - <details><summary>Telegram</summary>
[ddns-telegram-bot](https://github.com/WingLim/ddns-telegram-bot) [ddns-telegram-bot](https://github.com/WingLim/ddns-telegram-bot)
@ -207,7 +207,7 @@
"content": "你的公网IP变了 \n - IPv4地址#{ipv4Addr} \n - 域名更新结果:#{ipv4Result} \n" "content": "你的公网IP变了 \n - IPv4地址#{ipv4Addr} \n - 域名更新结果:#{ipv4Result} \n"
} }
``` ```
</details> </details>
- <details><summary>Discord</summary> - <details><summary>Discord</summary>
- Discord任意客户端 -> 伺服器 -> 频道设置 -> 整合 -> 查看Webhook -> 新Webhook -> 复制Webhook网址 - Discord任意客户端 -> 伺服器 -> 频道设置 -> 整合 -> 查看Webhook -> 新Webhook -> 复制Webhook网址
@ -230,7 +230,7 @@
] ]
} }
``` ```
</details> </details>
- [查看更多Webhook配置参考](https://github.com/jeessy2/ddns-go/issues/327) - [查看更多Webhook配置参考](https://github.com/jeessy2/ddns-go/issues/327)

View File

@ -16,7 +16,7 @@ Automatically obtain your public IPv4 or IPv6 address and resolve it to the corr
## Features ## Features
- Support Mac, Windows, Linux system, support ARM, x86 architecture - Support Mac, Windows, Linux system, support ARM, x86 architecture
- Support domain service providers `Aliyun` `Tencent` `Dnspod` `Cloudflare` `Huawei` `Callback` `Baidu` `Porkbun` `GoDaddy` `Namecheap` `NameSilo` `Dynadot` - Support domain service providers `Aliyun` `Tencent` `Dnspod` `Cloudflare` `Huawei` `Callback` `Baidu` `Porkbun` `GoDaddy` `Namecheap` `NameSilo` `Dynadot` `DNSLA`
- Support interface / netcard / command to get IP - Support interface / netcard / command to get IP
- Support running as a service - Support running as a service
- Default interval is 5 minutes - Default interval is 5 minutes
@ -143,7 +143,7 @@ Automatically obtain your public IPv4 or IPv6 address and resolve it to the corr
] ]
} }
``` ```
</details> </details>
- [More webhook configuration reference](https://github.com/jeessy2/ddns-go/issues/327) - [More webhook configuration reference](https://github.com/jeessy2/ddns-go/issues/327)

271
dns/dnsla.go Normal file
View File

@ -0,0 +1,271 @@
package dns
import (
"bytes"
"encoding/base64"
"encoding/json"
"github.com/jeessy2/ddns-go/v6/config"
"github.com/jeessy2/ddns-go/v6/util"
"io"
"net/http"
"strconv"
)
const (
recordList string = "http://api.dns.la/api/recordList"
recordModify string = "http://api.dns.la/api/record"
recordCreate string = "http://api.dns.la/api/record"
)
// https://www.dns.la/docs/ApiDoc
// dnsla dnsla实现
type Dnsla struct {
DNS config.DNS
Domains config.Domains
TTL int
}
// DnslaRecord
type DnslaRecord struct {
ID string `json:"id"`
Host string `json:"host"`
Type int `json:"type"`
Data string `json:"data"`
}
// DnslaRecordListResp recordListAPI结果
type DnslaRecordListResp struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data struct {
Total int `json:"total"`
Results []DnslaRecord `json:"results"`
} `json:"data"`
}
// DnslaStatus DnslaStatus
type DnslaStatus struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data struct {
Id string `json:"id"`
} `json:"data"`
}
// Init 初始化
func (dnsla *Dnsla) Init(dnsConf *config.DnsConfig, ipv4cache *util.IpCache, ipv6cache *util.IpCache) {
dnsla.Domains.Ipv4Cache = ipv4cache
dnsla.Domains.Ipv6Cache = ipv6cache
dnsla.DNS = dnsConf.DNS
dnsla.Domains.GetNewIp(dnsConf)
if dnsConf.TTL == "" {
// 默认600s
dnsla.TTL = 600
} else {
ttlInt, _ := strconv.Atoi(dnsConf.TTL)
dnsla.TTL = ttlInt
}
}
// AddUpdateDomainRecords 添加或更新IPv4/IPv6记录
func (dnsla *Dnsla) AddUpdateDomainRecords() config.Domains {
dnsla.addUpdateDomainRecords("A")
dnsla.addUpdateDomainRecords("AAAA")
return dnsla.Domains
}
func (dnsla *Dnsla) addUpdateDomainRecords(recordType string) {
ipAddr, domains := dnsla.Domains.GetNewIpResult(recordType)
if ipAddr == "" {
return
}
for _, domain := range domains {
resultByte, err := dnsla.getRecordList(domain, recordType)
if err != nil {
util.Log("查询域名信息发生异常! %s", err)
domain.UpdateStatus = config.UpdatedFailed
return
}
var jsonResult DnslaRecordListResp
errU := json.Unmarshal(resultByte, &jsonResult)
if errU != nil {
util.Log(errU.Error())
return
}
if jsonResult.Data.Total > 0 { // 默认第一个
recordSelected := jsonResult.Data.Results[0]
params := domain.GetCustomParams()
if params.Has("id") {
for i := 0; i < len(jsonResult.Data.Results); i++ {
if jsonResult.Data.Results[i].ID == params.Get("id") {
recordSelected = jsonResult.Data.Results[i]
}
}
}
// 更新
dnsla.modify(recordSelected, domain, recordType, ipAddr)
} else {
// 新增
dnsla.create(domain, recordType, ipAddr)
}
}
}
// 创建
func (dnsla *Dnsla) create(domain *config.Domain, recordType string, ipAddr string) {
recordTypeInt := 1
if recordType == "AAAA" {
recordTypeInt = 28
}
type CreateParams struct {
Domain string `json:"Domain"`
Host string `json:"Host"`
Type int `json:"Type"`
Data string `json:"Data"`
TTL int `json:"TTL"`
}
createParams := CreateParams{
Domain: domain.DomainName,
Host: domain.GetSubDomain(),
Type: recordTypeInt,
Data: ipAddr,
TTL: dnsla.TTL,
}
jsonData, _ := json.Marshal(createParams)
resultByte, err := dnsla.request("POST", recordCreate, jsonData)
if err != nil {
util.Log("新增域名解析 %s 失败! 异常信息: %s", domain, err)
domain.UpdateStatus = config.UpdatedFailed
return
}
var jsonResult DnslaStatus
errU := json.Unmarshal(resultByte, &jsonResult)
if errU != nil {
util.Log(errU.Error())
return
}
if jsonResult.Code == 200 {
util.Log("新增域名解析 %s 成功! IP: %s", domain, ipAddr)
domain.UpdateStatus = config.UpdatedSuccess
} else {
util.Log("新增域名解析 %s 失败! 异常信息: %s", domain, jsonResult.Msg)
domain.UpdateStatus = config.UpdatedFailed
}
}
// 修改
func (dnsla *Dnsla) modify(record DnslaRecord, domain *config.Domain, recordType string, ipAddr string) {
// 相同不修改
if record.Data == ipAddr {
util.Log("你的IP %s 没有变化, 域名 %s", ipAddr, domain)
return
}
recordTypeInt := 1
if recordType == "AAAA" {
recordTypeInt = 28
}
type ModifyParams struct {
ID string `json:"Id"`
Host string `json:"Host"`
Type int `json:"Type"`
Data string `json:"Data"`
TTL int `json:"TTL"`
}
modifyParams := ModifyParams{
ID: record.ID,
Host: domain.GetSubDomain(),
Type: recordTypeInt,
Data: ipAddr,
TTL: dnsla.TTL,
}
jsonData, _ := json.Marshal(modifyParams)
resultByte, err := dnsla.request("PUT", recordModify, jsonData)
if err != nil {
util.Log("更新域名解析 %s 失败! 异常信息: %s", domain, err)
domain.UpdateStatus = config.UpdatedFailed
return
}
var jsonResult DnslaStatus
errU := json.Unmarshal(resultByte, &jsonResult)
if errU != nil {
util.Log(errU.Error())
return
}
if jsonResult.Code == 200 {
util.Log("更新域名解析 %s 成功! IP: %s", domain, ipAddr)
domain.UpdateStatus = config.UpdatedSuccess
} else {
util.Log("更新域名解析 %s 失败! 异常信息: %s", domain, jsonResult.Msg)
domain.UpdateStatus = config.UpdatedFailed
}
}
// request sends a POST request to the given API with the given values.
func (dnsla *Dnsla) request(method, apiAddr string, values []byte) (body []byte, err error) {
req, err := http.NewRequest(
method,
apiAddr,
bytes.NewReader(values),
)
if err != nil {
panic(err)
}
// 设置自定义 Headers
byteBuff := []byte(dnsla.DNS.ID + ":" + dnsla.DNS.Secret)
token := "Basic " + base64.StdEncoding.EncodeToString(byteBuff)
req.Header.Set("Authorization", token)
req.Header.Set("Content-Type", "application/json;charset=utf-8")
// 4. 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ = io.ReadAll(resp.Body)
return
}
// 获得域名记录列表
func (dnsla *Dnsla) getRecordList(domain *config.Domain, typ string) (result []byte, err error) {
recordTypeInt := "1"
if typ == "AAAA" {
recordTypeInt = "28"
}
params := domain.GetCustomParams()
params.Set("domain", domain.DomainName)
params.Set("host", domain.GetSubDomain())
params.Set("type", recordTypeInt)
params.Set("pageIndex", "1")
params.Set("pageSize", "999")
url := recordList + "?" + params.Encode()
req, err := http.NewRequest("GET", url, nil)
if err != nil {
panic(err)
}
byteBuff := []byte(dnsla.DNS.ID + ":" + dnsla.DNS.Secret)
token := "Basic " + base64.StdEncoding.EncodeToString(byteBuff)
// 设置 Headers
req.Header.Set("Authorization", token)
// 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
// 读取响应
result, errR := io.ReadAll(resp.Body)
if errR != nil {
util.Log(errR.Error())
return
}
return
}

View File

@ -64,6 +64,8 @@ func RunOnce() {
dnsSelected = &TrafficRoute{} dnsSelected = &TrafficRoute{}
case "dnspod": case "dnspod":
dnsSelected = &Dnspod{} dnsSelected = &Dnspod{}
case "dnsla":
dnsSelected = &Dnsla{}
case "cloudflare": case "cloudflare":
dnsSelected = &Cloudflare{} dnsSelected = &Cloudflare{}
case "huaweicloud": case "huaweicloud":

View File

@ -180,6 +180,18 @@ const DNS_PROVIDERS = {
"zh-cn": "<a target='_blank' href='https://www.spaceship.com/application/api-manager/'>创建 API 密钥</a>", "zh-cn": "<a target='_blank' href='https://www.spaceship.com/application/api-manager/'>创建 API 密钥</a>",
} }
}, },
dnsla: {
name: {
"en": "Dnsla",
"zh-cn": "Dnsla",
},
idLabel: "APIID",
secretLabel: "API密钥",
helpHtml: {
"en": "<a target='_blank' href='https://console.dns.la/login?aksk=1'>Create AccessKey</a>",
"zh-cn": "<a target='_blank' href='https://console.dns.la/login?aksk=1'>创建 AccessKey</a>",
}
},
}; };
const SVG_CODE = { const SVG_CODE = {