Rearranged script hook UI
This commit is contained in:
parent
5145830f65
commit
2cab8c3ddb
5 changed files with 177 additions and 50 deletions
3
node.go
3
node.go
|
|
@ -93,7 +93,8 @@ func GetNode(nodeID int) (node Node, err error) { // {{{
|
|||
SELECT
|
||||
h.id,
|
||||
to_jsonb(s) AS script,
|
||||
ssh
|
||||
ssh,
|
||||
env
|
||||
FROM hook h
|
||||
INNER JOIN public.script s ON h.script_id = s.id
|
||||
WHERE
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ func SearchScripts(search string) (scripts []Script, err error) { // {{{
|
|||
return
|
||||
} // }}}
|
||||
func HookScript(nodeID, scriptID int) (err error) { // {{{
|
||||
_, err = db.Exec(`INSERT INTO hook(node_id, script_id, ssh, env) VALUES($1, $2, '<host>')`, nodeID, scriptID)
|
||||
_, err = db.Exec(`INSERT INTO hook(node_id, script_id, ssh) VALUES($1, $2, '<host>')`, nodeID, scriptID)
|
||||
return
|
||||
} // }}}
|
||||
|
||||
|
|
@ -197,7 +197,8 @@ func GetHook(hookID int) (hook Hook, err error) { // {{{
|
|||
return
|
||||
} // }}}
|
||||
func UpdateHook(hook Hook) (err error) { // {{{
|
||||
_, err = db.Exec(`UPDATE hook SET ssh=$2 WHERE id=$1`, hook.ID, strings.TrimSpace(hook.SSH))
|
||||
j, _ := json.Marshal(hook.Env)
|
||||
_, err = db.Exec(`UPDATE hook SET ssh=$2, env=$3 WHERE id=$1`, hook.ID, strings.TrimSpace(hook.SSH), j)
|
||||
if err != nil {
|
||||
err = werr.Wrap(err)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ select:focus {
|
|||
}
|
||||
#script-hooks .scripts-grid .script-group {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, min-content);
|
||||
grid-template-columns: repeat(3, min-content);
|
||||
align-items: center;
|
||||
grid-gap: 2px 0px;
|
||||
padding: 16px;
|
||||
|
|
@ -296,6 +296,7 @@ select:focus {
|
|||
#script-hooks .scripts-grid .script-ssh {
|
||||
margin-right: 16px;
|
||||
}
|
||||
#script-hooks .scripts-grid .script-name,
|
||||
#script-hooks .scripts-grid .script-ssh {
|
||||
cursor: pointer;
|
||||
color: #555;
|
||||
|
|
@ -519,3 +520,31 @@ dialog#connection-data div.button {
|
|||
cursor: pointer;
|
||||
margin-top: 4px;
|
||||
}
|
||||
#script-hook-dialog {
|
||||
display: grid;
|
||||
grid-gap: 8px;
|
||||
padding: 32px;
|
||||
}
|
||||
#script-hook-dialog .top {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr min-content;
|
||||
grid-gap: 16px;
|
||||
}
|
||||
#script-hook-dialog .top .header {
|
||||
font-size: 1.25em;
|
||||
font-weight: bold;
|
||||
color: var(--section-color);
|
||||
}
|
||||
#script-hook-dialog .top img {
|
||||
height: 32px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#script-hook-dialog .label {
|
||||
font-weight: bold;
|
||||
margin-top: 16px;
|
||||
}
|
||||
#script-hook-dialog textarea {
|
||||
height: 50vh;
|
||||
width: 50vw;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1096,62 +1096,24 @@ class ScriptHook extends Component {
|
|||
tmpl.innerHTML = `
|
||||
<div class="script-name">${this.hook.Script.Name}</div>
|
||||
<div class="script-ssh"></div>
|
||||
<div class="script-unhook"><img src="/images/${_VERSION}/node_modules/@mdi/svg/svg/trash-can.svg" /></div>
|
||||
<div class="script-schedule"><img src="/images/${_VERSION}/node_modules/@mdi/svg/svg/play-box.svg" /></div>
|
||||
`
|
||||
|
||||
|
||||
this.elementSchedule = tmpl.content.querySelector('.script-schedule')
|
||||
this.elementSSH = tmpl.content.querySelector('.script-ssh')
|
||||
this.elementSSH.innerText = `[ ${this.hook.SSH} ]`
|
||||
|
||||
tmpl.content.querySelector('.script-name').addEventListener('click', () => this.update())
|
||||
tmpl.content.querySelector('.script-ssh').addEventListener('click', () => this.update())
|
||||
tmpl.content.querySelector('.script-unhook').addEventListener('click', () => this.delete())
|
||||
tmpl.content.querySelector('.script-schedule').addEventListener('click', () => this.run())
|
||||
|
||||
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.elementSSH.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
|
||||
}
|
||||
this.parentList.hookDeleted(this.hook.ID)
|
||||
})
|
||||
.catch(err => showError(err))
|
||||
new ScriptHookDialog(this.hook, this.parentList, () => {
|
||||
this.elementSSH.innerText = `[ ${this.hook.SSH} ]`
|
||||
}).render()
|
||||
}// }}}
|
||||
run() {// {{{
|
||||
if (this.scheduleDisable)
|
||||
|
|
@ -1470,4 +1432,104 @@ class ScriptSelectDialog extends Component {
|
|||
}// }}}
|
||||
}
|
||||
|
||||
class ScriptHookDialog extends Component {
|
||||
constructor(hook, parentList, callback) {// {{{
|
||||
super()
|
||||
this.hook = hook
|
||||
this.callback = callback
|
||||
this.parentList = parentList
|
||||
|
||||
this.dlg = document.createElement('dialog')
|
||||
this.dlg.id = 'script-hook-dialog'
|
||||
this.dlg.addEventListener('close', () => this.dlg.remove())
|
||||
|
||||
this.env = null
|
||||
this.ssh = null
|
||||
}// }}}
|
||||
renderComponent() {// {{{
|
||||
const div = document.createElement('div')
|
||||
div.innerHTML = `
|
||||
<div class="top">
|
||||
<div class="header"></div>
|
||||
<img src="/images/${_VERSION}/node_modules/@mdi/svg/svg/trash-can.svg">
|
||||
</div>
|
||||
<div class="label">SSH</div>
|
||||
<div><input type="text" class="ssh" style="width: 100%" /></div>
|
||||
|
||||
<div class="label">
|
||||
Environment
|
||||
</div>
|
||||
<div style="font-size: 0.9em">A map with keys and values as strings.</div>
|
||||
|
||||
<div><textarea class="env"></textarea></div>
|
||||
<div><button>Update</button></div>
|
||||
`
|
||||
|
||||
div.querySelector('.header').innerText = `Hook for ${this.hook.Script.Name}`
|
||||
div.querySelector('.top img').addEventListener('click', () => this.delete())
|
||||
|
||||
this.ssh = div.querySelector('.ssh')
|
||||
this.ssh.value = this.hook.SSH
|
||||
this.ssh.addEventListener('keydown', event => {
|
||||
if (event.key == 'Enter') {
|
||||
event.stopPropagation()
|
||||
this.save()
|
||||
}
|
||||
})
|
||||
|
||||
this.env = div.querySelector('.env')
|
||||
this.env.value = JSON.stringify(this.hook.Env, null, " ")
|
||||
|
||||
const button = div.querySelector('button')
|
||||
this.env.addEventListener('keydown', event => {
|
||||
if (event.ctrlKey && event.key == 's') {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
this.save()
|
||||
}
|
||||
})
|
||||
button.addEventListener('click', () => this.save())
|
||||
|
||||
this.dlg.append(...div.children)
|
||||
document.body.append(this.dlg)
|
||||
this.dlg.showModal()
|
||||
|
||||
return []
|
||||
}// }}}
|
||||
save() {// {{{
|
||||
if (this.ssh.value.trim() === '') {
|
||||
alert('SSH has to be filled in.')
|
||||
return
|
||||
}
|
||||
|
||||
if (this.env.value.trim() === '')
|
||||
this.env.value = '{}'
|
||||
|
||||
try {
|
||||
this.hook.Env = JSON.parse(this.env.value)
|
||||
this.hook.SSH = this.ssh.value.trim()
|
||||
window._app.query('/hooks/update', this.hook)
|
||||
.then(() => {
|
||||
this.callback()
|
||||
this.dlg.close()
|
||||
})
|
||||
.catch(err => showError(err))
|
||||
} catch (err) {
|
||||
alert(`A JSON error occured:\n\n${err}`)
|
||||
}
|
||||
|
||||
}// }}}
|
||||
delete() {// {{{
|
||||
if (!confirm(`Unhook the '${this.hook.Script.Name}' script?`))
|
||||
return
|
||||
|
||||
window._app.query(`/hooks/delete/${this.hook.ID}`)
|
||||
.then(() => {
|
||||
this.dlg.close()
|
||||
this.parentList.hookDeleted(this.hook.ID)
|
||||
})
|
||||
.catch(err => showError(err))
|
||||
}// }}}
|
||||
}
|
||||
|
||||
// vim: foldmethod=marker
|
||||
|
|
|
|||
|
|
@ -365,7 +365,7 @@ select:focus {
|
|||
|
||||
.script-group {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, min-content);
|
||||
grid-template-columns: repeat(3, min-content);
|
||||
align-items: center;
|
||||
grid-gap: 2px 0px;
|
||||
padding: 16px;
|
||||
|
|
@ -389,7 +389,7 @@ select:focus {
|
|||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.script-ssh {
|
||||
.script-name, .script-ssh {
|
||||
cursor: pointer;
|
||||
color: #555;
|
||||
}
|
||||
|
|
@ -666,3 +666,37 @@ dialog#connection-data {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#script-hook-dialog {
|
||||
display: grid;
|
||||
grid-gap: 8px;
|
||||
padding: 32px;
|
||||
|
||||
.top {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr min-content;
|
||||
grid-gap: 16px;
|
||||
|
||||
.header {
|
||||
font-size: 1.25em;
|
||||
font-weight: bold;
|
||||
color: var(--section-color);
|
||||
}
|
||||
|
||||
img {
|
||||
height: 32px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
font-weight: bold;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
height: 50vh;
|
||||
width: 50vw;
|
||||
font-family: monospace;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue