Switched to purer libraries

This commit is contained in:
Magnus Åhall 2026-08-19 17:53:52 +02:00
parent 8fa8bb4c3a
commit 9496947f01
21 changed files with 390 additions and 316 deletions

211
main.go
View file

@ -2,14 +2,14 @@ package main
import (
// External
ws "git.gibonuddevalla.se/go/webservice"
"git.gibonuddevalla.se/go/webservice/session"
werr "git.gibonuddevalla.se/go/wrappederror"
"git.ahall.se/go/html_template"
werr "git.ahall.se/go/wrappederror"
// Internal
"smon/notification"
// Standard
"context"
"embed"
"encoding/json"
"flag"
@ -36,12 +36,11 @@ var (
flagConfigFile string
flagDev bool
flagVersion bool
service *ws.Service
logFile *os.File
parsedTemplates map[string]*template.Template
componentFilenames []string
notificationManager notification.Manager
fileConf FileConfiguration
config Config
htmlEngine HTMLTemplate.Engine
//go:embed sql
sqlFS embed.FS
@ -61,8 +60,8 @@ func init() { // {{{
if err != nil {
logger.Error("application", "error", werr.Wrap(err))
}
cfgPath := path.Join(confDir, "smon.yaml")
flag.StringVar(&flagConfigFile, "config", cfgPath, "Path and filename of the YAML configuration file")
cfgPath := path.Join(confDir, "smon.json")
flag.StringVar(&flagConfigFile, "config", cfgPath, "Path and filename of the JSON configuration file")
flag.BoolVar(&flagDev, "dev", false, "reload templates on each request")
flag.BoolVar(&flagVersion, "version", false, "Display version and exit")
flag.Parse()
@ -81,84 +80,85 @@ func main() { // {{{
os.Exit(0)
}
logger.Info("application", "version", VERSION)
var err error
werr.Init()
werr.SetLogCallback(logHandler)
service, err = ws.New(flagConfigFile, VERSION, logger)
logger.Info("application", "config", flagConfigFile)
config, err = initConfig(flagConfigFile)
if err != nil {
logger.Error("application", "error", err)
logger.Error("application", "op", "read_config", "error", err)
os.Exit(1)
}
if config.NodataInterval < 10 {
logger.Error("application → nodata_interval has to be larger or equal to 10.")
os.Exit(1)
}
j, _ := json.Marshal(service.Config.Application)
json.Unmarshal(j, &fileConf)
logFile, err = os.OpenFile(fileConf.LogFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
err = initDb(
config.Database.Host,
config.Database.Port,
config.Database.Name,
config.Database.Username,
config.Database.Password,
)
if err != nil {
logger.Error("application", "error", err)
return
}
if fileConf.NodataInterval < 10 {
logger.Error("application → nodata_interval has to be larger or equal to 10.")
return
logger.Error("database", "error", err)
os.Exit(1)
}
service.SetDatabase(sqlProvider)
service.SetStaticFS(staticFS, "static")
service.SetStaticDirectory("static", flagDev)
service.InitDatabaseConnection()
err = service.Db.Connect()
htmlEngine, err = initHTMLTemplate()
if err != nil {
logger.Error("application", "error", err)
return
}
notificationManager, err = InitNotificationManager()
notificationManager, err = initNotificationManager()
if err != nil {
err = werr.Wrap(err).Log()
logger.Error("notification", "error", err)
}
service.Register("/", false, false, staticHandler)
http.HandleFunc("/", staticHandler)
service.Register("/area/new/{name}", false, false, actionAreaNew)
service.Register("/area/rename/{id}/{name}", false, false, actionAreaRename)
service.Register("/area/delete/{id}", false, false, actionAreaDelete)
http.HandleFunc("/area/new/{name}", actionAreaNew)
http.HandleFunc("/area/rename/{id}/{name}", actionAreaRename)
http.HandleFunc("/area/delete/{id}", actionAreaDelete)
service.Register("/section/new/{areaID}/{name}", false, false, actionSectionNew)
service.Register("/section/rename/{id}/{name}", false, false, actionSectionRename)
service.Register("/section/delete/{id}", false, false, actionSectionDelete)
http.HandleFunc("/section/new/{areaID}/{name}", actionSectionNew)
http.HandleFunc("/section/rename/{id}/{name}", actionSectionRename)
http.HandleFunc("/section/delete/{id}", actionSectionDelete)
service.Register("/problems", false, false, pageProblems)
service.Register("/problem/acknowledge/{id}", false, false, actionProblemAcknowledge)
service.Register("/problem/unacknowledge/{id}", false, false, actionProblemUnacknowledge)
http.HandleFunc("/problems", pageProblems)
http.HandleFunc("/problem/acknowledge/{id}", actionProblemAcknowledge)
http.HandleFunc("/problem/unacknowledge/{id}", actionProblemUnacknowledge)
service.Register("/datapoints", false, false, pageDatapoints)
service.Register("/datapoint/edit/{id}", false, false, pageDatapointEdit)
service.Register("/datapoint/update/{id}", false, false, actionDatapointUpdate)
service.Register("/datapoint/delete/{id}", false, false, actionDatapointDelete)
service.Register("/datapoint/values/{id}", false, false, pageDatapointValues)
service.Register("/datapoint/json/{id}", false, false, actionDatapointJson)
http.HandleFunc("/datapoints", pageDatapoints)
http.HandleFunc("/datapoint/edit/{id}", pageDatapointEdit)
http.HandleFunc("/datapoint/update/{id}", actionDatapointUpdate)
http.HandleFunc("/datapoint/delete/{id}", actionDatapointDelete)
http.HandleFunc("/datapoint/values/{id}", pageDatapointValues)
http.HandleFunc("/datapoint/json/{id}", actionDatapointJson)
service.Register("/triggers", false, false, pageTriggers)
service.Register("/trigger/create/{sectionID}/{name}", false, false, actionTriggerCreate)
service.Register("/trigger/edit/{id}", false, false, pageTriggerEdit)
service.Register("/trigger/edit/{id}/{sectionID}", false, false, pageTriggerEdit)
service.Register("/trigger/addDatapoint/{id}/{datapointName}", false, false, actionTriggerDatapointAdd)
service.Register("/trigger/update/{id}", false, false, actionTriggerUpdate)
service.Register("/trigger/run/{id}", false, false, actionTriggerRun)
service.Register("/trigger/delete/{id}", false, false, actionTriggerDelete)
http.HandleFunc("/triggers", pageTriggers)
http.HandleFunc("/trigger/create/{sectionID}/{name}", actionTriggerCreate)
http.HandleFunc("/trigger/edit/{id}", pageTriggerEdit)
http.HandleFunc("/trigger/edit/{id}/{sectionID}", pageTriggerEdit)
http.HandleFunc("/trigger/addDatapoint/{id}/{datapointName}", actionTriggerDatapointAdd)
http.HandleFunc("/trigger/update/{id}", actionTriggerUpdate)
http.HandleFunc("/trigger/run/{id}", actionTriggerRun)
http.HandleFunc("/trigger/delete/{id}", actionTriggerDelete)
service.Register("/notifications", false, false, pageNotifications)
http.HandleFunc("/notifications", pageNotifications)
service.Register("/configuration", false, false, pageConfiguration)
service.Register("/configuration/theme", false, false, actionConfigurationTheme)
service.Register("/configuration/timezone", false, false, actionConfigurationTimezone)
service.Register("/configuration/notification", false, false, pageConfigurationNotification)
service.Register("/configuration/notification/update/{prio}", false, false, actionConfigurationNotificationUpdate)
service.Register("/configuration/notification/delete/{prio}", false, false, actionConfigurationNotificationDelete)
service.Register("/entry/{datapoint}", false, false, actionEntryDatapoint)
http.HandleFunc("/configuration", pageConfiguration)
http.HandleFunc("/configuration/theme", actionConfigurationTheme)
http.HandleFunc("/configuration/timezone", actionConfigurationTimezone)
http.HandleFunc("/configuration/notification", pageConfigurationNotification)
http.HandleFunc("/configuration/notification/update/{prio}", actionConfigurationNotificationUpdate)
http.HandleFunc("/configuration/notification/delete/{prio}", actionConfigurationNotificationDelete)
http.HandleFunc("/entry/{datapoint}", actionEntryDatapoint)
go nodataLoop()
@ -174,7 +174,9 @@ func main() { // {{{
os.Exit(1)
}
err = service.Start()
listen := fmt.Sprintf("%s:%d", config.Network.Address, config.Network.Port)
logger.Info("webserver", "listen", listen)
err = http.ListenAndServe(listen, nil)
if err != nil {
logger.Error("webserver", "error", werr.Wrap(err))
os.Exit(1)
@ -191,10 +193,8 @@ func sqlProvider(dbname string, version int) (sql []byte, found bool) { // {{{
found = true
return
} // }}}
func logHandler(err werr.Error) { // {{{
j, _ := json.Marshal(err)
logFile.Write(j)
logFile.Write([]byte("\n"))
func initHTMLTemplate() (HTMLTemplate.Engine, error) { // {{{
return HTMLTemplate.NewEngine(viewFS, staticFS, flagDev)
} // }}}
func httpError(w http.ResponseWriter, err error) { // {{{
@ -223,19 +223,19 @@ func pageError(w http.ResponseWriter, redirectURL string, pageErr error) { // {{
w.WriteHeader(302)
} // }}}
func staticHandler(w http.ResponseWriter, r *http.Request, sess *session.T) { // {{{
func staticHandler(w http.ResponseWriter, r *http.Request) { // {{{
if flagDev && !reloadTemplates(w) {
return
}
if r.URL.Path == "/" {
pageIndex(w, r, sess)
pageIndex(w, r)
return
}
service.StaticHandler(w, r, sess)
htmlEngine.StaticResource(w, r)
} // }}}
func actionEntryDatapoint(w http.ResponseWriter, r *http.Request, sess *session.T) { // {{{
func actionEntryDatapoint(w http.ResponseWriter, r *http.Request) { // {{{
dpoint := r.PathValue("datapoint")
value, _ := io.ReadAll(r.Body)
@ -307,15 +307,16 @@ func actionEntryDatapoint(w http.ResponseWriter, r *http.Request, sess *session.
} else {
errBody = nil
}
_, err = service.Db.Conn.Exec(
_, err = db.Exec(
context.Background(),
`
INSERT INTO notification_send(notification_id, problem_id, uuid, ok, error)
SELECT
id, $3, '', $4, $5
FROM notification
WHERE
service=$1 AND
prio=$2
INSERT INTO notification_send(notification_id, problem_id, uuid, ok, error)
SELECT
id, $3, '', $4, $5
FROM notification
WHERE
service=$1 AND
prio=$2
`,
(*notificationService).GetType(),
(*notificationService).GetPrio(),
@ -398,7 +399,7 @@ func getPage(layout, page string) (tmpl *template.Template, err error) { // {{{
return
} // }}}
func pageIndex(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func pageIndex(w http.ResponseWriter, r *http.Request) { // {{{
page := Page{
LAYOUT: "main",
PAGE: "index",
@ -407,7 +408,7 @@ func pageIndex(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
page.Render(w, r)
} // }}}
func actionAreaNew(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func actionAreaNew(w http.ResponseWriter, r *http.Request) { // {{{
name := r.PathValue("name")
err := AreaCreate(name)
if err != nil {
@ -419,7 +420,7 @@ func actionAreaNew(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{
w.WriteHeader(302)
return
} // }}}
func actionAreaRename(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func actionAreaRename(w http.ResponseWriter, r *http.Request) { // {{{
idStr := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
@ -438,7 +439,7 @@ func actionAreaRename(w http.ResponseWriter, r *http.Request, _ *session.T) { //
w.WriteHeader(302)
return
} // }}}
func actionAreaDelete(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func actionAreaDelete(w http.ResponseWriter, r *http.Request) { // {{{
idStr := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
@ -457,7 +458,7 @@ func actionAreaDelete(w http.ResponseWriter, r *http.Request, _ *session.T) { //
return
} // }}}
func actionSectionNew(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func actionSectionNew(w http.ResponseWriter, r *http.Request) { // {{{
idStr := r.PathValue("areaID")
areaID, err := strconv.Atoi(idStr)
if err != nil {
@ -476,7 +477,7 @@ func actionSectionNew(w http.ResponseWriter, r *http.Request, _ *session.T) { //
w.WriteHeader(302)
return
} // }}}
func actionSectionRename(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func actionSectionRename(w http.ResponseWriter, r *http.Request) { // {{{
idStr := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
@ -495,7 +496,7 @@ func actionSectionRename(w http.ResponseWriter, r *http.Request, _ *session.T) {
w.WriteHeader(302)
return
} // }}}
func actionSectionDelete(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func actionSectionDelete(w http.ResponseWriter, r *http.Request) { // {{{
idStr := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
@ -514,7 +515,7 @@ func actionSectionDelete(w http.ResponseWriter, r *http.Request, _ *session.T) {
return
} // }}}
func pageProblems(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func pageProblems(w http.ResponseWriter, r *http.Request) { // {{{
page := Page{
LAYOUT: "main",
PAGE: "problems",
@ -578,7 +579,7 @@ func pageProblems(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
page.Render(w, r)
return
} // }}}
func actionProblemAcknowledge(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func actionProblemAcknowledge(w http.ResponseWriter, r *http.Request) { // {{{
idStr := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
@ -597,7 +598,7 @@ func actionProblemAcknowledge(w http.ResponseWriter, r *http.Request, _ *session
return
} // }}}
func actionProblemUnacknowledge(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func actionProblemUnacknowledge(w http.ResponseWriter, r *http.Request) { // {{{
idStr := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
@ -617,7 +618,7 @@ func actionProblemUnacknowledge(w http.ResponseWriter, r *http.Request, _ *sessi
return
} // }}}
func pageDatapoints(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func pageDatapoints(w http.ResponseWriter, r *http.Request) { // {{{
page := Page{
LAYOUT: "main",
PAGE: "datapoints",
@ -643,7 +644,7 @@ func pageDatapoints(w http.ResponseWriter, r *http.Request, _ *session.T) { // {
page.Render(w, r)
return
} // }}}
func pageDatapointEdit(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func pageDatapointEdit(w http.ResponseWriter, r *http.Request) { // {{{
idStr := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
@ -700,7 +701,7 @@ func pageDatapointEdit(w http.ResponseWriter, r *http.Request, _ *session.T) { /
page.Render(w, r)
return
} // }}}
func actionDatapointUpdate(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func actionDatapointUpdate(w http.ResponseWriter, r *http.Request) { // {{{
idStr := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
@ -757,7 +758,7 @@ func actionDatapointUpdate(w http.ResponseWriter, r *http.Request, _ *session.T)
w.Header().Add("Location", "/datapoints")
w.WriteHeader(302)
} // }}}
func actionDatapointDelete(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func actionDatapointDelete(w http.ResponseWriter, r *http.Request) { // {{{
idStr := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
@ -774,7 +775,7 @@ func actionDatapointDelete(w http.ResponseWriter, r *http.Request, _ *session.T)
w.Header().Add("Location", "/datapoints")
w.WriteHeader(302)
} // }}}
func pageDatapointValues(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func pageDatapointValues(w http.ResponseWriter, r *http.Request) { // {{{
idStr := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
@ -833,7 +834,7 @@ func pageDatapointValues(w http.ResponseWriter, r *http.Request, _ *session.T) {
page.Render(w, r)
return
} // }}}
func actionDatapointJson(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func actionDatapointJson(w http.ResponseWriter, r *http.Request) { // {{{
idStr := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
@ -865,7 +866,7 @@ func actionDatapointJson(w http.ResponseWriter, r *http.Request, _ *session.T) {
w.Write(j)
} // }}}
func pageTriggers(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func pageTriggers(w http.ResponseWriter, r *http.Request) { // {{{
areas, err := TriggersRetrieve()
if err != nil {
httpError(w, werr.Wrap(err).Log())
@ -887,7 +888,7 @@ func pageTriggers(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
page.Render(w, r)
} // }}}
func actionTriggerCreate(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func actionTriggerCreate(w http.ResponseWriter, r *http.Request) { // {{{
name := r.PathValue("name")
sectionIDStr := r.PathValue("sectionID")
sectionID, err := strconv.Atoi(sectionIDStr)
@ -919,7 +920,7 @@ func actionTriggerCreate(w http.ResponseWriter, r *http.Request, _ *session.T) {
w.Header().Add("Content-Type", "application/json")
w.Write(j)
} // }}}
func pageTriggerEdit(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func pageTriggerEdit(w http.ResponseWriter, r *http.Request) { // {{{
idStr := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
@ -975,7 +976,7 @@ func pageTriggerEdit(w http.ResponseWriter, r *http.Request, _ *session.T) { //
page.Render(w, r)
} // }}}
func actionTriggerDatapointAdd(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func actionTriggerDatapointAdd(w http.ResponseWriter, r *http.Request) { // {{{
triggerID := r.PathValue("id")
dpName := r.PathValue("datapointName")
@ -1022,7 +1023,7 @@ func actionTriggerDatapointAdd(w http.ResponseWriter, r *http.Request, _ *sessio
w.Header().Add("Content-Type", "application/json")
w.Write(j)
} // }}}
func actionTriggerUpdate(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func actionTriggerUpdate(w http.ResponseWriter, r *http.Request) { // {{{
idStr := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
@ -1057,7 +1058,7 @@ func actionTriggerUpdate(w http.ResponseWriter, r *http.Request, _ *session.T) {
w.Header().Add("Location", "/triggers")
w.WriteHeader(302)
} // }}}
func actionTriggerRun(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func actionTriggerRun(w http.ResponseWriter, r *http.Request) { // {{{
idStr := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
@ -1092,7 +1093,7 @@ func actionTriggerRun(w http.ResponseWriter, r *http.Request, _ *session.T) { //
w.Header().Add("Content-Type", "application/json")
w.Write(j)
} // }}}
func actionTriggerDelete(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func actionTriggerDelete(w http.ResponseWriter, r *http.Request) { // {{{
idStr := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
@ -1110,7 +1111,7 @@ func actionTriggerDelete(w http.ResponseWriter, r *http.Request, _ *session.T) {
w.WriteHeader(302)
} // }}}
func pageConfiguration(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func pageConfiguration(w http.ResponseWriter, r *http.Request) { // {{{
areas, err := AreaRetrieve()
if err != nil {
httpError(w, werr.Wrap(err).Log())
@ -1134,7 +1135,7 @@ func pageConfiguration(w http.ResponseWriter, r *http.Request, _ *session.T) { /
page.Render(w, r)
} // }}}
func actionConfigurationTheme(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func actionConfigurationTheme(w http.ResponseWriter, r *http.Request) { // {{{
theme := r.FormValue("theme")
err := smonConfig.SetTheme(theme)
if err != nil {
@ -1145,7 +1146,7 @@ func actionConfigurationTheme(w http.ResponseWriter, r *http.Request, _ *session
w.Header().Add("Location", "/configuration")
w.WriteHeader(302)
} // }}}
func actionConfigurationTimezone(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func actionConfigurationTimezone(w http.ResponseWriter, r *http.Request) { // {{{
timezone := r.FormValue("timezone")
_, err := time.LoadLocation(timezone)
@ -1163,7 +1164,7 @@ func actionConfigurationTimezone(w http.ResponseWriter, r *http.Request, _ *sess
w.Header().Add("Location", "/configuration")
w.WriteHeader(302)
} // }}}
func pageConfigurationNotification(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func pageConfigurationNotification(w http.ResponseWriter, r *http.Request) { // {{{
// This function is either receiving a type when creating a new service,
// or a prio when editing an existing.
notificationType := r.URL.Query().Get("type")
@ -1215,7 +1216,7 @@ func pageConfigurationNotification(w http.ResponseWriter, r *http.Request, _ *se
page.Render(w, r)
} // }}}
func actionConfigurationNotificationUpdate(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func actionConfigurationNotificationUpdate(w http.ResponseWriter, r *http.Request) { // {{{
prioStr := r.PathValue("prio")
prio, err := strconv.Atoi(prioStr)
if err != nil {
@ -1268,7 +1269,7 @@ func actionConfigurationNotificationUpdate(w http.ResponseWriter, r *http.Reques
w.Header().Add("Location", "/configuration")
w.WriteHeader(302)
} // }}}
func actionConfigurationNotificationDelete(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func actionConfigurationNotificationDelete(w http.ResponseWriter, r *http.Request) { // {{{
prioStr := r.PathValue("prio")
prio, err := strconv.Atoi(prioStr)
if err != nil {
@ -1283,7 +1284,7 @@ func actionConfigurationNotificationDelete(w http.ResponseWriter, r *http.Reques
w.WriteHeader(302)
} // }}}
func pageNotifications(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
func pageNotifications(w http.ResponseWriter, r *http.Request) { // {{{
var err error
// Manage the values from the timefilter component