Rendering and unhooking of hooks

This commit is contained in:
Magnus Åhall 2025-08-07 17:46:37 +02:00
parent 2a04cba42a
commit 83f858285f
4 changed files with 122 additions and 24 deletions

View file

@ -26,7 +26,7 @@ type Hook struct {
SSH string
}
func GetScripts() (scripts []Script, err error) {
func GetScripts() (scripts []Script, err error) {// {{{
scripts = []Script{}
var rows *sqlx.Rows
@ -54,8 +54,8 @@ func GetScripts() (scripts []Script, err error) {
}
return
}
func UpdateScript(scriptID int, data []byte) (script Script, err error) {
}// }}}
func UpdateScript(scriptID int, data []byte) (script Script, err error) {// {{{
err = json.Unmarshal(data, &script)
if err != nil {
err = werr.Wrap(err)
@ -106,12 +106,29 @@ func UpdateScript(scriptID int, data []byte) (script Script, err error) {
}
return
}
func DeleteScript(scriptID int) (err error) {
}// }}}
func DeleteScript(scriptID int) (err error) {// {{{
_, err = db.Exec(`DELETE FROM script WHERE id = $1`, scriptID)
if err != nil {
err = werr.Wrap(err)
return
}
return
}
}// }}}
func UpdateHook(hook Hook) (err error) {// {{{
_, err = db.Exec(`UPDATE hook SET ssh=$2 WHERE id=$1`, hook.ID, strings.TrimSpace(hook.SSH))
if err != nil {
err = werr.Wrap(err)
return
}
return
}// }}}
func DeleteHook(hookID int) (err error) {// {{{
_, err = db.Exec(`DELETE FROM hook WHERE id=$1`, hookID)
if err != nil {
err = werr.Wrap(err)
return
}
return
}// }}}

1
sql/0012.sql Normal file
View file

@ -0,0 +1 @@
ALTER TABLE public.hook ADD CONSTRAINT hook_unique UNIQUE (node_id,script_id);

View file

@ -973,11 +973,18 @@ class ConnectedNode {
}
class ScriptHooks extends Component {
constructor(hooks) {
constructor(hooks) {// {{{
super()
this.hooks = hooks
}
renderComponent() {
this.scriptGrid = null
mbus.subscribe('hook_deleted', event => {
const deletedHook = event.detail
this.hooks = this.hooks.filter(h => h.ID !== deletedHook.ID)
this.renderHooks()
})
}// }}}
renderComponent() {// {{{
const div = document.createElement('div')
div.innerHTML = `
<div class="label">Script hooks</div>
@ -988,24 +995,30 @@ class ScriptHooks extends Component {
</div>
`
div.querySelector('.add').addEventListener('click', ()=>{
div.querySelector('.add').addEventListener('click', () => {
alert('FIXME')
})
const scriptsGrid = div.querySelector('.scripts-grid')
for(const hook of this.hooks) {
const h = new ScriptHook(hook)
scriptsGrid.append(h.render())
}
this.scriptGrid = div.querySelector('.scripts-grid')
this.renderHooks()
return div.children
}
}// }}}
renderHooks() {// {{{
this.scriptGrid.innerHTML = ''
for (const hook of this.hooks) {
const h = new ScriptHook(hook)
this.scriptGrid.append(h.render())
}
}// }}}
}
class ScriptHook extends Component {
constructor(hook) {// {{{
super()
this.hook = hook
this.element_ssh = null
}// }}}
renderComponent() {// {{{
const tmpl = document.createElement('template')
@ -1015,14 +1028,57 @@ class ScriptHook extends Component {
<div class="script-ssh">${this.hook.SSH}</div>
<div class="script-unhook"><img src="/images/${_VERSION}/node_modules/@mdi/svg/svg/trash-can.svg" /></div>
`
this.element_ssh = tmpl.content.querySelector('.script-ssh')
tmpl.content.querySelector('.script-ssh').addEventListener('click', () => {
prompt('SSH', this.hook.SSH)
//new ConnectionDataDialog(this.hook, () => _app.edit(_app.currentNode.ID)).render()
})
tmpl.content.querySelector('.script-ssh').addEventListener('click', () => this.update())
tmpl.content.querySelector('.script-unhook').addEventListener('click', () => this.delete())
return tmpl.content
}// }}}
update() {// {{{
const ssh = prompt('SSH', this.hook.SSH)
if (ssh === null)
return
if (ssh.trim() === '') {
alert(`SSH can't be empty.`)
return
}
const request = {
ID: this.hook.ID,
SSH: ssh,
}
fetch('/hooks/update', {
method: 'POST',
body: JSON.stringify(request),
})
.then(data => data.json())
.then(json => {
if (!json.OK) {
showError(json.Error)
return
}
this.hook.SSH = ssh
this.element_ssh.innerText = this.hook.SSH
})
.catch(err => showError(err))
}// }}}
delete() {// {{{
if (!confirm(`Unhook the '${this.hook.Script.Name}' script?`))
return
fetch(`/hooks/delete/${this.hook.ID}`)
.then(data => data.json())
.then(json => {
if (!json.OK) {
showError(json.Error)
return
}
mbus.dispatch('hook_deleted', this.hook)
})
.catch(err => showError(err))
}// }}}
}
class ScriptsList extends Component {

View file

@ -50,7 +50,8 @@ func initWebserver() (err error) {
http.HandleFunc("/scripts/", actionScripts)
http.HandleFunc("/scripts/update/{scriptID}", actionScriptUpdate)
http.HandleFunc("/scripts/delete/{scriptID}", actionScriptDelete)
http.HandleFunc("/hooks/update/{hookID}", actionHookUpdate)
http.HandleFunc("/hooks/update", actionHookUpdate)
http.HandleFunc("/hooks/delete/{hookID}", actionHookDelete)
err = http.ListenAndServe(address, nil)
return
@ -623,13 +624,36 @@ func actionScriptDelete(w http.ResponseWriter, r *http.Request) { // {{{
} // }}}
func actionHookUpdate(w http.ResponseWriter, r *http.Request) { // {{{
var hook Hook
body, _ := io.ReadAll(r.Body)
err := json.Unmarshal(body, &hook)
if err != nil {
err = werr.Wrap(err)
httpError(w, err)
return
}
err = UpdateHook(hook)
if err != nil {
err = werr.Wrap(err)
httpError(w, err)
return
}
out := struct {
OK bool
}{
true,
}
j, _ := json.Marshal(out)
w.Write(j)
} // }}}
func actionHookDelete(w http.ResponseWriter, r *http.Request) { // {{{
hookID := 0
hookIDStr := r.PathValue("hookID")
hookID, _ = strconv.Atoi(hookIDStr)
// XXX - here
err := UpdateHook(hook)
err := DeleteHook(hookID)
if err != nil {
err = werr.Wrap(err)
httpError(w, err)