Hooking
This commit is contained in:
parent
38eef01e34
commit
df4cee56af
7 changed files with 292 additions and 74 deletions
3
node.go
3
node.go
|
|
@ -98,6 +98,9 @@ func GetNode(nodeID int) (node Node, err error) { // {{{
|
|||
INNER JOIN public.script s ON h.script_id = s.id
|
||||
WHERE
|
||||
h.node_id = n.id
|
||||
ORDER BY
|
||||
s.group ASC,
|
||||
s.name ASC
|
||||
) AS res
|
||||
)
|
||||
, '[]'::jsonb
|
||||
|
|
|
|||
|
|
@ -124,6 +124,9 @@ func SearchScripts(search string) (scripts []Script, err error) { // {{{
|
|||
FROM public.script
|
||||
WHERE
|
||||
name ILIKE $1
|
||||
ORDER BY
|
||||
"group" ASC,
|
||||
name ASC
|
||||
`,
|
||||
search,
|
||||
)
|
||||
|
|
@ -143,6 +146,10 @@ 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) VALUES($1, $2, '<host>')`, nodeID, scriptID)
|
||||
return
|
||||
}// }}}
|
||||
|
||||
func UpdateHook(hook Hook) (err error) { // {{{
|
||||
_, err = db.Exec(`UPDATE hook SET ssh=$2 WHERE id=$1`, hook.ID, strings.TrimSpace(hook.SSH))
|
||||
|
|
|
|||
|
|
@ -210,17 +210,18 @@ select:focus {
|
|||
outline-offset: -2px;
|
||||
}
|
||||
#connected-nodes > .label {
|
||||
display: grid;
|
||||
grid-template-columns: min-content min-content;
|
||||
align-items: center;
|
||||
color: var(--section-color);
|
||||
font-weight: bold;
|
||||
font-size: 1.25em;
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
#connected-nodes > .add {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
#connected-nodes > .add img {
|
||||
#connected-nodes > .label > img.add {
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
margin-left: 8px;
|
||||
}
|
||||
#connected-nodes .connected-nodes {
|
||||
display: flex;
|
||||
|
|
@ -248,9 +249,9 @@ select:focus {
|
|||
}
|
||||
#script-hooks .scripts-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, min-content);
|
||||
grid-template-columns: repeat(3, min-content);
|
||||
align-items: center;
|
||||
grid-gap: 4px 0px;
|
||||
grid-gap: 2px 0px;
|
||||
}
|
||||
#script-hooks .scripts-grid .header {
|
||||
font-weight: bold;
|
||||
|
|
@ -259,10 +260,11 @@ select:focus {
|
|||
#script-hooks .scripts-grid div {
|
||||
white-space: nowrap;
|
||||
}
|
||||
#script-hooks .scripts-grid .script-icon {
|
||||
margin-right: 4px;
|
||||
#script-hooks .scripts-grid .script-group {
|
||||
grid-column: 1 / -1;
|
||||
font-weight: bold;
|
||||
margin-top: 8px;
|
||||
}
|
||||
#script-hooks .scripts-grid .script-icon img,
|
||||
#script-hooks .scripts-grid .script-unhook img {
|
||||
display: block;
|
||||
height: 24px;
|
||||
|
|
@ -274,19 +276,20 @@ select:focus {
|
|||
#script-hooks .scripts-grid .script-ssh {
|
||||
cursor: pointer;
|
||||
}
|
||||
#script-hooks > .add {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
#script-hooks > .add img {
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#script-hooks > .label {
|
||||
display: grid;
|
||||
grid-template-columns: min-content min-content;
|
||||
align-items: center;
|
||||
color: var(--section-color);
|
||||
font-weight: bold;
|
||||
font-size: 1.25em;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
#script-hooks > .label img.add {
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
margin-left: 8px;
|
||||
}
|
||||
#select-node {
|
||||
padding: 32px;
|
||||
display: grid;
|
||||
|
|
@ -298,6 +301,10 @@ select:focus {
|
|||
min-width: 300px;
|
||||
width: 100%;
|
||||
}
|
||||
#select-node .label {
|
||||
font-weight: bold;
|
||||
color: var(--section-color);
|
||||
}
|
||||
#select-node button {
|
||||
width: 100px !important;
|
||||
}
|
||||
|
|
@ -456,3 +463,21 @@ dialog#connection-data div.button {
|
|||
#editor-script button {
|
||||
margin-top: 8px;
|
||||
}
|
||||
#script-select-dialog {
|
||||
display: grid;
|
||||
grid-gap: 8px;
|
||||
padding: 32px;
|
||||
}
|
||||
#script-select-dialog > .header {
|
||||
font-weight: bold;
|
||||
color: var(--section-color);
|
||||
}
|
||||
#script-select-dialog .scripts .group {
|
||||
font-weight: bold;
|
||||
color: var(--section-color);
|
||||
margin-top: 16px;
|
||||
}
|
||||
#script-select-dialog .scripts .script {
|
||||
cursor: pointer;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@
|
|||
|
||||
<svg
|
||||
width="109.69499mm"
|
||||
height="47.615765mm"
|
||||
viewBox="0 0 109.695 47.615764"
|
||||
height="43.310902mm"
|
||||
viewBox="0 0 109.695 43.310901"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
inkscape:version="1.4 (e7c3feb, 2024-10-09)"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
|
||||
sodipodi:docname="logo.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
|
|
@ -24,13 +24,13 @@
|
|||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:zoom="1.6985744"
|
||||
inkscape:cx="278.17446"
|
||||
inkscape:cy="101.85012"
|
||||
inkscape:window-width="2190"
|
||||
inkscape:window-height="1404"
|
||||
inkscape:window-x="1463"
|
||||
inkscape:window-y="16"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:cx="278.17445"
|
||||
inkscape:cy="102.14448"
|
||||
inkscape:window-width="1916"
|
||||
inkscape:window-height="1161"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false" /><defs
|
||||
id="defs1" /><g
|
||||
|
|
@ -47,17 +47,7 @@
|
|||
id="circle1"
|
||||
cx="149.99937"
|
||||
cy="115.46115"
|
||||
r="4.4009533" /><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:30.5104px;line-height:normal;font-family:'Forgotten Futurist';-inkscape-font-specification:'Forgotten Futurist, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;text-decoration-color:#000000;text-anchor:middle;fill:#262626;stroke-width:1.71737;-inkscape-stroke:none"
|
||||
x="62.183887"
|
||||
y="178.34908"
|
||||
id="text4"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:30.5104px;font-family:'Forgotten Futurist';-inkscape-font-specification:'Forgotten Futurist, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:1.71737"
|
||||
x="62.183887"
|
||||
y="178.34908">JSON</tspan></text><circle
|
||||
r="4.4009533" /><circle
|
||||
style="fill:#1e8071;fill-opacity:1;stroke-width:0.529166;-inkscape-stroke:none"
|
||||
id="circle5"
|
||||
cx="113.69239"
|
||||
|
|
@ -92,7 +82,7 @@
|
|||
style="fill:#21608c;fill-opacity:1;stroke-width:0.529167;-inkscape-stroke:none"
|
||||
id="circle2"
|
||||
cx="107.89297"
|
||||
cy="142.85443"
|
||||
cy="138.54956"
|
||||
r="4.3678799" /><path
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#1e6380;fill-opacity:1;fill-rule:evenodd;stroke:#205d7b;stroke-width:0.529167;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000000;stop-opacity:1"
|
||||
d="m 145.92055,117.1137 -20.27428,8.21418"
|
||||
|
|
@ -134,7 +124,7 @@
|
|||
style="font-size:30.5104px;font-family:'Forgotten Futurist';-inkscape-font-specification:'Forgotten Futurist, Normal';text-align:center;text-anchor:middle;fill:#262626;stroke-width:2.13619"
|
||||
aria-label="GRAPH" /></g><path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 117.58714,131.21242 -6.89925,8.28551"
|
||||
d="m 116.99677,130.63452 -5.8076,5.04925"
|
||||
id="path13"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0"
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 9.3 KiB |
|
|
@ -29,6 +29,7 @@ export class App {
|
|||
'NODE_EDIT_NAME',
|
||||
'NODE_MOVE',
|
||||
'NODE_SELECTED',
|
||||
'NODE_HOOKED',
|
||||
'SCRIPT_CREATED',
|
||||
'SCRIPT_DELETED',
|
||||
'SCRIPT_EDIT',
|
||||
|
|
@ -86,6 +87,10 @@ export class App {
|
|||
this.nodeDelete(this.currentNode.ID)
|
||||
break
|
||||
|
||||
case 'NODE_HOOKED':
|
||||
this.edit(event.detail)
|
||||
break
|
||||
|
||||
case 'NODE_MOVE':
|
||||
const nodes = this.tree.markedNodes()
|
||||
if (!confirm(`Are you sure you want to move ${nodes.length} nodes here?`))
|
||||
|
|
@ -433,6 +438,29 @@ export class App {
|
|||
|
||||
return 0
|
||||
}// }}}
|
||||
async query(path, data) {// {{{
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = {}
|
||||
|
||||
if (data !== undefined) {
|
||||
request.method = 'POST'
|
||||
request.body = JSON.stringify(data)
|
||||
}
|
||||
|
||||
fetch(path, request)
|
||||
.then(data => data.json())
|
||||
.then(json => {
|
||||
if (!json.OK) {
|
||||
reject(json.Error)
|
||||
return
|
||||
}
|
||||
resolve(json)
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}// }}}
|
||||
}
|
||||
|
||||
class NodeCreateDialog {
|
||||
|
|
@ -921,8 +949,10 @@ class ConnectedNodes {
|
|||
render() {// {{{
|
||||
const div = document.createElement('template')
|
||||
div.innerHTML = `
|
||||
<div class="label">Connected nodes</div>
|
||||
<div class="add"><img src="/images/${_VERSION}/node_modules/@mdi/svg/svg/plus-box.svg" /></div>
|
||||
<div class="label">
|
||||
<div style="white-space: nowrap">Connected nodes</div>
|
||||
<img class="add" src="/images/${_VERSION}/node_modules/@mdi/svg/svg/plus-box.svg" />
|
||||
</div>
|
||||
<div class="connected-nodes"></div>
|
||||
`
|
||||
|
||||
|
|
@ -977,18 +1007,14 @@ class ScriptHooks extends Component {
|
|||
super()
|
||||
this.hooks = hooks
|
||||
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>
|
||||
<div class="add"><img src="/images/${_VERSION}/node_modules/@mdi/svg/svg/plus-box.svg" /></div>
|
||||
<div class="label">
|
||||
<div style="white-space: nowrap">Script hooks</div>
|
||||
<img class="add" src="/images/${_VERSION}/node_modules/@mdi/svg/svg/plus-box.svg" />
|
||||
</div>
|
||||
<div class="scripts-grid">
|
||||
<div class="header" style="grid-column: 1 / 3;">Script</div>
|
||||
<div class="header" style="grid-column: 3 / 5;">SSH</div>
|
||||
|
|
@ -996,7 +1022,10 @@ class ScriptHooks extends Component {
|
|||
`
|
||||
|
||||
div.querySelector('.add').addEventListener('click', () => {
|
||||
alert('FIXME')
|
||||
const dlg = new ScriptSelectDialog(s => {
|
||||
this.hookScript(s)
|
||||
})
|
||||
dlg.render()
|
||||
})
|
||||
|
||||
this.scriptGrid = div.querySelector('.scripts-grid')
|
||||
|
|
@ -1004,31 +1033,53 @@ class ScriptHooks extends Component {
|
|||
|
||||
return div.children
|
||||
}// }}}
|
||||
hookDeleted(deletedHookID) {// {{{
|
||||
this.hooks = this.hooks.filter(h => h.ID !== deletedHookID)
|
||||
this.renderHooks()
|
||||
}// }}}
|
||||
renderHooks() {// {{{
|
||||
this.scriptGrid.innerHTML = ''
|
||||
|
||||
let prevGroup = null
|
||||
for (const hook of this.hooks) {
|
||||
const h = new ScriptHook(hook)
|
||||
if (hook.Script.Group !== prevGroup) {
|
||||
const g = document.createElement('div')
|
||||
g.classList.add('script-group')
|
||||
g.innerText = hook.Script.Group
|
||||
this.scriptGrid.append(g)
|
||||
prevGroup = hook.Script.Group
|
||||
}
|
||||
|
||||
const h = new ScriptHook(hook, this)
|
||||
this.scriptGrid.append(h.render())
|
||||
}
|
||||
}// }}}
|
||||
hookScript(script) {// {{{
|
||||
_app.query(`/nodes/hook`, {
|
||||
NodeID: _app.currentNode.ID,
|
||||
ScriptID: script.ID,
|
||||
})
|
||||
.then(()=>mbus.dispatch('NODE_HOOKED', _app.currentNode.ID))
|
||||
.catch(err => showError(err))
|
||||
}// }}}
|
||||
}
|
||||
|
||||
class ScriptHook extends Component {
|
||||
constructor(hook) {// {{{
|
||||
constructor(hook, parentList) {// {{{
|
||||
super()
|
||||
this.hook = hook
|
||||
this.parentList = parentList
|
||||
this.element_ssh = null
|
||||
}// }}}
|
||||
renderComponent() {// {{{
|
||||
const tmpl = document.createElement('template')
|
||||
tmpl.innerHTML = `
|
||||
<div class="script-icon"><img src="/images/${_VERSION}/node_modules/@mdi/svg/svg/bash.svg" /></div>
|
||||
<div class="script-name">${this.hook.Script.Name}</div>
|
||||
<div class="script-ssh">${this.hook.SSH}</div>
|
||||
<div class="script-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')
|
||||
this.element_ssh.innerText = this.hook.SSH
|
||||
|
||||
tmpl.content.querySelector('.script-ssh').addEventListener('click', () => this.update())
|
||||
tmpl.content.querySelector('.script-unhook').addEventListener('click', () => this.delete())
|
||||
|
|
@ -1075,7 +1126,7 @@ class ScriptHook extends Component {
|
|||
showError(json.Error)
|
||||
return
|
||||
}
|
||||
mbus.dispatch('hook_deleted', this.hook)
|
||||
this.parentList.hookDeleted(this.hook.ID)
|
||||
})
|
||||
.catch(err => showError(err))
|
||||
}// }}}
|
||||
|
|
@ -1294,4 +1345,80 @@ class ScriptEditor extends Component {
|
|||
}// }}}
|
||||
}
|
||||
|
||||
class ScriptSelectDialog extends Component {
|
||||
constructor(callback) {// {{{
|
||||
super()
|
||||
this.dlg = document.createElement('dialog')
|
||||
this.dlg.id = 'script-select-dialog'
|
||||
this.dlg.addEventListener('close', () => this.dlg.remove())
|
||||
this.searchFor = null
|
||||
this.scripts = null
|
||||
this.callback = callback
|
||||
}// }}}
|
||||
renderComponent() {// {{{
|
||||
const div = document.createElement('div')
|
||||
div.innerHTML = `
|
||||
<div class="header">Search for script</div>
|
||||
<div><input class="search-for" type="text" value="%"></div>
|
||||
<div><button>Search</button></div>
|
||||
<div class="scripts"></div>
|
||||
`
|
||||
|
||||
this.searchFor = div.querySelector('.search-for')
|
||||
this.scripts = div.querySelector('.scripts')
|
||||
const button = div.querySelector('button')
|
||||
this.searchFor.addEventListener('keydown', event => {
|
||||
if (event.key == 'Enter')
|
||||
this.searchScripts()
|
||||
})
|
||||
button.addEventListener('click', () => this.searchScripts())
|
||||
|
||||
this.dlg.append(...div.children)
|
||||
document.body.append(this.dlg)
|
||||
this.dlg.showModal()
|
||||
|
||||
return []
|
||||
}// }}}
|
||||
searchScripts() {// {{{
|
||||
fetch('/scripts/search', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
Search: this.searchFor.value,
|
||||
}),
|
||||
})
|
||||
.then(data => data.json())
|
||||
.then(json => {
|
||||
if (!json.OK) {
|
||||
showError(json.Error)
|
||||
return
|
||||
}
|
||||
this.populateScripts(json.Scripts)
|
||||
})
|
||||
.catch(err => showError(err))
|
||||
}// }}}
|
||||
populateScripts(scripts) {// {{{
|
||||
this.scripts.innerHTML = ''
|
||||
|
||||
let prevGroup = null
|
||||
for (const s of scripts) {
|
||||
if (s.Group !== prevGroup) {
|
||||
const group = document.createElement('div')
|
||||
group.classList.add('group')
|
||||
group.innerText = s.Group
|
||||
this.scripts.append(group)
|
||||
prevGroup = s.Group
|
||||
}
|
||||
|
||||
const div = document.createElement('div')
|
||||
div.innerText = s.Name
|
||||
div.classList.add('script')
|
||||
div.addEventListener('click', () => {
|
||||
this.dlg.close()
|
||||
this.callback(s)
|
||||
})
|
||||
this.scripts.append(div)
|
||||
}
|
||||
}// }}}
|
||||
}
|
||||
|
||||
// vim: foldmethod=marker
|
||||
|
|
|
|||
|
|
@ -284,17 +284,19 @@ select:focus {
|
|||
|
||||
#connected-nodes {
|
||||
& > .label {
|
||||
display: grid;
|
||||
grid-template-columns: min-content min-content;
|
||||
align-items: center;
|
||||
|
||||
color: var(--section-color);
|
||||
font-weight: bold;
|
||||
font-size: 1.25em;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
margin-bottom: 16px;
|
||||
|
||||
& > .add {
|
||||
margin-bottom: 8px;
|
||||
img {
|
||||
& > img.add {
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -337,19 +339,21 @@ select:focus {
|
|||
}
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, min-content);
|
||||
grid-template-columns: repeat(3, min-content);
|
||||
align-items: center;
|
||||
grid-gap: 4px 0px;
|
||||
grid-gap: 2px 0px;
|
||||
|
||||
div {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.script-icon {
|
||||
margin-right: 4px;
|
||||
.script-group {
|
||||
grid-column: 1 / -1;
|
||||
font-weight: bold;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.script-icon, .script-unhook {
|
||||
.script-unhook {
|
||||
img {
|
||||
display: block;
|
||||
height: 24px;
|
||||
|
|
@ -365,19 +369,21 @@ select:focus {
|
|||
}
|
||||
}
|
||||
|
||||
& > .add {
|
||||
margin-bottom: 8px;
|
||||
img {
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
& > .label {
|
||||
display: grid;
|
||||
grid-template-columns: min-content min-content;
|
||||
align-items: center;
|
||||
|
||||
color: var(--section-color);
|
||||
font-weight: bold;
|
||||
font-size: 1.25em;
|
||||
margin-bottom: 8px;
|
||||
|
||||
img.add {
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -394,6 +400,11 @@ select:focus {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-weight: bold;
|
||||
color: var(--section-color);
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100px !important;
|
||||
}
|
||||
|
|
@ -589,3 +600,26 @@ dialog#connection-data {
|
|||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
#script-select-dialog {
|
||||
display: grid;
|
||||
grid-gap: 8px;
|
||||
padding: 32px;
|
||||
|
||||
& > .header {
|
||||
font-weight: bold;
|
||||
color: var(--section-color);
|
||||
}
|
||||
|
||||
.scripts {
|
||||
.group {
|
||||
font-weight: bold;
|
||||
color: var(--section-color);
|
||||
margin-top: 16px;
|
||||
}
|
||||
.script {
|
||||
cursor: pointer;
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
32
webserver.go
32
webserver.go
|
|
@ -41,6 +41,7 @@ func initWebserver() (err error) {
|
|||
http.HandleFunc("/nodes/move", actionNodeMove)
|
||||
http.HandleFunc("/nodes/search", actionNodeSearch)
|
||||
http.HandleFunc("/nodes/connect", actionNodeConnect)
|
||||
http.HandleFunc("/nodes/hook", actionNodeHook)
|
||||
http.HandleFunc("/types/{typeID}", actionType)
|
||||
http.HandleFunc("/types/", actionTypesAll)
|
||||
http.HandleFunc("/types/create", actionTypeCreate)
|
||||
|
|
@ -374,6 +375,37 @@ func actionNodeConnect(w http.ResponseWriter, r *http.Request) { // {{{
|
|||
j, _ := json.Marshal(res)
|
||||
w.Write(j)
|
||||
|
||||
} // }}}
|
||||
func actionNodeHook(w http.ResponseWriter, r *http.Request) { // {{{
|
||||
var req struct {
|
||||
NodeID int
|
||||
ScriptID int
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
err := json.Unmarshal(body, &req)
|
||||
if err != nil {
|
||||
err = werr.Wrap(err)
|
||||
httpError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = HookScript(req.NodeID, req.ScriptID)
|
||||
if err != nil {
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
if ok && pqErr.Code == "23505" {
|
||||
err = errors.New("This script is already hooked.")
|
||||
} else {
|
||||
err = werr.Wrap(err)
|
||||
}
|
||||
httpError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := struct{ OK bool }{true}
|
||||
j, _ := json.Marshal(res)
|
||||
w.Write(j)
|
||||
|
||||
} // }}}
|
||||
|
||||
func actionType(w http.ResponseWriter, r *http.Request) { // {{{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue