Create new records
This commit is contained in:
parent
7d7c0c9570
commit
3763367510
6 changed files with 117 additions and 19 deletions
10
dns.go
10
dns.go
|
|
@ -21,11 +21,11 @@ type DNSRecord struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type DNSEntry struct {
|
type DNSEntry struct {
|
||||||
ID string `json:".id"`
|
ID string `json:".id,omitempty"`
|
||||||
Disabled string `json:"disabled"`
|
Disabled string `json:"disabled,omitempty"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name,omitempty"`
|
||||||
TTL string `json:"ttl"`
|
TTL string `json:"ttl,omitempty"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type,omitempty"`
|
||||||
|
|
||||||
Address string `json:"address,omitempty"`
|
Address string `json:"address,omitempty"`
|
||||||
CNAME string `json:"cname,omitempty"`
|
CNAME string `json:"cname,omitempty"`
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ func (dev *RouterosDevice) GetIdentity() (identity string, err error) { // {{{
|
||||||
return
|
return
|
||||||
} // }}}
|
} // }}}
|
||||||
|
|
||||||
func (dev *RouterosDevice) StaticDNSEntries() (records []DNSRecord, err error) {
|
func (dev *RouterosDevice) StaticDNSEntries() (records []DNSRecord, err error) {// {{{
|
||||||
records = []DNSRecord{}
|
records = []DNSRecord{}
|
||||||
|
|
||||||
var routerosEntries []DNSEntry
|
var routerosEntries []DNSEntry
|
||||||
|
|
@ -127,11 +127,16 @@ func (dev *RouterosDevice) StaticDNSEntries() (records []DNSRecord, err error) {
|
||||||
logger.Info("FOO", "entry", records)
|
logger.Info("FOO", "entry", records)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}// }}}
|
||||||
func (dev *RouterosDevice) UpdateDNSEntry(record DNSEntry) (err error) {
|
func (dev *RouterosDevice) UpdateDNSEntry(record DNSEntry) (entry DNSEntry, err error) {// {{{
|
||||||
req, _ := json.Marshal(record)
|
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 {
|
if err != nil {
|
||||||
rosError := struct{ Detail string }{}
|
rosError := struct{ Detail string }{}
|
||||||
if jsonError := json.Unmarshal([]byte(err.Error()), &rosError); jsonError == nil {
|
if jsonError := json.Unmarshal([]byte(err.Error()), &rosError); jsonError == nil {
|
||||||
|
|
@ -142,8 +147,9 @@ func (dev *RouterosDevice) UpdateDNSEntry(record DNSEntry) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, &entry)
|
||||||
return
|
return
|
||||||
}
|
}// }}}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// FillPeerDetails retrieves RouterOS resource ID, allowed-address and comment from the router
|
// FillPeerDetails retrieves RouterOS resource ID, allowed-address and comment from the router
|
||||||
|
|
|
||||||
|
|
@ -81,11 +81,20 @@ button {
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#create-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 18px;
|
||||||
|
right: 64px;
|
||||||
|
width: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
#settings-icon {
|
#settings-icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 16px;
|
top: 16px;
|
||||||
right: 16px;
|
right: 16px;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#records-tree {
|
#records-tree {
|
||||||
|
|
|
||||||
67
static/images/icon_create.svg
Normal file
67
static/images/icon_create.svg
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
viewBox="0 0 4.7624999 4.7625001"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="1.4.3 (0d15f75, 2025-12-25)"
|
||||||
|
sodipodi:docname="icon_create.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1"
|
||||||
|
inkscape:cx="-71"
|
||||||
|
inkscape:cy="-52"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="2190"
|
||||||
|
inkscape:window-height="1404"
|
||||||
|
inkscape:window-x="1463"
|
||||||
|
inkscape:window-y="16"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:showpageshadow="true"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d6d6d6"
|
||||||
|
showborder="true" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(84.402084,-12.7)">
|
||||||
|
<title
|
||||||
|
id="title1">plus-box</title>
|
||||||
|
<path
|
||||||
|
d="m -80.697917,15.345833 h -1.058333 v 1.058334 h -0.529167 v -1.058334 h -1.058333 v -0.529166 h 1.058333 v -1.058334 h 0.529167 v 1.058334 h 1.058333 M -80.16875,12.7 h -3.704167 c -0.293687,0 -0.529167,0.235479 -0.529167,0.529167 v 3.704166 a 0.52916667,0.52916667 0 0 0 0.529167,0.529167 h 3.704167 a 0.52916667,0.52916667 0 0 0 0.529166,-0.529167 v -3.704166 c 0,-0.293688 -0.238125,-0.529167 -0.529166,-0.529167 z"
|
||||||
|
id="path1"
|
||||||
|
style="stroke-width:0.264583;fill:#89a02c" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.3 KiB |
|
|
@ -97,6 +97,11 @@ export class Application {
|
||||||
this.recordsTree = document.createElement('div')
|
this.recordsTree = document.createElement('div')
|
||||||
this.recordsTree.id = 'records-tree'
|
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 = document.createElement('img')
|
||||||
this.settingsIcon.id = 'settings-icon'
|
this.settingsIcon.id = 'settings-icon'
|
||||||
this.settingsIcon.src = `/images/${_VERSION}/icon_settings.svg`
|
this.settingsIcon.src = `/images/${_VERSION}/icon_settings.svg`
|
||||||
|
|
@ -104,8 +109,8 @@ export class Application {
|
||||||
|
|
||||||
document.body.appendChild(this.recordsTree)
|
document.body.appendChild(this.recordsTree)
|
||||||
document.body.appendChild(this.settingsIcon)
|
document.body.appendChild(this.settingsIcon)
|
||||||
|
document.body.appendChild(this.createIcon)
|
||||||
}
|
}
|
||||||
//this.recordsTree.replaceChildren()
|
|
||||||
|
|
||||||
// Top root folder doesn't have to be shown.
|
// Top root folder doesn't have to be shown.
|
||||||
const folders = Array.from(this.topFolder.subfolders.values())
|
const folders = Array.from(this.topFolder.subfolders.values())
|
||||||
|
|
@ -273,7 +278,7 @@ class Folder {
|
||||||
|
|
||||||
class Record {
|
class Record {
|
||||||
constructor(data) {// {{{
|
constructor(data) {// {{{
|
||||||
this.data = data
|
this.data = data || {}
|
||||||
|
|
||||||
this.imgIcon = null
|
this.imgIcon = null
|
||||||
this.divFQDN = null
|
this.divFQDN = null
|
||||||
|
|
@ -283,7 +288,7 @@ class Record {
|
||||||
}// }}}
|
}// }}}
|
||||||
|
|
||||||
id() {// {{{
|
id() {// {{{
|
||||||
return this.data['.id']
|
return this.data['.id'] || ''
|
||||||
}// }}}
|
}// }}}
|
||||||
disabled() {// {{{
|
disabled() {// {{{
|
||||||
return this.data.Disabled === 'true'
|
return this.data.Disabled === 'true'
|
||||||
|
|
@ -292,16 +297,16 @@ class Record {
|
||||||
return this.data.Dynamic === 'true'
|
return this.data.Dynamic === 'true'
|
||||||
}// }}}
|
}// }}}
|
||||||
name() {// {{{
|
name() {// {{{
|
||||||
return this.data.Name.toLowerCase()
|
return this.data.Name?.toLowerCase() || ''
|
||||||
}// }}}
|
}// }}}
|
||||||
ttl() {// {{{
|
ttl() {// {{{
|
||||||
return this.data.TTL
|
return this.data.TTL || '30m'
|
||||||
}// }}}
|
}// }}}
|
||||||
type() {// {{{
|
type() {// {{{
|
||||||
return this.data.Type.toUpperCase()
|
return this.data.Type?.toUpperCase() || 'A'
|
||||||
}// }}}
|
}// }}}
|
||||||
value() {// {{{
|
value() {// {{{
|
||||||
return this.data.ParsedValue
|
return this.data.ParsedValue || ''
|
||||||
}// }}}
|
}// }}}
|
||||||
matchSubdomain() {// {{{
|
matchSubdomain() {// {{{
|
||||||
return this.data.MatchSubdomain === 'true'
|
return this.data.MatchSubdomain === 'true'
|
||||||
|
|
@ -384,6 +389,8 @@ class Record {
|
||||||
return [this.imgIcon, this.divFQDN, this.divType, this.divValue, this.divSeparator]
|
return [this.imgIcon, this.divFQDN, this.divType, this.divValue, this.divSeparator]
|
||||||
}// }}}
|
}// }}}
|
||||||
save() {// {{{
|
save() {// {{{
|
||||||
|
const created = (this.id() == '')
|
||||||
|
|
||||||
fetch('/record/save', {
|
fetch('/record/save', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify(this.data),
|
body: JSON.stringify(this.data),
|
||||||
|
|
@ -394,6 +401,13 @@ class Record {
|
||||||
alert(json.Error)
|
alert(json.Error)
|
||||||
return
|
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.cleanFolders()
|
||||||
_app.renderFolders()
|
_app.renderFolders()
|
||||||
_app.render()
|
_app.render()
|
||||||
|
|
|
||||||
|
|
@ -101,13 +101,15 @@ func actionRecordSave(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = device.UpdateDNSEntry(record.toDNSEntry())
|
entry, err := device.UpdateDNSEntry(record.toDNSEntry())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, err)
|
httpError(w, err)
|
||||||
logger.Error("webserver", "op", "record_save", "error", err)
|
logger.Error("webserver", "op", "record_save", "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
j, _ := json.Marshal(struct{ OK bool }{true})
|
record = NewDNSRecord(entry)
|
||||||
|
|
||||||
|
j, _ := json.Marshal(struct{ OK bool; Record DNSRecord }{true, record})
|
||||||
w.Write(j)
|
w.Write(j)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue