Compare commits

..

No commits in common. "1215b13d4746ee30fe9d95921f976ee717be29a9" and "5d24baedac98a0f034e38c694dc472e344cfa49c" have entirely different histories.

8 changed files with 4 additions and 271 deletions

View File

@ -120,7 +120,7 @@ Problems can be acknowledged if they are known problems that will not be current
## Notifications ## Notifications
Smon has a couple of notification services (currently [[https://ntfy.sh|NTFY]], [[https://pushover.net|Pushover]] and script). Smon has a couple of notification services (currently [[https://ntfy.sh|NTFY]] and script).
Services are added with a prio. The service with the lowest prio is tried first. \ Services are added with a prio. The service with the lowest prio is tried first. \
If sending through the service fails, the next service is tried and so on until one succeeds. If sending through the service fails, the next service is tried and so on until one succeeds.
@ -134,10 +134,6 @@ What is sent isn't configurable (for now). What is sent is:
An URL is provided to the topic which should receive the notifications. An URL is provided to the topic which should receive the notifications.
## Pushover
The user key and API key for the Pushover application is needed. Additionally, a device key can be specified as well.
## Script ## Script
The script service is defined with a filename. The script service is defined with a filename.

View File

@ -29,7 +29,7 @@ import (
"time" "time"
) )
const VERSION = "v32" const VERSION = "v31"
var ( var (
logger *slog.Logger logger *slog.Logger
@ -1211,8 +1211,6 @@ func actionConfigurationNotificationUpdate(w http.ResponseWriter, r *http.Reques
notificationManager.AddService(*svc) notificationManager.AddService(*svc)
} }
notificationManager.Reprioritize()
w.Header().Add("Location", "/configuration") w.Header().Add("Location", "/configuration")
w.WriteHeader(302) w.WriteHeader(302)
} // }}} } // }}}

View File

@ -24,16 +24,6 @@ func ServiceFactory(t string, config []byte, prio int, ackURL string, logger *sl
} }
ntfy.SetLogger(logger) ntfy.SetLogger(logger)
return ntfy, nil return ntfy, nil
case "PUSHOVER":
pushover, err := NewPushover(config, prio, ackURL)
if err != nil {
err = werr.Wrap(err).WithData(config)
return nil, err
}
pushover.SetLogger(logger)
return pushover, nil
case "SCRIPT": case "SCRIPT":
script, err := NewScript(config, prio, ackURL) script, err := NewScript(config, prio, ackURL)
if err != nil { if err != nil {
@ -51,8 +41,6 @@ func NewInstance(typ string) Service {
switch typ { switch typ {
case "NTFY": case "NTFY":
return new(NTFY) return new(NTFY)
case "PUSHOVER":
return new(Pushover)
case "SCRIPT": case "SCRIPT":
return new(Script) return new(Script)
} }

View File

@ -39,9 +39,6 @@ func NewManager(logger *slog.Logger) (nm Manager) {
func (nm *Manager) AddService(service Service) { func (nm *Manager) AddService(service Service) {
service.SetExists(true) service.SetExists(true)
nm.services = append(nm.services, service) nm.services = append(nm.services, service)
}
func (nm *Manager) Reprioritize() {
slices.SortFunc(nm.services, func(a, b Service) int { slices.SortFunc(nm.services, func(a, b Service) int {
if a.GetPrio() < b.GetPrio() { if a.GetPrio() < b.GetPrio() {
return -1 return -1

View File

@ -1,198 +0,0 @@
package notification
import (
// External
werr "git.gibonuddevalla.se/go/wrappederror"
// Standard
"bytes"
"encoding/json"
"fmt"
"io"
"log/slog"
"net/http"
"net/url"
"strconv"
"strings"
)
type Pushover struct {
Description string
UserKey string `json:"user_key"`
APIKey string `json:"api_key"`
DeviceName string `json:"device_name"`
Prio int
AcknowledgeURL string
logger *slog.Logger
exists bool
updated Service
}
func init() {
allServices = append(allServices, &Pushover{})
}
func NewPushover(config []byte, prio int, ackURL string) (instance *Pushover, err error) {
instance = new(Pushover)
err = json.Unmarshal(config, &instance)
if err != nil {
err = werr.Wrap(err).WithData(config)
return
}
instance.Prio = prio
instance.AcknowledgeURL = ackURL
return instance, nil
}
func (po *Pushover) SetLogger(l *slog.Logger) {
po.logger = l
}
func (po *Pushover) GetType() string {
return "PUSHOVER"
}
func (po *Pushover) GetPrio() int {
return po.Prio
}
func (po *Pushover) SetPrio(prio int) {
po.Prio = prio
}
func (po *Pushover) SetExists(exists bool) {
po.exists = exists
}
func (po Pushover) Exists() bool {
return po.exists
}
func (po *Pushover) String() string {
if po.Description != "" {
return po.Description
}
return fmt.Sprintf("%s, %s", po.UserKey, po.APIKey)
}
func (po Pushover) Send(problemID int, msg []byte) (err error) {
var req *http.Request
var res *http.Response
pushoverRequest, _ := json.Marshal(map[string]string{
"token": po.APIKey,
"user": po.UserKey,
"device": po.DeviceName,
"message": string(msg),
})
req, err = http.NewRequest("POST", "https://api.pushover.net/1/messages.json", bytes.NewReader(pushoverRequest))
if err != nil {
err = werr.Wrap(err).WithData(struct {
UserKey string
APIKey string
Msg []byte
}{
po.UserKey,
po.APIKey,
msg,
},
)
return
}
//ackURL := fmt.Sprintf("http, OK, %s/notification/ack?problemID=%d", po.AcknowledgeURL, problemID)
req.Header.Add("Content-Type", "application/json")
res, err = http.DefaultClient.Do(req)
if err != nil {
err = werr.Wrap(err)
return
}
body, _ := io.ReadAll(res.Body)
poResp := struct {
Status int
Errors []string
}{}
err = json.Unmarshal(body, &poResp)
if err != nil {
err = werr.Wrap(err).WithData(body)
return
}
if poResp.Status != 1 {
err = werr.New("%s", strings.Join(poResp.Errors, ", "))
return
}
if res.StatusCode != 200 {
err = werr.New("Invalid Pushover response").WithData(body)
return
}
return
}
func (po *Pushover) Update(values url.Values) (err error) {
updated := Pushover{}
po.updated = &updated
// Prio
updated.Prio, err = strconv.Atoi(values.Get("prio"))
if err != nil {
return werr.Wrap(err)
}
// Description
updated.Description = strings.TrimSpace(values.Get("description"))
// API (application) key
givenAPIKey := values.Get("api_key")
if strings.TrimSpace(givenAPIKey) == "" {
return werr.New("API key cannot be empty")
}
updated.APIKey = strings.TrimSpace(givenAPIKey)
// User key
givenUserKey := values.Get("user_key")
if strings.TrimSpace(givenUserKey) == "" {
return werr.New("User key cannot be empty")
}
updated.UserKey = strings.TrimSpace(givenUserKey)
// Device name
updated.DeviceName = strings.TrimSpace(values.Get("device_name"))
return
}
func (po *Pushover) Updated() Service {
return po.updated
}
func (po *Pushover) Commit() {
updatedPushover := po.updated.(*Pushover)
po.Prio = updatedPushover.Prio
po.Description = updatedPushover.Description
po.APIKey = updatedPushover.APIKey
po.UserKey = updatedPushover.UserKey
po.DeviceName = updatedPushover.DeviceName
}
func (po Pushover) JSON() []byte {
data := struct {
Description string
APIKey string `json:"api_key"`
UserKey string `json:"user_key"`
DeviceName string `json:"device_name"`
}{
po.Description,
po.APIKey,
po.UserKey,
po.DeviceName,
}
j, _ := json.Marshal(data)
return j
}

View File

@ -101,7 +101,7 @@ func UpdateNotificationService(svc notification.Service) (created bool, err erro
// Check if this is just a duplicated prio, which isn't allowed. // Check if this is just a duplicated prio, which isn't allowed.
pgErr, isPgErr := err.(*pq.Error) pgErr, isPgErr := err.(*pq.Error)
if isPgErr && pgErr.Code == "23505" { if isPgErr && pgErr.Code == "23505" {
return false, werr.New("Prio %d is already used by another service", svc.Updated().GetPrio()) return false, werr.New("Prio %d is already used by another service", svc.GetPrio())
} }
return false, werr.Wrap(err).WithData( return false, werr.Wrap(err).WithData(

View File

@ -1 +0,0 @@
ALTER TYPE notification_type ADD VALUE 'PUSHOVER';

View File

@ -1,47 +0,0 @@
{{ define "page" }}
<h1>Pushover</h1>
<style>
.grid {
display: grid;
grid-template-columns: min-content 1fr;
grid-gap: 8px 16px;
align-items: center;
margin-top: 32px;
}
input[type=number] {
width: 64px;
padding: 4px;
}
button {
margin-top: 16px;
}
div.grid > div {
white-space: nowrap;
}
</style>
<form action="/configuration/notification/update/{{ if .Data.Service.Exists }}{{ .Data.Service.GetPrio }}{{ else }}-1{{ end }}" method="post">
<input type="hidden" name="type" value="PUSHOVER">
<div class="grid">
<div>Prio:</div>
<input type="number" min=0 name="prio" value="{{ .Data.Service.GetPrio }}">
<div>Description</div>
<input type="text" name="description" value="{{ .Data.Service.Description }}" style="width: 100%">
<div>User key: <span class="error">*</span></div>
<input type="text" name="user_key" value="{{ .Data.Service.UserKey }}" style="width: 100%">
<div>API (application) key: <span class="error">*</span></div>
<input type="text" name="api_key" value="{{ .Data.Service.APIKey }}" style="width: 100%">
<div>Device name:</div>
<input type="text" name="device_name" value="{{ .Data.Service.DeviceName }}" style="width: 100%">
<button style="grid-column: 1 / -1; width: min-content;">OK</button>
</div>
</form>
{{ end }}