Compare commits
No commits in common. "7213ee7a28e8e6b6b27135852996dadfede24203" and "e92022f1f1e3dcc3f08d4612a303fd9b771260f1" have entirely different histories.
7213ee7a28
...
e92022f1f1
4 changed files with 30 additions and 185 deletions
14
config.go
14
config.go
|
|
@ -48,7 +48,7 @@ func readConfig() (config Config, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *Config) UpdateDevice(currentName string, deviceToUpdate Device) (dev Device, err error) {// {{{
|
func (cfg *Config) UpdateDevice(currentName string, deviceToUpdate Device) (dev Device, err error) {
|
||||||
|
|
||||||
i := slices.IndexFunc(cfg.Devices, func(d Device) bool {
|
i := slices.IndexFunc(cfg.Devices, func(d Device) bool {
|
||||||
return strings.TrimSpace(strings.ToLower(d.Name)) == strings.TrimSpace(strings.ToLower(currentName))
|
return strings.TrimSpace(strings.ToLower(d.Name)) == strings.TrimSpace(strings.ToLower(currentName))
|
||||||
|
|
@ -104,14 +104,4 @@ func (cfg *Config) UpdateDevice(currentName string, deviceToUpdate Device) (dev
|
||||||
err = os.WriteFile(cfg.filename, j, 0600)
|
err = os.WriteFile(cfg.filename, j, 0600)
|
||||||
|
|
||||||
return
|
return
|
||||||
}// }}}
|
}
|
||||||
func (cfg *Config) DeleteDevice(devname string) (err error) {// {{{
|
|
||||||
cfg.Devices = slices.DeleteFunc(cfg.Devices, func(d Device) bool {
|
|
||||||
return strings.TrimSpace(strings.ToLower(d.Name)) == strings.TrimSpace(strings.ToLower(devname))
|
|
||||||
})
|
|
||||||
|
|
||||||
j, _ := json.Marshal(cfg)
|
|
||||||
err = os.WriteFile(cfg.filename, j, 0600)
|
|
||||||
|
|
||||||
return
|
|
||||||
}// }}}
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ html {
|
||||||
*:after {
|
*:after {
|
||||||
box-sizing: inherit;
|
box-sizing: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
*:focus {
|
*:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
@ -151,7 +150,6 @@ button {
|
||||||
|
|
||||||
&.show {
|
&.show {
|
||||||
border-right: var(--header-border);
|
border-right: var(--header-border);
|
||||||
|
|
||||||
&>* {
|
&>* {
|
||||||
display: initial;
|
display: initial;
|
||||||
}
|
}
|
||||||
|
|
@ -480,58 +478,31 @@ button {
|
||||||
#device-dialog {
|
#device-dialog {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: min-content min-content;
|
grid-template-columns: min-content min-content;
|
||||||
align-items: start;
|
align-items: top;
|
||||||
grid-gap: 16px;
|
grid-gap: 16px;
|
||||||
|
|
||||||
.devices {
|
.devices {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction:column;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
min-height: 256px;
|
|
||||||
height: 75vh;
|
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
width: 100%;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr min-content;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
.filter {
|
|
||||||
grid-column: 1 / -1;
|
|
||||||
margin-top: 8px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-list {
|
.device {
|
||||||
overflow-y: auto;
|
border: 1px solid #aaa;
|
||||||
min-width: 256px;
|
background-color: #f0f0f0;
|
||||||
justify-items: start;
|
padding: 8px 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
.device {
|
&.selected {
|
||||||
border: 1px solid #aaa;
|
background-color: var(--header-background);
|
||||||
background-color: #f0f0f0;
|
|
||||||
padding: 8px 16px;
|
|
||||||
border-radius: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
white-space: nowrap;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
margin-right: 16px;
|
|
||||||
|
|
||||||
&.selected {
|
|
||||||
background-color: var(--header-background);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.filtered {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.fields {
|
.fields {
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,7 @@ export class Application {
|
||||||
})
|
})
|
||||||
|
|
||||||
_mbus.subscribe('device_selected', event => this.connectDevice(event.detail.devName))
|
_mbus.subscribe('device_selected', event => this.connectDevice(event.detail.devName))
|
||||||
_mbus.subscribe('device_deleted', event => this.deleteDevice(event.detail.devName))
|
_mbus.subscribe('search', ()=>this.search())
|
||||||
_mbus.subscribe('search', () => this.search())
|
|
||||||
}// }}}
|
}// }}}
|
||||||
|
|
||||||
// resetDeviceState removes the device state such as the records, records tree and search field.
|
// resetDeviceState removes the device state such as the records, records tree and search field.
|
||||||
|
|
@ -97,15 +96,6 @@ export class Application {
|
||||||
}
|
}
|
||||||
}// }}}
|
}// }}}
|
||||||
|
|
||||||
// deleteDevice removes it from the list and resets app state if it was connected.
|
|
||||||
deleteDevice(devName) {// {{{
|
|
||||||
this.devices.delete(devName)
|
|
||||||
if (this.currentDevice?.name() == devName) {
|
|
||||||
this.resetDeviceState()
|
|
||||||
this.settings.set('last_device', '')
|
|
||||||
}
|
|
||||||
}// }}}
|
|
||||||
|
|
||||||
// filteredRecords takes the current search value and only returns records matching it.
|
// filteredRecords takes the current search value and only returns records matching it.
|
||||||
filteredRecords() {// {{{
|
filteredRecords() {// {{{
|
||||||
const searchFor = this.searchWidget.value()
|
const searchFor = this.searchWidget.value()
|
||||||
|
|
@ -430,9 +420,6 @@ class DeviceSelectWidget {
|
||||||
this.div = null
|
this.div = null
|
||||||
this.elDeviceSelect = null
|
this.elDeviceSelect = null
|
||||||
this.currentlySelected = null
|
this.currentlySelected = null
|
||||||
|
|
||||||
_mbus.subscribe('device_deleted', event => this.deleteDevice(event.detail.devName))
|
|
||||||
_mbus.subscribe('device_updated', event => this.updateDevice(event.detail.device))
|
|
||||||
}// }}}
|
}// }}}
|
||||||
setDevices(devices) {// {{{
|
setDevices(devices) {// {{{
|
||||||
this.devices = devices
|
this.devices = devices
|
||||||
|
|
@ -449,7 +436,7 @@ class DeviceSelectWidget {
|
||||||
|
|
||||||
this.elDeviceSelect = this.div.querySelector('select')
|
this.elDeviceSelect = this.div.querySelector('select')
|
||||||
this.elDeviceSelect.addEventListener('change', () => this.notifyDeviceSelect())
|
this.elDeviceSelect.addEventListener('change', () => this.notifyDeviceSelect())
|
||||||
this.div.querySelector('img').addEventListener('click', () => new DeviceDialog(this.devices).render())
|
this.div.querySelector('img').addEventListener('click', ()=>new DeviceDialog(this.devices).render())
|
||||||
}
|
}
|
||||||
this.restockDeviceSelect()
|
this.restockDeviceSelect()
|
||||||
return this.div
|
return this.div
|
||||||
|
|
@ -461,9 +448,7 @@ class DeviceSelectWidget {
|
||||||
emptyOption.dataset.devicename = "-"
|
emptyOption.dataset.devicename = "-"
|
||||||
this.elDeviceSelect.appendChild(emptyOption)
|
this.elDeviceSelect.appendChild(emptyOption)
|
||||||
|
|
||||||
const devNames = Array.from(this.devices.keys()).sort()
|
this.devices.forEach(dev => {
|
||||||
devNames.forEach(devName => {
|
|
||||||
const dev = this.devices.get(devName)
|
|
||||||
const option = document.createElement('option')
|
const option = document.createElement('option')
|
||||||
option.innerText = dev.name()
|
option.innerText = dev.name()
|
||||||
option.dataset.devicename = dev.name()
|
option.dataset.devicename = dev.name()
|
||||||
|
|
@ -482,15 +467,6 @@ class DeviceSelectWidget {
|
||||||
this.elDeviceSelect.value = devname
|
this.elDeviceSelect.value = devname
|
||||||
this.notifyDeviceSelect()
|
this.notifyDeviceSelect()
|
||||||
}// }}}
|
}// }}}
|
||||||
deleteDevice(devName) {// {{{
|
|
||||||
this.devices.delete(devName)
|
|
||||||
this.restockDeviceSelect()
|
|
||||||
}// }}}
|
|
||||||
updateDevice(dev) {// {{{
|
|
||||||
console.log(dev)
|
|
||||||
this.devices.set(dev.name(), dev)
|
|
||||||
this.restockDeviceSelect()
|
|
||||||
}// }}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Folder {
|
class Folder {
|
||||||
|
|
@ -1036,14 +1012,7 @@ class DeviceDialog {
|
||||||
this.dlg.id = 'device-dialog'
|
this.dlg.id = 'device-dialog'
|
||||||
this.dlg.innerHTML = `
|
this.dlg.innerHTML = `
|
||||||
<div class="devices">
|
<div class="devices">
|
||||||
<div class="header">
|
<div class="header">Devices</div>
|
||||||
<div>Devices</div>
|
|
||||||
<img class="create" src="/images/${_VERSION}/icon_create.svg">
|
|
||||||
<input type="text" class="filter" placeholder="Filter">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="device-list">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
<div>Name</div>
|
<div>Name</div>
|
||||||
|
|
@ -1063,13 +1032,12 @@ class DeviceDialog {
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button class="delete">Delete</button>
|
<button class="delete">Delete</button>
|
||||||
<button class="update">Save</button>
|
<button class="update">Update</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
|
|
||||||
this.elDevices = this.dlg.querySelector('.devices')
|
this.elDevices = this.dlg.querySelector('.devices')
|
||||||
this.elDeviceList = this.dlg.querySelector('.device-list')
|
|
||||||
this.elFields = this.dlg.querySelector('.fields')
|
this.elFields = this.dlg.querySelector('.fields')
|
||||||
this.elName = this.dlg.querySelector('.fields .name')
|
this.elName = this.dlg.querySelector('.fields .name')
|
||||||
this.elAddress = this.dlg.querySelector('.fields .address')
|
this.elAddress = this.dlg.querySelector('.fields .address')
|
||||||
|
|
@ -1077,50 +1045,31 @@ class DeviceDialog {
|
||||||
this.elUsername = this.dlg.querySelector('.fields .username')
|
this.elUsername = this.dlg.querySelector('.fields .username')
|
||||||
this.elPassword = this.dlg.querySelector('.fields .password')
|
this.elPassword = this.dlg.querySelector('.fields .password')
|
||||||
|
|
||||||
this.dlg.querySelector('.create').addEventListener('click', () => this.createDevice())
|
|
||||||
this.dlg.querySelector('.filter').addEventListener('input', () => this.filterDevices())
|
|
||||||
this.dlg.querySelector('.update').addEventListener('click', () => this.updateDevice())
|
|
||||||
this.dlg.querySelector('.delete').addEventListener('click', () => this.deleteDevice())
|
|
||||||
this.dlg.addEventListener('close', () => this.dlg.remove())
|
|
||||||
|
|
||||||
this.updateDeviceList()
|
|
||||||
|
|
||||||
document.body.appendChild(this.dlg)
|
|
||||||
this.dlg.showModal()
|
|
||||||
}// }}}
|
|
||||||
filterDevices() {// {{{
|
|
||||||
const filter = this.dlg.querySelector('.filter').value.toLowerCase()
|
|
||||||
|
|
||||||
this.elDeviceList.querySelectorAll('.device').forEach(dev=>{
|
|
||||||
if (dev.innerText.toLowerCase().includes(filter))
|
|
||||||
dev.classList.remove('filtered')
|
|
||||||
else
|
|
||||||
dev.classList.add('filtered')
|
|
||||||
})
|
|
||||||
}// }}}
|
|
||||||
updateDeviceList() {// {{{
|
|
||||||
this.elDeviceList.replaceChildren()
|
|
||||||
|
|
||||||
const devices = Array.from(this.devices.values())
|
const devices = Array.from(this.devices.values())
|
||||||
devices.sort((a, b) => {
|
devices.sort((a, b)=>{
|
||||||
if (a.name() < b.name()) return -1
|
if (a.name() < b.name()) return -1
|
||||||
if (a.name() > b.name()) return 1
|
if (a.name() > b.name()) return 1
|
||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
devices.forEach(dev => {
|
devices.forEach(dev=>{
|
||||||
const devEl = document.createElement('div')
|
const devEl = document.createElement('div')
|
||||||
devEl.classList.add('device')
|
devEl.classList.add('device')
|
||||||
devEl.dataset.name = dev.name()
|
|
||||||
devEl.innerText = dev.name()
|
devEl.innerText = dev.name()
|
||||||
devEl.addEventListener('click', () => this.editDevice(dev, devEl))
|
devEl.addEventListener('click', ()=>this.editDevice(dev, devEl))
|
||||||
this.elDeviceList.appendChild(devEl)
|
this.elDevices.appendChild(devEl)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.dlg.querySelector('.update').addEventListener('click', ()=>this.updateDevice())
|
||||||
|
this.dlg.addEventListener('close', ()=>this.dlg.remove())
|
||||||
|
|
||||||
|
document.body.appendChild(this.dlg)
|
||||||
|
this.dlg.showModal()
|
||||||
}// }}}
|
}// }}}
|
||||||
|
|
||||||
editDevice(dev, devEl) {// {{{
|
editDevice(dev, devEl) {// {{{
|
||||||
this.device = dev
|
this.device = dev
|
||||||
|
|
||||||
this.elDevices.querySelectorAll('.device.selected').forEach(el => el.classList.remove('selected'))
|
this.elDevices.querySelectorAll('.device.selected').forEach(el=>el.classList.remove('selected'))
|
||||||
devEl.classList.add('selected')
|
devEl.classList.add('selected')
|
||||||
|
|
||||||
this.elName.value = dev.name()
|
this.elName.value = dev.name()
|
||||||
|
|
@ -1129,26 +1078,6 @@ class DeviceDialog {
|
||||||
this.elUsername.value = dev.username()
|
this.elUsername.value = dev.username()
|
||||||
this.elPassword.value = dev.password()
|
this.elPassword.value = dev.password()
|
||||||
}// }}}
|
}// }}}
|
||||||
async createDevice() {// {{{
|
|
||||||
let name = prompt('Name of new device')
|
|
||||||
if (name === null || name.trim() === '')
|
|
||||||
return
|
|
||||||
name = name.trim().toLowerCase()
|
|
||||||
|
|
||||||
// Make sure it doesn't already exist.
|
|
||||||
if (this.devices.has(name)) {
|
|
||||||
alert('The device already exist.')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const dev = new Device({Name: name, Port: 443}, true)
|
|
||||||
|
|
||||||
this.devices.set(name, dev)
|
|
||||||
this.updateDeviceList()
|
|
||||||
|
|
||||||
const devEl = this.elDeviceList.querySelector(`[data-name="${dev.name()}"]`)
|
|
||||||
this.editDevice(dev, devEl)
|
|
||||||
}// }}}
|
|
||||||
async updateDevice() {// {{{
|
async updateDevice() {// {{{
|
||||||
const req = {
|
const req = {
|
||||||
CurrentName: this.device.name(),
|
CurrentName: this.device.name(),
|
||||||
|
|
@ -1167,10 +1096,6 @@ class DeviceDialog {
|
||||||
body: JSON.stringify(req),
|
body: JSON.stringify(req),
|
||||||
})
|
})
|
||||||
const json = await data.json()
|
const json = await data.json()
|
||||||
if (!json.OK) {
|
|
||||||
alert(json.Error)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.device.data.Name = json.Device.Name
|
this.device.data.Name = json.Device.Name
|
||||||
this.device.data.Address = json.Device.Address
|
this.device.data.Address = json.Device.Address
|
||||||
|
|
@ -1180,35 +1105,7 @@ class DeviceDialog {
|
||||||
_mbus.dispatch('device_updated', { device: this.device })
|
_mbus.dispatch('device_updated', { device: this.device })
|
||||||
this.dlg.close()
|
this.dlg.close()
|
||||||
|
|
||||||
} catch (err) {
|
} catch(err) {
|
||||||
console.error(err)
|
|
||||||
alert(err)
|
|
||||||
}
|
|
||||||
}// }}}
|
|
||||||
async deleteDevice() {// {{{
|
|
||||||
const devname = this.device.name()
|
|
||||||
if (!confirm(`Do you want to delete '${devname}'?`))
|
|
||||||
return
|
|
||||||
|
|
||||||
try {
|
|
||||||
const data = await fetch(`/device/${devname}`, { method: 'DELETE' })
|
|
||||||
const json = await data.json()
|
|
||||||
if (!json.OK) {
|
|
||||||
alert(json.Error)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.elName.value = ''
|
|
||||||
this.elAddress.value = ''
|
|
||||||
this.elPort.value = ''
|
|
||||||
this.elUsername.value = ''
|
|
||||||
this.elPassword.value = ''
|
|
||||||
|
|
||||||
this.devices.delete(devname)
|
|
||||||
this.device = null
|
|
||||||
this.updateDeviceList()
|
|
||||||
_mbus.dispatch('device_deleted', { devName: devname })
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
console.error(err)
|
||||||
alert(err)
|
alert(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
webserver.go
13
webserver.go
|
|
@ -35,7 +35,6 @@ func registerWebserverHandlers() { // {{{
|
||||||
http.HandleFunc("/", rootHandler)
|
http.HandleFunc("/", rootHandler)
|
||||||
http.HandleFunc("GET /devices", actionDevices)
|
http.HandleFunc("GET /devices", actionDevices)
|
||||||
http.HandleFunc("POST /device", actionDeviceUpdate)
|
http.HandleFunc("POST /device", actionDeviceUpdate)
|
||||||
http.HandleFunc("DELETE /device/{dev}", actionDeviceDelete)
|
|
||||||
http.HandleFunc("GET /device/{dev}/dns_records", actionDNSRecords)
|
http.HandleFunc("GET /device/{dev}/dns_records", actionDNSRecords)
|
||||||
http.HandleFunc("POST /device/{dev}/record", actionRecordSave)
|
http.HandleFunc("POST /device/{dev}/record", actionRecordSave)
|
||||||
http.HandleFunc("DELETE /device/{dev}/record/{id}", actionRecordDelete)
|
http.HandleFunc("DELETE /device/{dev}/record/{id}", actionRecordDelete)
|
||||||
|
|
@ -127,18 +126,6 @@ func actionDeviceUpdate(w http.ResponseWriter, r *http.Request) { // {{{
|
||||||
})
|
})
|
||||||
w.Write(j)
|
w.Write(j)
|
||||||
} // }}}
|
} // }}}
|
||||||
func actionDeviceDelete(w http.ResponseWriter, r *http.Request) { // {{{
|
|
||||||
devname := r.PathValue("dev")
|
|
||||||
|
|
||||||
err := config.DeleteDevice(devname)
|
|
||||||
if err != nil {
|
|
||||||
httpError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
j, _ := json.Marshal(struct{ OK bool }{true})
|
|
||||||
w.Write(j)
|
|
||||||
} // }}}
|
|
||||||
|
|
||||||
func actionDNSRecords(w http.ResponseWriter, r *http.Request) { // {{{
|
func actionDNSRecords(w http.ResponseWriter, r *http.Request) { // {{{
|
||||||
devname := r.PathValue("dev")
|
devname := r.PathValue("dev")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue