diff --git a/area.go b/area.go index 49868a3..18e2b38 100644 --- a/area.go +++ b/area.go @@ -2,11 +2,10 @@ package main import ( // External - werr "git.ahall.se/go/wrappederror" - "github.com/jackc/pgx/v5" + werr "git.gibonuddevalla.se/go/wrappederror" // Standard - "context" + "database/sql" "encoding/json" "sort" ) @@ -20,7 +19,7 @@ type Area struct { func AreaRetrieve() (areas []Area, err error) { // {{{ areas = []Area{} - row := db.QueryRow(context.Background(), ` + row := service.Db.Conn.QueryRow(` SELECT jsonb_agg(jsonsections) FROM ( @@ -60,23 +59,21 @@ func AreaRetrieve() (areas []Area, err error) { // {{{ return } // }}} 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 } // }}} 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 } // }}} func AreaDelete(id int) (err error) { // {{{ - ctx := context.Background() - - var trx pgx.Tx - trx, err = db.Begin(ctx) + var trx *sql.Tx + trx, err = service.Db.Conn.Begin() if err != nil { err = werr.Wrap(err).WithData(id) } - _, err = trx.Exec(context.Background(), ` + _, err = trx.Exec(` DELETE FROM trigger t USING section s @@ -87,34 +84,34 @@ func AreaDelete(id int) (err error) { // {{{ id, ) if err != nil { - err2 := trx.Rollback(ctx) + err2 := trx.Rollback() if err2 != nil { return werr.Wrap(err2).WithData(err) } 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 { - err2 := trx.Rollback(ctx) + err2 := trx.Rollback() if err2 != nil { return werr.Wrap(err2).WithData(err) } 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 { - err2 := trx.Rollback(ctx) + err2 := trx.Rollback() if err2 != nil { return werr.Wrap(err2).WithData(err) } return werr.Wrap(err).WithData(id) } - err = trx.Commit(ctx) + err = trx.Commit() if err != nil { - err2 := trx.Rollback(ctx) + err2 := trx.Rollback() if err2 != nil { return werr.Wrap(err2).WithData(err) } diff --git a/config.go b/config.go index 1ddae2a..5fe5d4e 100644 --- a/config.go +++ b/config.go @@ -1,42 +1,6 @@ package main -import ( - // Standard - "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 - +type FileConfiguration struct { + LogFile string 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 -}// }}} diff --git a/configuration.go b/configuration.go index 4c41e5c..8803069 100644 --- a/configuration.go +++ b/configuration.go @@ -2,17 +2,16 @@ package main import ( // External - werr "git.ahall.se/go/wrappederror" - "github.com/jackc/pgx/v5" + werr "git.gibonuddevalla.se/go/wrappederror" // Standard - "context" + "database/sql" "time" ) type Configuration struct { timezoneLocation *time.Location - Settings map[string]string + Settings map[string]string } var smonConfig Configuration @@ -20,8 +19,8 @@ var smonConfig Configuration func SmonConfigInit() (cfg Configuration, err error) { cfg.Settings = make(map[string]string, 8) - var rows pgx.Rows - rows, err = db.Query(context.Background(), `SELECT * FROM public.configuration`) + var rows *sql.Rows + rows, err = service.Db.Conn.Query(`SELECT * FROM public.configuration`) if err != nil { err = werr.Wrap(err) return @@ -47,7 +46,7 @@ func (cfg *Configuration) Validate() (err error) { mandatorySettings := []string{"THEME", "TIMEZONE"} for _, settingsKey := range mandatorySettings { if _, found := cfg.Settings[settingsKey]; !found { - return werr.New("Configuration missing setting '%s' in database", settingsKey) + return werr.New("Configuration missing setting '%s' in database", settingsKey) } } return @@ -64,7 +63,7 @@ func (cfg *Configuration) Timezone() *time.Location { func (cfg *Configuration) SetTheme(theme string) (err error) { 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 } @@ -75,7 +74,7 @@ func (cfg *Configuration) SetTimezone(tz string) (err error) { 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 { return werr.Wrap(err).WithData(tz) } diff --git a/datapoint.go b/datapoint.go index b47ccc9..0690bbd 100644 --- a/datapoint.go +++ b/datapoint.go @@ -2,11 +2,10 @@ package main import ( // External - werr "git.ahall.se/go/wrappederror" - "github.com/jackc/pgx/v5" + werr "git.gibonuddevalla.se/go/wrappederror" + "github.com/jmoiron/sqlx" // Standard - "context" "database/sql" "errors" "strings" @@ -74,8 +73,7 @@ func (dp Datapoint) Update() (err error) { // {{{ } if dp.ID == 0 { - _, err = db.Exec( - context.Background(), + _, err = service.Db.Conn.Exec( `INSERT INTO datapoint("group", name, datatype, nodata_problem_seconds, comment) VALUES($1, $2, $3, $4, $5)`, dp.Group, name, @@ -87,8 +85,7 @@ func (dp Datapoint) Update() (err error) { // {{{ /* Keep nodata_is_problem as is unless the nodata_problem_seconds is changed. * Otherwise unnecessary nodata problems could be notified when updating unrelated * datapoint properties. */ - _, err = db.Exec( - context.Background(), + _, err = service.Db.Conn.Exec( ` UPDATE datapoint SET @@ -128,7 +125,7 @@ func DatapointAdd[T any](name string, value T) (err error) { // {{{ 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 dpType DatapointType @@ -143,9 +140,9 @@ func DatapointAdd[T any](name string, value T) (err error) { // {{{ switch dpType { 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: - _, 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: // Time value is required to be a RFC 3339 formatted time string 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() } - _, 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 { 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) { // {{{ dps = []Datapoint{} - var rows pgx.Rows - rows, err = db.Query( - context.Background(), - ` + var rows *sqlx.Rows + rows, err = service.Db.Conn.Queryx(` SELECT id, name, datatype, last_value, "group", comment, nodata_problem_seconds, last_value_id AS v_id, @@ -210,43 +205,37 @@ func DatapointsRetrieve() (dps []Datapoint, err error) { // {{{ ValueDateTime sql.NullTime `db:"value_datetime"` } - dps, err = pgx.CollectRows( - rows, - func(row pgx.CollectableRow) (dp Datapoint, err error) { - var dpv DatapointValue - var res DbRes - res, err = pgx.RowToStructByName[DbRes](row) - if err != nil { - err = werr.Wrap(err) - return - } - - dp.ID = res.ID - dp.Name = res.Name - dp.Group = res.Group - dp.Datatype = res.Datatype - dp.Comment = res.Comment - dp.LastValue = res.LastValue - dp.Found = true - dp.NodataProblemSeconds = res.NodataProblemSeconds - - if res.VID.Valid { - dpv.ID = int(res.VID.Int64) - dpv.Ts = res.Ts.Time - dpv.ValueInt = res.ValueInt - dpv.ValueString = res.ValueString - dpv.ValueDateTime = res.ValueDateTime - - dp.LastDatapointValue = dpv - } + for rows.Next() { + dp := Datapoint{} + dpv := DatapointValue{} + res := DbRes{} + err = rows.StructScan(&res) + if err != nil { + err = werr.Wrap(err) return - }) + } - if err != nil { - err = werr.Wrap(err) - return + dp.ID = res.ID + dp.Name = res.Name + dp.Group = res.Group + dp.Datatype = res.Datatype + dp.Comment = res.Comment + dp.LastValue = res.LastValue + dp.Found = true + dp.NodataProblemSeconds = res.NodataProblemSeconds + + if res.VID.Valid { + dpv.ID = int(res.VID.Int64) + dpv.Ts = res.Ts.Time + dpv.ValueInt = res.ValueInt + dpv.ValueString = res.ValueString + dpv.ValueDateTime = res.ValueDateTime + + dp.LastDatapointValue = dpv + } + + dps = append(dps, dp) } - return } // }}} 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 } - rows, _ := db.Query(context.Background(), query, param) - dp, err = pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Datapoint]) - if err != nil { - err = werr.Wrap(err) - return - } + row := service.Db.Conn.QueryRowx(query, param) + err = row.StructScan(&dp) if err == sql.ErrNoRows { dp = Datapoint{ @@ -281,9 +266,7 @@ func DatapointRetrieve(id int, name string) (dp Datapoint, err error) { // {{{ return } - rows, _ = db.Query( - context.Background(), - ` + row = service.Db.Conn.QueryRowx(` SELECT * FROM datapoint_value WHERE datapoint_id = $1 @@ -292,7 +275,7 @@ func DatapointRetrieve(id int, name string) (dp Datapoint, err error) { // {{{ `, dp.ID, ) - dp, err = pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Datapoint]) + err = row.StructScan(&dp.LastDatapointValue) if err == sql.ErrNoRows { err = nil return @@ -307,41 +290,50 @@ func DatapointRetrieve(id int, name string) (dp Datapoint, err error) { // {{{ } // }}} func DatapointDelete(id int) (err error) { // {{{ 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 { err = werr.Wrap(err).WithData(id) return } - var triggerNames []string - rows, _ := db.Query(context.Background(), `SELECT name FROM public.trigger WHERE datapoints ? $1`, dpName) - triggerNames, err = pgx.CollectRows(rows, pgx.RowTo[string]) + var rows *sql.Rows + rows, err = service.Db.Conn.Query(`SELECT name FROM public.trigger WHERE datapoints ? $1`, dpName) if err != nil { - err = werr.Wrap(err).WithData(id) + err = werr.Wrap(err).WithData(dpName) 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 { 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 { err = werr.Wrap(err).WithData(id) - return } - return } // }}} 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 { err = werr.Wrap(err).WithData(smonConfig.Timezone().String()) return } - rows, _ := db.Query( - context.Background(), + rows, err := service.Db.Conn.Queryx( ` SELECT id, @@ -362,11 +354,20 @@ func DatapointValues(id int, from, to time.Time) (values []DatapointValue, err e from, to, ) - values, err = pgx.CollectRows(rows, pgx.RowToStructByName[DatapointValue]) if err != nil { err = werr.Wrap(err).WithData(id) 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 } // }}} diff --git a/db.go b/db.go deleted file mode 100644 index bc5e909..0000000 --- a/db.go +++ /dev/null @@ -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 -} diff --git a/go.mod b/go.mod index 6880382..f0f481e 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,19 @@ module smon -go 1.25.0 +go 1.22.0 require ( - git.ahall.se/go/html_template v0.1.1 - git.ahall.se/go/wrappederror v1.0.0 + git.gibonuddevalla.se/go/webservice v0.2.16 + git.gibonuddevalla.se/go/wrappederror v0.3.4 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 ( - github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect - github.com/jackc/puddle/v2 v2.2.2 // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/text v0.29.0 // indirect + git.gibonuddevalla.se/go/dbschema v1.3.0 // indirect + github.com/google/uuid v1.5.0 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + golang.org/x/net v0.17.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 1a0544f..03a3465 100644 --- a/go.sum +++ b/go.sum @@ -1,32 +1,33 @@ -git.ahall.se/go/html_template v0.1.1 h1:GrSNvs/ZAWKkuxQ4OEOTqHBTtYnx8C88JBxVERDxv18= -git.ahall.se/go/html_template v0.1.1/go.mod h1:bBpM0K1HXZadhZg4VeT39IgyGwwhWoZet99nw8QeZKg= -git.ahall.se/go/wrappederror v1.0.0 h1:TxiAUTZUnmkVpaPKQjhTGSFsvc0qLkULsIj4Rwh4UsY= -git.ahall.se/go/wrappederror v1.0.0/go.mod h1:trMBDaw5SdUrsZu3MCh1q+INsrmB4T9L+3DuV7JM+c8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +git.gibonuddevalla.se/go/dbschema v1.3.0 h1:HzFMR29tWfy/ibIjltTbIMI4inVktj/rh8bESALibgM= +git.gibonuddevalla.se/go/dbschema v1.3.0/go.mod h1:BNw3q/574nXbGoeWyK+tLhRfggVkw2j2aXZzrBKC3ig= +git.gibonuddevalla.se/go/webservice v0.2.15 h1:ECe63fRDSrg3RJcgYV2pG+WsAQLVG8wvfHennz7aHsY= +git.gibonuddevalla.se/go/webservice v0.2.15/go.mod h1:3uBS6nLbK9qbuGzDls8MZD5Xr9ORY1Srbj6v06BIhws= +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/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/go.mod h1:uCkhfG+x7fcZ5A5sXHKuQ07jGZRl6J0FCAaf2k4PtVQ= -github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= -github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.9.1 h1:uwrxJXBnx76nyISkhr33kQLlUqjv7et7b9FjCen/tdc= -github.com/jackc/pgx/v5 v5.9.1/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= -github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= -github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= +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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -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= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/helper.go b/helper.go index 7d06dca..e8fcbe0 100644 --- a/helper.go +++ b/helper.go @@ -2,7 +2,7 @@ package main import ( // External - werr "git.ahall.se/go/wrappederror" + werr "git.gibonuddevalla.se/go/wrappederror" // Standard "regexp" diff --git a/main.go b/main.go index d6f81cd..d771363 100644 --- a/main.go +++ b/main.go @@ -2,14 +2,14 @@ package main import ( // External - "git.ahall.se/go/html_template" - werr "git.ahall.se/go/wrappederror" + ws "git.gibonuddevalla.se/go/webservice" + "git.gibonuddevalla.se/go/webservice/session" + werr "git.gibonuddevalla.se/go/wrappederror" // Internal "smon/notification" // Standard - "context" "embed" "encoding/json" "flag" @@ -36,11 +36,12 @@ var ( flagConfigFile string flagDev bool flagVersion bool + service *ws.Service + logFile *os.File parsedTemplates map[string]*template.Template componentFilenames []string notificationManager notification.Manager - config Config - htmlEngine HTMLTemplate.Engine + fileConf FileConfiguration //go:embed sql sqlFS embed.FS @@ -60,8 +61,8 @@ func init() { // {{{ if err != nil { logger.Error("application", "error", werr.Wrap(err)) } - cfgPath := path.Join(confDir, "smon.json") - flag.StringVar(&flagConfigFile, "config", cfgPath, "Path and filename of the JSON configuration file") + cfgPath := path.Join(confDir, "smon.yaml") + 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(&flagVersion, "version", false, "Display version and exit") flag.Parse() @@ -80,85 +81,84 @@ func main() { // {{{ os.Exit(0) } - logger.Info("application", "version", VERSION) var err error werr.Init() + werr.SetLogCallback(logHandler) - logger.Info("application", "config", flagConfigFile) - config, err = initConfig(flagConfigFile) + service, err = ws.New(flagConfigFile, VERSION, logger) if err != nil { - logger.Error("application", "op", "read_config", "error", err) + logger.Error("application", "error", err) 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.") - os.Exit(1) + return } - err = initDb( - config.Database.Host, - config.Database.Port, - config.Database.Name, - config.Database.Username, - config.Database.Password, - ) - if err != nil { - logger.Error("database", "error", err) - os.Exit(1) - } - - htmlEngine, err = initHTMLTemplate() + service.SetDatabase(sqlProvider) + service.SetStaticFS(staticFS, "static") + service.SetStaticDirectory("static", flagDev) + service.InitDatabaseConnection() + err = service.Db.Connect() 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) } - http.HandleFunc("/", staticHandler) + service.Register("/", false, false, staticHandler) - http.HandleFunc("/area/new/{name}", actionAreaNew) - http.HandleFunc("/area/rename/{id}/{name}", actionAreaRename) - http.HandleFunc("/area/delete/{id}", actionAreaDelete) + 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("/section/new/{areaID}/{name}", actionSectionNew) - http.HandleFunc("/section/rename/{id}/{name}", actionSectionRename) - http.HandleFunc("/section/delete/{id}", actionSectionDelete) + 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("/problems", pageProblems) - http.HandleFunc("/problem/acknowledge/{id}", actionProblemAcknowledge) - http.HandleFunc("/problem/unacknowledge/{id}", actionProblemUnacknowledge) + service.Register("/problems", false, false, pageProblems) + service.Register("/problem/acknowledge/{id}", false, false, actionProblemAcknowledge) + service.Register("/problem/unacknowledge/{id}", false, false, actionProblemUnacknowledge) - 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("/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("/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("/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("/notifications", pageNotifications) + service.Register("/notifications", false, false, pageNotifications) - 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) + 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) go nodataLoop() @@ -174,9 +174,7 @@ func main() { // {{{ os.Exit(1) } - listen := fmt.Sprintf("%s:%d", config.Network.Address, config.Network.Port) - logger.Info("webserver", "listen", listen) - err = http.ListenAndServe(listen, nil) + err = service.Start() if err != nil { logger.Error("webserver", "error", werr.Wrap(err)) os.Exit(1) @@ -193,8 +191,10 @@ func sqlProvider(dbname string, version int) (sql []byte, found bool) { // {{{ found = true return } // }}} -func initHTMLTemplate() (HTMLTemplate.Engine, error) { // {{{ - return HTMLTemplate.NewEngine(viewFS, staticFS, flagDev) +func logHandler(err werr.Error) { // {{{ + j, _ := json.Marshal(err) + logFile.Write(j) + logFile.Write([]byte("\n")) } // }}} 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) { // {{{ +func staticHandler(w http.ResponseWriter, r *http.Request, sess *session.T) { // {{{ if flagDev && !reloadTemplates(w) { return } if r.URL.Path == "/" { - pageIndex(w, r) + pageIndex(w, r, sess) 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") value, _ := io.ReadAll(r.Body) @@ -307,16 +307,15 @@ func actionEntryDatapoint(w http.ResponseWriter, r *http.Request) { // {{{ } else { errBody = nil } - _, err = db.Exec( - context.Background(), + _, err = service.Db.Conn.Exec( ` - 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(), @@ -399,7 +398,7 @@ func getPage(layout, page string) (tmpl *template.Template, err error) { // {{{ return } // }}} -func pageIndex(w http.ResponseWriter, r *http.Request) { // {{{ +func pageIndex(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{ page := Page{ LAYOUT: "main", PAGE: "index", @@ -408,7 +407,7 @@ func pageIndex(w http.ResponseWriter, r *http.Request) { // {{{ 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") err := AreaCreate(name) if err != nil { @@ -420,7 +419,7 @@ func actionAreaNew(w http.ResponseWriter, r *http.Request) { // {{{ w.WriteHeader(302) return } // }}} -func actionAreaRename(w http.ResponseWriter, r *http.Request) { // {{{ +func actionAreaRename(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{ idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { @@ -439,7 +438,7 @@ func actionAreaRename(w http.ResponseWriter, r *http.Request) { // {{{ w.WriteHeader(302) return } // }}} -func actionAreaDelete(w http.ResponseWriter, r *http.Request) { // {{{ +func actionAreaDelete(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{ idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { @@ -458,7 +457,7 @@ func actionAreaDelete(w http.ResponseWriter, r *http.Request) { // {{{ return } // }}} -func actionSectionNew(w http.ResponseWriter, r *http.Request) { // {{{ +func actionSectionNew(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{ idStr := r.PathValue("areaID") areaID, err := strconv.Atoi(idStr) if err != nil { @@ -477,7 +476,7 @@ func actionSectionNew(w http.ResponseWriter, r *http.Request) { // {{{ w.WriteHeader(302) return } // }}} -func actionSectionRename(w http.ResponseWriter, r *http.Request) { // {{{ +func actionSectionRename(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{ idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { @@ -496,7 +495,7 @@ func actionSectionRename(w http.ResponseWriter, r *http.Request) { // {{{ w.WriteHeader(302) return } // }}} -func actionSectionDelete(w http.ResponseWriter, r *http.Request) { // {{{ +func actionSectionDelete(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{ idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { @@ -515,7 +514,7 @@ func actionSectionDelete(w http.ResponseWriter, r *http.Request) { // {{{ return } // }}} -func pageProblems(w http.ResponseWriter, r *http.Request) { // {{{ +func pageProblems(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{ page := Page{ LAYOUT: "main", PAGE: "problems", @@ -579,7 +578,7 @@ func pageProblems(w http.ResponseWriter, r *http.Request) { // {{{ page.Render(w, r) return } // }}} -func actionProblemAcknowledge(w http.ResponseWriter, r *http.Request) { // {{{ +func actionProblemAcknowledge(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{ idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { @@ -598,7 +597,7 @@ func actionProblemAcknowledge(w http.ResponseWriter, r *http.Request) { // {{{ return } // }}} -func actionProblemUnacknowledge(w http.ResponseWriter, r *http.Request) { // {{{ +func actionProblemUnacknowledge(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{ idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { @@ -618,7 +617,7 @@ func actionProblemUnacknowledge(w http.ResponseWriter, r *http.Request) { // {{{ return } // }}} -func pageDatapoints(w http.ResponseWriter, r *http.Request) { // {{{ +func pageDatapoints(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{ page := Page{ LAYOUT: "main", PAGE: "datapoints", @@ -644,7 +643,7 @@ func pageDatapoints(w http.ResponseWriter, r *http.Request) { // {{{ page.Render(w, r) return } // }}} -func pageDatapointEdit(w http.ResponseWriter, r *http.Request) { // {{{ +func pageDatapointEdit(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{ idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { @@ -701,7 +700,7 @@ func pageDatapointEdit(w http.ResponseWriter, r *http.Request) { // {{{ page.Render(w, r) return } // }}} -func actionDatapointUpdate(w http.ResponseWriter, r *http.Request) { // {{{ +func actionDatapointUpdate(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{ idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { @@ -758,7 +757,7 @@ func actionDatapointUpdate(w http.ResponseWriter, r *http.Request) { // {{{ w.Header().Add("Location", "/datapoints") 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") id, err := strconv.Atoi(idStr) if err != nil { @@ -775,7 +774,7 @@ func actionDatapointDelete(w http.ResponseWriter, r *http.Request) { // {{{ w.Header().Add("Location", "/datapoints") 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") id, err := strconv.Atoi(idStr) if err != nil { @@ -834,7 +833,7 @@ func pageDatapointValues(w http.ResponseWriter, r *http.Request) { // {{{ page.Render(w, r) return } // }}} -func actionDatapointJson(w http.ResponseWriter, r *http.Request) { // {{{ +func actionDatapointJson(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{ idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { @@ -866,7 +865,7 @@ func actionDatapointJson(w http.ResponseWriter, r *http.Request) { // {{{ w.Write(j) } // }}} -func pageTriggers(w http.ResponseWriter, r *http.Request) { // {{{ +func pageTriggers(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{ areas, err := TriggersRetrieve() if err != nil { httpError(w, werr.Wrap(err).Log()) @@ -888,7 +887,7 @@ func pageTriggers(w http.ResponseWriter, r *http.Request) { // {{{ 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") sectionIDStr := r.PathValue("sectionID") 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.Write(j) } // }}} -func pageTriggerEdit(w http.ResponseWriter, r *http.Request) { // {{{ +func pageTriggerEdit(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{ idStr := r.PathValue("id") id, err := strconv.Atoi(idStr) if err != nil { @@ -976,7 +975,7 @@ func pageTriggerEdit(w http.ResponseWriter, r *http.Request) { // {{{ 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") dpName := r.PathValue("datapointName") @@ -1023,7 +1022,7 @@ func actionTriggerDatapointAdd(w http.ResponseWriter, r *http.Request) { // {{{ w.Header().Add("Content-Type", "application/json") 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") id, err := strconv.Atoi(idStr) if err != nil { @@ -1058,7 +1057,7 @@ func actionTriggerUpdate(w http.ResponseWriter, r *http.Request) { // {{{ w.Header().Add("Location", "/triggers") 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") id, err := strconv.Atoi(idStr) if err != nil { @@ -1093,7 +1092,7 @@ func actionTriggerRun(w http.ResponseWriter, r *http.Request) { // {{{ w.Header().Add("Content-Type", "application/json") 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") id, err := strconv.Atoi(idStr) if err != nil { @@ -1111,7 +1110,7 @@ func actionTriggerDelete(w http.ResponseWriter, r *http.Request) { // {{{ w.WriteHeader(302) } // }}} -func pageConfiguration(w http.ResponseWriter, r *http.Request) { // {{{ +func pageConfiguration(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{ areas, err := AreaRetrieve() if err != nil { httpError(w, werr.Wrap(err).Log()) @@ -1135,7 +1134,7 @@ func pageConfiguration(w http.ResponseWriter, r *http.Request) { // {{{ 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") err := smonConfig.SetTheme(theme) if err != nil { @@ -1146,7 +1145,7 @@ func actionConfigurationTheme(w http.ResponseWriter, r *http.Request) { // {{{ w.Header().Add("Location", "/configuration") 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") _, err := time.LoadLocation(timezone) @@ -1164,7 +1163,7 @@ func actionConfigurationTimezone(w http.ResponseWriter, r *http.Request) { // {{ w.Header().Add("Location", "/configuration") 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, // or a prio when editing an existing. notificationType := r.URL.Query().Get("type") @@ -1216,7 +1215,7 @@ func pageConfigurationNotification(w http.ResponseWriter, r *http.Request) { // 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") prio, err := strconv.Atoi(prioStr) if err != nil { @@ -1269,7 +1268,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) { // {{{ +func actionConfigurationNotificationDelete(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{ prioStr := r.PathValue("prio") prio, err := strconv.Atoi(prioStr) if err != nil { @@ -1284,7 +1283,7 @@ func actionConfigurationNotificationDelete(w http.ResponseWriter, r *http.Reques w.WriteHeader(302) } // }}} -func pageNotifications(w http.ResponseWriter, r *http.Request) { // {{{ +func pageNotifications(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{ var err error // Manage the values from the timefilter component diff --git a/nodata.go b/nodata.go index a093bd9..40244c1 100644 --- a/nodata.go +++ b/nodata.go @@ -2,14 +2,13 @@ package main import ( // External - werr "git.ahall.se/go/wrappederror" - "github.com/jackc/pgx/v5" + werr "git.gibonuddevalla.se/go/wrappederror" // Internal "smon/notification" // Standard - "context" + "database/sql" "encoding/json" "time" ) @@ -25,7 +24,7 @@ func nodataLoop() { var datapoints []Datapoint var err error - ticker := time.NewTicker(time.Second * time.Duration(config.NodataInterval)) + ticker := time.NewTicker(time.Second * time.Duration(fileConf.NodataInterval)) for { <-ticker.C datapoints, err = nodataDatapoints() @@ -63,8 +62,7 @@ func nodataNotify(datapoint Datapoint, state string) (err error) { } else { errBody = nil } - _, err = db.Exec( - context.Background(), + _, err = service.Db.Conn.Exec( ` INSERT INTO notification_send(notification_id, datapoint_nodata_id, uuid, ok, error) SELECT @@ -95,9 +93,8 @@ func nodataNotify(datapoint Datapoint, state string) (err error) { func nodataDatapoints() (datapoints []Datapoint, err error) { datapoints = []Datapoint{} - rows, _ := db.Query( - context.Background(), - ` + var rows *sql.Rows + rows, err = service.Db.Conn.Query(` UPDATE datapoint SET nodata_is_problem = true @@ -117,12 +114,20 @@ func nodataDatapoints() (datapoints []Datapoint, err error) { datapoint.id, datapoint.name - `) - datapoints, err = pgx.CollectRows(rows, pgx.RowToStructByName[Datapoint]) + `) if err != nil { err = werr.Wrap(err) 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 } diff --git a/notification/factory.go b/notification/factory.go index e4ec32c..1506b43 100644 --- a/notification/factory.go +++ b/notification/factory.go @@ -2,7 +2,7 @@ package notification import ( // External - werr "git.ahall.se/go/wrappederror" + werr "git.gibonuddevalla.se/go/wrappederror" // Standard "log/slog" diff --git a/notification/ntfy.go b/notification/ntfy.go index 0b5c4c2..8132d08 100644 --- a/notification/ntfy.go +++ b/notification/ntfy.go @@ -2,7 +2,7 @@ package notification import ( // External - werr "git.ahall.se/go/wrappederror" + werr "git.gibonuddevalla.se/go/wrappederror" // Standard "bytes" diff --git a/notification/pkg.go b/notification/pkg.go index 76a32d0..948d7b3 100644 --- a/notification/pkg.go +++ b/notification/pkg.go @@ -2,7 +2,7 @@ package notification import ( // External - werr "git.ahall.se/go/wrappederror" + werr "git.gibonuddevalla.se/go/wrappederror" // Standard "log/slog" diff --git a/notification/pushover.go b/notification/pushover.go index 74e28e3..023ff9d 100644 --- a/notification/pushover.go +++ b/notification/pushover.go @@ -2,7 +2,7 @@ package notification import ( // External - werr "git.ahall.se/go/wrappederror" + werr "git.gibonuddevalla.se/go/wrappederror" // Standard "bytes" diff --git a/notification/script.go b/notification/script.go index bd08071..7eb4d5b 100644 --- a/notification/script.go +++ b/notification/script.go @@ -2,7 +2,7 @@ package notification import ( // External - werr "git.ahall.se/go/wrappederror" + werr "git.gibonuddevalla.se/go/wrappederror" // Standard "encoding/json" diff --git a/notification_log.go b/notification_log.go index 5c41e9d..e1f7c2a 100644 --- a/notification_log.go +++ b/notification_log.go @@ -2,14 +2,13 @@ package main import ( // External - werr "git.ahall.se/go/wrappederror" - "github.com/jackc/pgx/v5" + werr "git.gibonuddevalla.se/go/wrappederror" + "github.com/jmoiron/sqlx" // Internal "smon/notification" // Standard - "context" "database/sql" "encoding/json" "time" @@ -31,24 +30,19 @@ type NotificationSend struct { func notificationLog(notificationService *notification.Service, problemID int, err error) { if err == nil { logger.Info("notification", "service", (*notificationService).GetType(), "problemID", problemID, "prio", (*notificationService).GetPrio(), "ok", true) - _, dbErr := db.Exec( - context.Background(), + service.Db.Conn.Query( ` INSERT INTO notification_send() `, ) - if dbErr != nil { - logger.Error("notification", "op", "notification_send", "error", werr.Wrap(err)) - } - } else { 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) { - rows, _ := db.Query( - context.Background(), + var rows *sqlx.Rows + rows, err = service.Db.Conn.Queryx( ` SELECT n.prio, @@ -77,8 +71,15 @@ func notificationsSent(from, to time.Time) (nss []NotificationSend, err error) { from, to, ) - nss, err = pgx.CollectRows(rows, func(row pgx.CollectableRow) (ns NotificationSend, err error) { - ns, err = pgx.RowToStructByName[NotificationSend](row) + if err != nil { + err = werr.Wrap(err) + return + } + defer rows.Close() + + for rows.Next() { + ns := NotificationSend{} + err = rows.StructScan(&ns) if err != nil { err = werr.Wrap(err) return @@ -92,12 +93,7 @@ func notificationsSent(from, to time.Time) (nss []NotificationSend, err error) { j, err = json.MarshalIndent(foo, "", " ") ns.ErrorIndented = string(j) - return - }) - if err != nil { - err = werr.Wrap(err) - return + nss = append(nss, ns) } - return } diff --git a/notification_manager.go b/notification_manager.go index 3145bc1..28afa62 100644 --- a/notification_manager.go +++ b/notification_manager.go @@ -2,16 +2,14 @@ package main import ( // External - werr "git.ahall.se/go/wrappederror" - "github.com/jackc/pgx/v5/pgconn" + werr "git.gibonuddevalla.se/go/wrappederror" + "github.com/lib/pq" // Internal "smon/notification" // Standard - "context" "encoding/json" - "errors" ) type DbNotificationService struct { @@ -21,11 +19,9 @@ type DbNotificationService struct { Prio int } -func initNotificationManager() (nm notification.Manager, err error) { // {{{ +func InitNotificationManager() (nm notification.Manager, err error) { // {{{ var dbServices []DbNotificationService - row := db.QueryRow( - context.Background(), - ` + row := service.Db.Conn.QueryRow(` WITH services AS ( SELECT id, @@ -40,7 +36,6 @@ func initNotificationManager() (nm notification.Manager, err error) { // {{{ FROM services s `, ) - var dbData []byte err = row.Scan(&dbData) if err != nil { @@ -75,8 +70,7 @@ func initNotificationManager() (nm notification.Manager, err error) { // {{{ } // }}} func UpdateNotificationService(svc notification.Service) (created bool, err error) { // {{{ if svc.Exists() { - _, err = db.Exec( - context.Background(), + _, err = service.Db.Conn.Exec( ` UPDATE public.notification SET @@ -90,11 +84,11 @@ func UpdateNotificationService(svc notification.Service) (created bool, err erro svc.Updated().JSON(), ) } else { - _, err = db.Exec( - context.Background(), + _, err = service.Db.Conn.Exec( ` INSERT INTO public.notification(prio, configuration, service) VALUES($1, $2, $3) + `, svc.Updated().GetPrio(), svc.Updated().JSON(), @@ -105,8 +99,8 @@ func UpdateNotificationService(svc notification.Service) (created bool, err erro if err != nil { // Check if this is just a duplicated prio, which isn't allowed. - var pgErr *pgconn.PgError - if errors.As(err, &pgErr); pgErr.Code == "23505" { + pgErr, isPgErr := err.(*pq.Error) + if isPgErr && pgErr.Code == "23505" { 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 } // }}} func DeleteNotificationService(prio int) (err error) { // {{{ - _, err = db.Exec( - context.Background(), + _, err = service.Db.Conn.Exec( ` DELETE FROM public.notification WHERE prio = $1 + `, prio, ) @@ -141,7 +135,7 @@ func DeleteNotificationService(prio int) (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 { err = werr.Wrap(err).WithData(uuid) } diff --git a/page.go b/page.go index 44415ec..a83ed12 100644 --- a/page.go +++ b/page.go @@ -2,7 +2,7 @@ package main import ( // External - werr "git.ahall.se/go/wrappederror" + werr "git.gibonuddevalla.se/go/wrappederror" // Standard "fmt" diff --git a/problem.go b/problem.go index 1d4390d..425dc63 100644 --- a/problem.go +++ b/problem.go @@ -2,10 +2,9 @@ package main import ( // External - werr "git.ahall.se/go/wrappederror" + werr "git.gibonuddevalla.se/go/wrappederror" // Standard - "context" "database/sql" "encoding/json" "fmt" @@ -29,9 +28,7 @@ type Problem struct { // {{{ func ProblemsRetrieve(showCurrent bool, from, to time.Time) (problems []Problem, err error) { // {{{ problems = []Problem{} - row := db.QueryRow( - context.Background(), - ` + row := service.Db.Conn.QueryRow(` SELECT jsonb_agg(problems.*) FROM ( @@ -107,9 +104,7 @@ func ProblemsRetrieve(showCurrent bool, from, to time.Time) (problems []Problem, return } // }}} func ProblemStart(trigger Trigger) (problemID int, err error) { // {{{ - row := db.QueryRow( - context.Background(), - ` + row := service.Db.Conn.QueryRow(` SELECT COUNT(id) FROM problem WHERE @@ -129,8 +124,7 @@ func ProblemStart(trigger Trigger) (problemID int, err error) { // {{{ // Open up a new problem if no open exists. if openProblems == 0 { datapointValuesJson, _ := json.Marshal(trigger.DatapointValues) - row = db.QueryRow( - context.Background(), + row = service.Db.Conn.QueryRow( `INSERT INTO problem(trigger_id, trigger_name, datapoints, trigger_expression) VALUES($1, $2, $3, $4) RETURNING id`, trigger.ID, trigger.Name, @@ -145,13 +139,9 @@ func ProblemStart(trigger Trigger) (problemID int, err error) { // {{{ return } // }}} func ProblemClose(trigger Trigger) (problemID int, err error) { // {{{ - row := db.QueryRow( - context.Background(), - `UPDATE problem SET "end"=NOW() WHERE trigger_id=$1 AND "end" IS NULL RETURNING id`, - trigger.ID, - ) - + row := service.Db.Conn.QueryRow(`UPDATE problem SET "end"=NOW() WHERE trigger_id=$1 AND "end" IS NULL RETURNING id`, trigger.ID) err = row.Scan(&problemID) + if err == sql.ErrNoRows { err = nil return @@ -164,7 +154,7 @@ func ProblemClose(trigger Trigger) (problemID int, err error) { // {{{ return } // }}} 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 { err = werr.Wrap(err).WithData(id) return diff --git a/section.go b/section.go index ebcbb31..82177d9 100644 --- a/section.go +++ b/section.go @@ -2,10 +2,9 @@ package main import ( // External - werr "git.ahall.se/go/wrappederror" + werr "git.gibonuddevalla.se/go/wrappederror" // Standard - "context" "sort" ) @@ -27,26 +26,20 @@ func (s *Section) SortedTriggers() []Trigger { } func SectionCreate(areaID int, name string) (err error) { // {{{ - _, err = db.Exec(context.Background(), `INSERT INTO section(area_id, name) VALUES($1, $2)`, areaID, name) - if err != nil { - err = werr.Wrap(err) - } + _, err = service.Db.Conn.Exec(`INSERT INTO section(area_id, name) VALUES($1, $2)`, areaID, name) return } // }}} func SectionRename(id int, name string) (err error) { // {{{ - _, err = db.Exec(context.Background(), `UPDATE section SET name=$2 WHERE id=$1`, id, name) - if err != nil { - err = werr.Wrap(err) - } + _, err = service.Db.Conn.Exec(`UPDATE section SET name=$2 WHERE id=$1`, id, name) return } // }}} 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 { 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 { return werr.Wrap(err).WithData(id) } diff --git a/timefilter.go b/timefilter.go index fb71641..8d221db 100644 --- a/timefilter.go +++ b/timefilter.go @@ -2,7 +2,7 @@ package main import ( // External - werr "git.ahall.se/go/wrappederror" + werr "git.gibonuddevalla.se/go/wrappederror" // Standard "time" diff --git a/trigger.go b/trigger.go index 147e459..d663564 100644 --- a/trigger.go +++ b/trigger.go @@ -2,16 +2,15 @@ package main import ( // External - werr "git.ahall.se/go/wrappederror" + werr "git.gibonuddevalla.se/go/wrappederror" "github.com/expr-lang/expr" "github.com/expr-lang/expr/ast" "github.com/expr-lang/expr/parser" - "github.com/jackc/pgx/v5/pgconn" + "github.com/lib/pq" // Standard - "context" + "database/sql" "encoding/json" - "errors" "fmt" "strings" ) @@ -46,9 +45,7 @@ func TriggerCreate(sectionID int, name string) (t Trigger, err error) { // {{{ func TriggersRetrieve() (areas []Area, err error) { // {{{ areas = []Area{} - row := db.QueryRow( - context.Background(), - ` + row := service.Db.Conn.QueryRow(` WITH section_triggers AS ( SELECT s.id AS id, @@ -102,9 +99,7 @@ func TriggersRetrieve() (areas []Area, err error) { // {{{ } // }}} func TriggersRetrieveByDatapoint(datapointName string) (triggers []Trigger, err error) { // {{{ triggers = []Trigger{} - row := db.QueryRow( - context.Background(), - ` + row := service.Db.Conn.QueryRow(` SELECT jsonb_agg(t.*) FROM public."trigger" t WHERE @@ -134,7 +129,7 @@ func TriggersRetrieveByDatapoint(datapointName string) (triggers []Trigger, err return } // }}} 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 err = row.Scan(&jsonData) if err != nil { @@ -146,7 +141,7 @@ func TriggerRetrieve(id int) (trigger Trigger, err error) { // {{{ return } // }}} 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 { return werr.Wrap(err).WithData(id) } @@ -177,9 +172,8 @@ func (t *Trigger) Update() (err error) { // {{{ } jsonDatapoints, _ := json.Marshal(t.Datapoints) if t.ID == 0 { - row := db.QueryRow( - context.Background(), - ` + var row *sql.Row + row = service.Db.Conn.QueryRow(` INSERT INTO "trigger"(name, section_id, expression, datapoints) VALUES($1, $2, $3, $4) RETURNING id @@ -207,9 +201,7 @@ func (t *Trigger) Update() (err error) { // {{{ return } } else { - _, err = db.Exec( - context.Background(), - ` + _, err = service.Db.Conn.Exec(` UPDATE "trigger" SET name=$2, @@ -225,26 +217,20 @@ func (t *Trigger) Update() (err error) { // {{{ ) } - var pgErr *pgconn.PgError - if errors.As(err, &pgErr) { + if pqErr, ok := err.(*pq.Error); ok { err = werr.Wrap(err).WithData( struct { Trigger *Trigger - PostgresCode string + PostgresCode pq.ErrorCode PostgresMsg string }{ t, - pgErr.Code, - pgErr.Message, + pqErr.Code, + pqErr.Code.Name(), }) - return - } - - if err != nil { + } else if err != nil { err = werr.Wrap(err).WithData(t) - return } - return } // }}} func (t *Trigger) Run() (output any, err error) { // {{{