diff options
| author | Jeff Carr <[email protected]> | 2023-12-14 10:36:56 -0600 |
|---|---|---|
| committer | Jeff Carr <[email protected]> | 2023-12-14 10:36:56 -0600 |
| commit | 282119d970faed3f8a60d5105a2f26ee14681ff4 (patch) | |
| tree | 1680731c899f0e147487b9ba4d50ace2f3e96eb1 /examples | |
| parent | 9d075afb1df62276dea06be4a188eaee8fc69420 (diff) | |
tabs, windows + gocui dropdown menu (almost)
dropdown menu figures out what text was clicked
dropdown menu movement changes line colors
dropdown menus force user to select a response
accidentally committed a binary
tab selection works
tab and window views almost working
tabs and windows almost working
window widgets selection works
better color handling
using gocui view.Visable flag
removal of old color setting code
still need an artificial delay for andlabs SetText()
catching more 'nil' errors
fixed the stupid duplicate tab problem in andlabs
figured out how andlabs had a tab/box mess
works on more than one domain
builds and runs again
debugging double tabs in andlabs gui
GO111MODULE compile notes
code reorg
further improvements
example cloudflare app does first successful dns update
add NewEntryLine() for single line entry boxes
Signed-off-by: Jeff Carr <[email protected]>
Diffstat (limited to 'examples')
| -rwxr-xr-x | examples/buttons/buttons | bin | 6062120 -> 0 bytes | |||
| -rw-r--r-- | examples/cloudflare/Makefile | 3 | ||||
| -rw-r--r-- | examples/cloudflare/api.go | 187 | ||||
| -rw-r--r-- | examples/cloudflare/curl.sh | 3 | ||||
| -rw-r--r-- | examples/cloudflare/dns.go | 132 | ||||
| -rw-r--r-- | examples/cloudflare/gui.go | 122 | ||||
| -rw-r--r-- | examples/cloudflare/main.go | 186 | ||||
| -rw-r--r-- | examples/cloudflare/structs.go | 77 |
8 files changed, 559 insertions, 151 deletions
diff --git a/examples/buttons/buttons b/examples/buttons/buttons Binary files differdeleted file mode 100755 index 01ea9aa..0000000 --- a/examples/buttons/buttons +++ /dev/null diff --git a/examples/cloudflare/Makefile b/examples/cloudflare/Makefile index bcd88c6..fd82fdc 100644 --- a/examples/cloudflare/Makefile +++ b/examples/cloudflare/Makefile @@ -16,3 +16,6 @@ update: log: reset tail -f /tmp/witgui.* /tmp/guilogfile + +gocui: build + ./cloudflare -gui gocui >/tmp/witgui.log.stderr 2>&1 diff --git a/examples/cloudflare/api.go b/examples/cloudflare/api.go new file mode 100644 index 0000000..8cf550e --- /dev/null +++ b/examples/cloudflare/api.go @@ -0,0 +1,187 @@ +// This is a simple example +package main + +import ( + "os" + "log" + "encoding/json" + "io/ioutil" + "net/http" + "strconv" + "bytes" + + "github.com/davecgh/go-spew/spew" +) + +func doChange(dnsRow *RRT) { + log.Println("Look for changes in row", dnsRow.ID) + log.Println("Proxy", dnsRow.Proxied, "vs", dnsRow.proxyNode.S) + log.Println("Content", dnsRow.Content, "vs", dnsRow.valueNode.S) + if (dnsRow.Content != dnsRow.valueNode.S) { + log.Println("UPDATE VALUE", dnsRow.nameNode.Name, dnsRow.typeNode.Name, "to", dnsRow.valueNode.S) + httpPut(dnsRow) + } + dnsRow.saveNode.Disable() +} + +func getZonefile(c *configT) *DNSRecords { + var url = cloudflareURL + c.zoneID + "/dns_records/" + log.Println("getZonefile()", c.domain, url) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + log.Println("http.NewRequest error:", err) + return nil + } + + // Set headers + req.Header.Set("X-Auth-Key", c.auth) + req.Header.Set("X-Auth-Email", c.email) + + log.Println("getZonefile() auth, email", c.auth, c.email) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + log.Println("http.Client error:", err) + return nil + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Println("ioutil.ReadAll() error", err) + return nil + } + + var records DNSRecords + if err := json.Unmarshal(body, &records); err != nil { + log.Println("json.Unmarshal() error", err) + return nil + } + + log.Println("getZonefile() worked", records) + return &records +} + +/* + pass in a DNS Resource Records (the stuff in a zonefile) + + This will talk to the cloudflare API and generate a resource record in the zonefile: + + For example: + gitea.wit.com. 3600 IN CNAME git.wit.org. + go.wit.com. 3600 IN A 1.1.1.9 + test.wit.com. 3600 IN NS ns1.wit.com. +*/ +func httpPut(dnsRow *RRT) { + var url string = cloudflareURL + os.Getenv("CLOUDFLARE_ZONEID") + "/dns_records/" + dnsRow.ID + var authKey string = os.Getenv("CLOUDFLARE_AUTHKEY") + var email string = os.Getenv("CLOUDFLARE_EMAIL") + + // make a json record to send on port 90 to cloudflare + var tmp string + tmp = `{"content": "` + dnsRow.valueNode.S + `", ` + tmp += `"name": "` + dnsRow.Name + `", ` + tmp += `"type": "` + dnsRow.Type + `", ` + tmp+= `"ttl": "` + strconv.Itoa(dnsRow.TTL) + `", ` + tmp += `"comment": "WIT DNS Control Panel"` + tmp += `}` + data := []byte(tmp) + + log.Println("http PUT url =", url) + log.Println("http PUT data =", data) + spew.Dump(data) + + req, err := http.NewRequest(http.MethodPut, url, bytes.NewBuffer(data)) + + // Set headers + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Auth-Key", authKey) + req.Header.Set("X-Auth-Email", email) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + log.Println(err) + return + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Println(err) + return + } + log.Println("http PUT body =", body) + spew.Dump(body) + + return +} + +// https://api.cloudflare.com/client/v4/zones +func getZones(auth, email string) *DNSRecords { + var url = "https://api.cloudflare.com/client/v4/zones" + log.Println("getZones()", url) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + log.Println("http.NewRequest error:", err) + return nil + } + + // Set headers + req.Header.Set("X-Auth-Key", auth) + req.Header.Set("X-Auth-Email", email) + + log.Println("getZones() auth, email", auth, email) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + log.Println("getZones() http.Client error:", err) + return nil + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Println("getZones() ioutil.ReadAll() error", err) + return nil + } + + var records DNSRecords + if err := json.Unmarshal(body, &records); err != nil { + log.Println("getZones() json.Unmarshal() error", err) + return nil + } + + /* Cloudflare API returns struct[] of: + struct { ID string "json:\"id\""; Type string "json:\"type\""; Name string "json:\"name\""; + Content string "json:\"content\""; Proxied bool "json:\"proxied\""; + Proxiable bool "json:\"proxiable\""; TTL int "json:\"ttl\"" } + */ + + // log.Println("getZones() worked", records) + // log.Println("spew dump:") + spew.Dump(records) + for _, record := range records.Result { + log.Println("spew record:", record) + log.Println("record:", record.Name, record.ID) + + var newc *configT + newc = new(configT) + + newc.domain = record.Name + newc.zoneID = record.ID + newc.auth = auth + newc.email = email + + config[record.Name] = newc + zonedrop.AddText(record.Name) + log.Println("zonedrop.AddText:", record.Name, record.ID) + } + for d, _ := range config { + log.Println("config entry:", d) + } + + return &records +} diff --git a/examples/cloudflare/curl.sh b/examples/cloudflare/curl.sh new file mode 100644 index 0000000..ec8c014 --- /dev/null +++ b/examples/cloudflare/curl.sh @@ -0,0 +1,3 @@ +curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \ + -H "Authorization: Bearer AAAPutYourTokenInHereSoYouCanTestItL5Cl3" \ + -H "Content-Type:application/json" diff --git a/examples/cloudflare/dns.go b/examples/cloudflare/dns.go deleted file mode 100644 index eb8de23..0000000 --- a/examples/cloudflare/dns.go +++ /dev/null @@ -1,132 +0,0 @@ -// This is a simple example -package main - -import ( - "os" - "log" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "strconv" -) - -// Define a struct to match the JSON structure of the response. -// This structure should be adjusted based on the actual format of the response. -type DNSRecords struct { - Result []struct { - ID string `json:"id"` - Type string `json:"type"` - Name string `json:"name"` - Content string `json:"content"` - Proxied bool `json:"proxied"` - Proxiable bool `json:"proxiable"` - TTL int `json:"ttl"` - } `json:"result"` -} - -// var domain string = "wit.org" -// var os.Getenv("CLOUDFLARE_DOMAIN") - -func loadDNS(hostname string) { - log.Println("adding DNS record") - - newt := mainWindow.NewTab(hostname) - newg := newt.NewGroup("more") - grid := newg.NewGrid("gridnuts", 5, gridH) - -// grid.NewButton("Type", func () { -// log.Println("sort by Type") -// }) - typedrop := grid.NewDropdown("type") - typedrop.AddText("A") - typedrop.AddText("AAAA") - typedrop.AddText("CNAME") - typedrop.Custom = func () { - log.Println("custom dropdown() a =", typedrop.Name, typedrop.S) - } - grid.NewButton("Name", func () { - log.Println("sort by Name") - }) - grid.NewButton("Protection", func () { - log.Println("sort proxied") - }) - grid.NewButton("TTL", func () { - log.Println("sort by TTL") - }) - grid.NewButton("Value", func () { - log.Println("sort by Value") - }) - - newt.NewButton("Save", func () { - log.Println("save stuff to cloudflare") - }) - - records := getRecords() - for _, record := range records.Result { - grid.NewLabel(record.Type) - textbox := grid.NewTextbox(record.Name) - textbox.SetText(record.Name) - if (record.Proxied) { - grid.NewLabel("Proxied") - } else { - grid.NewLabel("DNS") - } - var ttl, short string - if (record.TTL == 1) { - ttl = "Auto" - } else { - ttl = strconv.Itoa(record.TTL) - } - grid.NewLabel(ttl) - // short = fmt.Sprintf("%80s", record.Content) - short = record.Content - if len(short) > 40 { - short = short[:40] // Slice the first 20 characters - } - - namebox := grid.NewTextbox(short) - namebox.SetText(short) - - fmt.Printf("ID: %s, Type: %s, Name: %s, short Content: %s\n", record.ID, record.Type, record.Name, short) - fmt.Printf("\tproxied: %b, %b, string TTL: %i\n", record.Proxied, record.Proxiable, ttl) - } -} - -func getRecords() *DNSRecords { - var url string = os.Getenv("CLOUDFLARE_URL") - req, err := http.NewRequest("GET", url, nil) - if err != nil { - fmt.Println(err) - return nil - } - - var authKey string = os.Getenv("CLOUDFLARE_AUTHKEY") - var email string = os.Getenv("CLOUDFLARE_EMAIL") - - // Set headers - req.Header.Set("X-Auth-Key", authKey) - req.Header.Set("X-Auth-Email", email) - - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - fmt.Println(err) - return nil - } - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - fmt.Println(err) - return nil - } - - var records DNSRecords - if err := json.Unmarshal(body, &records); err != nil { - fmt.Println(err) - return nil - } - - return &records -} diff --git a/examples/cloudflare/gui.go b/examples/cloudflare/gui.go new file mode 100644 index 0000000..60fd5fe --- /dev/null +++ b/examples/cloudflare/gui.go @@ -0,0 +1,122 @@ +// This is a simple example +package main + +import ( + "log" + "strconv" +) + +func loadDNS(c *configT) { + hostname := c.domain + log.Println("adding DNS record", hostname) + + newt := mainWindow.NewTab(hostname) + newg := newt.NewGroup("more") + + // make a grid 6 things wide + grid := newg.NewGrid("gridnuts", 6, gridH) + +// grid.NewButton("Type", func () { +// log.Println("sort by Type") +// }) + typedrop := grid.NewDropdown("type") + typedrop.AddText("A") + typedrop.AddText("AAAA") + typedrop.AddText("CNAME") + typedrop.Custom = func () { + log.Println("custom dropdown() a =", typedrop.Name, typedrop.S) + } + nb := grid.NewButton("Name", func () { + log.Println("sort by Name") + }) + nb.Disable() + + grid.NewButton("Protection", func () { + log.Println("sort proxied") + }) + grid.NewButton("TTL", func () { + log.Println("sort by TTL") + }) + nb = grid.NewButton("Value", func () { + log.Println("sort by Value") + }) + nb.Disable() + nb = grid.NewButton("Save", func () { + log.Println("click below to save") + }) + nb.Disable() + + masterSave = newt.NewButton("Master Save", func () { + log.Println("save stuff to cloudflare") + }) + masterSave.Disable() + + records := getZonefile(c) + for _, record := range records.Result { + var rr RRT // dns zonefile resource record + + // copy all the JSON values into the row record. + rr.ID = record.ID + rr.Type = record.Type + rr.Name = record.Name + rr.Content = record.Content + rr.Proxied = record.Proxied + rr.Proxiable = record.Proxiable + rr.TTL = record.TTL + + rr.typeNode = grid.NewLabel(record.Type) + rr.nameNode = grid.NewEntryLine(record.Name) + rr.nameNode.SetText(record.Name) + rr.nameNode.Disable() + + // set proxy or unproxied + rr.proxyNode = grid.NewDropdown("proxy") + if (record.Proxied) { + rr.proxyNode.AddText("Proxied") + rr.proxyNode.AddText("DNS") + } else { + rr.proxyNode.AddText("DNS") + rr.proxyNode.AddText("Proxied") + } + rr.proxyNode.Custom = func () { + log.Println("proxy dropdown() a =", rr.proxyNode.Name, rr.proxyNode.S, rr.ID) + rr.saveNode.Enable() + masterSave.Enable() + } + + var ttl, short string + if (record.TTL == 1) { + ttl = "Auto" + } else { + ttl = strconv.Itoa(record.TTL) + } + rr.ttlNode = grid.NewLabel(ttl) + // short = fmt.Sprintf("%80s", record.Content) + short = record.Content + if len(short) > 40 { + short = short[:40] // Slice the first 20 characters + } + + rr.valueNode = grid.NewEntryLine(short) + rr.valueNode.SetText(record.Content) + + rr.valueNode.Custom = func () { + log.Println("value changed =", rr.valueNode.Name, rr.proxyNode.S, rr.ID) + rr.saveNode.Enable() + masterSave.Enable() + } + + // fmt.Printf("ID: %s, Type: %s, Name: %s, short Content: %s\n", record.ID, record.Type, record.Name, short) + // fmt.Printf("\tproxied: %b, %b, string TTL: %i\n", record.Proxied, record.Proxiable, ttl) + + rr.saveNode = grid.NewButton("Save", nil) + rr.saveNode.Disable() + rr.saveNode.Custom = func () { + name := "save stuff to cloudflare for " + rr.ID + log.Println(name) + doChange(&rr) + } + } + + grid.Pad() +} diff --git a/examples/cloudflare/main.go b/examples/cloudflare/main.go index b83d276..badf97a 100644 --- a/examples/cloudflare/main.go +++ b/examples/cloudflare/main.go @@ -5,11 +5,14 @@ import ( "os" "fmt" "log" + "bufio" + "strings" "git.wit.org/wit/gui" ) var title string = "Cloudflare DNS Control Panel" var outfile string = "/tmp/guilogfile" +var configfile string = ".config/wit/cloudflare" var myGui *gui.Node var buttonCounter int = 5 @@ -19,8 +22,10 @@ var gridH int = 3 var mainWindow, more, more2 *gui.Node func main() { + config = make(map[string]*configT) + readConfig() myGui = gui.New().Default() - buttonWindow() + makeCloudflareWindow() // This is just a optional goroutine to watch that things are alive gui.Watchdog() @@ -28,30 +33,90 @@ func main() { } // This creates a window -func buttonWindow() { - var t, g *gui.Node +func makeCloudflareWindow() { + var t *gui.Node log.Println("buttonWindow() START") mainWindow = myGui.NewWindow(title).SetText(title) - t = mainWindow.NewTab("Cloudflare") - g = t.NewGroup("buttons") - g1 := t.NewGroup("buttonGroup 2") - more = g1.NewGroup("more") - showCloudflareCredentials(more) + // this tab has the master cloudflare API credentials + makeConfigTab(mainWindow) + + t = mainWindow.NewTab("Zones") + g1 := t.NewGroup("zones") - // more2 = g1.NewGrid("gridnuts", gridW, gridH) + // make dropdown list of zones + zonedrop = g1.NewDropdown("zone") + zonedrop.AddText("example.org") + for d, _ := range config { + zonedrop.AddText(d) + } - var domain string = os.Getenv("CLOUDFLARE_DOMAIN") - if (domain == "") { - domain = "example.org" + zonedrop.Custom = func () { + domain := zonedrop.S + log.Println("custom dropdown() zone (domain name) =", zonedrop.Name, domain) + if (config[domain] == nil) { + log.Println("custom dropdown() config[domain] = nil for domain =", domain) + domainWidget.SetText(domain) + zoneWidget.SetText("") + authWidget.SetText("") + emailWidget.SetText("") + } else { + log.Println("custom dropdown() a =", domain, config[domain].zoneID, config[domain].auth, config[domain].email) + domainWidget.SetText(config[domain].domain) + zoneWidget.SetText(config[domain].zoneID) + authWidget.SetText(config[domain].auth) + emailWidget.SetText(config[domain].email) + } } - g.NewButton("Load " + domain + " DNS", func () { - loadDNS(domain) + more = g1.NewGroup("data") + showCloudflareCredentials(more) + + makeDebugTab(mainWindow) +} + +func makeConfigTab(window *gui.Node) { + t := window.NewTab("Get Zones") + vb := t.NewBox("vBox", false) + g1 := vb.NewGroup("Cloudflare API Config") + + g1.NewLabel("If you have an API key with access to list all of /n your zone files, enter it here. \n \n Alternatively, you can set the enviroment variables: \n env $CLOUDFLARE_AUTHKEY \n env $CLOUDFLARE_EMAIL \n env $CLOUDFLARE_URL \n") + + // make grid to display credentials + grid := g1.NewGrid("credsGrid", 2, 4) // width = 2 + + grid.NewLabel("Auth Key") + aw := grid.NewEntryLine(os.Getenv("CLOUDFLARE_AUTHKEY")) + aw.SetText(os.Getenv("CLOUDFLARE_AUTHKEY")) + + grid.NewLabel("Email") + ew := grid.NewEntryLine(os.Getenv("CLOUDFLARE_EMAIL")) + ew.SetText(os.Getenv("CLOUDFLARE_EMAIL")) + + var url string = "https://api.cloudflare.com/client/v4/zones/" + grid.NewLabel("Cloudflare API") + grid.NewLabel(url) + + grid.Pad() + + vb.NewButton("getZones()", func () { + log.Println("getZones()") + getZones(aw.S, ew.S) }) + t.Pad() + t.Margin() + vb.Pad() + vb.Margin() + g1.Pad() + g1.Margin() +} + +func makeDebugTab(window *gui.Node) { + t2 := window.NewTab("debug") + g := t2.NewGroup("debug") g.NewButton("Load 'gocui'", func () { // this set the xterm and mate-terminal window title. maybe works generally? fmt.Println("\033]0;" + title + "blah \007") @@ -65,20 +130,103 @@ func buttonWindow() { g.NewButton("gui.DebugWindow()", func () { gui.DebugWindow() }) + + g.NewButton("List all Widgets", func () { + myGui.ListChildren(true) + }) + g.NewButton("Dump all Widgets", func () { + myGui.Dump() + }) } func showCloudflareCredentials(box *gui.Node) { + // make grid to display credentials grid := box.NewGrid("credsGrid", 2, 4) // width = 2 grid.NewLabel("Domain") - grid.NewLabel(os.Getenv("CLOUDFLARE_DOMAIN")) + domainWidget = grid.NewEntryLine(os.Getenv("CLOUDFLARE_DOMAIN")) + + grid.NewLabel("Zone ID") + zoneWidget = grid.NewEntryLine(os.Getenv("CLOUDFLARE_ZONEID")) grid.NewLabel("Auth Key") - grid.NewLabel(os.Getenv("CLOUDFLARE_AUTHKEY")) + authWidget = grid.NewEntryLine(os.Getenv("CLOUDFLARE_AUTHKEY")) grid.NewLabel("Email") - grid.NewLabel(os.Getenv("CLOUDFLARE_EMAIL")) + emailWidget = grid.NewEntryLine(os.Getenv("CLOUDFLARE_EMAIL")) + + var url string = "https://api.cloudflare.com/client/v4/zones/" + grid.NewLabel("Cloudflare API") + grid.NewLabel(url) + + grid.Pad() + + saveButton = box.NewButton("Save to config", func () { + }) + saveButton.Disable() + + loadButton = box.NewButton("Load Cloudflare DNS zonefile", func () { + var domain configT + domain.domain = domainWidget.S + domain.zoneID = zoneWidget.S + domain.auth = authWidget.S + domain.email = emailWidget.S + loadDNS(&domain) + }) +} + +func readConfig() { + homeDir, err := os.UserHomeDir() + if err != nil { + log.Println("searchPaths() error. exiting here?") + } + filename := homeDir + "/" + configfile + log.Println("filename =", filename) + + readFileLineByLine(filename) + // os.Exit(0) +} + +// readFileLineByLine opens a file and reads through each line. +func readFileLineByLine(filename string) error { + // Open the file. + file, err := os.Open(filename) + if err != nil { + return err + } + defer file.Close() + + log.Println("readFileLineByLine() =", filename) + + // Create a new Scanner for the file. + scanner := bufio.NewScanner(file) + + // Read through each line using scanner. + for scanner.Scan() { + var newc *configT + newc = new(configT) + + line := scanner.Text() + parts := strings.Fields(line) + + if (len(parts) < 4) { + log.Println("readFileLineByLine() SKIP =", parts) + continue + } + + newc.domain = parts[0] + newc.zoneID = parts[1] + newc.auth = parts[2] + newc.email = parts[3] + + config[parts[0]] = newc + log.Println("readFileLineByLine() =", newc.domain, newc.zoneID, newc.auth, newc.email) + } + + // Check for errors during Scan. + if err := scanner.Err(); err != nil { + return err + } - grid.NewLabel("URL") - grid.NewLabel(os.Getenv("CLOUDFLARE_URL")) + return nil } diff --git a/examples/cloudflare/structs.go b/examples/cloudflare/structs.go new file mode 100644 index 0000000..af4d7f3 --- /dev/null +++ b/examples/cloudflare/structs.go @@ -0,0 +1,77 @@ +// This is a simple example +package main + +import ( + "git.wit.org/wit/gui" +) + +var cloudflareURL string = "https://api.cloudflare.com/client/v4/zones/" + +// Define a struct to match the JSON structure of the response. +// This structure should be adjusted based on the actual format of the response. +type DNSRecords struct { + Result []struct { + ID string `json:"id"` + Type string `json:"type"` + Name string `json:"name"` + Content string `json:"content"` + Proxied bool `json:"proxied"` + Proxiable bool `json:"proxiable"` + TTL int `json:"ttl"` + } `json:"result"` +} + +var masterSave *gui.Node + +var domainWidget *gui.Node +var zoneWidget *gui.Node +var authWidget *gui.Node +var emailWidget *gui.Node + +var loadButton *gui.Node +var saveButton *gui.Node +var zonedrop *gui.Node + +// Resource Record (used in a DNS zonefile) +type RRT struct { + typeNode *gui.Node + nameNode *gui.Node + proxyNode *gui.Node + ttlNode *gui.Node + valueNode *gui.Node + saveNode *gui.Node + + ID string + Type string + Name string + Content string + Proxied bool + Proxiable bool + TTL int +} + +/* + This is a structure of all the RR's (Resource Records) + in the DNS zonefiile for a hostname. For example: + + For the host test.wit.com: + + test.wit.com A 127.0.0.1 + test.wit.com AAAA + test.wit.com TXT email [email protected] + test.wit.com TXT phone 212-555-1212 + test.wit.com CNAME real.wit.com +*/ +type hostT struct { + hostname string + RRs []configT +} + +type configT struct { + domain string + zoneID string + auth string + email string +} + +var config map[string]*configT |
