Skip to content

Commit

Permalink
feat: add provider dnsla
Browse files Browse the repository at this point in the history
  • Loading branch information
eryajf committed Sep 7, 2024
1 parent fae921a commit f7d2d51
Show file tree
Hide file tree
Showing 12 changed files with 562 additions and 4 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ record_cert_info{
- [x] Tencent DnsPod
- [x] Aliyun Dns
- [x] Godaddy
- [x] DNSLA

## Grafana 仪表板

Expand Down
7 changes: 6 additions & 1 deletion config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,9 @@ cloud_providers:
- name: g1
secretId: "xxxxx"
secretKey: "xxxxx"
# 目前支持Tencent, Aliyun, Godaddy,如需支持更多云厂商,请提交 issue,也欢迎 PR
dnsla:
accounts:
- name: d1
secretId: "xxxxx"
secretKey: "xxxxx"
# 目前支持Tencent, Aliyun, Godaddy,DNALA,如需支持更多云厂商,请提交 issue,也欢迎 PR
37 changes: 37 additions & 0 deletions dnslib/dnsla/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package dnsla

import (
"errors"
"time"

"github.com/go-resty/resty/v2"
)

// Client DNS.LA 客户端
type Client struct {
client *resty.Client

// Services
Domains *DomainService
Records *RecordService
}

var baseUrl = "https://api.dns.la"

// NewClient 初始化客户端
func NewClient(key, secret string) (*Client, error) {
c := new(Client)
if key == "" {
return c, errors.New("missing dns.la API key")
}
if secret == "" {
return c, errors.New("missing dns.la API secret")
}
c.client = resty.New().SetBaseURL(baseUrl).SetBasicAuth(key, secret).
SetTimeout(3 * time.Second).SetRetryCount(3).SetRetryWaitTime(2 * time.Second)
// Initialize services
c.Domains = &DomainService{c}
c.Records = &RecordService{c}

return c, nil
}
38 changes: 38 additions & 0 deletions dnslib/dnsla/domain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package dnsla

import (
"fmt"
"net/url"
"strconv"
)

// DomainService 域名服务
type DomainService struct{ *Client }

// List 获取域名列表
func (d *DomainService) List(page PageOption, options ...DomainListOption) (*DomainListResponse, error) {
params := url.Values{}
params.Set("pageIndex", strconv.Itoa(page.PageIndex))
params.Set("pageSize", strconv.Itoa(page.PageSize))
for _, option := range options {
option(params)
}
resp, err := d.client.R().
SetQueryParamsFromValues(params).
SetResult(&DomainListResponse{}).
Get("/api/domainList")
if err != nil {
return nil, err
}

if resp.IsError() {
return nil, fmt.Errorf("API request failed with status code %d: %s", resp.StatusCode(), resp.String())
}

result, ok := resp.Result().(*DomainListResponse)
if !ok {
return nil, fmt.Errorf("failed to cast response to *Response")
}

return result, nil
}
177 changes: 177 additions & 0 deletions dnslib/dnsla/models.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package dnsla

import (
"net/url"
"strconv"
)

// DomainResponse 解析记录列表响应
type DomainListResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data struct {
Total int `json:"total"`
Results []Domain `json:"results"`
} `json:"data"`
}

// Domain 域名
type Domain struct {
ID string `json:"id"` // 域名ID
CreatedAt int64 `json:"createdAt"` // 域名添加时间 Unix 时间戳
UpdatedAt int64 `json:"updatedAt"` // 域名最后修改时间 Unix 时间戳
UserID string `json:"userId"` // 用户ID
UserAccount string `json:"userAccount"` // 用户账号
AssetID string `json:"assetId"` // 域名当前生效套餐的资产ID
GroupID string `json:"groupId"` // 分组ID,空为默认分组
GroupName string `json:"groupName"` // 分组名称,空为默认分组
Domain string `json:"domain"` // Punycode 编码后的域名
DisplayDomain string `json:"displayDomain"` // Punycode 编码前的域名
State int `json:"state"` // 域名状态 1 正常 | 2 暂停 其他状态咨询客服
NsState int `json:"nsState"` // 域名NS状态 0 未知 | 1 匹配 | 2 未匹配 | 3 未加入
NsCheckedAt int64 `json:"nsCheckedAt"` // 域名NS检查时间
ProductCode string `json:"productCode"` // 域名套餐代码
ProductName string `json:"productName"` // 域名套餐名称
ExpiredAt int64 `json:"expiredAt"` // 过期时间 Unix 时间戳,免费域名过期时间 2100-01-01
QuoteDomainID string `json:"quoteDomainId"` // 引用域名ID
QuoteDomain string `json:"quoteDomain"` // 引用域名
Suffix string `json:"suffix"` // Punycode 编码后的顶级域名
DisplaySuffix string `json:"displaySuffix"` // Punycode 编码前的顶级域名
}

// RecordListResponse 解析记录列表响应
type RecordListResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data struct {
Total int `json:"total"`
Results []Record `json:"results"`
} `json:"data"`
}

// Record 解析记录
type Record struct {
ID string `json:"id"` // 记录ID
CreatedAt int64 `json:"createdAt"` // 记录添加时间 Unix 时间戳
UpdatedAt int64 `json:"updatedAt"` // 记录最后修改时间 Unix 时间戳
DomainID string `json:"domainId"` // 域名ID
GroupID string `json:"groupId"` // 分组ID,空为默认分组
GroupName string `json:"groupName"` // 分组名称,空为默认分组
Host string `json:"host"` // Punycode 编码后的主机头
DisplayHost string `json:"displayHost"` // Punycode 编码前的主机头
Type int `json:"type"` // 记录类型
LineID string `json:"lineId"` // 线路id,参考线路文档
LineCode string `json:"lineCode"` // 线路code,参考线路文档
LineName string `json:"lineName"` // 线路名称,参考线路文档
Data string `json:"data"` // Punycode 编码后的记录值
DisplayData string `json:"displayData"` // Punycode 编码前的记录值
TTL int `json:"ttl"` // TTL
Weight int `json:"weight"` // 权重
Preference int `json:"preference"` // MX优先级
Dominant bool `json:"domaint"` // 是否显性URL转发
System bool `json:"system"` // 是否系统解析记录
Disable bool `json:"disable"` // 是否暂停
}

// DomainListOption 获取域名列表的参数
type DomainListOption func(url.Values)

func DLWithGroupID(groupID string) DomainListOption {
return func(v url.Values) {
if groupID != "" {
v.Set("groupId", groupID)
}
}
}

func DLWithState(state int) DomainListOption {
return func(v url.Values) {
v.Set("state", strconv.Itoa(state))
}
}

func DLWithProductCode(productCode string) DomainListOption {
return func(v url.Values) {
if productCode != "" {
v.Set("productCode", productCode)
}
}
}

func DLWithQuoteDomainID(quoteDomainID string) DomainListOption {
return func(v url.Values) {
if quoteDomainID != "" {
v.Set("quoteDomainId", quoteDomainID)
}
}
}

func DLWithExpiredAtRange(begin, end int64) DomainListOption {
return func(v url.Values) {
if begin != 0 {
v.Set("expiredAtBegin", strconv.FormatInt(begin, 10))
}
if end != 0 {
v.Set("expiredAtEnd", strconv.FormatInt(end, 10))
}
}
}

// RecordListOption 获取解析记录列表的参数
type RecordListOption func(url.Values)

func RLWithRecordType(recordType int) RecordListOption {
return func(v url.Values) {
v.Set("type", strconv.Itoa(recordType))
}
}

func RLWithGroupID(groupID string) RecordListOption {
return func(v url.Values) {
if groupID != "" {
v.Set("groupId", groupID)
}
}
}

func RLWithLineID(lineID string) RecordListOption {
return func(v url.Values) {
if lineID != "" {
v.Set("lineId", lineID)
}
}
}

func RLWithHost(host string) RecordListOption {
return func(v url.Values) {
if host != "" {
v.Set("host", host)
}
}
}

func RLWithData(data string) RecordListOption {
return func(v url.Values) {
if data != "" {
v.Set("data", data)
}
}
}

func RLWithDisable(disable bool) RecordListOption {
return func(v url.Values) {
v.Set("disable", strconv.FormatBool(disable))
}
}

func RLWithSystem(system bool) RecordListOption {
return func(v url.Values) {
v.Set("system", strconv.FormatBool(system))
}
}

func RLWithDominant(dominant bool) RecordListOption {
return func(v url.Values) {
v.Set("dominant", strconv.FormatBool(dominant))
}
}
20 changes: 20 additions & 0 deletions dnslib/dnsla/public.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package dnsla

type PageOption struct {
PageIndex int `json:"pageIndex"`
PageSize int `json:"pageSize"`
}

// NewPageOption 创建一个分页参数
func NewPageOption(pageIndex, pageSize int) PageOption {
if !(pageSize > 0 && pageSize <= 1000) || pageIndex < 0 || pageSize <= 0 {
return PageOption{
PageIndex: 1,
PageSize: 1000,
}
}
return PageOption{
PageIndex: pageIndex,
PageSize: pageSize,
}
}
36 changes: 36 additions & 0 deletions dnslib/dnsla/record.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package dnsla

import (
"fmt"
"net/url"
"strconv"
)

// RecordService 解析记录服务
type RecordService struct{ *Client }

// ListRecords 获取域名解析记录列表
func (r *RecordService) List(page PageOption, domainID string, options ...RecordListOption) (*RecordListResponse, error) {
params := url.Values{}
params.Set("pageIndex", strconv.Itoa(page.PageIndex))
params.Set("pageSize", strconv.Itoa(page.PageSize))
params.Set("domainId", domainID)
for _, option := range options {
option(params)
}
resp, err := r.client.R().
SetQueryParamsFromValues(params).
SetResult(&RecordListResponse{}).
Get("/api/recordList")
if err != nil {
return nil, err
}
if resp.IsError() {
return nil, fmt.Errorf("API request failed with status code %d: %s", resp.StatusCode(), resp.String())
}
result, ok := resp.Result().(*RecordListResponse)
if !ok {
return nil, fmt.Errorf("failed to cast response to *RecordResponse")
}
return result, nil
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/allegro/bigcache/v3 v3.1.0
github.com/alyx/go-daddy v0.0.0-20240819232932-c2e4d209da9b
github.com/charmbracelet/log v0.2.2
github.com/go-resty/resty/v2 v2.14.0
github.com/golang-module/carbon/v2 v2.3.12
github.com/prometheus/client_golang v1.16.0
github.com/robfig/cron/v3 v3.0.1
Expand All @@ -30,7 +31,6 @@ require (
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
github.com/aliyun/credentials-go v1.3.1 // indirect
github.com/clbanning/mxj/v2 v2.5.5 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.12.1-0.20240709150035-ccf4b4329d21 // indirect
Expand Down
Loading

0 comments on commit f7d2d51

Please sign in to comment.