310 lines
7.0 KiB
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"
|
|
*/
|