package main import ( "context" "fmt" "log" "os" "strconv" "strings" "github.com/cloudflare/cloudflare-go" "github.com/goccy/go-json" // "golang.org/x/net/idna" // "github.com/urfave/cli/v2" ) // App is the main structure of a cli application. type app struct { // The name of the program. Defaults to path.Base(os.Args[0]) Name string // Full name of command for help, defaults to Name HelpName string // Description of the program. Usage string // Description of the program. Port int } var api *cloudflare.API var c *app func (a *app) String(b string) string { log.Println(a, b) return b } func (a *app) Int(b string) int { log.Println(a, b) return a.Port } func (a *app) Uint(b string) uint { return uint(a.Int(b)) } func (a *app) Bool(b string) bool { log.Println(a, b) return true } func testCloudflare() { // Construct a new API object using a global API key api, err := cloudflare.New(os.Getenv("CLOUDFLARE_API_KEY"), os.Getenv("CLOUDFLARE_API_EMAIL")) // alternatively, you can use a scoped API token // api, err := cloudflare.NewWithAPIToken(os.Getenv("CLOUDFLARE_API_TOKEN")) if err != nil { log.Fatal(err) } // Most API calls require a Context ctx := context.Background() // Fetch user details on the account u, err := api.UserDetails(ctx) if err != nil { log.Fatal(err) } // Print user details fmt.Println(u) } func formatDNSRecord(record cloudflare.DNSRecord) []string { return []string{ record.ID, record.Name, record.Type, record.Content, strconv.FormatInt(int64(record.TTL), 10), strconv.FormatBool(record.Proxiable), strconv.FormatBool(*record.Proxied), strconv.FormatBool(record.Locked), } } func dnsCreate() error { zone := c.String("zone") name := c.String("name") rtype := c.String("type") content := c.String("content") ttl := c.Int("ttl") proxy := c.Bool("proxy") priority := uint16(c.Uint("priority")) zoneID, err := api.ZoneIDByName(zone) if err != nil { fmt.Println(err) return err } record := cloudflare.CreateDNSRecordParams{ Name: name, Type: strings.ToUpper(rtype), Content: content, TTL: ttl, Proxied: &proxy, Priority: &priority, } result, err := api.CreateDNSRecord(context.Background(), cloudflare.ZoneIdentifier(zoneID), record) if err != nil { fmt.Fprintln(os.Stderr, "Error creating DNS record: ", err) return err } output := [][]string{ formatDNSRecord(result), } writeTable(c, output, "ID", "Name", "Type", "Content", "TTL", "Proxiable", "Proxy", "Locked") return nil } func dnsCreateOrUpdate() error { zone := c.String("zone") name := c.String("name") rtype := strings.ToUpper(c.String("type")) content := c.String("content") ttl := c.Int("ttl") proxy := c.Bool("proxy") priority := uint16(c.Uint("priority")) zoneID, err := api.ZoneIDByName(zone) if err != nil { fmt.Fprintln(os.Stderr, "Error updating DNS record: ", err) return err } records, _, err := api.ListDNSRecords(context.Background(), cloudflare.ZoneIdentifier(zoneID), cloudflare.ListDNSRecordsParams{Name: name + "." + zone}) if err != nil { fmt.Fprintln(os.Stderr, "Error fetching DNS records: ", err) return err } var result cloudflare.DNSRecord if len(records) > 0 { // Record exists - find the ID and update it. // This is imprecise without knowing the original content; if a label // has multiple RRs we'll just update the first one. for _, r := range records { if r.Type == rtype { rr := cloudflare.UpdateDNSRecordParams{} rr.ID = r.ID rr.Type = r.Type rr.Content = content rr.TTL = ttl rr.Proxied = &proxy rr.Priority = &priority result, err = api.UpdateDNSRecord(context.Background(), cloudflare.ZoneIdentifier(zoneID), rr) if err != nil { fmt.Println("Error updating DNS record:", err) return err } } } } else { // Record doesn't exist - create it rr := cloudflare.CreateDNSRecordParams{ Name: name, Type: rtype, Content: content, TTL: ttl, Proxied: &proxy, Priority: &priority, } // TODO: Print the response. result, err = api.CreateDNSRecord(context.Background(), cloudflare.ZoneIdentifier(zoneID), rr) if err != nil { fmt.Println("Error creating DNS record:", err) return err } } output := [][]string{ formatDNSRecord(result), } writeTable(c, output, "ID", "Name", "Type", "Content", "TTL", "Proxiable", "Proxy", "Locked") return nil } func dnsUpdate() error { zone := c.String("zone") recordID := c.String("id") name := c.String("name") rtype := c.String("type") content := c.String("content") ttl := c.Int("ttl") proxy := c.Bool("proxy") priority := uint16(c.Uint("priority")) zoneID, err := api.ZoneIDByName(zone) if err != nil { fmt.Println(err) return err } record := cloudflare.UpdateDNSRecordParams{ ID: recordID, Name: name, Type: strings.ToUpper(rtype), Content: content, TTL: ttl, Proxied: &proxy, Priority: &priority, } _, err = api.UpdateDNSRecord(context.Background(), cloudflare.ZoneIdentifier(zoneID), record) if err != nil { fmt.Fprintln(os.Stderr, "Error updating DNS record: ", err) return err } return nil } func dnsDelete() error { zone := c.String("zone") recordID := c.String("id") zoneID, err := api.ZoneIDByName(zone) if err != nil { fmt.Println(err) return err } err = api.DeleteDNSRecord(context.Background(), cloudflare.ZoneIdentifier(zoneID), recordID) if err != nil { fmt.Fprintln(os.Stderr, "Error deleting DNS record: ", err) return err } return nil } // writeTableTabular outputs tabular data to STDOUT. func writeTableTabular(data [][]string, cols ...string) { // table := tablewriter.NewWriter(os.Stdout) // table.SetHeader(cols) // table.SetBorder(false) // table.AppendBulk(data) // table.Render() } // writeTableJSON outputs JSON data to STDOUT. func writeTableJSON(data [][]string, cols ...string) { mappedData := make([]map[string]string, 0) for i := range data { rowData := make(map[string]string) for j := range data[i] { rowData[cols[j]] = data[i][j] } mappedData = append(mappedData, rowData) } jsonData, err := json.Marshal(mappedData) if err != nil { fmt.Println(err) return } fmt.Println(string(jsonData)) } // writeTable outputs JSON or tabular data to STDOUT. func writeTable(c *app, data [][]string, cols ...string) { if c.Bool("json") { writeTableJSON(data, cols...) } else { writeTableTabular(data, cols...) } } // Utility function to check if CLI flags were given. func checkFlags(c *app, flags ...string) error { for _, flag := range flags { if c.String(flag) == "" { err := fmt.Errorf("error: the required flag %q was empty or not provided", flag) fmt.Fprintln(os.Stderr, err) return err } } return nil } // https://docs.digitalocean.com/products/networking/dns/ // https://docs.digitalocean.com/reference/api/api-reference/#operation/domains_create /* curl -X GET \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \ "https://api.digitalocean.com/v2/domains" */