diff --git a/src/api.go b/src/api.go index fd0bb2386..1fae02c80 100644 --- a/src/api.go +++ b/src/api.go @@ -8,6 +8,54 @@ type uiapihandler struct{} var uiapi uiapihandler +// GetMetrics returns the list of monitored databases +func (uiapi uiapihandler) GetMetrics() (res string, err error) { + sql := `select coalesce(jsonb_agg(to_jsonb(m)), '[]') from metric m` + err = configDb.Get(&res, sql) + return +} + +// DeleteMetric removes the database from the list of monitored databases +func (uiapi uiapihandler) DeleteMetric(id int) error { + _, err := configDb.Exec("DELETE FROM pgwatch3.metric WHERE m_id = $1", id) + return err +} + +// AddMetric adds the metric to the list of stored metrics +func (uiapi uiapihandler) AddMetric(params []byte) error { + sql := `INSERT INTO pgwatch3.metric( +m_name, m_pg_version_from, m_sql, m_comment, m_is_active, m_is_helper, +m_master_only, m_standby_only, m_column_attrs, m_sql_su) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)` + var m map[string]any + err := json.Unmarshal(params, &m) + if err == nil { + _, err = configDb.Exec(sql, m["m_name"], m["m_pg_version_from"], + m["m_sql"], m["m_comment"], m["m_is_active"], + m["m_is_helper"], m["m_master_only"], m["m_standby_only"], + m["m_column_attrs"], m["m_sql_su"]) + } + return err +} + +// UpdateMetric updates the stored metric information +func (uiapi uiapihandler) UpdateMetric(id int, params []byte) error { + sql := `UPDATE pgwatch3.metric( +m_name, m_pg_version_from, m_sql, m_comment, m_is_active, m_is_helper, +m_master_only, m_standby_only, m_column_attrs, m_sql_su) += ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) +WHERE m_id = $11` + var m map[string]any + err := json.Unmarshal(params, &m) + if err == nil { + _, err = configDb.Exec(sql, m["m_name"], m["m_pg_version_from"], + m["m_sql"], m["m_comment"], m["m_is_active"], + m["m_is_helper"], m["m_master_only"], m["m_standby_only"], + m["m_column_attrs"], m["m_sql_su"], id) + } + return err +} + // GetDatabases returns the list of monitored databases func (uiapi uiapihandler) GetDatabases() (res string, err error) { sql := `select coalesce(jsonb_agg(to_jsonb(db)), '[]') from monitored_db db` diff --git a/src/webserver/restapi.go b/src/webserver/restapi.go index 23bcdf3b8..399720e49 100644 --- a/src/webserver/restapi.go +++ b/src/webserver/restapi.go @@ -3,8 +3,65 @@ package webserver import ( "io" "net/http" + "strconv" ) +func (Server *WebUIServer) handleMetrics(w http.ResponseWriter, r *http.Request) { + var ( + err error + params []byte + res string + id int + ) + + defer func() { + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + }() + + switch r.Method { + case http.MethodGet: + // return stored metrics + if res, err = Server.api.GetMetrics(); err != nil { + return + } + _, err = w.Write([]byte(res)) + + case http.MethodPost: + // add new stored metric + if params, err = io.ReadAll(r.Body); err != nil { + return + } + err = Server.api.AddMetric(params) + + case http.MethodPatch: + // update monitored database + if params, err = io.ReadAll(r.Body); err != nil { + return + } + if id, err = strconv.Atoi(r.URL.Query().Get("id")); err != nil { + return + } + err = Server.api.UpdateMetric(id, params) + + case http.MethodDelete: + // delete stored metric + if id, err = strconv.Atoi(r.URL.Query().Get("id")); err != nil { + return + } + err = Server.api.DeleteMetric(id) + + case http.MethodOptions: + w.Header().Set("Allow", "GET, POST, PATCH, DELETE, OPTIONS") + w.WriteHeader(http.StatusNoContent) + + default: + w.Header().Set("Allow", "GET, POST, PATCH, DELETE, OPTIONS") + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + } +} + func (Server *WebUIServer) handleDBs(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: @@ -21,6 +78,7 @@ func (Server *WebUIServer) handleDBs(w http.ResponseWriter, r *http.Request) { p, err := io.ReadAll(r.Body) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) + return } if err := Server.api.AddDatabase(p); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) @@ -30,6 +88,7 @@ func (Server *WebUIServer) handleDBs(w http.ResponseWriter, r *http.Request) { p, err := io.ReadAll(r.Body) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) + return } if err := Server.api.UpdateDatabase(r.URL.Query().Get("id"), p); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) diff --git a/src/webserver/server.go b/src/webserver/server.go index 4a6a3824e..4b8a80d02 100644 --- a/src/webserver/server.go +++ b/src/webserver/server.go @@ -19,6 +19,10 @@ type apiHandler interface { AddDatabase(params []byte) error DeleteDatabase(id string) error UpdateDatabase(id string, params []byte) error + GetMetrics() (res string, err error) + AddMetric(params []byte) error + DeleteMetric(id int) error + UpdateMetric(id int, params []byte) error } type WebUIServer struct { @@ -50,6 +54,7 @@ func Init(addr string, webuifs fs.FS, api apiHandler) *WebUIServer { mux.HandleFunc("/health", s.handleHealth) mux.HandleFunc("/db", s.handleDBs) + mux.HandleFunc("/metric", s.handleMetrics) mux.HandleFunc("/", s.handleStatic) if 8080 != 0 {