code moved to the cloudflare package
use the cloudflare package add a protobuf attempt better change detection, but formatting is broken don't redraw widgets if they are not visible create new dns entry worked attempting a DNS RR create attempt a create API call use cloudflare recommended ENV vars turn off debugging Signed-off-by: Jeff Carr <jcarr@wit.com>
This commit is contained in:
parent
f67e03db16
commit
2d147a9115
|
@ -11,6 +11,9 @@ examples/textbox/textbox
|
|||
examples/cloudflare/cloudflare
|
||||
examples/*/helloconsole
|
||||
|
||||
# protobuf compiled files
|
||||
protobuf/*.pb.go
|
||||
|
||||
# temporary files when building debian packages
|
||||
/*.deb
|
||||
/files
|
||||
|
|
11
common.go
11
common.go
|
@ -170,6 +170,13 @@ func (n *Node) Unpad() *Node {
|
|||
return n
|
||||
}
|
||||
|
||||
func (n *Node) Expand() *Node {
|
||||
a := newAction(n, toolkit.Pad)
|
||||
a.Expand = true
|
||||
sendAction(a)
|
||||
return n
|
||||
}
|
||||
|
||||
// is this better?
|
||||
// yes, this is better. it allows Internationalization very easily
|
||||
// me.window = myGui.New2().Window("DNS and IPv6 Control Panel").Standard()
|
||||
|
@ -182,8 +189,9 @@ func (n *Node) Window(title string) *Node {
|
|||
|
||||
// This should not really do anything. as per the docs, the "Standard()" way
|
||||
// should be the default way
|
||||
/*
|
||||
func (n *Node) Standard() *Node {
|
||||
log(debugError, "Standard() not implemented yet")
|
||||
log(debugInfo, "Standard() not implemented yet")
|
||||
return n
|
||||
}
|
||||
|
||||
|
@ -191,3 +199,4 @@ func (n *Node) SetMargin() *Node {
|
|||
log(debugError, "DoMargin() not implemented yet")
|
||||
return n
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -4,10 +4,11 @@ package main
|
|||
import (
|
||||
"os"
|
||||
"log"
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
// "strconv"
|
||||
"bytes"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
|
@ -19,7 +20,17 @@ func doChange(dnsRow *RRT) {
|
|||
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)
|
||||
stuff, result := httpPut(dnsRow)
|
||||
if (dnsRow.curlNode != nil) {
|
||||
pretty, _ := formatJSON(stuff)
|
||||
log.Println("http PUT curl =", pretty)
|
||||
dnsRow.curlNode.SetText(pretty)
|
||||
}
|
||||
if (dnsRow.resultNode != nil) {
|
||||
pretty, _ := formatJSON(result)
|
||||
log.Println("http PUT result =", pretty)
|
||||
dnsRow.resultNode.SetText(pretty)
|
||||
}
|
||||
}
|
||||
dnsRow.saveNode.Disable()
|
||||
}
|
||||
|
@ -73,24 +84,26 @@ func getZonefile(c *configT) *DNSRecords {
|
|||
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")
|
||||
func httpPut(dnsRow *RRT) (string, string) {
|
||||
var url string = cloudflareURL + os.Getenv("CF_API_ZONEID") + "/dns_records/" + dnsRow.ID
|
||||
var authKey string = os.Getenv("CF_API_KEY")
|
||||
var email string = os.Getenv("CF_API_EMAIL")
|
||||
|
||||
// make a json record to send on port 90 to cloudflare
|
||||
// make a json record to send on port 80 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+= `"ttl": "` + "1" + `", `
|
||||
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)
|
||||
// log.Println("http PUT data =", data)
|
||||
// spew.Dump(data)
|
||||
pretty, _ := formatJSON(string(data))
|
||||
log.Println("http PUT data =", pretty)
|
||||
|
||||
req, err := http.NewRequest(http.MethodPut, url, bytes.NewBuffer(data))
|
||||
|
||||
|
@ -103,19 +116,19 @@ func httpPut(dnsRow *RRT) {
|
|||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
return tmp, fmt.Sprintf("blah err =", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
return tmp, fmt.Sprintf("blah err =", err)
|
||||
}
|
||||
log.Println("http PUT body =", body)
|
||||
spew.Dump(body)
|
||||
// log.Println("http PUT body =", body)
|
||||
// spew.Dump(body)
|
||||
|
||||
return
|
||||
return tmp, string(body)
|
||||
}
|
||||
|
||||
// https://api.cloudflare.com/client/v4/zones
|
||||
|
@ -185,3 +198,22 @@ func getZones(auth, email string) *DNSRecords {
|
|||
|
||||
return &records
|
||||
}
|
||||
|
||||
// formatJSON takes an unformatted JSON string and returns a formatted version.
|
||||
func formatJSON(unformattedJSON string) (string, error) {
|
||||
var jsonData interface{}
|
||||
|
||||
// Decode the JSON string into an interface
|
||||
err := json.Unmarshal([]byte(unformattedJSON), &jsonData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Re-encode the JSON with indentation for formatting
|
||||
formattedJSON, err := json.MarshalIndent(jsonData, "", " ")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(formattedJSON), nil
|
||||
}
|
||||
|
|
|
@ -1,3 +1,28 @@
|
|||
curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \
|
||||
-H "Authorization: Bearer AAAPutYourTokenInHereSoYouCanTestItL5Cl3" \
|
||||
-H "Content-Type:application/json"
|
||||
#curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \
|
||||
# -H "Authorization: Bearer AAAPutYourTokenInHereSoYouCanTestItL5Cl3" \
|
||||
# -H "Content-Type:application/json"
|
||||
|
||||
# https://api.cloudflare.com/client/v4/zones/27b900d9e05cfb9f3a64fecff2497f90/dns_records
|
||||
#
|
||||
# {
|
||||
# "comment": "WIT DNS Control Panel",
|
||||
# "content": "2001:4860:4860::8888",
|
||||
# "name": "www",
|
||||
# "proxied": false,
|
||||
# "ttl": 3600,
|
||||
# "type": "AAAA"
|
||||
#}
|
||||
|
||||
curl --request POST \
|
||||
--url https://api.cloudflare.com/client/v4/zones/27b900d9e05cfb9f3a64fecff2497f90/dns_records \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'X-Auth-Key: e08806ad85ef97aebaacd2d7fa462a7d417a7x' \
|
||||
--header 'X-Auth-Email: basilarchia@gmail.com' \
|
||||
--data '{
|
||||
"comment": "WIT DNS Control Panel",
|
||||
"content": "2001:4860:4860::5555",
|
||||
"name": "www5",
|
||||
"proxied": false,
|
||||
"ttl": 3600,
|
||||
"type": "AAAA"
|
||||
}'
|
||||
|
|
|
@ -4,6 +4,8 @@ package main
|
|||
import (
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"git.wit.org/jcarr/control-panel-dns/cloudflare"
|
||||
)
|
||||
|
||||
func loadDNS(c *configT) {
|
||||
|
@ -11,7 +13,8 @@ func loadDNS(c *configT) {
|
|||
log.Println("adding DNS record", hostname)
|
||||
|
||||
newt := mainWindow.NewTab(hostname)
|
||||
newg := newt.NewGroup("more")
|
||||
vb := newt.NewBox("vBox", false)
|
||||
newg := vb.NewGroup("more zoneID = " + c.zoneID)
|
||||
|
||||
// make a grid 6 things wide
|
||||
grid := newg.NewGrid("gridnuts", 6, gridH)
|
||||
|
@ -19,41 +22,22 @@ func loadDNS(c *configT) {
|
|||
// 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.NewLabel("RR type")
|
||||
grid.NewLabel("hostname")
|
||||
|
||||
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()
|
||||
grid.NewLabel("Proxy")
|
||||
grid.NewLabel("TTL")
|
||||
grid.NewLabel("Value")
|
||||
grid.NewLabel("Save")
|
||||
|
||||
masterSave = newt.NewButton("Master Save", func () {
|
||||
masterSave = vb.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
|
||||
var rr cloudflare.RRT // dns zonefile resource record
|
||||
|
||||
// copy all the JSON values into the row record.
|
||||
rr.ID = record.ID
|
||||
|
@ -62,59 +46,34 @@ func loadDNS(c *configT) {
|
|||
rr.Content = record.Content
|
||||
rr.Proxied = record.Proxied
|
||||
rr.Proxiable = record.Proxiable
|
||||
rr.TTL = record.TTL
|
||||
// rr.Ttl = record.TTL
|
||||
|
||||
rr.typeNode = grid.NewLabel(record.Type)
|
||||
rr.nameNode = grid.NewEntryLine(record.Name)
|
||||
rr.nameNode.SetText(record.Name)
|
||||
rr.nameNode.Disable()
|
||||
grid.NewLabel(record.Type)
|
||||
grid.NewLabel(record.Name)
|
||||
|
||||
// set proxy or unproxied
|
||||
rr.proxyNode = grid.NewDropdown("proxy")
|
||||
proxy := grid.NewLabel("proxy")
|
||||
if (record.Proxied) {
|
||||
rr.proxyNode.AddText("Proxied")
|
||||
rr.proxyNode.AddText("DNS")
|
||||
proxy.SetText("On")
|
||||
} 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()
|
||||
proxy.SetText("Off")
|
||||
}
|
||||
|
||||
var ttl, short string
|
||||
var ttl 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
|
||||
}
|
||||
grid.NewLabel(ttl)
|
||||
|
||||
rr.valueNode = grid.NewEntryLine(short)
|
||||
rr.valueNode.SetText(record.Content)
|
||||
val := grid.NewLabel("Value")
|
||||
val.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 () {
|
||||
load := grid.NewButton("Load", nil)
|
||||
load.Custom = func () {
|
||||
name := "save stuff to cloudflare for " + rr.ID
|
||||
log.Println(name)
|
||||
doChange(&rr)
|
||||
// doChange(&rr)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,9 @@ import (
|
|||
"log"
|
||||
"bufio"
|
||||
"strings"
|
||||
|
||||
"git.wit.org/wit/gui"
|
||||
"git.wit.org/jcarr/control-panel-dns/cloudflare"
|
||||
)
|
||||
|
||||
var title string = "Cloudflare DNS Control Panel"
|
||||
|
@ -44,7 +46,8 @@ func makeCloudflareWindow() {
|
|||
makeConfigTab(mainWindow)
|
||||
|
||||
t = mainWindow.NewTab("Zones")
|
||||
g1 := t.NewGroup("zones")
|
||||
vb := t.NewBox("vBox", false)
|
||||
g1 := vb.NewGroup("zones")
|
||||
|
||||
// make dropdown list of zones
|
||||
zonedrop = g1.NewDropdown("zone")
|
||||
|
@ -52,6 +55,7 @@ func makeCloudflareWindow() {
|
|||
for d, _ := range config {
|
||||
zonedrop.AddText(d)
|
||||
}
|
||||
zonedrop.AddText("stablesid.org")
|
||||
|
||||
zonedrop.Custom = func () {
|
||||
domain := zonedrop.S
|
||||
|
@ -82,18 +86,18 @@ func makeConfigTab(window *gui.Node) {
|
|||
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")
|
||||
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 $CF_API_KEY \n env $CF_API_EMAIL\n")
|
||||
|
||||
// make grid to display credentials
|
||||
grid := g1.NewGrid("credsGrid", 2, 4) // width = 2
|
||||
|
||||
grid.NewLabel("Auth Key")
|
||||
aw := grid.NewEntryLine("CLOUDFLARE_AUTHKEY")
|
||||
aw.SetText(os.Getenv("CLOUDFLARE_AUTHKEY"))
|
||||
aw := grid.NewEntryLine("CF_API_KEY")
|
||||
aw.SetText(os.Getenv("CF_API_KEY"))
|
||||
|
||||
grid.NewLabel("Email")
|
||||
ew := grid.NewEntryLine("CLOUDFLARE_EMAIL")
|
||||
ew.SetText(os.Getenv("CLOUDFLARE_EMAIL"))
|
||||
ew := grid.NewEntryLine("CF_API_EMAIL")
|
||||
ew.SetText(os.Getenv("CF_API_EMAIL"))
|
||||
|
||||
var url string = "https://api.cloudflare.com/client/v4/zones/"
|
||||
grid.NewLabel("Cloudflare API")
|
||||
|
@ -106,6 +110,10 @@ func makeConfigTab(window *gui.Node) {
|
|||
getZones(aw.S, ew.S)
|
||||
})
|
||||
|
||||
vb.NewButton("cloudflare wit.com", func () {
|
||||
cloudflare.CreateRR(myGui, "wit.com", "3777302ac4a78cd7fa4f6d3f72086d06")
|
||||
})
|
||||
|
||||
t.Pad()
|
||||
t.Margin()
|
||||
vb.Pad()
|
||||
|
@ -144,16 +152,16 @@ func showCloudflareCredentials(box *gui.Node) {
|
|||
grid := box.NewGrid("credsGrid", 2, 4) // width = 2
|
||||
|
||||
grid.NewLabel("Domain")
|
||||
domainWidget = grid.NewEntryLine("CLOUDFLARE_DOMAIN")
|
||||
domainWidget = grid.NewEntryLine("CF_API_DOMAIN")
|
||||
|
||||
grid.NewLabel("Zone ID")
|
||||
zoneWidget = grid.NewEntryLine("CLOUDFLARE_ZONEID")
|
||||
zoneWidget = grid.NewEntryLine("CF_API_ZONEID")
|
||||
|
||||
grid.NewLabel("Auth Key")
|
||||
authWidget = grid.NewEntryLine("CLOUDFLARE_AUTHKEY")
|
||||
authWidget = grid.NewEntryLine("CF_API_KEY")
|
||||
|
||||
grid.NewLabel("Email")
|
||||
emailWidget = grid.NewEntryLine("CLOUDFLARE_EMAIL")
|
||||
emailWidget = grid.NewEntryLine("CF_API_EMAIL")
|
||||
|
||||
var url string = "https://api.cloudflare.com/client/v4/zones/"
|
||||
grid.NewLabel("Cloudflare API")
|
||||
|
@ -161,10 +169,6 @@ func showCloudflareCredentials(box *gui.Node) {
|
|||
|
||||
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
|
||||
|
|
|
@ -34,20 +34,23 @@ 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
|
||||
typeNode *gui.Node // CNAME, A, AAAA, ...
|
||||
nameNode *gui.Node // www, mail, ...
|
||||
proxyNode *gui.Node // If cloudflare is a port 80 & 443 proxy
|
||||
ttlNode *gui.Node // just set to 1 which means automatic to cloudflare
|
||||
valueNode *gui.Node // 4.2.2.2, "dkim stuff", etc
|
||||
curlNode *gui.Node // shows you what you could run via curl
|
||||
resultNode *gui.Node // what the cloudflare API returned
|
||||
saveNode *gui.Node // button to send it to cloudflare
|
||||
|
||||
ID string
|
||||
Type string
|
||||
Name string
|
||||
Content string
|
||||
ProxyS string
|
||||
Proxied bool
|
||||
Proxiable bool
|
||||
TTL int
|
||||
Ttl string
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
30
main.go
30
main.go
|
@ -37,17 +37,17 @@ func init() {
|
|||
func watchCallback() {
|
||||
log(logInfo, "watchCallback() START")
|
||||
for {
|
||||
log(logNow, "watchCallback() restarted select for toolkit user events")
|
||||
log(logInfo, "watchCallback() restarted select for toolkit user events")
|
||||
select {
|
||||
case a := <-me.guiChan:
|
||||
if (a.ActionType == toolkit.UserQuit) {
|
||||
log(logNow, "doUserEvent() User sent Quit()")
|
||||
log(logInfo, "doUserEvent() User sent Quit()")
|
||||
me.rootNode.doCustom()
|
||||
exit("wit/gui toolkit.UserQuit")
|
||||
break
|
||||
}
|
||||
if (a.ActionType == toolkit.EnableDebug) {
|
||||
log(logNow, "doUserEvent() Enable Debugging Window")
|
||||
log(logInfo, "doUserEvent() Enable Debugging Window")
|
||||
DebugWindow()
|
||||
break
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ func watchCallback() {
|
|||
if (n == nil) {
|
||||
log(logError, "watchCallback() UNKNOWN widget id =", a.WidgetId, a.Name)
|
||||
} else {
|
||||
log(logNow, "watchCallback() FOUND widget id =", n.id, n.Name)
|
||||
log(logInfo, "watchCallback() FOUND widget id =", n.id, n.Name)
|
||||
n.doUserEvent(a)
|
||||
}
|
||||
// this maybe a good idea?
|
||||
|
@ -67,7 +67,7 @@ func watchCallback() {
|
|||
}
|
||||
|
||||
func (n *Node) doCustom() {
|
||||
log(logNow, "doUserEvent() widget =", n.id, n.Name, n.WidgetType, n.B)
|
||||
log(logInfo, "doUserEvent() widget =", n.id, n.Name, n.WidgetType, n.B)
|
||||
if (n.Custom == nil) {
|
||||
log(debugError, "Custom() = nil. SKIPPING")
|
||||
return
|
||||
|
@ -76,40 +76,40 @@ func (n *Node) doCustom() {
|
|||
}
|
||||
|
||||
func (n *Node) doUserEvent(a toolkit.Action) {
|
||||
log(logNow, "doUserEvent() node =", n.id, n.Name)
|
||||
log(logInfo, "doUserEvent() node =", n.id, n.Name)
|
||||
switch n.WidgetType {
|
||||
case toolkit.Checkbox:
|
||||
n.B = a.B
|
||||
log(logNow, "doUserEvent() node =", n.id, n.Name, "set to:", n.B)
|
||||
log(logInfo, "doUserEvent() node =", n.id, n.Name, "set to:", n.B)
|
||||
n.doCustom()
|
||||
case toolkit.Button:
|
||||
log(logNow, "doUserEvent() node =", n.id, n.Name, "button clicked")
|
||||
log(logInfo, "doUserEvent() node =", n.id, n.Name, "button clicked")
|
||||
n.doCustom()
|
||||
case toolkit.Combobox:
|
||||
n.S = a.S
|
||||
log(logNow, "doUserEvent() node =", n.id, n.Name, "set to:", n.S)
|
||||
log(logInfo, "doUserEvent() node =", n.id, n.Name, "set to:", n.S)
|
||||
n.doCustom()
|
||||
case toolkit.Dropdown:
|
||||
n.S = a.S
|
||||
log(logNow, "doUserEvent() node =", n.id, n.Name, "set to:", n.S)
|
||||
log(logInfo, "doUserEvent() node =", n.id, n.Name, "set to:", n.S)
|
||||
n.doCustom()
|
||||
case toolkit.Textbox:
|
||||
n.S = a.S
|
||||
log(logNow, "doUserEvent() node =", n.id, n.Name, "set to:", n.S)
|
||||
log(logInfo, "doUserEvent() node =", n.id, n.Name, "set to:", n.S)
|
||||
n.doCustom()
|
||||
case toolkit.Spinner:
|
||||
n.I = a.I
|
||||
log(logNow, "doUserEvent() node =", n.id, n.Name, "set to:", n.I)
|
||||
log(logInfo, "doUserEvent() node =", n.id, n.Name, "set to:", n.I)
|
||||
n.doCustom()
|
||||
case toolkit.Slider:
|
||||
n.I = a.I
|
||||
log(logNow, "doUserEvent() node =", n.id, n.Name, "set to:", n.I)
|
||||
log(logInfo, "doUserEvent() node =", n.id, n.Name, "set to:", n.I)
|
||||
n.doCustom()
|
||||
case toolkit.Window:
|
||||
log(logNow, "doUserEvent() node =", n.id, n.Name, "window closed")
|
||||
log(logInfo, "doUserEvent() node =", n.id, n.Name, "window closed")
|
||||
n.doCustom()
|
||||
default:
|
||||
log(logNow, "doUserEvent() type =", n.WidgetType)
|
||||
log(logInfo, "doUserEvent() type =", n.WidgetType)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
all:
|
||||
# You must use the current protoc-gen-go
|
||||
# protoc --version 3.6++ does not mean that protoc will generate version3 .go files
|
||||
#
|
||||
# apt remove golang-goprotobuf-dev
|
||||
# apt install protobuf-compiler
|
||||
#
|
||||
# Then:
|
||||
# go get -u github.com/golang/protobuf/protoc-gen-go
|
||||
# cd ~/go/src/github.com/golang/protobuf/protoc-gen-go
|
||||
# go install
|
||||
#
|
||||
# Then:
|
||||
protoc --version
|
||||
make widget.pb.go
|
||||
|
||||
clean:
|
||||
rm -f *.pb.go
|
||||
|
||||
widget.pb.go: widget.proto
|
||||
protoc --go_out=. widget.proto
|
||||
|
||||
compile:
|
||||
protoc --go_out=. *.proto
|
||||
|
||||
deps:
|
||||
apt install golang-goprotobuf-dev
|
||||
apt install protobuf-compiler
|
||||
|
||||
push:
|
||||
git pull
|
||||
git add --all
|
||||
git commit -a -s
|
||||
git push
|
|
@ -0,0 +1,117 @@
|
|||
syntax = "proto3";
|
||||
package guiProtobuf;
|
||||
|
||||
message Action {
|
||||
WidgetType widgetType = 1;
|
||||
ActionType actionType = 2;
|
||||
int64 widgetId = 3;
|
||||
int64 parentId = 4;
|
||||
string text = 5; // what is visable to the user
|
||||
string name = 6; // a name useful for programming
|
||||
|
||||
// This is how the values are passed back and forth
|
||||
// values from things like checkboxes & dropdown's
|
||||
bool b = 7;
|
||||
int64 i = 8;
|
||||
string s = 9;
|
||||
|
||||
// This is used for things like a slider(0,100)
|
||||
int64 x = 10;
|
||||
int64 y = 11;
|
||||
|
||||
// This is for the grid size & widget position
|
||||
int64 w = 12;
|
||||
int64 h = 13;
|
||||
int64 atw = 14;
|
||||
int64 ath = 15;
|
||||
|
||||
bool margin = 16; // Put space around elements to improve look & feel
|
||||
bool expand = 17; // Make widgets fill up the space available
|
||||
|
||||
repeated Response results = 18;
|
||||
repeated Network networks = 19;
|
||||
repeated VM vms = 20;
|
||||
|
||||
enum WidgetType {
|
||||
Unknown = 0;
|
||||
Root = 1; // the master 'root' node of the binary tree
|
||||
Flag = 2; // used to send configuration values to plugins
|
||||
Window = 3; // in certain gui's (ncurses), these are tabs
|
||||
Tab = 4; // internally, this is a window
|
||||
Frame = 5; // deprecate?
|
||||
Grid = 6; // like drawers in a chest
|
||||
Group = 7; // like the 'Appetizers' section on a menu
|
||||
Box = 8; // a vertical or horizontal stack of widgets
|
||||
Button = 9;
|
||||
Checkbox = 10; // select 'on' or 'off'
|
||||
Dropdown = 11;
|
||||
Combobox = 12; // dropdown with edit=true
|
||||
Label = 13;
|
||||
Textbox = 14; // is this a Label with edit=true
|
||||
Slider = 15; // like a progress bar
|
||||
Spinner = 16; // like setting the oven temperature
|
||||
Separator = 17; // TODO
|
||||
Image = 18; // TODO
|
||||
Area = 19; // TODO
|
||||
Form = 20; // TODO
|
||||
Font = 21; // TODO
|
||||
Color = 22; // TODO
|
||||
Dialog = 23; // TODO
|
||||
Stdout = 24; // can be used to capture and display log output
|
||||
}
|
||||
|
||||
enum ActionType {
|
||||
Health = 0;
|
||||
Add = 1;
|
||||
Delete = 2;
|
||||
Get = 3;
|
||||
Set = 4;
|
||||
GetText = 5;
|
||||
SetText = 6;
|
||||
AddText = 7;
|
||||
Show = 8;
|
||||
Hide = 9;
|
||||
Enable = 10;
|
||||
Disable = 11;
|
||||
Margin = 12;
|
||||
Unmargin = 13;
|
||||
Pad = 14;
|
||||
Unpad = 15;
|
||||
Append = 16;
|
||||
Move = 17;
|
||||
Dump = 18;
|
||||
User = 19; // the user did something (mouse, keyboard, etc)
|
||||
InitToolkit = 20; // initializes the toolkit
|
||||
CloseToolkit = 21; // closes the toolkit
|
||||
UserQuit = 22; // the user closed the GUI
|
||||
EnableDebug = 23; // open the debugging window
|
||||
}
|
||||
|
||||
message Response {
|
||||
// ActionType type = 1;
|
||||
int64 id = 2;
|
||||
string name = 3;
|
||||
string error = 4;
|
||||
repeated string snippets = 5;
|
||||
}
|
||||
|
||||
message Network {
|
||||
int64 id = 1;
|
||||
string name = 2;
|
||||
int64 total_cpu = 3;
|
||||
int64 total_mem = 4;
|
||||
string login_url = 5;
|
||||
}
|
||||
|
||||
message VM {
|
||||
int64 id = 1;
|
||||
string name = 2;
|
||||
string hostname = 3;
|
||||
int64 cpus = 4;
|
||||
int64 memory = 5;
|
||||
int64 disk = 6;
|
||||
string IPv6 = 7;
|
||||
string role = 8;
|
||||
string baseImage = 9;
|
||||
}
|
||||
}
|
|
@ -38,11 +38,11 @@ func (n *node) enable(b bool) {
|
|||
}
|
||||
|
||||
func (n *node) pad(at toolkit.ActionType) {
|
||||
log(debugError, "pad()")
|
||||
log(logInfo, "pad() on WidgetId =", n.WidgetId)
|
||||
|
||||
t := n.tk
|
||||
if (t == nil) {
|
||||
log(debugError, "pad() toolkit struct == nil. for", n.WidgetId)
|
||||
log(logError, "pad() toolkit struct == nil. for", n.WidgetId)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -127,15 +127,16 @@ func (p *node) place(n *node) bool {
|
|||
return true
|
||||
case toolkit.Tab:
|
||||
if (p.tk.uiTab == nil) {
|
||||
log(logError, "p.tk.uiTab == nil", p.tk)
|
||||
log(logError, "p.tk.uiTab == nil for n.WidgetId =", n.WidgetId, "p.tk =", p.tk)
|
||||
panic("p.tk.uiTab == nil")
|
||||
}
|
||||
if (n.tk.uiControl == nil) {
|
||||
log(logError, "n.tk.uiControl == nil", n.tk)
|
||||
log(logError, "n.tk.uiControl == nil for n.WidgetId =", n.WidgetId, "n.tk =", n.tk)
|
||||
panic("n.tk.uiControl == nil")
|
||||
}
|
||||
log(logError, "THIS SHOULD NEVER HAPPEN ??????? trying to place() node=", n.WidgetId, n.Name, n.Text, n.WidgetType)
|
||||
log(logError, "THIS SHOULD NEVER HAPPEN ??????? trying to place() on parent=", p.WidgetId, p.Name, p.Text, p.WidgetType)
|
||||
log(logError, "CHECK LOGIC ON THIS. APPENDING directly into a window without a tab")
|
||||
// log(logError, "THIS SHOULD NEVER HAPPEN ??????? trying to place() node=", n.WidgetId, n.Name, n.Text, n.WidgetType)
|
||||
// log(logError, "THIS SHOULD NEVER HAPPEN ??????? trying to place() on parent=", p.WidgetId, p.Name, p.Text, p.WidgetType)
|
||||
// panic("n.tk.uiControl == nil")
|
||||
p.tk.uiTab.Append(n.Text, n.tk.uiControl)
|
||||
p.tk.boxC += 1
|
||||
|
|
|
@ -14,12 +14,12 @@ var stretchy bool // expand things like buttons to the maximum size
|
|||
var padded bool // add space between things like buttons
|
||||
var margin bool // add space around the frames of windows
|
||||
|
||||
var debugToolkit bool = true
|
||||
var debugChange bool = true
|
||||
var debugPlugin bool = true
|
||||
var debugAction bool = true
|
||||
var debugFlags bool = true
|
||||
var debugGrid bool = true
|
||||
var debugToolkit bool = false
|
||||
var debugChange bool = false
|
||||
var debugPlugin bool = false
|
||||
var debugAction bool = false
|
||||
var debugFlags bool = false
|
||||
var debugGrid bool = false
|
||||
var debugNow bool = true
|
||||
var debugError bool = true
|
||||
|
||||
|
|
|
@ -40,13 +40,18 @@ func init() {
|
|||
// log(debugToolkit, "init() Setting defaultBehavior = true")
|
||||
setDefaultBehavior(true)
|
||||
|
||||
|
||||
// TODO: this is messed up. run ui.Main() from the first add? Initialize it with an empty thing first?
|
||||
// fake out the OS toolkit by making a fake window. This is probably needed for macos & windows
|
||||
// actually, this probably breaks the macos build
|
||||
go ui.Main(func() {
|
||||
demoUI()
|
||||
})
|
||||
|
||||
// andlabs = make(map[int]*andlabsT)
|
||||
pluginChan = make(chan toolkit.Action, 1)
|
||||
|
||||
log(logNow, "Init() start channel reciever")
|
||||
go ui.Main(func() {
|
||||
demoUI()
|
||||
})
|
||||
go catchActionChannel()
|
||||
log(logNow, "Init() END")
|
||||
}
|
||||
|
|
|
@ -86,7 +86,8 @@ func demoUI() {
|
|||
messageLabel.SetText("")
|
||||
})
|
||||
|
||||
mainWindow.Show()
|
||||
// this is messed up.
|
||||
// mainWindow.Show()
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -45,7 +45,8 @@ func (n *node) showWidgetPlacement(b bool, s string) {
|
|||
s1 += fmt.Sprintf("At(%2d,%2d) ", n.AtW, n.AtH)
|
||||
}
|
||||
}
|
||||
log(b, s1, s, n.WidgetType, ",", n.Name) // , "text=", w.text)
|
||||
tmp := "." + n.Name + "."
|
||||
log(b, s1, s, n.WidgetType, ",", tmp) // , "text=", w.text)
|
||||
}
|
||||
|
||||
func (n *node) dumpWidget(pad string) {
|
||||
|
|
|
@ -43,7 +43,7 @@ func catchActionChannel() {
|
|||
log(logError,"ERROR: console did not initialize")
|
||||
continue
|
||||
}
|
||||
log(logNow, "catchActionChannel()", a.WidgetId, a.ActionType, a.WidgetType, a.Name)
|
||||
log(logInfo, "catchActionChannel()", a.WidgetId, a.ActionType, a.WidgetType, a.Name)
|
||||
action(&a)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,8 +51,22 @@ func action(a *toolkit.Action) {
|
|||
case toolkit.CloseToolkit:
|
||||
log(logNow, "attempting to close the plugin and release stdout and stderr")
|
||||
standardExit()
|
||||
case toolkit.Enable:
|
||||
if n.Visible() {
|
||||
// widget was already shown
|
||||
} else {
|
||||
log(logInfo, "Setting Visable to true", a.Name)
|
||||
n.SetVisible(true)
|
||||
}
|
||||
case toolkit.Disable:
|
||||
if n.Visible() {
|
||||
log(logInfo, "Setting Visable to false", a.Name)
|
||||
n.SetVisible(false)
|
||||
} else {
|
||||
// widget was already hidden
|
||||
}
|
||||
default:
|
||||
log(logError, "action() Unknown =", a.ActionType, a.WidgetType, a.Name)
|
||||
log(logError, "action() ActionType =", a.ActionType, "WidgetType =", a.WidgetType, "Name =", a.Name)
|
||||
}
|
||||
log(logInfo, "action() END")
|
||||
}
|
||||
|
@ -70,16 +84,28 @@ func (n *node) AddText(text string) {
|
|||
}
|
||||
|
||||
func (n *node) SetText(text string) {
|
||||
var changed bool = false
|
||||
if (n == nil) {
|
||||
log(logNow, "widget is nil")
|
||||
return
|
||||
}
|
||||
n.S = text
|
||||
n.Text = text
|
||||
if (n.Text != text) {
|
||||
n.Text = text
|
||||
changed = true
|
||||
}
|
||||
if (n.S != text) {
|
||||
n.S = text
|
||||
changed = true
|
||||
}
|
||||
if (! changed) {
|
||||
return
|
||||
}
|
||||
|
||||
n.textResize()
|
||||
n.deleteView()
|
||||
n.showView()
|
||||
if (n.Visible()) {
|
||||
n.textResize()
|
||||
n.deleteView()
|
||||
n.showView()
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) Set(val any) {
|
||||
|
|
|
@ -20,20 +20,30 @@ func splitLines(s string) []string {
|
|||
return lines
|
||||
}
|
||||
|
||||
func (n *node) textResize() {
|
||||
func (n *node) textResize() bool {
|
||||
w := n.tk
|
||||
var width, height int = 0, 0
|
||||
var changed bool = false
|
||||
|
||||
for i, s := range splitLines(n.Text) {
|
||||
log(logNow, "textResize() len =", len(s), i, s)
|
||||
log(logInfo, "textResize() len =", len(s), i, s)
|
||||
if (width < len(s)) {
|
||||
width = len(s)
|
||||
}
|
||||
height += 1
|
||||
}
|
||||
w.gocuiSize.w1 = w.gocuiSize.w0 + width + me.FramePadW
|
||||
w.gocuiSize.h1 = w.gocuiSize.h0 + height + me.FramePadH
|
||||
n.showWidgetPlacement(logNow, "textResize()")
|
||||
if (w.gocuiSize.w1 != w.gocuiSize.w0 + width + me.FramePadW) {
|
||||
w.gocuiSize.w1 = w.gocuiSize.w0 + width + me.FramePadW
|
||||
changed = true
|
||||
}
|
||||
if (w.gocuiSize.h1 != w.gocuiSize.h0 + height + me.FramePadH) {
|
||||
w.gocuiSize.h1 = w.gocuiSize.h0 + height + me.FramePadH
|
||||
changed = true
|
||||
}
|
||||
if (changed) {
|
||||
n.showWidgetPlacement(logNow, "textResize() changed")
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
func (n *node) hideView() {
|
||||
|
@ -58,17 +68,38 @@ func (n *node) showView() {
|
|||
x0, y0, x1, y1, err := me.baseGui.ViewPosition(w.cuiName)
|
||||
log(logInfo, "showView() w.v already defined for widget", n.Name, err)
|
||||
|
||||
// n.smartGocuiSize()
|
||||
changed := n.textResize()
|
||||
|
||||
if (changed) {
|
||||
log(logNow, "showView() textResize() changed. Should recreateView here wId =", w.cuiName)
|
||||
} else {
|
||||
log(logNow, "showView() Clear() and Fprint() here wId =", w.cuiName)
|
||||
w.v.Clear()
|
||||
fmt.Fprint(w.v, n.Text)
|
||||
n.SetVisible(false)
|
||||
n.SetVisible(true)
|
||||
return
|
||||
}
|
||||
|
||||
// if the gocui element has changed where it is supposed to be on the screen
|
||||
// recreate it
|
||||
if (x0 != w.gocuiSize.w0) || (y0 != w.gocuiSize.h0) {
|
||||
log(logError, "showView() w.v.w0 != x0", n.Name, w.gocuiSize.w0, x0)
|
||||
log(logError, "showView() w.v.h0 != y0", n.Name, w.gocuiSize.h0, y0)
|
||||
if (x0 != w.gocuiSize.w0) {
|
||||
n.recreateView()
|
||||
return
|
||||
}
|
||||
if (x1 != w.gocuiSize.w1) || (y1 != w.gocuiSize.h1) {
|
||||
log(logError, "showView() w.v.w1 != x1", n.Name, w.gocuiSize.w1, x1)
|
||||
log(logError, "showView() w.v.h1 != y1", n.Name, w.gocuiSize.h1, y1)
|
||||
if (y0 != w.gocuiSize.h0) {
|
||||
log(logError, "showView() start hight mismatch id=", w.cuiName, "gocui h vs computed h =", w.gocuiSize.h0, y0)
|
||||
n.recreateView()
|
||||
return
|
||||
}
|
||||
if (x1 != w.gocuiSize.w1) {
|
||||
log(logError, "showView() too wide", w.cuiName, "w,w", w.gocuiSize.w1, x1)
|
||||
n.recreateView()
|
||||
return
|
||||
}
|
||||
if (y1 != w.gocuiSize.h1) {
|
||||
log(logError, "showView() too high", w.cuiName, "h,h", w.gocuiSize.h1, y1)
|
||||
n.recreateView()
|
||||
return
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue