diff --git a/dns.go b/dns.go index b4a019f..9aaff4f 100644 --- a/dns.go +++ b/dns.go @@ -21,11 +21,11 @@ type DNSRecord struct { } type DNSEntry struct { - ID string `json:".id"` - Disabled string `json:"disabled"` - Name string `json:"name"` - TTL string `json:"ttl"` - Type string `json:"type"` + ID string `json:".id,omitempty"` + Disabled string `json:"disabled,omitempty"` + Name string `json:"name,omitempty"` + TTL string `json:"ttl,omitempty"` + Type string `json:"type,omitempty"` Address string `json:"address,omitempty"` CNAME string `json:"cname,omitempty"` diff --git a/routeros_device.go b/routeros_device.go index 1a907de..4c65e25 100644 --- a/routeros_device.go +++ b/routeros_device.go @@ -106,7 +106,7 @@ func (dev *RouterosDevice) GetIdentity() (identity string, err error) { // {{{ return } // }}} -func (dev *RouterosDevice) StaticDNSEntries() (records []DNSRecord, err error) { +func (dev *RouterosDevice) StaticDNSEntries() (records []DNSRecord, err error) {// {{{ records = []DNSRecord{} var routerosEntries []DNSEntry @@ -127,11 +127,16 @@ func (dev *RouterosDevice) StaticDNSEntries() (records []DNSRecord, err error) { logger.Info("FOO", "entry", records) return -} -func (dev *RouterosDevice) UpdateDNSEntry(record DNSEntry) (err error) { +}// }}} +func (dev *RouterosDevice) UpdateDNSEntry(record DNSEntry) (entry DNSEntry, err error) {// {{{ req, _ := json.Marshal(record) - _, err = dev.query("PATCH", "/ip/dns/static/"+record.ID, req) + var body []byte + if record.ID == "" { + body, err = dev.query("PUT", "/ip/dns/static", req) + } else { + body, err = dev.query("PATCH", "/ip/dns/static/"+record.ID, req) + } if err != nil { rosError := struct{ Detail string }{} if jsonError := json.Unmarshal([]byte(err.Error()), &rosError); jsonError == nil { @@ -142,8 +147,9 @@ func (dev *RouterosDevice) UpdateDNSEntry(record DNSEntry) (err error) { return } + err = json.Unmarshal(body, &entry) 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 f076a7c..f4f02c0 100644 --- a/static/css/index.css +++ b/static/css/index.css @@ -81,11 +81,20 @@ button { padding: 4px 8px; } +#create-icon { + position: absolute; + top: 18px; + right: 64px; + width: 24px; + cursor: pointer; +} + #settings-icon { position: absolute; top: 16px; right: 16px; width: 32px; + cursor: pointer; } #records-tree { diff --git a/static/images/icon_create.svg b/static/images/icon_create.svg new file mode 100644 index 0000000..cc1dbd4 --- /dev/null +++ b/static/images/icon_create.svg @@ -0,0 +1,67 @@ + + + + + + + + + + image/svg+xml + + + + + + plus-box + + + diff --git a/static/js/dns.mjs b/static/js/dns.mjs index a46a185..c128d08 100644 --- a/static/js/dns.mjs +++ b/static/js/dns.mjs @@ -97,6 +97,11 @@ export class Application { this.recordsTree = document.createElement('div') this.recordsTree.id = 'records-tree' + this.createIcon = document.createElement('img') + this.createIcon.id = 'create-icon' + this.createIcon.src = `/images/${_VERSION}/icon_create.svg` + this.createIcon.addEventListener('click', () => new RecordDialog(new Record()).show()) + this.settingsIcon = document.createElement('img') this.settingsIcon.id = 'settings-icon' this.settingsIcon.src = `/images/${_VERSION}/icon_settings.svg` @@ -104,8 +109,8 @@ export class Application { document.body.appendChild(this.recordsTree) document.body.appendChild(this.settingsIcon) + document.body.appendChild(this.createIcon) } - //this.recordsTree.replaceChildren() // Top root folder doesn't have to be shown. const folders = Array.from(this.topFolder.subfolders.values()) @@ -273,7 +278,7 @@ class Folder { class Record { constructor(data) {// {{{ - this.data = data + this.data = data || {} this.imgIcon = null this.divFQDN = null @@ -283,7 +288,7 @@ class Record { }// }}} id() {// {{{ - return this.data['.id'] + return this.data['.id'] || '' }// }}} disabled() {// {{{ return this.data.Disabled === 'true' @@ -292,16 +297,16 @@ class Record { return this.data.Dynamic === 'true' }// }}} name() {// {{{ - return this.data.Name.toLowerCase() + return this.data.Name?.toLowerCase() || '' }// }}} ttl() {// {{{ - return this.data.TTL + return this.data.TTL || '30m' }// }}} type() {// {{{ - return this.data.Type.toUpperCase() + return this.data.Type?.toUpperCase() || 'A' }// }}} value() {// {{{ - return this.data.ParsedValue + return this.data.ParsedValue || '' }// }}} matchSubdomain() {// {{{ return this.data.MatchSubdomain === 'true' @@ -384,6 +389,8 @@ class Record { return [this.imgIcon, this.divFQDN, this.divType, this.divValue, this.divSeparator] }// }}} save() {// {{{ + const created = (this.id() == '') + fetch('/record/save', { method: 'POST', body: JSON.stringify(this.data), @@ -394,6 +401,13 @@ class Record { alert(json.Error) return } + + // The data is read from the server/routeros device + // since it could have manipulated the data. + this.data = json.Record + if (created) + _app.records.push(this) + _app.cleanFolders() _app.renderFolders() _app.render() diff --git a/webserver.go b/webserver.go index 862a23d..56f75c8 100644 --- a/webserver.go +++ b/webserver.go @@ -101,13 +101,15 @@ func actionRecordSave(w http.ResponseWriter, r *http.Request) { return } - err = device.UpdateDNSEntry(record.toDNSEntry()) + entry, err := device.UpdateDNSEntry(record.toDNSEntry()) if err != nil { httpError(w, err) logger.Error("webserver", "op", "record_save", "error", err) return } - j, _ := json.Marshal(struct{ OK bool }{true}) + record = NewDNSRecord(entry) + + j, _ := json.Marshal(struct{ OK bool; Record DNSRecord }{true, record}) w.Write(j) }