control-panel-dns/cloudflare.go

310 lines
7.0 KiB
Go

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"
*/