Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dns): support vercel dns #1049

Merged
merged 3 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion dns/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ func RunOnce() {
dnsSelected = &NameCheap{}
case "namesilo":
dnsSelected = &NameSilo{}
case "vercel":
dnsSelected = &Vercel{}
default:
dnsSelected = &Alidns{}
}
Expand All @@ -99,5 +101,4 @@ func RunOnce() {
}

util.ForceCompareGlobal = false

}
176 changes: 176 additions & 0 deletions dns/vercel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package dns

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"

"github.com/jeessy2/ddns-go/v6/config"
"github.com/jeessy2/ddns-go/v6/util"
)

type Vercel struct {
DNS config.DNS
Domains config.Domains
TTL int
}

type ListExistingRecordsResponse struct {
Records []Record `json:"records"`
}

type Record struct {
ID string `json:"id"` // 记录ID
Slug string `json:"slug"`
Name string `json:"name"` // 记录名称
Type string `json:"type"` // 记录类型
Value string `json:"value"` // 记录值
Creator string `json:"creator"`
Created int64 `json:"created"`
Updated int64 `json:"updated"`
CreatedAt int64 `json:"createdAt"`
UpdatedAt int64 `json:"updatedAt"`
TTL int64 `json:"ttl"`
Comment *string `json:"comment,omitempty"`
}

func (v *Vercel) Init(dnsConf *config.DnsConfig, ipv4cache *util.IpCache, ipv6cache *util.IpCache) {
v.Domains.Ipv4Cache = ipv4cache
v.Domains.Ipv6Cache = ipv6cache
v.DNS = dnsConf.DNS
v.Domains.GetNewIp(dnsConf)

// Must be greater than 60
ttl, err := strconv.Atoi(dnsConf.TTL)
if err != nil {
ttl = 60
}
if ttl < 60 {
ttl = 60
}
v.TTL = ttl
}

func (v *Vercel) AddUpdateDomainRecords() (domains config.Domains) {
v.addUpdateDomainRecords("A")
v.addUpdateDomainRecords("AAAA")
return v.Domains
}

func (v *Vercel) addUpdateDomainRecords(recordType string) {
ipAddr, domains := v.Domains.GetNewIpResult(recordType)

if ipAddr == "" {
return
}

ipAddr = strings.ToLower(ipAddr)

var (
records []Record
err error
)
for _, domain := range domains {
records, err = v.listExistingRecords(domain)
if err != nil {
util.Log("查询域名信息发生异常! %s", err)
continue
}

var targetRecord *Record
for _, record := range records {
if record.Name == domain.SubDomain {
targetRecord = &record
break
}
}

if targetRecord == nil {
err = v.createRecord(domain, recordType, ipAddr)
} else {
if strings.ToLower(targetRecord.Value) == ipAddr {
util.Log("你的IP %s 没有变化, 域名 %s", ipAddr, domain)
domain.UpdateStatus = config.UpdatedNothing
continue
} else {
err = v.updateRecord(targetRecord, recordType, ipAddr)
}
}

operation := "新增"
if targetRecord != nil {
operation = "更新"
}
if err == nil {
util.Log(operation+"域名解析 %s 成功! IP: %s", domain, ipAddr)
domain.UpdateStatus = config.UpdatedSuccess
} else {
util.Log(operation+"域名解析 %s 失败! 异常信息: %s", domain, err)
domain.UpdateStatus = config.UpdatedFailed
}
}
}

func (v *Vercel) listExistingRecords(domain *config.Domain) (records []Record, err error) {
var result ListExistingRecordsResponse
err = v.request(http.MethodGet, "https://api.vercel.com/v4/domains/"+domain.DomainName+"/records", nil, &result)
if err != nil {
return
}
records = result.Records
return
}

func (v *Vercel) createRecord(domain *config.Domain, recordType string, recordValue string) (err error) {
err = v.request(http.MethodPost, "https://api.vercel.com/v2/domains/"+domain.DomainName+"/records", map[string]interface{}{
"name": domain.SubDomain,
"type": recordType,
"value": recordValue,
"ttl": v.TTL,
"comment": "Created by ddns-go",
}, nil)
return
}

func (v *Vercel) updateRecord(record *Record, recordType string, recordValue string) (err error) {
err = v.request(http.MethodPatch, "https://api.vercel.com/v1/domains/records/"+record.ID, map[string]interface{}{
"type": recordType,
"value": recordValue,
"ttl": v.TTL,
}, nil)
return
}

func (v *Vercel) request(method, api string, data, result interface{}) (err error) {
var payload []byte
if data != nil {
payload, _ = json.Marshal(data)
}

req, err := http.NewRequest(
method,
api,
bytes.NewBuffer(payload),
)
if err != nil {
return
}
req.Header.Set("Authorization", "Bearer "+v.DNS.Secret)
req.Header.Set("Content-Type", "application/json")

client := util.CreateHTTPClient()
resp, err := client.Do(req)
if err != nil {
return err
}
if resp.StatusCode != 200 {
return fmt.Errorf("Vercel API returned status code %d", resp.StatusCode)
}
if result != nil {
err = util.GetHTTPResponse(resp, err, result)
}
return
}
15 changes: 13 additions & 2 deletions static/constant.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,17 @@ const DNS_PROVIDERS = {
"zh-cn": "<a target='_blank' href='https://www.namesilo.com/account/api-manager'>开启namesilo动态域名解析</a> <b>请注意namesilo的TTL最低1小时</b>",
}
},
vercel: {
name: {
"en": "Vercel",
},
idLabel: "",
secretLabel: "Token",
helpHtml: {
"en": "<a target='_blank' href='https://vercel.com/account/tokens'>Create Token</a>",
"zh-cn": "<a target='_blank' href='https://vercel.com/account/tokens'>创建令牌</a>",
}
},
};

const SVG_CODE = {
Expand Down Expand Up @@ -172,7 +183,7 @@ const I18N_MAP = {
'domainsHelp': `
Enter one domain per line.
If the domain is unregistrable, manually separate it into a subdomain and a root domain by using a colon. e.g. <code>www:domain.example.com</code><br />

Support for <a target="blank" href="https://github.com/jeessy2/ddns-go/wiki/传递自定义参数">custom parameters</a> (Simplified Chinese)
`,
'Regular exp.': 'Regular exp.',
Expand Down Expand Up @@ -231,7 +242,7 @@ const I18N_MAP = {
'domainsHelp': `
每行一个域名。
如果域名不可注册,请使用冒号手动将其分为子域名和根域名。如 <code>www:domain.example.com</code><br />

支持<a target="blank" href="https://github.com/jeessy2/ddns-go/wiki/传递自定义参数">自定义参数</a>
`,
'Regular exp.': '匹配正则表达式',
Expand Down