diff --git a/config.go b/config.go index 1293c5f..d98f201 100644 --- a/config.go +++ b/config.go @@ -3,7 +3,10 @@ package main import ( // Standard "encoding/json" + "fmt" "os" + "slices" + "strings" ) type Config struct { @@ -20,6 +23,8 @@ type Config struct { } Devices []Device + + filename string } type Device struct { @@ -39,5 +44,64 @@ func readConfig() (config Config, err error) { } err = json.Unmarshal(configData, &config) + config.filename = flagConfig + return +} + +func (cfg *Config) UpdateDevice(currentName string, deviceToUpdate Device) (dev Device, err error) { + + i := slices.IndexFunc(cfg.Devices, func(d Device) bool { + return strings.TrimSpace(strings.ToLower(d.Name)) == strings.TrimSpace(strings.ToLower(currentName)) + }) + + if i > -1 { + dev = cfg.Devices[i] + } + + if strings.TrimSpace(deviceToUpdate.Name) == "" { + err = fmt.Errorf("Name can't be empty") + return + } + if strings.TrimSpace(deviceToUpdate.Address) == "" { + err = fmt.Errorf("Address can't be empty") + return + } + if strings.TrimSpace(deviceToUpdate.Username) == "" { + err = fmt.Errorf("Username can't be empty") + return + } + if deviceToUpdate.Port < 1 || deviceToUpdate.Port > 65535 { + err = fmt.Errorf("Invalid port") + return + } + + dev.Name = strings.TrimSpace(strings.ToLower(deviceToUpdate.Name)) + dev.Address = strings.TrimSpace(strings.ToLower(deviceToUpdate.Address)) + dev.Port = deviceToUpdate.Port + dev.Username = strings.TrimSpace(deviceToUpdate.Username) + + // TODO - Should be configurable... + if dev.Timeout == 0 { + dev.Timeout = 10 + } + + // Device not found - create it! + if i == -1 { + if strings.TrimSpace(deviceToUpdate.Password) == "" { + err = fmt.Errorf("Password can't be empty") + return + } + dev.Password = strings.TrimSpace(deviceToUpdate.Password) + cfg.Devices = append(cfg.Devices, dev) + } else { + if deviceToUpdate.Password != "" { + dev.Password = strings.TrimSpace(deviceToUpdate.Password) + } + cfg.Devices[i] = dev + } + + j, _ := json.Marshal(cfg) + err = os.WriteFile(cfg.filename, j, 0600) + return } diff --git a/routeros_device.go b/routeros_device.go index 50c19d1..93e5738 100644 --- a/routeros_device.go +++ b/routeros_device.go @@ -58,6 +58,7 @@ func (dev *RouterosDevice) Init() { // {{{ // query sends a RouterOS REST API query and returns the unparsed body. func (dev RouterosDevice) query(method, path string, reqBody []byte) (body []byte, err error) { // {{{ + logger.Debug("FOO", "port", dev.Port) url := fmt.Sprintf("https://%s:%d/rest%s", dev.Host, dev.Port, path) logger.Info("URL", "method", method, "url", url) @@ -150,7 +151,7 @@ func (dev *RouterosDevice) UpdateDNSEntry(record DNSEntry) (entry DNSEntry, err err = json.Unmarshal(body, &entry) return }// }}} -func (dev *RouterosDevice) DeleteDNSEntry(id string) (err error) { +func (dev *RouterosDevice) DeleteDNSEntry(id string) (err error) {// {{{ _, err = dev.query("DELETE", "/ip/dns/static/"+id, []byte{}) if err != nil { rosError := struct{ Detail string }{} @@ -162,7 +163,7 @@ func (dev *RouterosDevice) DeleteDNSEntry(id string) (err error) { return } return -} +}// }}} /* // FillPeerDetails retrieves RouterOS resource ID, allowed-address and comment from the router diff --git a/static/css/index.css b/static/css/index.css index cf514f9..407ae3a 100644 --- a/static/css/index.css +++ b/static/css/index.css @@ -1,5 +1,5 @@ :root { - --header-background: #acbb78; + --header-background: #e1eabe; --header-border: 1px solid #869a41; --line-color: #ccc; @@ -120,12 +120,21 @@ button { background-color: var(--header-background); .device-select { + display: grid; + grid-template-columns: min-content min-content; + align-items: center; + padding: 16px; border-right: var(--header-border); - .device-name { + .label { + grid-column: 1 / -1; font-weight: bold; } + + img { + margin-left: 8px; + } } #search { @@ -465,3 +474,49 @@ button { margin-top: 8px; } } + +#device-dialog { + display: grid; + grid-template-columns: min-content min-content; + align-items: top; + grid-gap: 16px; + + .devices { + display: flex; + flex-direction:column; + gap: 8px; + align-items: flex-start; + + .header { + font-weight: bold; + } + + .device { + border: 1px solid #aaa; + background-color: #f0f0f0; + padding: 8px 16px; + border-radius: 8px; + cursor: pointer; + user-select: none; + + &.selected { + background-color: var(--header-background); + } + } + } + + .fields { + display: grid; + grid-template-columns: min-content 1fr; + grid-gap: 8px 16px; + align-items: center; + + border: 1px solid #aaa; + padding: 16px; + + .actions { + grid-column: 1 / -1; + justify-self: right; + } + } +} diff --git a/static/images/icon_create.svg b/static/images/icon_create.svg index 1fb9dea..e91d683 100644 --- a/static/images/icon_create.svg +++ b/static/images/icon_create.svg @@ -25,9 +25,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="1.1646825" - inkscape:cx="-71.693356" - inkscape:cy="-55.809199" + inkscape:zoom="5.6568542" + inkscape:cx="-20.948038" + inkscape:cy="9.0156115" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="false" diff --git a/static/js/dns.mjs b/static/js/dns.mjs index 766e39d..707b6ed 100644 --- a/static/js/dns.mjs +++ b/static/js/dns.mjs @@ -303,6 +303,10 @@ export class Application { this.searchWidget.searchField.focus() break + case 'd': + new DeviceDialog(this.devices).render() + break + default: handled = false } @@ -385,6 +389,21 @@ class Device { name() {// {{{ return this.data.Name?.toLowerCase() || '' }// }}} + address() {// {{{ + return this.data.Address || '' + }// }}} + port() {// {{{ + return parseInt(this.data.Port || 0) + }// }}} + username() {// {{{ + return this.data.Username || '' + }// }}} + password() {// {{{ + return this.data.Password || '' + }// }}} + name() {// {{{ + return this.data.Name?.toLowerCase() || '' + }// }}} async retrieveRecords() {// {{{ const data = await fetch(`/device/${this.name()}/dns_records`) const json = await data.json() @@ -410,12 +429,14 @@ class DeviceSelectWidget { this.div = document.createElement('div') this.div.classList.add('device-select') this.div.innerHTML = ` -