Added automatical hook scheduling

This commit is contained in:
Magnus Åhall 2025-08-11 20:54:36 +02:00
parent e0628bc480
commit 463d2805b3
6 changed files with 124 additions and 28 deletions

View file

@ -94,7 +94,8 @@ func GetNode(nodeID int) (node Node, err error) { // {{{
h.id,
to_jsonb(s) AS script,
ssh,
env
env,
schedule_on_child_update AS ScheduleOnChildUpdate
FROM hook h
INNER JOIN public.script s ON h.script_id = s.id
WHERE

110
script.go
View file

@ -23,11 +23,12 @@ type Script struct {
}
type Hook struct {
ID int
Node Node
Script Script
SSH string
Env map[string]string
ID int
Node Node
Script Script
SSH string
Env map[string]string
ScheduleOnChildUpdate bool `db:"schedule_on_child_update"`
}
func GetScript(scriptID int) (script Script, err error) { // {{{
@ -135,13 +136,20 @@ func SearchScripts(search string) (scripts []Script, err error) { // {{{
row := db.QueryRow(`
SELECT
json_agg(script) AS scripts
FROM public.script
WHERE
name ILIKE $1
ORDER BY
"group" ASC,
name ASC
COALESCE(
json_agg(scripts),
'[]'::json
) AS scripts
FROM (
SELECT
to_json(script) AS scripts
FROM public.script
WHERE
name ILIKE $1
ORDER BY
"group" ASC,
name ASC
) scripts
`,
search,
)
@ -172,11 +180,12 @@ func GetHook(hookID int) (hook Hook, err error) { // {{{
to_json(res)
FROM (
SELECT
h.id,
h.ssh,
h.env,
(SELECT to_json(node) FROM node WHERE id = h.node_id) AS node,
(SELECT to_json(script) FROM script WHERE id = h.script_id) AS script
h.id,
h.ssh,
h.env,
h.schedule_on_child_update,
(SELECT to_json(node) FROM node WHERE id = h.node_id) AS node,
(SELECT to_json(script) FROM script WHERE id = h.script_id) AS script
FROM hook h
WHERE
h.id = $1
@ -198,7 +207,20 @@ func GetHook(hookID int) (hook Hook, err error) { // {{{
} // }}}
func UpdateHook(hook Hook) (err error) { // {{{
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)
_, err = db.Exec(`
UPDATE hook
SET
ssh=$2,
env=$3,
schedule_on_child_update=$4
WHERE
id=$1
`,
hook.ID,
strings.TrimSpace(hook.SSH),
j,
hook.ScheduleOnChildUpdate,
)
if err != nil {
err = werr.Wrap(err)
return
@ -259,6 +281,58 @@ func ScheduleHook(hookID int) (err error) { // {{{
return
} // }}}
func ScheduleHookRecursivelyUpwards(nodeID int) (err error) { // {{{
var rows *sql.Rows
rows, err = db.Query(`
WITH RECURSIVE rec AS (
SELECT
id,
parent_id
FROM public.node
WHERE
id = $1
UNION ALL
SELECT
n.id,
n.parent_id
FROM node n
INNER JOIN rec ON n.id = rec.parent_id
)
SELECT
hook.id
FROM rec
INNER JOIN hook ON
hook.node_id = rec.id AND
hook.schedule_on_child_update
`,
nodeID,
)
if err != nil {
err = werr.Wrap(err)
return
}
defer rows.Close()
for rows.Next() {
var hookID int
err = rows.Scan(&hookID)
if err != nil {
err = werr.Wrap(err)
return
}
err = ScheduleHook(hookID)
if err != nil {
err = werr.Wrap(err)
return
}
}
return
} // }}}
func ScriptPreservedID(name, source string) (id int, err error) { // {{{
sum := md5.Sum([]byte(source))

1
sql/0018.sql Normal file
View file

@ -0,0 +1 @@
ALTER TABLE public.hook DROP CONSTRAINT hook_unique;

1
sql/0019.sql Normal file
View file

@ -0,0 +1 @@
ALTER TABLE public.hook ADD schedule_on_child_update bool DEFAULT false NOT NULL;

View file

@ -1464,6 +1464,7 @@ class ScriptHookDialog extends Component {
this.env = null
this.ssh = null
this.schedule_on_child_update = null
}// }}}
renderComponent() {// {{{
const div = document.createElement('div')
@ -1472,9 +1473,16 @@ class ScriptHookDialog extends Component {
<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">Schedule automatically</div>
<div>
<input type="checkbox" class="schedule-on-child" id="schedule-on-child" />
<label for="schedule-on-child">on child update</label>
</div>
<div class="label">
Environment
</div>
@ -1499,6 +1507,9 @@ class ScriptHookDialog extends Component {
this.env = div.querySelector('.env')
this.env.value = JSON.stringify(this.hook.Env, null, " ")
this.schedule_on_child_update = div.querySelector('.schedule-on-child')
this.schedule_on_child_update.checked = this.hook.ScheduleOnChildUpdate
const button = div.querySelector('button')
this.env.addEventListener('keydown', event => {
if (event.ctrlKey && event.key == 's') {
@ -1527,6 +1538,7 @@ class ScriptHookDialog extends Component {
try {
this.hook.Env = JSON.parse(this.env.value)
this.hook.SSH = this.ssh.value.trim()
this.hook.ScheduleOnChildUpdate = this.schedule_on_child_update.checked
window._app.query('/hooks/update', this.hook)
.then(() => {
this.callback()
@ -1659,7 +1671,7 @@ class ScriptExecutionValueDialog extends Component {
this.value = null
}// }}}
getValue(execution) {
getValue(execution) {// {{{
switch (this.valueName) {
case 'Source':
return execution.Source
@ -1677,7 +1689,7 @@ class ScriptExecutionValueDialog extends Component {
case 'OutputStderr':
return execution[this.valueName]?.String
}
}
}// }}}
renderComponent() {// {{{
const div = document.createElement('div')
div.innerHTML = `
@ -1710,5 +1722,4 @@ class ScriptExecutionValueDialog extends Component {
}// }}}
}
// vim: foldmethod=marker

View file

@ -51,7 +51,7 @@ func initWebserver() (err error) {
http.HandleFunc("/scripts/", actionScripts)
http.HandleFunc("/scripts/update/{scriptID}", actionScriptUpdate)
http.HandleFunc("/scripts/delete/{scriptID}", actionScriptDelete)
http.HandleFunc("/hooks/search", actionScriptsSearch)
http.HandleFunc("/scripts/search", actionScriptsSearch)
http.HandleFunc("/hooks/update", actionHookUpdate)
http.HandleFunc("/hooks/delete/{hookID}", actionHookDelete)
http.HandleFunc("/hooks/schedule/{hookID}", actionHookSchedule)
@ -173,6 +173,14 @@ func actionNodeUpdate(w http.ResponseWriter, r *http.Request) { // {{{
return
}
// Recursively schedule hooks with automatic trigger.
err = ScheduleHookRecursivelyUpwards(nodeID)
if err != nil {
err = werr.Wrap(err)
httpError(w, err)
return
}
out := struct {
OK bool
}{
@ -605,7 +613,7 @@ func actionScripts(w http.ResponseWriter, r *http.Request) { // {{{
}
out := struct {
OK bool
OK bool
Scripts []Script
}{
true,
@ -629,7 +637,7 @@ func actionScriptUpdate(w http.ResponseWriter, r *http.Request) { // {{{
}
out := struct {
OK bool
OK bool
Script Script
}{
true,
@ -678,7 +686,7 @@ func actionScriptsSearch(w http.ResponseWriter, r *http.Request) { // {{{
}
out := struct {
OK bool
OK bool
Scripts []Script
}{
true,
@ -763,7 +771,7 @@ func actionScriptExecutions(w http.ResponseWriter, r *http.Request) { // {{{
}
out := struct {
OK bool
OK bool
ScriptExecutions []ScriptExecutionBrief
}{
true,
@ -785,7 +793,7 @@ func actionScriptExecutionGet(w http.ResponseWriter, r *http.Request) { // {{{
}
out := struct {
OK bool
OK bool
ScriptExecution ScriptExecution
}{
true,