Added notification manager
This commit is contained in:
parent
317c233255
commit
49af9dc33c
112
main.go
112
main.go
@ -4,7 +4,10 @@ import (
|
|||||||
// External
|
// External
|
||||||
ws "git.gibonuddevalla.se/go/webservice"
|
ws "git.gibonuddevalla.se/go/webservice"
|
||||||
"git.gibonuddevalla.se/go/webservice/session"
|
"git.gibonuddevalla.se/go/webservice/session"
|
||||||
we "git.gibonuddevalla.se/go/wrappederror"
|
werr "git.gibonuddevalla.se/go/wrappederror"
|
||||||
|
|
||||||
|
// Internal
|
||||||
|
"smon/notification"
|
||||||
|
|
||||||
// Standard
|
// Standard
|
||||||
"embed"
|
"embed"
|
||||||
@ -26,13 +29,14 @@ import (
|
|||||||
const VERSION = "v6"
|
const VERSION = "v6"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
flagConfigFile string
|
flagConfigFile string
|
||||||
flagDev bool
|
flagDev bool
|
||||||
service *ws.Service
|
service *ws.Service
|
||||||
logFile *os.File
|
logFile *os.File
|
||||||
parsedTemplates map[string]*template.Template
|
parsedTemplates map[string]*template.Template
|
||||||
componentFilenames []string
|
componentFilenames []string
|
||||||
|
notificationManager notification.Manager
|
||||||
|
|
||||||
//go:embed sql
|
//go:embed sql
|
||||||
sqlFS embed.FS
|
sqlFS embed.FS
|
||||||
@ -50,7 +54,7 @@ func init() { // {{{
|
|||||||
|
|
||||||
confDir, err := os.UserConfigDir()
|
confDir, err := os.UserConfigDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("application", "error", we.Wrap(err))
|
logger.Error("application", "error", werr.Wrap(err))
|
||||||
}
|
}
|
||||||
cfgPath := path.Join(confDir, "smon.yaml")
|
cfgPath := path.Join(confDir, "smon.yaml")
|
||||||
flag.StringVar(&flagConfigFile, "config", cfgPath, "Path and filename of the YAML configuration file")
|
flag.StringVar(&flagConfigFile, "config", cfgPath, "Path and filename of the YAML configuration file")
|
||||||
@ -68,8 +72,8 @@ func init() { // {{{
|
|||||||
func main() { // {{{
|
func main() { // {{{
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
we.Init()
|
werr.Init()
|
||||||
we.SetLogCallback(logHandler)
|
werr.SetLogCallback(logHandler)
|
||||||
|
|
||||||
service, err = ws.New(flagConfigFile, VERSION, logger)
|
service, err = ws.New(flagConfigFile, VERSION, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -96,6 +100,12 @@ func main() { // {{{
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = InitNotificationManager()
|
||||||
|
if err != nil {
|
||||||
|
err = werr.Wrap(err).Log()
|
||||||
|
logger.Error("notification", "error", err)
|
||||||
|
}
|
||||||
|
|
||||||
service.Register("/", false, false, staticHandler)
|
service.Register("/", false, false, staticHandler)
|
||||||
|
|
||||||
service.Register("/area/new/{name}", false, false, areaNew)
|
service.Register("/area/new/{name}", false, false, areaNew)
|
||||||
@ -124,7 +134,7 @@ func main() { // {{{
|
|||||||
|
|
||||||
err = service.Start()
|
err = service.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("webserver", "error", we.Wrap(err))
|
logger.Error("webserver", "error", werr.Wrap(err))
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,7 +149,7 @@ func sqlProvider(dbname string, version int) (sql []byte, found bool) { // {{{
|
|||||||
found = true
|
found = true
|
||||||
return
|
return
|
||||||
} // }}}
|
} // }}}
|
||||||
func logHandler(err we.Error) { // {{{
|
func logHandler(err werr.Error) { // {{{
|
||||||
j, _ := json.Marshal(err)
|
j, _ := json.Marshal(err)
|
||||||
logFile.Write(j)
|
logFile.Write(j)
|
||||||
logFile.Write([]byte("\n"))
|
logFile.Write([]byte("\n"))
|
||||||
@ -175,7 +185,7 @@ func entryDatapoint(w http.ResponseWriter, r *http.Request, sess *session.T) { /
|
|||||||
|
|
||||||
err := DatapointAdd(dpoint, value)
|
err := DatapointAdd(dpoint, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +198,7 @@ func entryDatapoint(w http.ResponseWriter, r *http.Request, sess *session.T) { /
|
|||||||
var out any
|
var out any
|
||||||
out, err = trigger.Run()
|
out, err = trigger.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = we.Wrap(err).Log()
|
err = werr.Wrap(err).Log()
|
||||||
logger.Error("entry", "error", err)
|
logger.Error("entry", "error", err)
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -200,13 +210,13 @@ func entryDatapoint(w http.ResponseWriter, r *http.Request, sess *session.T) { /
|
|||||||
if v {
|
if v {
|
||||||
err = ProblemStart(trigger)
|
err = ProblemStart(trigger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = we.Wrap(err).Log()
|
err = werr.Wrap(err).Log()
|
||||||
logger.Error("entry", "error", err)
|
logger.Error("entry", "error", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = ProblemClose(trigger)
|
err = ProblemClose(trigger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = we.Wrap(err).Log()
|
err = werr.Wrap(err).Log()
|
||||||
logger.Error("entry", "error", err)
|
logger.Error("entry", "error", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -214,7 +224,7 @@ func entryDatapoint(w http.ResponseWriter, r *http.Request, sess *session.T) { /
|
|||||||
default:
|
default:
|
||||||
err := fmt.Errorf(`Expression for trigger %s not returning bool (%T)`, trigger.Name, v)
|
err := fmt.Errorf(`Expression for trigger %s not returning bool (%T)`, trigger.Name, v)
|
||||||
logger.Info("entry", "error", err)
|
logger.Info("entry", "error", err)
|
||||||
we.Wrap(err).WithData(v).Log()
|
werr.Wrap(err).WithData(v).Log()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,7 +278,7 @@ func getPage(layout, page string) (tmpl *template.Template, err error) { // {{{
|
|||||||
tmpl, err = template.New("main.gotmpl").Funcs(funcMap).ParseFS(viewFS, filenames...)
|
tmpl, err = template.New("main.gotmpl").Funcs(funcMap).ParseFS(viewFS, filenames...)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = we.Wrap(err).Log()
|
err = werr.Wrap(err).Log()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,7 +298,7 @@ func areaNew(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
|
|||||||
name := r.PathValue("name")
|
name := r.PathValue("name")
|
||||||
err := AreaCreate(name)
|
err := AreaCreate(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,14 +310,14 @@ func areaRename(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
|
|||||||
idStr := r.PathValue("id")
|
idStr := r.PathValue("id")
|
||||||
id, err := strconv.Atoi(idStr)
|
id, err := strconv.Atoi(idStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
name := r.PathValue("name")
|
name := r.PathValue("name")
|
||||||
err = AreaRename(id, name)
|
err = AreaRename(id, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,14 +330,14 @@ func sectionNew(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
|
|||||||
idStr := r.PathValue("areaID")
|
idStr := r.PathValue("areaID")
|
||||||
areaID, err := strconv.Atoi(idStr)
|
areaID, err := strconv.Atoi(idStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
name := r.PathValue("name")
|
name := r.PathValue("name")
|
||||||
err = SectionCreate(areaID, name)
|
err = SectionCreate(areaID, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,14 +349,14 @@ func sectionRename(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{
|
|||||||
idStr := r.PathValue("id")
|
idStr := r.PathValue("id")
|
||||||
id, err := strconv.Atoi(idStr)
|
id, err := strconv.Atoi(idStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
name := r.PathValue("name")
|
name := r.PathValue("name")
|
||||||
err = SectionRename(id, name)
|
err = SectionRename(id, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,7 +373,7 @@ func pageProblems(w http.ResponseWriter, _ *http.Request, _ *session.T) { // {{{
|
|||||||
|
|
||||||
problems, err := ProblemsRetrieve()
|
problems, err := ProblemsRetrieve()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,13 +387,13 @@ func pageProblemAcknowledge(w http.ResponseWriter, r *http.Request, _ *session.T
|
|||||||
idStr := r.PathValue("id")
|
idStr := r.PathValue("id")
|
||||||
id, err := strconv.Atoi(idStr)
|
id, err := strconv.Atoi(idStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ProblemAcknowledge(id, true)
|
err = ProblemAcknowledge(id, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,13 +406,13 @@ func pageProblemUnacknowledge(w http.ResponseWriter, r *http.Request, _ *session
|
|||||||
idStr := r.PathValue("id")
|
idStr := r.PathValue("id")
|
||||||
id, err := strconv.Atoi(idStr)
|
id, err := strconv.Atoi(idStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ProblemAcknowledge(id, false)
|
err = ProblemAcknowledge(id, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,7 +430,7 @@ func pageDatapoints(w http.ResponseWriter, r *http.Request, _ *session.T) { // {
|
|||||||
|
|
||||||
datapoints, err := DatapointsRetrieve()
|
datapoints, err := DatapointsRetrieve()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,7 +451,7 @@ func pageDatapointEdit(w http.ResponseWriter, r *http.Request, _ *session.T) { /
|
|||||||
idStr := r.PathValue("id")
|
idStr := r.PathValue("id")
|
||||||
id, err := strconv.Atoi(idStr)
|
id, err := strconv.Atoi(idStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,7 +462,7 @@ func pageDatapointEdit(w http.ResponseWriter, r *http.Request, _ *session.T) { /
|
|||||||
} else {
|
} else {
|
||||||
datapoint, err = DatapointRetrieve(id, "")
|
datapoint, err = DatapointRetrieve(id, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -475,7 +485,7 @@ func pageDatapointUpdate(w http.ResponseWriter, r *http.Request, _ *session.T) {
|
|||||||
idStr := r.PathValue("id")
|
idStr := r.PathValue("id")
|
||||||
id, err := strconv.Atoi(idStr)
|
id, err := strconv.Atoi(idStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -485,7 +495,7 @@ func pageDatapointUpdate(w http.ResponseWriter, r *http.Request, _ *session.T) {
|
|||||||
dp.Datatype = DatapointType(r.FormValue("datatype"))
|
dp.Datatype = DatapointType(r.FormValue("datatype"))
|
||||||
err = dp.Update()
|
err = dp.Update()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -496,13 +506,13 @@ func pageDatapointDelete(w http.ResponseWriter, r *http.Request, _ *session.T) {
|
|||||||
idStr := r.PathValue("id")
|
idStr := r.PathValue("id")
|
||||||
id, err := strconv.Atoi(idStr)
|
id, err := strconv.Atoi(idStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = DatapointDelete(id)
|
err = DatapointDelete(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,7 +523,7 @@ func pageDatapointDelete(w http.ResponseWriter, r *http.Request, _ *session.T) {
|
|||||||
func pageTriggers(w http.ResponseWriter, _ *http.Request, _ *session.T) { // {{{
|
func pageTriggers(w http.ResponseWriter, _ *http.Request, _ *session.T) { // {{{
|
||||||
areas, err := TriggersRetrieve()
|
areas, err := TriggersRetrieve()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -535,7 +545,7 @@ func pageTriggerEdit(w http.ResponseWriter, r *http.Request, _ *session.T) { //
|
|||||||
idStr := r.PathValue("id")
|
idStr := r.PathValue("id")
|
||||||
id, err := strconv.Atoi(idStr)
|
id, err := strconv.Atoi(idStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,7 +556,7 @@ func pageTriggerEdit(w http.ResponseWriter, r *http.Request, _ *session.T) { //
|
|||||||
if id > 0 {
|
if id > 0 {
|
||||||
trigger, err = TriggerRetrieve(id)
|
trigger, err = TriggerRetrieve(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -555,7 +565,7 @@ func pageTriggerEdit(w http.ResponseWriter, r *http.Request, _ *session.T) { //
|
|||||||
if sectionIDStr != "" {
|
if sectionIDStr != "" {
|
||||||
trigger.SectionID, err = strconv.Atoi(sectionIDStr)
|
trigger.SectionID, err = strconv.Atoi(sectionIDStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -565,7 +575,7 @@ func pageTriggerEdit(w http.ResponseWriter, r *http.Request, _ *session.T) { //
|
|||||||
for _, dpname := range trigger.Datapoints {
|
for _, dpname := range trigger.Datapoints {
|
||||||
dp, err := DatapointRetrieve(0, dpname)
|
dp, err := DatapointRetrieve(0, dpname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
dp.LastDatapointValue.TemplateValue = dp.LastDatapointValue.Value()
|
dp.LastDatapointValue.TemplateValue = dp.LastDatapointValue.Value()
|
||||||
@ -590,7 +600,7 @@ func pageTriggerUpdate(w http.ResponseWriter, r *http.Request, _ *session.T) { /
|
|||||||
idStr := r.PathValue("id")
|
idStr := r.PathValue("id")
|
||||||
id, err := strconv.Atoi(idStr)
|
id, err := strconv.Atoi(idStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -598,13 +608,13 @@ func pageTriggerUpdate(w http.ResponseWriter, r *http.Request, _ *session.T) { /
|
|||||||
if id > 0 {
|
if id > 0 {
|
||||||
trigger, err = TriggerRetrieve(id)
|
trigger, err = TriggerRetrieve(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
trigger.SectionID, err = strconv.Atoi(r.FormValue("sectionID"))
|
trigger.SectionID, err = strconv.Atoi(r.FormValue("sectionID"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -614,7 +624,7 @@ func pageTriggerUpdate(w http.ResponseWriter, r *http.Request, _ *session.T) { /
|
|||||||
trigger.Datapoints = r.Form["datapoints[]"]
|
trigger.Datapoints = r.Form["datapoints[]"]
|
||||||
err = trigger.Update()
|
err = trigger.Update()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -625,14 +635,14 @@ func pageTriggerRun(w http.ResponseWriter, r *http.Request, _ *session.T) { // {
|
|||||||
idStr := r.PathValue("id")
|
idStr := r.PathValue("id")
|
||||||
id, err := strconv.Atoi(idStr)
|
id, err := strconv.Atoi(idStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var trigger Trigger
|
var trigger Trigger
|
||||||
trigger, err = TriggerRetrieve(id)
|
trigger, err = TriggerRetrieve(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -647,7 +657,7 @@ func pageTriggerRun(w http.ResponseWriter, r *http.Request, _ *session.T) { // {
|
|||||||
}
|
}
|
||||||
resp.Output, err = trigger.Run()
|
resp.Output, err = trigger.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
we.Wrap(err).Log()
|
werr.Wrap(err).Log()
|
||||||
httpError(w, err)
|
httpError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -660,7 +670,7 @@ func pageTriggerRun(w http.ResponseWriter, r *http.Request, _ *session.T) { // {
|
|||||||
func pageConfiguration(w http.ResponseWriter, _ *http.Request, _ *session.T) { // {{{
|
func pageConfiguration(w http.ResponseWriter, _ *http.Request, _ *session.T) { // {{{
|
||||||
areas, err := AreaRetrieve()
|
areas, err := AreaRetrieve()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
23
notification/factory.go
Normal file
23
notification/factory.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package notification
|
||||||
|
|
||||||
|
import (
|
||||||
|
// External
|
||||||
|
werr "git.gibonuddevalla.se/go/wrappederror"
|
||||||
|
|
||||||
|
// Standard
|
||||||
|
"log/slog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ServiceFactory(t string, config []byte, prio int, ackURL string, logger *slog.Logger) (Service, error) {
|
||||||
|
switch t {
|
||||||
|
case "NTFY":
|
||||||
|
ntfy, err := NewNTFY(config, prio, ackURL)
|
||||||
|
if err != nil {
|
||||||
|
err = werr.Wrap(err).WithData(config)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ntfy, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, werr.New("Unknown notification service, '%s'", t).WithCode("002-0000")
|
||||||
|
}
|
83
notification/ntfy.go
Normal file
83
notification/ntfy.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package notification
|
||||||
|
|
||||||
|
import (
|
||||||
|
// External
|
||||||
|
werr "git.gibonuddevalla.se/go/wrappederror"
|
||||||
|
|
||||||
|
// Standard
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NTFY struct {
|
||||||
|
URL string
|
||||||
|
Prio int
|
||||||
|
AcknowledgeURL string
|
||||||
|
logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNTFY(config []byte, prio int, ackURL string) (instance *NTFY, err error) {
|
||||||
|
instance = new(NTFY)
|
||||||
|
err = json.Unmarshal(config, &instance)
|
||||||
|
if err != nil {
|
||||||
|
err = werr.Wrap(err).WithCode("002-0001").WithData(config)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
instance.Prio = prio
|
||||||
|
instance.AcknowledgeURL = ackURL
|
||||||
|
return instance, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ntfy *NTFY) SetLogger(l *slog.Logger) {
|
||||||
|
ntfy.logger = l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ntfy *NTFY) GetType() string {
|
||||||
|
return "NTFY"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ntfy *NTFY) GetPrio() int {
|
||||||
|
return ntfy.Prio
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ntfy NTFY) Send(uuid string, msg []byte) (err error) {
|
||||||
|
var req *http.Request
|
||||||
|
var res *http.Response
|
||||||
|
req, err = http.NewRequest("POST", ntfy.URL, bytes.NewReader(msg))
|
||||||
|
if err != nil {
|
||||||
|
err = werr.Wrap(err).WithCode("002-0002").WithData(ntfy.URL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ackURL := fmt.Sprintf("http, OK, %s/notification/ack?uuid=%s", ntfy.AcknowledgeURL, uuid)
|
||||||
|
req.Header.Add("X-Actions", ackURL)
|
||||||
|
req.Header.Add("X-Priority", "3") // XXX: should be 5
|
||||||
|
req.Header.Add("X-Tags", "calendar")
|
||||||
|
|
||||||
|
res, err = http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
err = werr.Wrap(err).WithCode("002-0003")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
body, _ := io.ReadAll(res.Body)
|
||||||
|
if res.StatusCode != 200 {
|
||||||
|
err = werr.New("Invalid NTFY response").WithCode("002-0004").WithData(body)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ntfyResp := struct {
|
||||||
|
ID string
|
||||||
|
}{}
|
||||||
|
err = json.Unmarshal(body, &ntfyResp)
|
||||||
|
if err != nil {
|
||||||
|
err = werr.Wrap(err).WithCode("002-0005").WithData(body)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
61
notification/pkg.go
Normal file
61
notification/pkg.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package notification
|
||||||
|
|
||||||
|
import (
|
||||||
|
// External
|
||||||
|
werr "git.gibonuddevalla.se/go/wrappederror"
|
||||||
|
|
||||||
|
// Standard
|
||||||
|
"log/slog"
|
||||||
|
"slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service interface {
|
||||||
|
SetLogger(*slog.Logger)
|
||||||
|
GetPrio() int
|
||||||
|
GetType() string
|
||||||
|
Send(string, []byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Manager struct {
|
||||||
|
services []Service
|
||||||
|
logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewManager(logger *slog.Logger) (nm Manager) {
|
||||||
|
nm.services = []Service{}
|
||||||
|
nm.logger = logger
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nm *Manager) AddService(service Service) {
|
||||||
|
nm.services = append(nm.services, service)
|
||||||
|
slices.SortFunc(nm.services, func(a, b Service) int {
|
||||||
|
if a.GetPrio() < b.GetPrio() {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if a.GetPrio() > b.GetPrio() {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nm *Manager) Send(uuid string, msg []byte) (err error) {
|
||||||
|
for _, service := range nm.services {
|
||||||
|
nm.logger.Info("notification", "service", service.GetType(), "prio", service.GetPrio())
|
||||||
|
if err = service.Send(uuid, msg); err == nil {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
data := struct {
|
||||||
|
UUID string
|
||||||
|
Msg []byte
|
||||||
|
}{
|
||||||
|
uuid,
|
||||||
|
msg,
|
||||||
|
}
|
||||||
|
werr.Wrap(err).WithData(data).Log()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
80
notification_manager.go
Normal file
80
notification_manager.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
// External
|
||||||
|
werr "git.gibonuddevalla.se/go/wrappederror"
|
||||||
|
|
||||||
|
// Internal
|
||||||
|
"smon/notification"
|
||||||
|
|
||||||
|
// Standard
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DbNotificationService struct {
|
||||||
|
ID int
|
||||||
|
Service string
|
||||||
|
Configuration string
|
||||||
|
Prio int
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitNotificationManager() (err error) { // {{{
|
||||||
|
var dbServices []DbNotificationService
|
||||||
|
var row *sql.Row
|
||||||
|
|
||||||
|
row = service.Db.Conn.QueryRow(`
|
||||||
|
WITH services AS (
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
prio,
|
||||||
|
service,
|
||||||
|
configuration::varchar
|
||||||
|
FROM notification n
|
||||||
|
ORDER BY
|
||||||
|
prio ASC
|
||||||
|
)
|
||||||
|
SELECT COALESCE(jsonb_agg(s.*), '[]')
|
||||||
|
FROM services s
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
var dbData []byte
|
||||||
|
err = row.Scan(&dbData)
|
||||||
|
if err != nil {
|
||||||
|
err = werr.Wrap(err).WithCode("002-0006")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(dbData, &dbServices)
|
||||||
|
if err != nil {
|
||||||
|
err = werr.Wrap(err).WithCode("002-0007")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationManager = notification.NewManager(logger)
|
||||||
|
var service notification.Service
|
||||||
|
for _, dbService := range dbServices {
|
||||||
|
service, err = notification.ServiceFactory(
|
||||||
|
dbService.Service,
|
||||||
|
[]byte(dbService.Configuration),
|
||||||
|
dbService.Prio,
|
||||||
|
"blah",
|
||||||
|
//config.Application.NotificationBaseURL,
|
||||||
|
logger,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
err = werr.Wrap(err).WithData(dbService.Service)
|
||||||
|
}
|
||||||
|
notificationManager.AddService(service)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
} // }}}
|
||||||
|
|
||||||
|
func AcknowledgeNotification(uuid string) (err error) { // {{{
|
||||||
|
_, err = service.Db.Conn.Exec(`UPDATE schedule SET acknowledged=true WHERE schedule_uuid=$1`, uuid)
|
||||||
|
if err != nil {
|
||||||
|
err = werr.Wrap(err).WithData(uuid)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} // }}}
|
10
sql/00011.sql
Normal file
10
sql/00011.sql
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
CREATE TYPE notification_type AS ENUM('NTFY', 'PUSHOVER', 'HTTP', 'EMAIL');
|
||||||
|
|
||||||
|
CREATE TABLE public.notification (
|
||||||
|
id serial4 NOT NULL,
|
||||||
|
service notification_type DEFAULT 'NTFY' NOT NULL,
|
||||||
|
"configuration" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||||
|
prio int4 DEFAULT 0 NOT NULL,
|
||||||
|
CONSTRAINT notification_pk PRIMARY KEY (id),
|
||||||
|
CONSTRAINT notification_unique UNIQUE (prio)
|
||||||
|
);
|
Loading…
Reference in New Issue
Block a user