diff --git a/script.go b/script.go index 96af03b..2a8a27e 100644 --- a/script.go +++ b/script.go @@ -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 +}// }}} diff --git a/sql/0012.sql b/sql/0012.sql new file mode 100644 index 0000000..1297758 --- /dev/null +++ b/sql/0012.sql @@ -0,0 +1 @@ +ALTER TABLE public.hook ADD CONSTRAINT hook_unique UNIQUE (node_id,script_id); diff --git a/static/js/app.mjs b/static/js/app.mjs index 503ffc4..fac5cfe 100644 --- a/static/js/app.mjs +++ b/static/js/app.mjs @@ -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 = `
Script hooks
@@ -988,24 +995,30 @@ class ScriptHooks extends Component { ` - 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 {
${this.hook.SSH}
` + 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 { diff --git a/webserver.go b/webserver.go index cd8d9b3..1b6e90e 100644 --- a/webserver.go +++ b/webserver.go @@ -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)