Compare commits

..

No commits in common. "b7595b4c7b2a50ee44536d98f27c8dd9b1b7c936" and "8fa8bb4c3adbf677953766960d1a16d5a42e0b6a" have entirely different histories.

22 changed files with 315 additions and 432 deletions

33
area.go
View file

@ -2,11 +2,10 @@ package main
import ( import (
// External // External
werr "git.ahall.se/go/wrappederror" werr "git.gibonuddevalla.se/go/wrappederror"
"github.com/jackc/pgx/v5"
// Standard // Standard
"context" "database/sql"
"encoding/json" "encoding/json"
"sort" "sort"
) )
@ -20,7 +19,7 @@ type Area struct {
func AreaRetrieve() (areas []Area, err error) { // {{{ func AreaRetrieve() (areas []Area, err error) { // {{{
areas = []Area{} areas = []Area{}
row := db.QueryRow(context.Background(), ` row := service.Db.Conn.QueryRow(`
SELECT SELECT
jsonb_agg(jsonsections) jsonb_agg(jsonsections)
FROM ( FROM (
@ -60,23 +59,21 @@ func AreaRetrieve() (areas []Area, err error) { // {{{
return return
} // }}} } // }}}
func AreaCreate(name string) (err error) { // {{{ func AreaCreate(name string) (err error) { // {{{
_, err = db.Exec(context.Background(), `INSERT INTO area(name) VALUES($1)`, name) _, err = service.Db.Conn.Exec(`INSERT INTO area(name) VALUES($1)`, name)
return return
} // }}} } // }}}
func AreaRename(id int, name string) (err error) { // {{{ func AreaRename(id int, name string) (err error) { // {{{
_, err = db.Exec(context.Background(), `UPDATE area SET name=$2 WHERE id=$1`, id, name) _, err = service.Db.Conn.Exec(`UPDATE area SET name=$2 WHERE id=$1`, id, name)
return return
} // }}} } // }}}
func AreaDelete(id int) (err error) { // {{{ func AreaDelete(id int) (err error) { // {{{
ctx := context.Background() var trx *sql.Tx
trx, err = service.Db.Conn.Begin()
var trx pgx.Tx
trx, err = db.Begin(ctx)
if err != nil { if err != nil {
err = werr.Wrap(err).WithData(id) err = werr.Wrap(err).WithData(id)
} }
_, err = trx.Exec(context.Background(), ` _, err = trx.Exec(`
DELETE DELETE
FROM trigger t FROM trigger t
USING section s USING section s
@ -87,34 +84,34 @@ func AreaDelete(id int) (err error) { // {{{
id, id,
) )
if err != nil { if err != nil {
err2 := trx.Rollback(ctx) err2 := trx.Rollback()
if err2 != nil { if err2 != nil {
return werr.Wrap(err2).WithData(err) return werr.Wrap(err2).WithData(err)
} }
return werr.Wrap(err).WithData(id) return werr.Wrap(err).WithData(id)
} }
_, err = trx.Exec(ctx, `DELETE FROM public.section WHERE area_id = $1`, id) _, err = trx.Exec(`DELETE FROM public.section WHERE area_id = $1`, id)
if err != nil { if err != nil {
err2 := trx.Rollback(ctx) err2 := trx.Rollback()
if err2 != nil { if err2 != nil {
return werr.Wrap(err2).WithData(err) return werr.Wrap(err2).WithData(err)
} }
return werr.Wrap(err).WithData(id) return werr.Wrap(err).WithData(id)
} }
_, err = trx.Exec(ctx, `DELETE FROM public.area WHERE id = $1`, id) _, err = trx.Exec(`DELETE FROM public.area WHERE id = $1`, id)
if err != nil { if err != nil {
err2 := trx.Rollback(ctx) err2 := trx.Rollback()
if err2 != nil { if err2 != nil {
return werr.Wrap(err2).WithData(err) return werr.Wrap(err2).WithData(err)
} }
return werr.Wrap(err).WithData(id) return werr.Wrap(err).WithData(id)
} }
err = trx.Commit(ctx) err = trx.Commit()
if err != nil { if err != nil {
err2 := trx.Rollback(ctx) err2 := trx.Rollback()
if err2 != nil { if err2 != nil {
return werr.Wrap(err2).WithData(err) return werr.Wrap(err2).WithData(err)
} }

View file

@ -1,42 +1,6 @@
package main package main
import ( type FileConfiguration struct {
// Standard LogFile string
"encoding/json"
"os"
)
type DatabaseDetails struct {
Host string
Port int
Name string
Username string
Password string
}
type Config struct {
Network struct {
Address string
Port int
}
Websocket struct {
Domains []string
}
Database DatabaseDetails
NodataInterval int `json:"nodata_interval"` // in seconds NodataInterval int `json:"nodata_interval"` // in seconds
} }
func initConfig(fname string) (cfg Config, err error) {// {{{
var data []byte
data, err = os.ReadFile(fname)
if err != nil {
return
}
err = json.Unmarshal(data, &cfg)
return
}// }}}

View file

@ -2,11 +2,10 @@ package main
import ( import (
// External // External
werr "git.ahall.se/go/wrappederror" werr "git.gibonuddevalla.se/go/wrappederror"
"github.com/jackc/pgx/v5"
// Standard // Standard
"context" "database/sql"
"time" "time"
) )
@ -20,8 +19,8 @@ var smonConfig Configuration
func SmonConfigInit() (cfg Configuration, err error) { func SmonConfigInit() (cfg Configuration, err error) {
cfg.Settings = make(map[string]string, 8) cfg.Settings = make(map[string]string, 8)
var rows pgx.Rows var rows *sql.Rows
rows, err = db.Query(context.Background(), `SELECT * FROM public.configuration`) rows, err = service.Db.Conn.Query(`SELECT * FROM public.configuration`)
if err != nil { if err != nil {
err = werr.Wrap(err) err = werr.Wrap(err)
return return
@ -64,7 +63,7 @@ func (cfg *Configuration) Timezone() *time.Location {
func (cfg *Configuration) SetTheme(theme string) (err error) { func (cfg *Configuration) SetTheme(theme string) (err error) {
cfg.Settings["THEME"] = theme cfg.Settings["THEME"] = theme
_, err = db.Exec(context.Background(), `UPDATE public.configuration SET value=$1 WHERE setting='THEME'`, theme) _, err = service.Db.Conn.Exec(`UPDATE public.configuration SET value=$1 WHERE setting='THEME'`, theme)
return return
} }
@ -75,7 +74,7 @@ func (cfg *Configuration) SetTimezone(tz string) (err error) {
return werr.Wrap(err).WithData(tz) return werr.Wrap(err).WithData(tz)
} }
_, err = db.Exec(context.Background(), `UPDATE public.configuration SET value=$1 WHERE setting='TIMEZONE'`, tz) _, err = service.Db.Conn.Exec(`UPDATE public.configuration SET value=$1 WHERE setting='TIMEZONE'`, tz)
if err != nil { if err != nil {
return werr.Wrap(err).WithData(tz) return werr.Wrap(err).WithData(tz)
} }

View file

@ -2,11 +2,10 @@ package main
import ( import (
// External // External
werr "git.ahall.se/go/wrappederror" werr "git.gibonuddevalla.se/go/wrappederror"
"github.com/jackc/pgx/v5" "github.com/jmoiron/sqlx"
// Standard // Standard
"context"
"database/sql" "database/sql"
"errors" "errors"
"strings" "strings"
@ -74,8 +73,7 @@ func (dp Datapoint) Update() (err error) { // {{{
} }
if dp.ID == 0 { if dp.ID == 0 {
_, err = db.Exec( _, err = service.Db.Conn.Exec(
context.Background(),
`INSERT INTO datapoint("group", name, datatype, nodata_problem_seconds, comment) VALUES($1, $2, $3, $4, $5)`, `INSERT INTO datapoint("group", name, datatype, nodata_problem_seconds, comment) VALUES($1, $2, $3, $4, $5)`,
dp.Group, dp.Group,
name, name,
@ -87,8 +85,7 @@ func (dp Datapoint) Update() (err error) { // {{{
/* Keep nodata_is_problem as is unless the nodata_problem_seconds is changed. /* Keep nodata_is_problem as is unless the nodata_problem_seconds is changed.
* Otherwise unnecessary nodata problems could be notified when updating unrelated * Otherwise unnecessary nodata problems could be notified when updating unrelated
* datapoint properties. */ * datapoint properties. */
_, err = db.Exec( _, err = service.Db.Conn.Exec(
context.Background(),
` `
UPDATE datapoint UPDATE datapoint
SET SET
@ -128,7 +125,7 @@ func DatapointAdd[T any](name string, value T) (err error) { // {{{
value any value any
} }
row := db.QueryRow(context.Background(), `SELECT id, datatype FROM datapoint WHERE name=$1`, name) row := service.Db.Conn.QueryRow(`SELECT id, datatype FROM datapoint WHERE name=$1`, name)
var dpID int var dpID int
var dpType DatapointType var dpType DatapointType
@ -143,9 +140,9 @@ func DatapointAdd[T any](name string, value T) (err error) { // {{{
switch dpType { switch dpType {
case INT: case INT:
_, err = db.Exec(context.Background(), `INSERT INTO datapoint_value(datapoint_id, value_int) VALUES($1, $2)`, dpID, value) _, err = service.Db.Conn.Exec(`INSERT INTO datapoint_value(datapoint_id, value_int) VALUES($1, $2)`, dpID, value)
case STRING: case STRING:
_, err = db.Exec(context.Background(), `INSERT INTO datapoint_value(datapoint_id, value_string) VALUES($1, $2)`, dpID, value) _, err = service.Db.Conn.Exec(`INSERT INTO datapoint_value(datapoint_id, value_string) VALUES($1, $2)`, dpID, value)
case DATETIME: case DATETIME:
// Time value is required to be a RFC 3339 formatted time string // Time value is required to be a RFC 3339 formatted time string
var t time.Time var t time.Time
@ -158,7 +155,7 @@ func DatapointAdd[T any](name string, value T) (err error) { // {{{
return werr.Wrap(err).WithData(dpRequest{dpID, value}).Log() return werr.Wrap(err).WithData(dpRequest{dpID, value}).Log()
} }
_, err = db.Exec(context.Background(), `INSERT INTO datapoint_value(datapoint_id, value_datetime) VALUES($1, $2)`, dpID, t) _, err = service.Db.Conn.Exec(`INSERT INTO datapoint_value(datapoint_id, value_datetime) VALUES($1, $2)`, dpID, t)
} }
if err != nil { if err != nil {
err = werr.Wrap(err).WithData(dpRequest{dpID, value}) err = werr.Wrap(err).WithData(dpRequest{dpID, value})
@ -170,10 +167,8 @@ func DatapointAdd[T any](name string, value T) (err error) { // {{{
func DatapointsRetrieve() (dps []Datapoint, err error) { // {{{ func DatapointsRetrieve() (dps []Datapoint, err error) { // {{{
dps = []Datapoint{} dps = []Datapoint{}
var rows pgx.Rows var rows *sqlx.Rows
rows, err = db.Query( rows, err = service.Db.Conn.Queryx(`
context.Background(),
`
SELECT SELECT
id, name, datatype, last_value, "group", comment, nodata_problem_seconds, id, name, datatype, last_value, "group", comment, nodata_problem_seconds,
last_value_id AS v_id, last_value_id AS v_id,
@ -210,12 +205,11 @@ func DatapointsRetrieve() (dps []Datapoint, err error) { // {{{
ValueDateTime sql.NullTime `db:"value_datetime"` ValueDateTime sql.NullTime `db:"value_datetime"`
} }
dps, err = pgx.CollectRows( for rows.Next() {
rows, dp := Datapoint{}
func(row pgx.CollectableRow) (dp Datapoint, err error) { dpv := DatapointValue{}
var dpv DatapointValue res := DbRes{}
var res DbRes err = rows.StructScan(&res)
res, err = pgx.RowToStructByName[DbRes](row)
if err != nil { if err != nil {
err = werr.Wrap(err) err = werr.Wrap(err)
return return
@ -239,14 +233,9 @@ func DatapointsRetrieve() (dps []Datapoint, err error) { // {{{
dp.LastDatapointValue = dpv dp.LastDatapointValue = dpv
} }
return
})
if err != nil { dps = append(dps, dp)
err = werr.Wrap(err)
return
} }
return return
} // }}} } // }}}
func DatapointRetrieve(id int, name string) (dp Datapoint, err error) { // {{{ func DatapointRetrieve(id int, name string) (dp Datapoint, err error) { // {{{
@ -261,12 +250,8 @@ func DatapointRetrieve(id int, name string) (dp Datapoint, err error) { // {{{
param = name param = name
} }
rows, _ := db.Query(context.Background(), query, param) row := service.Db.Conn.QueryRowx(query, param)
dp, err = pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Datapoint]) err = row.StructScan(&dp)
if err != nil {
err = werr.Wrap(err)
return
}
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
dp = Datapoint{ dp = Datapoint{
@ -281,9 +266,7 @@ func DatapointRetrieve(id int, name string) (dp Datapoint, err error) { // {{{
return return
} }
rows, _ = db.Query( row = service.Db.Conn.QueryRowx(`
context.Background(),
`
SELECT * SELECT *
FROM datapoint_value FROM datapoint_value
WHERE datapoint_id = $1 WHERE datapoint_id = $1
@ -292,7 +275,7 @@ func DatapointRetrieve(id int, name string) (dp Datapoint, err error) { // {{{
`, `,
dp.ID, dp.ID,
) )
dp, err = pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Datapoint]) err = row.StructScan(&dp.LastDatapointValue)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
err = nil err = nil
return return
@ -307,41 +290,50 @@ func DatapointRetrieve(id int, name string) (dp Datapoint, err error) { // {{{
} // }}} } // }}}
func DatapointDelete(id int) (err error) { // {{{ func DatapointDelete(id int) (err error) { // {{{
var dpName string var dpName string
err = db.QueryRow(context.Background(), `SELECT name FROM public.datapoint WHERE id = $1`, id).Scan(&dpName) row := service.Db.Conn.QueryRow(`SELECT name FROM public.datapoint WHERE id = $1`, id)
err = row.Scan(&dpName)
if err != nil { if err != nil {
err = werr.Wrap(err).WithData(id) err = werr.Wrap(err).WithData(id)
return return
} }
var triggerNames []string var rows *sql.Rows
rows, _ := db.Query(context.Background(), `SELECT name FROM public.trigger WHERE datapoints ? $1`, dpName) rows, err = service.Db.Conn.Query(`SELECT name FROM public.trigger WHERE datapoints ? $1`, dpName)
triggerNames, err = pgx.CollectRows(rows, pgx.RowTo[string])
if err != nil { if err != nil {
err = werr.Wrap(err).WithData(id) err = werr.Wrap(err).WithData(dpName)
return return
} }
defer rows.Close()
var triggerNames []string
var name string
for rows.Next() {
err = rows.Scan(&name)
if err != nil {
err = werr.Wrap(err)
return
}
triggerNames = append(triggerNames, name)
}
if len(triggerNames) > 0 { if len(triggerNames) > 0 {
return werr.New("Datapoint '%s' used in the following triggers: %s", dpName, strings.Join(triggerNames, ", ")) return werr.New("Datapoint '%s' used in the following triggers: %s", dpName, strings.Join(triggerNames, ", "))
} }
_, err = db.Exec(context.Background(), `DELETE FROM datapoint WHERE id=$1`, id) _, err = service.Db.Conn.Exec(`DELETE FROM datapoint WHERE id=$1`, id)
if err != nil { if err != nil {
err = werr.Wrap(err).WithData(id) err = werr.Wrap(err).WithData(id)
return
} }
return return
} // }}} } // }}}
func DatapointValues(id int, from, to time.Time) (values []DatapointValue, err error) { // {{{ func DatapointValues(id int, from, to time.Time) (values []DatapointValue, err error) { // {{{
_, err = db.Exec(context.Background(), `SELECT set_config('timezone', $1, false)`, smonConfig.Timezone().String()) _, err = service.Db.Conn.Exec(`SELECT set_config('timezone', $1, false)`, smonConfig.Timezone().String())
if err != nil { if err != nil {
err = werr.Wrap(err).WithData(smonConfig.Timezone().String()) err = werr.Wrap(err).WithData(smonConfig.Timezone().String())
return return
} }
rows, _ := db.Query( rows, err := service.Db.Conn.Queryx(
context.Background(),
` `
SELECT SELECT
id, id,
@ -362,11 +354,20 @@ func DatapointValues(id int, from, to time.Time) (values []DatapointValue, err e
from, from,
to, to,
) )
values, err = pgx.CollectRows(rows, pgx.RowToStructByName[DatapointValue])
if err != nil { if err != nil {
err = werr.Wrap(err).WithData(id) err = werr.Wrap(err).WithData(id)
return return
} }
defer rows.Close()
for rows.Next() {
dpv := DatapointValue{}
err = rows.StructScan(&dpv)
if err != nil {
err = werr.Wrap(err).WithData(id)
return
}
values = append(values, dpv)
}
return return
} // }}} } // }}}

43
db.go
View file

@ -1,43 +0,0 @@
package main
import (
// External
"github.com/jackc/pgx/v5/pgxpool"
// Standard
"context"
"fmt"
)
var (
db *pgxpool.Pool
)
func initDb(host string, port int, name, username, password string) (err error) {
// The main database connection is configured.
connString := fmt.Sprintf(
"host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
host,
port,
username,
password,
name,
)
db, err = pgxpool.New(context.Background(), connString)
// The schema upgrader keeps track of the SQL schema version and
// runs SQL scripts to upgrade it if necessary.
/*
upgrader := dbschema.NewUpgrader("_jsonstore")
upgrader.SetLogCallback(sqlLogCallback)
upgrader.SetSqlCallback(sqlSourceCallback)
_, err = upgrader.AddDatabaseInstance(db, config.Database.Db)
if err != nil {
return
}
err = upgrader.Run()
*/
return
}

19
go.mod
View file

@ -1,18 +1,19 @@
module smon module smon
go 1.25.0 go 1.22.0
require ( require (
git.ahall.se/go/html_template v0.1.1 git.gibonuddevalla.se/go/webservice v0.2.16
git.ahall.se/go/wrappederror v1.0.0 git.gibonuddevalla.se/go/wrappederror v0.3.4
github.com/expr-lang/expr v1.16.5 github.com/expr-lang/expr v1.16.5
github.com/jackc/pgx/v5 v5.9.1 github.com/jmoiron/sqlx v1.3.5
github.com/lib/pq v1.10.9
) )
require ( require (
github.com/jackc/pgpassfile v1.0.0 // indirect git.gibonuddevalla.se/go/dbschema v1.3.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/google/uuid v1.5.0 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/gorilla/websocket v1.5.1 // indirect
golang.org/x/sync v0.17.0 // indirect golang.org/x/net v0.17.0 // indirect
golang.org/x/text v0.29.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

47
go.sum
View file

@ -1,32 +1,33 @@
git.ahall.se/go/html_template v0.1.1 h1:GrSNvs/ZAWKkuxQ4OEOTqHBTtYnx8C88JBxVERDxv18= git.gibonuddevalla.se/go/dbschema v1.3.0 h1:HzFMR29tWfy/ibIjltTbIMI4inVktj/rh8bESALibgM=
git.ahall.se/go/html_template v0.1.1/go.mod h1:bBpM0K1HXZadhZg4VeT39IgyGwwhWoZet99nw8QeZKg= git.gibonuddevalla.se/go/dbschema v1.3.0/go.mod h1:BNw3q/574nXbGoeWyK+tLhRfggVkw2j2aXZzrBKC3ig=
git.ahall.se/go/wrappederror v1.0.0 h1:TxiAUTZUnmkVpaPKQjhTGSFsvc0qLkULsIj4Rwh4UsY= git.gibonuddevalla.se/go/webservice v0.2.15 h1:ECe63fRDSrg3RJcgYV2pG+WsAQLVG8wvfHennz7aHsY=
git.ahall.se/go/wrappederror v1.0.0/go.mod h1:trMBDaw5SdUrsZu3MCh1q+INsrmB4T9L+3DuV7JM+c8= git.gibonuddevalla.se/go/webservice v0.2.15/go.mod h1:3uBS6nLbK9qbuGzDls8MZD5Xr9ORY1Srbj6v06BIhws=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= git.gibonuddevalla.se/go/wrappederror v0.3.4 h1:dcKp9/+QrZSO3S4fVnq7yG2p7DUZVmlztBAb/OzoZNY=
git.gibonuddevalla.se/go/wrappederror v0.3.4/go.mod h1:j4w320Hk1wvhOPjUaK4GgLvmtnjUUM5yVu6JFO1OCSc=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/expr-lang/expr v1.16.5 h1:m2hvtguFeVaVNTHj8L7BoAyt7O0PAIBaSVbjdHgRXMs= github.com/expr-lang/expr v1.16.5 h1:m2hvtguFeVaVNTHj8L7BoAyt7O0PAIBaSVbjdHgRXMs=
github.com/expr-lang/expr v1.16.5/go.mod h1:uCkhfG+x7fcZ5A5sXHKuQ07jGZRl6J0FCAaf2k4PtVQ= github.com/expr-lang/expr v1.16.5/go.mod h1:uCkhfG+x7fcZ5A5sXHKuQ07jGZRl6J0FCAaf2k4PtVQ=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jackc/pgx/v5 v5.9.1 h1:uwrxJXBnx76nyISkhr33kQLlUqjv7et7b9FjCen/tdc= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/jackc/pgx/v5 v5.9.1/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -2,7 +2,7 @@ package main
import ( import (
// External // External
werr "git.ahall.se/go/wrappederror" werr "git.gibonuddevalla.se/go/wrappederror"
// Standard // Standard
"regexp" "regexp"

199
main.go
View file

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

View file

@ -2,14 +2,13 @@ package main
import ( import (
// External // External
werr "git.ahall.se/go/wrappederror" werr "git.gibonuddevalla.se/go/wrappederror"
"github.com/jackc/pgx/v5"
// Internal // Internal
"smon/notification" "smon/notification"
// Standard // Standard
"context" "database/sql"
"encoding/json" "encoding/json"
"time" "time"
) )
@ -25,7 +24,7 @@ func nodataLoop() {
var datapoints []Datapoint var datapoints []Datapoint
var err error var err error
ticker := time.NewTicker(time.Second * time.Duration(config.NodataInterval)) ticker := time.NewTicker(time.Second * time.Duration(fileConf.NodataInterval))
for { for {
<-ticker.C <-ticker.C
datapoints, err = nodataDatapoints() datapoints, err = nodataDatapoints()
@ -63,8 +62,7 @@ func nodataNotify(datapoint Datapoint, state string) (err error) {
} else { } else {
errBody = nil errBody = nil
} }
_, err = db.Exec( _, err = service.Db.Conn.Exec(
context.Background(),
` `
INSERT INTO notification_send(notification_id, datapoint_nodata_id, uuid, ok, error) INSERT INTO notification_send(notification_id, datapoint_nodata_id, uuid, ok, error)
SELECT SELECT
@ -95,9 +93,8 @@ func nodataNotify(datapoint Datapoint, state string) (err error) {
func nodataDatapoints() (datapoints []Datapoint, err error) { func nodataDatapoints() (datapoints []Datapoint, err error) {
datapoints = []Datapoint{} datapoints = []Datapoint{}
rows, _ := db.Query( var rows *sql.Rows
context.Background(), rows, err = service.Db.Conn.Query(`
`
UPDATE datapoint UPDATE datapoint
SET SET
nodata_is_problem = true nodata_is_problem = true
@ -118,11 +115,19 @@ func nodataDatapoints() (datapoints []Datapoint, err error) {
datapoint.name datapoint.name
`) `)
datapoints, err = pgx.CollectRows(rows, pgx.RowToStructByName[Datapoint])
if err != nil { if err != nil {
err = werr.Wrap(err) err = werr.Wrap(err)
return return
} }
defer rows.Close()
var dp Datapoint
for rows.Next() {
if err = rows.Scan(&dp.ID, &dp.Name); err != nil {
err = werr.Wrap(err)
return
}
datapoints = append(datapoints, dp)
}
return return
} }

View file

@ -2,7 +2,7 @@ package notification
import ( import (
// External // External
werr "git.ahall.se/go/wrappederror" werr "git.gibonuddevalla.se/go/wrappederror"
// Standard // Standard
"log/slog" "log/slog"

View file

@ -2,7 +2,7 @@ package notification
import ( import (
// External // External
werr "git.ahall.se/go/wrappederror" werr "git.gibonuddevalla.se/go/wrappederror"
// Standard // Standard
"bytes" "bytes"

View file

@ -2,7 +2,7 @@ package notification
import ( import (
// External // External
werr "git.ahall.se/go/wrappederror" werr "git.gibonuddevalla.se/go/wrappederror"
// Standard // Standard
"log/slog" "log/slog"

View file

@ -2,7 +2,7 @@ package notification
import ( import (
// External // External
werr "git.ahall.se/go/wrappederror" werr "git.gibonuddevalla.se/go/wrappederror"
// Standard // Standard
"bytes" "bytes"

View file

@ -2,7 +2,7 @@ package notification
import ( import (
// External // External
werr "git.ahall.se/go/wrappederror" werr "git.gibonuddevalla.se/go/wrappederror"
// Standard // Standard
"encoding/json" "encoding/json"

View file

@ -2,14 +2,13 @@ package main
import ( import (
// External // External
werr "git.ahall.se/go/wrappederror" werr "git.gibonuddevalla.se/go/wrappederror"
"github.com/jackc/pgx/v5" "github.com/jmoiron/sqlx"
// Internal // Internal
"smon/notification" "smon/notification"
// Standard // Standard
"context"
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"time" "time"
@ -31,24 +30,19 @@ type NotificationSend struct {
func notificationLog(notificationService *notification.Service, problemID int, err error) { func notificationLog(notificationService *notification.Service, problemID int, err error) {
if err == nil { if err == nil {
logger.Info("notification", "service", (*notificationService).GetType(), "problemID", problemID, "prio", (*notificationService).GetPrio(), "ok", true) logger.Info("notification", "service", (*notificationService).GetType(), "problemID", problemID, "prio", (*notificationService).GetPrio(), "ok", true)
_, dbErr := db.Exec( service.Db.Conn.Query(
context.Background(),
` `
INSERT INTO notification_send() INSERT INTO notification_send()
`, `,
) )
if dbErr != nil {
logger.Error("notification", "op", "notification_send", "error", werr.Wrap(err))
}
} else { } else {
logger.Error("notification", "service", (*notificationService).GetType(), "problemID", problemID, "prio", (*notificationService).GetPrio(), "ok", false, "error", err) logger.Error("notification", "service", (*notificationService).GetType(), "problemID", problemID, "prio", (*notificationService).GetPrio(), "ok", false, "error", err)
} }
} }
func notificationsSent(from, to time.Time) (nss []NotificationSend, err error) { func notificationsSent(from, to time.Time) (nss []NotificationSend, err error) {
rows, _ := db.Query( var rows *sqlx.Rows
context.Background(), rows, err = service.Db.Conn.Queryx(
` `
SELECT SELECT
n.prio, n.prio,
@ -77,8 +71,15 @@ func notificationsSent(from, to time.Time) (nss []NotificationSend, err error) {
from, from,
to, to,
) )
nss, err = pgx.CollectRows(rows, func(row pgx.CollectableRow) (ns NotificationSend, err error) { if err != nil {
ns, err = pgx.RowToStructByName[NotificationSend](row) err = werr.Wrap(err)
return
}
defer rows.Close()
for rows.Next() {
ns := NotificationSend{}
err = rows.StructScan(&ns)
if err != nil { if err != nil {
err = werr.Wrap(err) err = werr.Wrap(err)
return return
@ -92,12 +93,7 @@ func notificationsSent(from, to time.Time) (nss []NotificationSend, err error) {
j, err = json.MarshalIndent(foo, "", " ") j, err = json.MarshalIndent(foo, "", " ")
ns.ErrorIndented = string(j) ns.ErrorIndented = string(j)
return nss = append(nss, ns)
})
if err != nil {
err = werr.Wrap(err)
return
} }
return return
} }

View file

@ -2,16 +2,14 @@ package main
import ( import (
// External // External
werr "git.ahall.se/go/wrappederror" werr "git.gibonuddevalla.se/go/wrappederror"
"github.com/jackc/pgx/v5/pgconn" "github.com/lib/pq"
// Internal // Internal
"smon/notification" "smon/notification"
// Standard // Standard
"context"
"encoding/json" "encoding/json"
"errors"
) )
type DbNotificationService struct { type DbNotificationService struct {
@ -21,11 +19,9 @@ type DbNotificationService struct {
Prio int Prio int
} }
func initNotificationManager() (nm notification.Manager, err error) { // {{{ func InitNotificationManager() (nm notification.Manager, err error) { // {{{
var dbServices []DbNotificationService var dbServices []DbNotificationService
row := db.QueryRow( row := service.Db.Conn.QueryRow(`
context.Background(),
`
WITH services AS ( WITH services AS (
SELECT SELECT
id, id,
@ -40,7 +36,6 @@ func initNotificationManager() (nm notification.Manager, err error) { // {{{
FROM services s FROM services s
`, `,
) )
var dbData []byte var dbData []byte
err = row.Scan(&dbData) err = row.Scan(&dbData)
if err != nil { if err != nil {
@ -75,8 +70,7 @@ func initNotificationManager() (nm notification.Manager, err error) { // {{{
} // }}} } // }}}
func UpdateNotificationService(svc notification.Service) (created bool, err error) { // {{{ func UpdateNotificationService(svc notification.Service) (created bool, err error) { // {{{
if svc.Exists() { if svc.Exists() {
_, err = db.Exec( _, err = service.Db.Conn.Exec(
context.Background(),
` `
UPDATE public.notification UPDATE public.notification
SET SET
@ -90,11 +84,11 @@ func UpdateNotificationService(svc notification.Service) (created bool, err erro
svc.Updated().JSON(), svc.Updated().JSON(),
) )
} else { } else {
_, err = db.Exec( _, err = service.Db.Conn.Exec(
context.Background(),
` `
INSERT INTO public.notification(prio, configuration, service) INSERT INTO public.notification(prio, configuration, service)
VALUES($1, $2, $3) VALUES($1, $2, $3)
`, `,
svc.Updated().GetPrio(), svc.Updated().GetPrio(),
svc.Updated().JSON(), svc.Updated().JSON(),
@ -105,8 +99,8 @@ func UpdateNotificationService(svc notification.Service) (created bool, err erro
if err != nil { if err != nil {
// Check if this is just a duplicated prio, which isn't allowed. // Check if this is just a duplicated prio, which isn't allowed.
var pgErr *pgconn.PgError pgErr, isPgErr := err.(*pq.Error)
if errors.As(err, &pgErr); 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.Updated().GetPrio())
} }
@ -123,12 +117,12 @@ func UpdateNotificationService(svc notification.Service) (created bool, err erro
return return
} // }}} } // }}}
func DeleteNotificationService(prio int) (err error) { // {{{ func DeleteNotificationService(prio int) (err error) { // {{{
_, err = db.Exec( _, err = service.Db.Conn.Exec(
context.Background(),
` `
DELETE FROM public.notification DELETE FROM public.notification
WHERE WHERE
prio = $1 prio = $1
`, `,
prio, prio,
) )
@ -141,7 +135,7 @@ func DeleteNotificationService(prio int) (err error) { // {{{
func AcknowledgeNotification(uuid string) (err error) { // {{{ func AcknowledgeNotification(uuid string) (err error) { // {{{
/* /*
_, err = db.Exec(context.Background(), `UPDATE schedule SET acknowledged=true WHERE schedule_uuid=$1`, uuid) _, err = service.Db.Conn.Exec(`UPDATE schedule SET acknowledged=true WHERE schedule_uuid=$1`, uuid)
if err != nil { if err != nil {
err = werr.Wrap(err).WithData(uuid) err = werr.Wrap(err).WithData(uuid)
} }

View file

@ -2,7 +2,7 @@ package main
import ( import (
// External // External
werr "git.ahall.se/go/wrappederror" werr "git.gibonuddevalla.se/go/wrappederror"
// Standard // Standard
"fmt" "fmt"

View file

@ -2,10 +2,9 @@ package main
import ( import (
// External // External
werr "git.ahall.se/go/wrappederror" werr "git.gibonuddevalla.se/go/wrappederror"
// Standard // Standard
"context"
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -29,9 +28,7 @@ type Problem struct { // {{{
func ProblemsRetrieve(showCurrent bool, from, to time.Time) (problems []Problem, err error) { // {{{ func ProblemsRetrieve(showCurrent bool, from, to time.Time) (problems []Problem, err error) { // {{{
problems = []Problem{} problems = []Problem{}
row := db.QueryRow( row := service.Db.Conn.QueryRow(`
context.Background(),
`
SELECT SELECT
jsonb_agg(problems.*) jsonb_agg(problems.*)
FROM ( FROM (
@ -107,9 +104,7 @@ func ProblemsRetrieve(showCurrent bool, from, to time.Time) (problems []Problem,
return return
} // }}} } // }}}
func ProblemStart(trigger Trigger) (problemID int, err error) { // {{{ func ProblemStart(trigger Trigger) (problemID int, err error) { // {{{
row := db.QueryRow( row := service.Db.Conn.QueryRow(`
context.Background(),
`
SELECT COUNT(id) SELECT COUNT(id)
FROM problem FROM problem
WHERE WHERE
@ -129,8 +124,7 @@ func ProblemStart(trigger Trigger) (problemID int, err error) { // {{{
// Open up a new problem if no open exists. // Open up a new problem if no open exists.
if openProblems == 0 { if openProblems == 0 {
datapointValuesJson, _ := json.Marshal(trigger.DatapointValues) datapointValuesJson, _ := json.Marshal(trigger.DatapointValues)
row = db.QueryRow( row = service.Db.Conn.QueryRow(
context.Background(),
`INSERT INTO problem(trigger_id, trigger_name, datapoints, trigger_expression) VALUES($1, $2, $3, $4) RETURNING id`, `INSERT INTO problem(trigger_id, trigger_name, datapoints, trigger_expression) VALUES($1, $2, $3, $4) RETURNING id`,
trigger.ID, trigger.ID,
trigger.Name, trigger.Name,
@ -145,13 +139,9 @@ func ProblemStart(trigger Trigger) (problemID int, err error) { // {{{
return return
} // }}} } // }}}
func ProblemClose(trigger Trigger) (problemID int, err error) { // {{{ func ProblemClose(trigger Trigger) (problemID int, err error) { // {{{
row := db.QueryRow( row := service.Db.Conn.QueryRow(`UPDATE problem SET "end"=NOW() WHERE trigger_id=$1 AND "end" IS NULL RETURNING id`, trigger.ID)
context.Background(),
`UPDATE problem SET "end"=NOW() WHERE trigger_id=$1 AND "end" IS NULL RETURNING id`,
trigger.ID,
)
err = row.Scan(&problemID) err = row.Scan(&problemID)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
err = nil err = nil
return return
@ -164,7 +154,7 @@ func ProblemClose(trigger Trigger) (problemID int, err error) { // {{{
return return
} // }}} } // }}}
func ProblemAcknowledge(id int, state bool) (err error) { // {{{ func ProblemAcknowledge(id int, state bool) (err error) { // {{{
_, err = db.Exec(context.Background(), `UPDATE problem SET "acknowledged"=$2 WHERE id=$1`, id, state) _, err = service.Db.Conn.Exec(`UPDATE problem SET "acknowledged"=$2 WHERE id=$1`, id, state)
if err != nil { if err != nil {
err = werr.Wrap(err).WithData(id) err = werr.Wrap(err).WithData(id)
return return

View file

@ -2,10 +2,9 @@ package main
import ( import (
// External // External
werr "git.ahall.se/go/wrappederror" werr "git.gibonuddevalla.se/go/wrappederror"
// Standard // Standard
"context"
"sort" "sort"
) )
@ -27,26 +26,20 @@ func (s *Section) SortedTriggers() []Trigger {
} }
func SectionCreate(areaID int, name string) (err error) { // {{{ func SectionCreate(areaID int, name string) (err error) { // {{{
_, err = db.Exec(context.Background(), `INSERT INTO section(area_id, name) VALUES($1, $2)`, areaID, name) _, err = service.Db.Conn.Exec(`INSERT INTO section(area_id, name) VALUES($1, $2)`, areaID, name)
if err != nil {
err = werr.Wrap(err)
}
return return
} // }}} } // }}}
func SectionRename(id int, name string) (err error) { // {{{ func SectionRename(id int, name string) (err error) { // {{{
_, err = db.Exec(context.Background(), `UPDATE section SET name=$2 WHERE id=$1`, id, name) _, err = service.Db.Conn.Exec(`UPDATE section SET name=$2 WHERE id=$1`, id, name)
if err != nil {
err = werr.Wrap(err)
}
return return
} // }}} } // }}}
func SectionDelete(id int) (err error) { // {{{ func SectionDelete(id int) (err error) { // {{{
_, err = db.Exec(context.Background(), `DELETE FROM public.trigger WHERE section_id = $1`, id) _, err = service.Db.Conn.Exec(`DELETE FROM public.trigger WHERE section_id = $1`, id)
if err != nil { if err != nil {
return werr.Wrap(err).WithData(id) return werr.Wrap(err).WithData(id)
} }
_, err = db.Exec(context.Background(), `DELETE FROM public.section WHERE id = $1`, id) _, err = service.Db.Conn.Exec(`DELETE FROM public.section WHERE id = $1`, id)
if err != nil { if err != nil {
return werr.Wrap(err).WithData(id) return werr.Wrap(err).WithData(id)
} }

View file

@ -2,7 +2,7 @@ package main
import ( import (
// External // External
werr "git.ahall.se/go/wrappederror" werr "git.gibonuddevalla.se/go/wrappederror"
// Standard // Standard
"time" "time"

View file

@ -2,16 +2,15 @@ package main
import ( import (
// External // External
werr "git.ahall.se/go/wrappederror" werr "git.gibonuddevalla.se/go/wrappederror"
"github.com/expr-lang/expr" "github.com/expr-lang/expr"
"github.com/expr-lang/expr/ast" "github.com/expr-lang/expr/ast"
"github.com/expr-lang/expr/parser" "github.com/expr-lang/expr/parser"
"github.com/jackc/pgx/v5/pgconn" "github.com/lib/pq"
// Standard // Standard
"context" "database/sql"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"strings" "strings"
) )
@ -46,9 +45,7 @@ func TriggerCreate(sectionID int, name string) (t Trigger, err error) { // {{{
func TriggersRetrieve() (areas []Area, err error) { // {{{ func TriggersRetrieve() (areas []Area, err error) { // {{{
areas = []Area{} areas = []Area{}
row := db.QueryRow( row := service.Db.Conn.QueryRow(`
context.Background(),
`
WITH section_triggers AS ( WITH section_triggers AS (
SELECT SELECT
s.id AS id, s.id AS id,
@ -102,9 +99,7 @@ func TriggersRetrieve() (areas []Area, err error) { // {{{
} // }}} } // }}}
func TriggersRetrieveByDatapoint(datapointName string) (triggers []Trigger, err error) { // {{{ func TriggersRetrieveByDatapoint(datapointName string) (triggers []Trigger, err error) { // {{{
triggers = []Trigger{} triggers = []Trigger{}
row := db.QueryRow( row := service.Db.Conn.QueryRow(`
context.Background(),
`
SELECT jsonb_agg(t.*) SELECT jsonb_agg(t.*)
FROM public."trigger" t FROM public."trigger" t
WHERE WHERE
@ -134,7 +129,7 @@ func TriggersRetrieveByDatapoint(datapointName string) (triggers []Trigger, err
return return
} // }}} } // }}}
func TriggerRetrieve(id int) (trigger Trigger, err error) { // {{{ func TriggerRetrieve(id int) (trigger Trigger, err error) { // {{{
row := db.QueryRow(context.Background(), `SELECT to_jsonb(t.*) FROM "trigger" t WHERE id=$1`, id) row := service.Db.Conn.QueryRow(`SELECT to_jsonb(t.*) FROM "trigger" t WHERE id=$1`, id)
var jsonData []byte var jsonData []byte
err = row.Scan(&jsonData) err = row.Scan(&jsonData)
if err != nil { if err != nil {
@ -146,7 +141,7 @@ func TriggerRetrieve(id int) (trigger Trigger, err error) { // {{{
return return
} // }}} } // }}}
func TriggerDelete(id int) (err error) { // {{{ func TriggerDelete(id int) (err error) { // {{{
_, err = db.Exec(context.Background(), `DELETE FROM public.trigger WHERE id=$1`, id) _, err = service.Db.Conn.Exec(`DELETE FROM public.trigger WHERE id=$1`, id)
if err != nil { if err != nil {
return werr.Wrap(err).WithData(id) return werr.Wrap(err).WithData(id)
} }
@ -177,9 +172,8 @@ func (t *Trigger) Update() (err error) { // {{{
} }
jsonDatapoints, _ := json.Marshal(t.Datapoints) jsonDatapoints, _ := json.Marshal(t.Datapoints)
if t.ID == 0 { if t.ID == 0 {
row := db.QueryRow( var row *sql.Row
context.Background(), row = service.Db.Conn.QueryRow(`
`
INSERT INTO "trigger"(name, section_id, expression, datapoints) INSERT INTO "trigger"(name, section_id, expression, datapoints)
VALUES($1, $2, $3, $4) VALUES($1, $2, $3, $4)
RETURNING id RETURNING id
@ -207,9 +201,7 @@ func (t *Trigger) Update() (err error) { // {{{
return return
} }
} else { } else {
_, err = db.Exec( _, err = service.Db.Conn.Exec(`
context.Background(),
`
UPDATE "trigger" UPDATE "trigger"
SET SET
name=$2, name=$2,
@ -225,26 +217,20 @@ func (t *Trigger) Update() (err error) { // {{{
) )
} }
var pgErr *pgconn.PgError if pqErr, ok := err.(*pq.Error); ok {
if errors.As(err, &pgErr) {
err = werr.Wrap(err).WithData( err = werr.Wrap(err).WithData(
struct { struct {
Trigger *Trigger Trigger *Trigger
PostgresCode string PostgresCode pq.ErrorCode
PostgresMsg string PostgresMsg string
}{ }{
t, t,
pgErr.Code, pqErr.Code,
pgErr.Message, pqErr.Code.Name(),
}) })
return } else if err != nil {
}
if err != nil {
err = werr.Wrap(err).WithData(t) err = werr.Wrap(err).WithData(t)
return
} }
return return
} // }}} } // }}}
func (t *Trigger) Run() (output any, err error) { // {{{ func (t *Trigger) Run() (output any, err error) { // {{{