You've already forked cloudflare-dns-cli
							
							Support deleting records
This commit is contained in:
		
							
								
								
									
										194
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										194
									
								
								main.go
									
									
									
									
									
								
							@@ -8,6 +8,8 @@ import (
 | 
				
			|||||||
	"log"
 | 
						"log"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"sort"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/libdns/cloudflare"
 | 
						"github.com/libdns/cloudflare"
 | 
				
			||||||
	"github.com/libdns/libdns"
 | 
						"github.com/libdns/libdns"
 | 
				
			||||||
@@ -16,76 +18,184 @@ import (
 | 
				
			|||||||
var (
 | 
					var (
 | 
				
			||||||
	domain = os.Getenv("DOMAIN")
 | 
						domain = os.Getenv("DOMAIN")
 | 
				
			||||||
	token  = os.Getenv("CF_TOKEN")
 | 
						token  = os.Getenv("CF_TOKEN")
 | 
				
			||||||
 | 
						ip     = net.IPv4(127, 0, 0, 1)
 | 
				
			||||||
 | 
						cname  string
 | 
				
			||||||
 | 
						sub    = "<UNSET>"
 | 
				
			||||||
 | 
						path   string
 | 
				
			||||||
 | 
						del    = false
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func genRecord() (libdns.Record, error) {
 | 
				
			||||||
 | 
						switch {
 | 
				
			||||||
 | 
						case cname != "":
 | 
				
			||||||
 | 
							return libdns.Record{
 | 
				
			||||||
 | 
								Type:  "CNAME",
 | 
				
			||||||
 | 
								Name:  sub,
 | 
				
			||||||
 | 
								Value: cname,
 | 
				
			||||||
 | 
							}, nil
 | 
				
			||||||
 | 
						case len(ip) == net.IPv4len:
 | 
				
			||||||
 | 
							return libdns.Record{
 | 
				
			||||||
 | 
								Type:  "A",
 | 
				
			||||||
 | 
								Name:  sub,
 | 
				
			||||||
 | 
								Value: ip.To4().String(),
 | 
				
			||||||
 | 
							}, nil
 | 
				
			||||||
 | 
						case len(ip) == net.IPv6len:
 | 
				
			||||||
 | 
							return libdns.Record{
 | 
				
			||||||
 | 
								Type:  "AAAA",
 | 
				
			||||||
 | 
								Name:  sub,
 | 
				
			||||||
 | 
								Value: ip.To16().String(),
 | 
				
			||||||
 | 
							}, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return libdns.Record{}, fmt.Errorf("neither cname nor valid ip is set")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func selectRecordsToDelete(recs []libdns.Record) []libdns.Record {
 | 
				
			||||||
 | 
						name := func() string {
 | 
				
			||||||
 | 
							if sub == "@" {
 | 
				
			||||||
 | 
								return domain
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return fmt.Sprintf("%s.%s", sub, domain)
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
						recs = func() (result []libdns.Record) {
 | 
				
			||||||
 | 
							for _, r := range recs {
 | 
				
			||||||
 | 
								if r.Name == name {
 | 
				
			||||||
 | 
									result = append(result, r)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
						fmt.Printf("Records matching %s\n", name)
 | 
				
			||||||
 | 
						for i, r := range recs {
 | 
				
			||||||
 | 
							fmt.Printf("%5d  %6s   %s\n", i, r.Type, r.Value)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var delRange string
 | 
				
			||||||
 | 
						fmt.Print("Enter record indexes seperated by comma(,) to delete. Use hyphen(-) for a closed range. Also can be all or none\nIndexes: ")
 | 
				
			||||||
 | 
						if _, err := fmt.Scanln(&delRange); err != nil {
 | 
				
			||||||
 | 
							log.Panicln(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						switch {
 | 
				
			||||||
 | 
						case delRange == "all":
 | 
				
			||||||
 | 
							return recs
 | 
				
			||||||
 | 
						case delRange == "none":
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							indexGen := func() func() int {
 | 
				
			||||||
 | 
								indexes := parseRange(delRange, len(recs)-1)
 | 
				
			||||||
 | 
								indexIndex := 0
 | 
				
			||||||
 | 
								return func() int {
 | 
				
			||||||
 | 
									if indexIndex == len(indexes) {
 | 
				
			||||||
 | 
										return -1
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									val := indexes[indexIndex]
 | 
				
			||||||
 | 
									indexIndex++
 | 
				
			||||||
 | 
									return val
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}()
 | 
				
			||||||
 | 
							return func() (result []libdns.Record) {
 | 
				
			||||||
 | 
								nextIndex := indexGen()
 | 
				
			||||||
 | 
								for i, r := range recs {
 | 
				
			||||||
 | 
									if i == nextIndex {
 | 
				
			||||||
 | 
										result = append(result, r)
 | 
				
			||||||
 | 
										nextIndex = indexGen()
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
	ctx := context.TODO()
 | 
						ctx := context.TODO()
 | 
				
			||||||
	ip := net.IPv4(127, 0, 0, 1)
 | 
					 | 
				
			||||||
	var cname string
 | 
					 | 
				
			||||||
	sub := "<UNSET>"
 | 
					 | 
				
			||||||
	var path string
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	flag.StringVar(&domain, "d", domain, "Domain name, e.g. example.com")
 | 
						flag.StringVar(&domain, "d", domain, "Domain name, e.g. example.com")
 | 
				
			||||||
	flag.StringVar(&sub, "s", sub, "Subdomain to add dns entry for, e.g. blog")
 | 
						flag.StringVar(&sub, "s", sub, "Subdomain, e.g. blog. Use @ for root")
 | 
				
			||||||
	flag.StringVar(&token, "a", token, "Cloudflare API Token")
 | 
						flag.StringVar(&token, "a", token, "Cloudflare API Token")
 | 
				
			||||||
	flag.TextVar(&ip, "i", ip, "IP address")
 | 
						flag.TextVar(&ip, "i", ip, "IP address")
 | 
				
			||||||
	flag.StringVar(&cname, "c", cname, "CNAME target")
 | 
						flag.StringVar(&cname, "c", cname, "CNAME target")
 | 
				
			||||||
	flag.StringVar(&path, "o", path, "path to save all records as json, e.g. ./records.json")
 | 
						flag.StringVar(&path, "o", path, "Path to save all records as json, e.g. ./records.json")
 | 
				
			||||||
 | 
						flag.BoolVar(&del, "x", del, "Delete records of subdomain")
 | 
				
			||||||
	flag.Parse()
 | 
						flag.Parse()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	provider := cloudflare.Provider{APIToken: token}
 | 
						provider := cloudflare.Provider{APIToken: token}
 | 
				
			||||||
	zone := domain + "."
 | 
						zone := domain + "."
 | 
				
			||||||
	fmt.Println(zone, sub, cname)
 | 
					 | 
				
			||||||
	makeRecord := func() (libdns.Record, error) {
 | 
					 | 
				
			||||||
		switch {
 | 
					 | 
				
			||||||
		case cname != "":
 | 
					 | 
				
			||||||
			return libdns.Record{
 | 
					 | 
				
			||||||
				Type:  "CNAME",
 | 
					 | 
				
			||||||
				Name:  sub,
 | 
					 | 
				
			||||||
				Value: cname,
 | 
					 | 
				
			||||||
			}, nil
 | 
					 | 
				
			||||||
		case len(ip) == net.IPv4len:
 | 
					 | 
				
			||||||
			return libdns.Record{
 | 
					 | 
				
			||||||
				Type:  "A",
 | 
					 | 
				
			||||||
				Name:  sub,
 | 
					 | 
				
			||||||
				Value: ip.To4().String(),
 | 
					 | 
				
			||||||
			}, nil
 | 
					 | 
				
			||||||
		case len(ip) == net.IPv6len:
 | 
					 | 
				
			||||||
			return libdns.Record{
 | 
					 | 
				
			||||||
				Type:  "AAAA",
 | 
					 | 
				
			||||||
				Name:  sub,
 | 
					 | 
				
			||||||
				Value: ip.To16().String(),
 | 
					 | 
				
			||||||
			}, nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return libdns.Record{}, fmt.Errorf("neither cname nor valid ip is set")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if sub != "<UNSET>" {
 | 
						if sub != "<UNSET>" {
 | 
				
			||||||
		record, err := makeRecord()
 | 
							if del {
 | 
				
			||||||
		if err != nil {
 | 
								recs, err := provider.GetRecords(ctx, zone)
 | 
				
			||||||
			panic(err)
 | 
								if err != nil {
 | 
				
			||||||
 | 
									log.Panicln(err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								recs = selectRecordsToDelete(recs)
 | 
				
			||||||
 | 
								recs, err = provider.DeleteRecords(ctx, zone, recs)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									log.Panicln(err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if len(recs) == 0 {
 | 
				
			||||||
 | 
									fmt.Println("Nothing was deleted")
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									fmt.Println("Records deleted:")
 | 
				
			||||||
 | 
									for i, r := range recs {
 | 
				
			||||||
 | 
										fmt.Printf("%5d  %6s   %s\n", i, r.Type, r.Value)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								record, err := genRecord()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									log.Panicln(err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								log.Printf("setting record, %+v", record)
 | 
				
			||||||
 | 
								newRecs, err := provider.SetRecords(ctx, zone, []libdns.Record{record})
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									log.Panicln(err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								fmt.Println(newRecs)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		log.Printf("setting record, %+v", record)
 | 
					 | 
				
			||||||
		newRecs, err := provider.SetRecords(ctx, zone, []libdns.Record{record})
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			panic(err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		fmt.Println(newRecs)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if path != "" {
 | 
						if path != "" {
 | 
				
			||||||
		recs, err := provider.GetRecords(ctx, zone)
 | 
							recs, err := provider.GetRecords(ctx, zone)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			panic(err)
 | 
								log.Panicln(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		data, err := json.Marshal(recs)
 | 
							data, err := json.Marshal(recs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			panic(err)
 | 
								log.Panicln(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		err = os.WriteFile(path, data, 0644)
 | 
							err = os.WriteFile(path, data, 0644)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			panic(err)
 | 
								log.Panicln(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func parseRange(rs string, max int) []int {
 | 
				
			||||||
 | 
						var indexes []int
 | 
				
			||||||
 | 
						m := uint(max)
 | 
				
			||||||
 | 
						for _, s := range strings.Split(rs, ",") {
 | 
				
			||||||
 | 
							s = strings.TrimSpace(s)
 | 
				
			||||||
 | 
							var i, a, b uint
 | 
				
			||||||
 | 
							if _, err := fmt.Sscanf(s, "%v-%v", &a, &b); err == nil {
 | 
				
			||||||
 | 
								for s := a; s <= b && s <= m; {
 | 
				
			||||||
 | 
									indexes = append(indexes, int(s))
 | 
				
			||||||
 | 
									s++
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								if _, err := fmt.Sscanf(s, "%v", &i); err == nil && i <= m {
 | 
				
			||||||
 | 
									indexes = append(indexes, int(i))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						sort.Ints(indexes)
 | 
				
			||||||
 | 
						var uniq []int
 | 
				
			||||||
 | 
						last := -1
 | 
				
			||||||
 | 
						for _, i := range indexes {
 | 
				
			||||||
 | 
							if i != last {
 | 
				
			||||||
 | 
								uniq = append(uniq, i)
 | 
				
			||||||
 | 
								last = i
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return uniq
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										15
									
								
								main_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								main_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestParseRange(t *testing.T) {
 | 
				
			||||||
 | 
						expected := []int{0, 1, 2, 6}
 | 
				
			||||||
 | 
						actual := parseRange("0-2,-5,6,-7-7,1, 11", 10)
 | 
				
			||||||
 | 
						if fmt.Sprint(expected) != fmt.Sprint(actual) {
 | 
				
			||||||
 | 
							fmt.Printf("unexpected %#v\n", actual)
 | 
				
			||||||
 | 
							t.Fail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user