From 8f6b384fe60b438dcea184ece0649a5b095a6d41 Mon Sep 17 00:00:00 2001 From: James Read Date: Wed, 25 Oct 2023 22:32:18 +0100 Subject: [PATCH] Notification support (#183) * feature: #158 shellAfterComplete support for notifications * fmt: reduce cyclo --- config.yaml | 8 +++--- internal/config/config.go | 1 + internal/config/config_helpers.go | 11 ++++++++ internal/executor/executor.go | 45 +++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 3 deletions(-) diff --git a/config.yaml b/config.yaml index 44a7c2b..1bc6761 100644 --- a/config.yaml +++ b/config.yaml @@ -10,15 +10,17 @@ logLevel: "INFO" # Actions (buttons) to show up on the WebUI: actions: +- title: date + shell: date + popupOnStart: true + # This will run a simple script that you create. - title: Run backup script shell: /opt/backupScript.sh + shellAfterCompleted: "apprise -t 'Notification: Backup script completed' -b 'The backup script completed with code {{ exitCode}}. The log is: \n {{ stdout }} '" maxConcurrent: 1 icon: backup -- title: date - shell: date - # This will send 1 ping (-c 1) # Docs: https://docs.olivetin.app/action-ping.html - title: Ping host diff --git a/internal/config/config.go b/internal/config/config.go index 3f9348c..6ea2536 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -7,6 +7,7 @@ type Action struct { Title string Icon string Shell string + ShellAfterCompleted string CSS map[string]string `mapstructure:"omitempty"` Timeout int Acls []string diff --git a/internal/config/config_helpers.go b/internal/config/config_helpers.go index 8731def..8cba6b5 100644 --- a/internal/config/config_helpers.go +++ b/internal/config/config_helpers.go @@ -13,6 +13,17 @@ func (cfg *Config) FindAction(actionTitle string) *Action { // FindArg will return an arg if there is a match on Name func (action *Action) FindArg(name string) *ActionArgument { + if name == "stdout" || name == "exitCode" { + return &ActionArgument{ + Name: name, + Type: "very_dangerous_raw_string", + } + } + + return action.findArg(name) +} + +func (action *Action) findArg(name string) *ActionArgument { for _, arg := range action.Arguments { if arg.Name == name { return &arg diff --git a/internal/executor/executor.go b/internal/executor/executor.go index b1d4fec..2fed3d0 100644 --- a/internal/executor/executor.go +++ b/internal/executor/executor.go @@ -83,6 +83,7 @@ func DefaultExecutor() *Executor { stepParseArgs, stepLogStart, stepExec, + stepExecAfter, stepLogFinish, } @@ -300,3 +301,47 @@ func stepExec(req *ExecutionRequest) bool { return true } + +func stepExecAfter(req *ExecutionRequest) bool { + if req.action.ShellAfterCompleted == "" { + return true + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(req.action.Timeout)*time.Second) + defer cancel() + + var stdout bytes.Buffer + var stderr bytes.Buffer + + args := map[string]string{ + "stdout": req.logEntry.Stdout, + "exitCode": fmt.Sprintf("%v", req.logEntry.ExitCode), + } + + finalParsedCommand, _ := parseActionArguments(req.action.ShellAfterCompleted, args, req.action) + + cmd := wrapCommandInShell(ctx, finalParsedCommand) + cmd.Stdout = &stdout + cmd.Stderr = &stderr + req.logEntry.StdoutBuffer, _ = cmd.StdoutPipe() + req.logEntry.StderrBuffer, _ = cmd.StderrPipe() + + runerr := cmd.Start() + + cmd.Wait() + + req.logEntry.Stdout += "---\n" + stdout.String() + req.logEntry.Stderr += "---\n" + stderr.String() + + if runerr != nil { + req.logEntry.Stderr = runerr.Error() + "\n\n" + req.logEntry.Stderr + } + + if ctx.Err() == context.DeadlineExceeded { + req.logEntry.Stderr += "Your shellAfterCommand command timed out." + } + + req.logEntry.Stdout += fmt.Sprintf("Your shellAfterCommand exited with code %v", cmd.ProcessState.ExitCode()) + + return true +}