Skip to content

Commit

Permalink
refact: webhook
Browse files Browse the repository at this point in the history
  • Loading branch information
jeessy2 committed Oct 20, 2020
1 parent 896066f commit 5ca553c
Show file tree
Hide file tree
Showing 13 changed files with 284 additions and 255 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,21 @@ docker run -d \
- [可选] 使用IPV6后,建议设置登录用户名和密码

## Webhook
- 支持webhook, 当IP有变化时, 会回调填写的URL
- 支持webhook, 域名更新成功或不成功时, 会回调填写的URL
- 支持的变量

| 变量名 | 说明 |
| ---- | ---- |
| #{ipv4New} | 新的IPV4地址 |
| #{ipv4Old} | 旧的IPV4地址 |
| #{ipv6New} | 新的IPV6地址 |
| #{ipv6Old} | 旧的IPV6地址 |
| #{ipv4Addr} | 新的IPV4地址 |
| #{ipv4Result} | IPV4地址更新结果: `未改变` `失败` `成功`|
| #{ipv4Domains} | IPV4的域名,多个以`,`分割 |
| #{ipv6Addr} | 新的IPV6地址 |
| #{ipv6Result} | IPV6地址更新结果: `未改变` `失败` `成功`|
| #{ipv6Domains} | IPV6的域名,多个以`,`分割 |

- RequestBody为空GET请求,不为空POST请求
- 例(URL): `https://sc.ftqq.com/[SCKEY].send?text=主人IP变了#{ipv4New}`
- 例(RequestBody): `{"text":"你的IPv4已变为#{ipv4New}","desp":"域名有#{ipv4Domains}"}}`
- 例(URL): `https://sc.ftqq.com/[SCKEY].send?text=主人IPv4变了#{ipv4Addr},更新结果:#{ipv4Result}`
- 例(RequestBody): `{"text":"你的IPv4已变为#{ipv4Addr}","desp":"更新结果: #{ipv4Result}"}}`


![avatar](https://raw.githubusercontent.com/jeessy2/ddns-go/master/ddns-web.png)
Expand Down
1 change: 1 addition & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Config struct {
}
DNS DNSConfig
User
Webhook
}

// DNSConfig DNS配置
Expand Down
184 changes: 184 additions & 0 deletions config/domains.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package config

import (
"ddns-go/util"
"fmt"
"log"
"net/http"
"strings"
"time"
)

// updateStatusType 更新状态
type updateStatusType string

const (
// UpdatedNothing 未改变
UpdatedNothing updateStatusType = "未改变"
// UpdatedFailed 更新失败
UpdatedFailed = "失败"
// UpdatedSuccess 更新成功
UpdatedSuccess = "成功"
)

// Domains Ipv4/Ipv6 domains
type Domains struct {
Ipv4Addr string
Ipv4Domains []*Domain
Ipv6Addr string
Ipv6Domains []*Domain
}

// Domain 域名实体
type Domain struct {
DomainName string
SubDomain string
UpdateStatus updateStatusType // 更新状态
}

func (d Domain) String() string {
if d.SubDomain != "" {
return d.SubDomain + "." + d.DomainName
}
return d.DomainName
}

// GetFullDomain 获得全部的,子域名
func (d Domain) GetFullDomain() string {
if d.SubDomain != "" {
return d.SubDomain + "." + d.DomainName
}
return "@" + "." + d.DomainName
}

// GetSubDomain 获得子域名,为空返回@
// 阿里云,dnspod需要
func (d Domain) GetSubDomain() string {
if d.SubDomain != "" {
return d.SubDomain
}
return "@"
}

// ParseDomain 接口获得ip并校验用户输入的域名
func (domains *Domains) ParseDomain(conf *Config) {
// IPV4
ipv4Addr := conf.GetIpv4Addr()
if ipv4Addr != "" {
domains.Ipv4Addr = ipv4Addr
domains.Ipv4Domains = parseDomainArr(conf.Ipv4.Domains)
}
// IPV6
ipv6Addr := conf.GetIpv6Addr()
if ipv6Addr != "" {
domains.Ipv6Addr = ipv6Addr
domains.Ipv6Domains = parseDomainArr(conf.Ipv6.Domains)
}
}

// parseDomainArr 校验用户输入的域名
func parseDomainArr(domainArr []string) (domains []*Domain) {
for _, domainStr := range domainArr {
domainStr = strings.Trim(domainStr, " ")
if domainStr != "" {
domain := &Domain{}
sp := strings.Split(domainStr, ".")
length := len(sp)
if length <= 1 {
log.Println(domainStr, "域名不正确")
continue
} else if length == 2 {
domain.DomainName = domainStr
} else {
// >=3
domain.DomainName = sp[length-2] + "." + sp[length-1]
domain.SubDomain = domainStr[:len(domainStr)-len(domain.DomainName)-1]
}
domains = append(domains, domain)
}
}
return
}

// ParseDomainResult 获得ParseDomain结果
func (domains *Domains) ParseDomainResult(recordType string) (ipAddr string, retDomains []*Domain) {
if recordType == "AAAA" {
return domains.Ipv6Addr, domains.Ipv6Domains
}
return domains.Ipv4Addr, domains.Ipv4Domains

}

// ExecWebhook 添加或更新IPV4/IPV6记录
func (domains *Domains) ExecWebhook(conf *Config) {
v4Status := getDomainsStatus(domains.Ipv4Domains)
v6Status := getDomainsStatus(domains.Ipv6Domains)

if conf.WebhookURL != "" && (v4Status != UpdatedNothing || v6Status != UpdatedNothing) {
// 成功和失败都要触发webhook
method := "GET"
postPara := ""
if conf.DNS.Secret != "" {
method = "POST"
postPara = domains.replacePara(conf.WebhookRequestBody, v4Status, v6Status)
}
requestURL := domains.replacePara(conf.WebhookURL, v4Status, v6Status)
req, err := http.NewRequest(method, requestURL, strings.NewReader(postPara))

clt := http.Client{}
clt.Timeout = 30 * time.Second
resp, err := clt.Do(req)
body, err := util.GetHTTPResponseOrg(resp, requestURL, err)
if err == nil {
log.Println(fmt.Sprintf("Webhook调用成功, 返回数据: %s", string(body)))
} else {
log.Println("Webhook调用失败")
}
}
}

// getDomainsStr 用逗号分割域名
func getDomainsStatus(domains []*Domain) updateStatusType {
successNum := 0
for _, v46 := range domains {
switch v46.UpdateStatus {
case UpdatedFailed:
// 一个失败,全部失败
return UpdatedFailed
case UpdatedSuccess:
successNum++
}
}

if successNum > 0 {
// 迭代完成后一个成功,就成功
return UpdatedSuccess
}
return UpdatedNothing
}

// replacePara 替换参数
func (domains *Domains) replacePara(orgPara string, ipv4Result updateStatusType, ipv6Result updateStatusType) (newPara string) {
orgPara = strings.ReplaceAll(orgPara, "#{ipv4Addr}", domains.Ipv4Addr)
orgPara = strings.ReplaceAll(orgPara, "#{ipv4Result}", string(ipv4Result))
orgPara = strings.ReplaceAll(orgPara, "#{ipv4Domains}", getDomainsStr(domains.Ipv4Domains))

orgPara = strings.ReplaceAll(orgPara, "#{ipv6New}", domains.Ipv6Addr)
orgPara = strings.ReplaceAll(orgPara, "#{ipv6Result}", string(ipv6Result))
orgPara = strings.ReplaceAll(orgPara, "#{ipv6Domains}", getDomainsStr(domains.Ipv6Domains))

return orgPara
}

// getDomainsStr 用逗号分割域名
func getDomainsStr(domains []*Domain) string {
str := ""
for i, v46 := range domains {
str += v46.String()
if i != len(domains)-1 {
str += ","
}
}

return str
}
7 changes: 7 additions & 0 deletions config/webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package config

// Webhook Webhook
type Webhook struct {
WebhookURL string
WebhookRequestBody string
}
19 changes: 10 additions & 9 deletions dns/alidns.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (

// Alidns 阿里云dns实现
type Alidns struct {
client *alidnssdk.Client
Domains
client *alidnssdk.Client
Domains config.Domains
}

// Init 初始化
Expand All @@ -26,18 +26,14 @@ func (ali *Alidns) Init(conf *config.Config) {
}

// AddUpdateDomainRecords 添加或更新IPV4/IPV6记录
func (ali *Alidns) AddUpdateDomainRecords() {
func (ali *Alidns) AddUpdateDomainRecords() config.Domains {
ali.addUpdateDomainRecords("A")
ali.addUpdateDomainRecords("AAAA")
return ali.Domains
}

func (ali *Alidns) addUpdateDomainRecords(recordType string) {
ipAddr := ali.Ipv4Addr
domains := ali.Ipv4Domains
if recordType == "AAAA" {
ipAddr = ali.Ipv6Addr
domains = ali.Ipv6Domains
}
ipAddr, domains := ali.Domains.ParseDomainResult(recordType)

if ipAddr == "" {
return
Expand All @@ -50,6 +46,7 @@ func (ali *Alidns) addUpdateDomainRecords(recordType string) {
existReq.SubDomain = domain.GetFullDomain()
rep, err := ali.client.DescribeSubDomainRecords(existReq)
if err != nil {
domain.UpdateStatus = config.UpdatedFailed
log.Println(err)
}
if rep.TotalCount > 0 {
Expand All @@ -69,8 +66,10 @@ func (ali *Alidns) addUpdateDomainRecords(recordType string) {
updateResp, err := ali.client.UpdateDomainRecord(request)
if err == nil && updateResp.BaseResponse.IsSuccess() {
log.Printf("更新域名解析 %s 成功!IP: %s", domain, ipAddr)
domain.UpdateStatus = config.UpdatedSuccess
} else {
log.Printf("更新域名解析 %s 失败!IP: %s, Error: %s, Response: %s", domain, ipAddr, err, updateResp.GetHttpContentString())
domain.UpdateStatus = config.UpdatedFailed
}
}
} else {
Expand All @@ -85,8 +84,10 @@ func (ali *Alidns) addUpdateDomainRecords(recordType string) {
createResp, err := ali.client.AddDomainRecord(request)
if err == nil && createResp.BaseResponse.IsSuccess() {
log.Printf("新增域名解析 %s 成功!IP: %s", domain, ipAddr)
domain.UpdateStatus = config.UpdatedSuccess
} else {
log.Printf("新增域名解析 %s 失败!IP: %s, Error: %s, Response: %s", domain, ipAddr, err, createResp.GetHttpContentString())
domain.UpdateStatus = config.UpdatedFailed
}
}
}
Expand Down
23 changes: 12 additions & 11 deletions dns/cloudflare.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const (
// Cloudflare Cloudflare实现
type Cloudflare struct {
DNSConfig config.DNSConfig
Domains
Domains config.Domains
}

// CloudflareZonesResp cloudflare zones返回结果
Expand Down Expand Up @@ -61,18 +61,14 @@ func (cf *Cloudflare) Init(conf *config.Config) {
}

// AddUpdateDomainRecords 添加或更新IPV4/IPV6记录
func (cf *Cloudflare) AddUpdateDomainRecords() {
func (cf *Cloudflare) AddUpdateDomainRecords() config.Domains {
cf.addUpdateDomainRecords("A")
cf.addUpdateDomainRecords("AAAA")
return cf.Domains
}

func (cf *Cloudflare) addUpdateDomainRecords(recordType string) {
ipAddr := cf.Ipv4Addr
domains := cf.Ipv4Domains
if recordType == "AAAA" {
ipAddr = cf.Ipv6Addr
domains = cf.Ipv6Domains
}
ipAddr, domains := cf.Domains.ParseDomainResult(recordType)

if ipAddr == "" {
return
Expand All @@ -82,6 +78,7 @@ func (cf *Cloudflare) addUpdateDomainRecords(recordType string) {
// get zone
result, err := cf.getZones(domain)
if err != nil || len(result.Result) != 1 {
domain.UpdateStatus = config.UpdatedFailed
return
}
zoneID := result.Result[0].ID
Expand Down Expand Up @@ -110,7 +107,7 @@ func (cf *Cloudflare) addUpdateDomainRecords(recordType string) {
}

// 创建
func (cf *Cloudflare) create(zoneID string, domain *Domain, recordType string, ipAddr string) {
func (cf *Cloudflare) create(zoneID string, domain *config.Domain, recordType string, ipAddr string) {
record := &CloudflareRecord{
Type: recordType,
Name: domain.String(),
Expand All @@ -128,13 +125,15 @@ func (cf *Cloudflare) create(zoneID string, domain *Domain, recordType string, i
)
if err == nil && status.Success {
log.Printf("新增域名解析 %s 成功!IP: %s", domain, ipAddr)
domain.UpdateStatus = config.UpdatedSuccess
} else {
log.Printf("新增域名解析 %s 失败!Messages: %s", domain, status.Messages)
domain.UpdateStatus = config.UpdatedFailed
}
}

// 修改
func (cf *Cloudflare) modify(result CloudflareRecordsResp, zoneID string, domain *Domain, recordType string, ipAddr string) {
func (cf *Cloudflare) modify(result CloudflareRecordsResp, zoneID string, domain *config.Domain, recordType string, ipAddr string) {

for _, record := range result.Result {
// 相同不修改
Expand All @@ -154,14 +153,16 @@ func (cf *Cloudflare) modify(result CloudflareRecordsResp, zoneID string, domain

if err == nil && status.Success {
log.Printf("更新域名解析 %s 成功!IP: %s", domain, ipAddr)
domain.UpdateStatus = config.UpdatedSuccess
} else {
log.Printf("更新域名解析 %s 失败!Messages: %s", domain, status.Messages)
domain.UpdateStatus = config.UpdatedFailed
}
}
}

// 获得域名记录列表
func (cf *Cloudflare) getZones(domain *Domain) (result CloudflareZonesResp, err error) {
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"),
Expand Down
Loading

0 comments on commit 5ca553c

Please sign in to comment.