1 package webserver
2
3 import (
4 "errors"
5 "io"
6 "net/http"
7
8 jsoniter "github.com/json-iterator/go"
9
10 "github.com/cybertec-postgresql/pgwatch/v3/internal/metrics"
11 )
12
13 func (server *WebUIServer) handleMetrics(w http.ResponseWriter, r *http.Request) {
14 var (
15 err error
16 status = http.StatusInternalServerError
17 params []byte
18 res string
19 )
20
21 defer func() {
22 if err != nil {
23 http.Error(w, err.Error(), status)
24 }
25 }()
26
27 switch r.Method {
28 case http.MethodGet:
29
30 if res, err = server.GetMetrics(); err != nil {
31 return
32 }
33 _, err = w.Write([]byte(res))
34
35 case http.MethodPost:
36
37 if params, err = io.ReadAll(r.Body); err != nil {
38 return
39 }
40
41
42 err = server.CreateMetric(params)
43 if err != nil {
44 if errors.Is(err, metrics.ErrMetricExists) {
45 status = http.StatusConflict
46 return
47 }
48 return
49 }
50 w.WriteHeader(http.StatusCreated)
51
52 case http.MethodOptions:
53 w.Header().Set("Allow", "GET, POST, OPTIONS")
54 w.WriteHeader(http.StatusOK)
55
56 default:
57 w.Header().Set("Allow", "GET, POST, OPTIONS")
58 http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
59 }
60 }
61
62
63 func (server *WebUIServer) GetMetrics() (res string, err error) {
64 var mr *metrics.Metrics
65 if mr, err = server.metricsReaderWriter.GetMetrics(); err != nil {
66 return
67 }
68 b, _ := jsoniter.ConfigFastest.Marshal(mr.MetricDefs)
69 res = string(b)
70 return
71 }
72
73
74 func (server *WebUIServer) UpdateMetric(name string, params []byte) error {
75 var m metrics.Metric
76 err := jsoniter.ConfigFastest.Unmarshal(params, &m)
77 if err != nil {
78 return err
79 }
80 return server.metricsReaderWriter.UpdateMetric(name, m)
81 }
82
83
84
85 func (server *WebUIServer) CreateMetric(params []byte) error {
86
87 var namedMetrics map[string]metrics.Metric
88 err := jsoniter.ConfigFastest.Unmarshal(params, &namedMetrics)
89 if err != nil {
90 return err
91 }
92 if len(namedMetrics) == 0 {
93 return metrics.ErrInvalidMetric
94 }
95
96
97 for metricName, metric := range namedMetrics {
98 if err := server.metricsReaderWriter.CreateMetric(metricName, metric); err != nil {
99 return err
100 }
101 }
102 return nil
103 }
104
105
106 func (server *WebUIServer) DeleteMetric(name string) error {
107 return server.metricsReaderWriter.DeleteMetric(name)
108 }
109
110
111
112 func (server *WebUIServer) handleMetricItem(w http.ResponseWriter, r *http.Request) {
113 name := r.PathValue("name")
114 if name == "" {
115 http.Error(w, "metric name is required", http.StatusBadRequest)
116 return
117 }
118
119 switch r.Method {
120 case http.MethodGet:
121 server.getMetricByName(w, name)
122 case http.MethodPut:
123 server.updateMetricByName(w, r, name)
124 case http.MethodDelete:
125 server.deleteMetricByName(w, name)
126 case http.MethodOptions:
127 w.Header().Set("Allow", "GET, PUT, DELETE, OPTIONS")
128 w.WriteHeader(http.StatusOK)
129 default:
130 w.Header().Set("Allow", "GET, PUT, DELETE, OPTIONS")
131 http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
132 }
133 }
134
135
136 func (server *WebUIServer) getMetricByName(w http.ResponseWriter, name string) {
137 var (
138 err error
139 status = http.StatusInternalServerError
140 mr *metrics.Metrics
141 )
142
143 defer func() {
144 if err != nil {
145 http.Error(w, err.Error(), status)
146 }
147 }()
148
149 if mr, err = server.metricsReaderWriter.GetMetrics(); err != nil {
150 return
151 }
152
153 if metric, exists := mr.MetricDefs[name]; exists {
154 b, _ := jsoniter.ConfigFastest.Marshal(metric)
155 w.Header().Set("Content-Type", "application/json")
156 _, err = w.Write(b)
157 return
158 }
159
160 err = metrics.ErrMetricNotFound
161 status = http.StatusNotFound
162 }
163
164
165 func (server *WebUIServer) updateMetricByName(w http.ResponseWriter, r *http.Request, name string) {
166 var (
167 err error
168 params []byte
169 status = http.StatusInternalServerError
170 )
171
172 defer func() {
173 if err != nil {
174 http.Error(w, err.Error(), status)
175 }
176 }()
177
178 if params, err = io.ReadAll(r.Body); err != nil {
179 status = http.StatusBadRequest
180 return
181 }
182
183 var m metrics.Metric
184 if err = jsoniter.ConfigFastest.Unmarshal(params, &m); err != nil {
185 status = http.StatusBadRequest
186 return
187 }
188
189 if err = server.metricsReaderWriter.UpdateMetric(name, m); err != nil {
190 return
191 }
192
193 w.WriteHeader(http.StatusOK)
194 }
195
196
197 func (server *WebUIServer) deleteMetricByName(w http.ResponseWriter, name string) {
198 if err := server.metricsReaderWriter.DeleteMetric(name); err != nil {
199 http.Error(w, err.Error(), http.StatusInternalServerError)
200 return
201 }
202
203 w.WriteHeader(http.StatusOK)
204 }
205
206 func (server *WebUIServer) handlePresets(w http.ResponseWriter, r *http.Request) {
207 var (
208 err error
209 status = http.StatusInternalServerError
210 params []byte
211 res string
212 )
213
214 defer func() {
215 if err != nil {
216 http.Error(w, err.Error(), status)
217 }
218 }()
219
220 switch r.Method {
221 case http.MethodGet:
222
223 if res, err = server.GetPresets(); err != nil {
224 return
225 }
226 _, err = w.Write([]byte(res))
227
228 case http.MethodPost:
229
230 if params, err = io.ReadAll(r.Body); err != nil {
231 return
232 }
233 err = server.CreatePreset(params)
234 if err != nil {
235 if errors.Is(err, metrics.ErrPresetExists) {
236 status = http.StatusConflict
237 }
238 return
239 }
240 w.WriteHeader(http.StatusCreated)
241
242 case http.MethodOptions:
243 w.Header().Set("Allow", "GET, POST, OPTIONS")
244 w.WriteHeader(http.StatusOK)
245
246 default:
247 w.Header().Set("Allow", "GET, POST, OPTIONS")
248 http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
249 }
250 }
251
252
253 func (server *WebUIServer) UpdatePreset(name string, params []byte) error {
254 var p metrics.Preset
255 err := jsoniter.ConfigFastest.Unmarshal(params, &p)
256 if err != nil {
257 return err
258 }
259 return server.metricsReaderWriter.UpdatePreset(name, p)
260 }
261
262
263
264 func (server *WebUIServer) CreatePreset(params []byte) error {
265
266 var namedPresets map[string]metrics.Preset
267 err := jsoniter.ConfigFastest.Unmarshal(params, &namedPresets)
268 if err != nil {
269 return err
270 }
271 if len(namedPresets) == 0 {
272 return metrics.ErrInvalidPreset
273 }
274
275
276 for presetName, preset := range namedPresets {
277 if err := server.metricsReaderWriter.CreatePreset(presetName, preset); err != nil {
278 return err
279 }
280 }
281 return nil
282 }
283
284
285 func (server *WebUIServer) GetPresets() (res string, err error) {
286 var mr *metrics.Metrics
287 if mr, err = server.metricsReaderWriter.GetMetrics(); err != nil {
288 return
289 }
290 b, _ := jsoniter.ConfigFastest.Marshal(mr.PresetDefs)
291 res = string(b)
292 return
293 }
294
295
296 func (server *WebUIServer) DeletePreset(name string) error {
297 return server.metricsReaderWriter.DeletePreset(name)
298 }
299
300
301
302 func (server *WebUIServer) handlePresetItem(w http.ResponseWriter, r *http.Request) {
303 name := r.PathValue("name")
304 if name == "" {
305 http.Error(w, "preset name is required", http.StatusBadRequest)
306 return
307 }
308
309 switch r.Method {
310 case http.MethodGet:
311 server.getPresetByName(w, name)
312 case http.MethodPut:
313 server.updatePresetByName(w, r, name)
314 case http.MethodDelete:
315 server.deletePresetByName(w, name)
316 case http.MethodOptions:
317 w.Header().Set("Allow", "GET, PUT, DELETE, OPTIONS")
318 w.WriteHeader(http.StatusOK)
319 default:
320 w.Header().Set("Allow", "GET, PUT, DELETE, OPTIONS")
321 http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
322 }
323 }
324
325
326 func (server *WebUIServer) getPresetByName(w http.ResponseWriter, name string) {
327
328 var (
329 err error
330 mr *metrics.Metrics
331 )
332
333 defer func() {
334 if err != nil {
335 http.Error(w, err.Error(), http.StatusInternalServerError)
336 }
337 }()
338
339 if mr, err = server.metricsReaderWriter.GetMetrics(); err != nil {
340 return
341 }
342
343 if preset, exists := mr.PresetDefs[name]; exists {
344 b, _ := jsoniter.ConfigFastest.Marshal(preset)
345 w.Header().Set("Content-Type", "application/json")
346 _, err = w.Write(b)
347 return
348 }
349
350 http.Error(w, "preset not found", http.StatusNotFound)
351 }
352
353
354 func (server *WebUIServer) updatePresetByName(w http.ResponseWriter, r *http.Request, name string) {
355 params, err := io.ReadAll(r.Body)
356 if err != nil {
357 http.Error(w, "invalid request body", http.StatusBadRequest)
358 return
359 }
360
361 var p metrics.Preset
362 if err := jsoniter.ConfigFastest.Unmarshal(params, &p); err != nil {
363 http.Error(w, "invalid JSON format", http.StatusBadRequest)
364 return
365 }
366
367 if err := server.metricsReaderWriter.UpdatePreset(name, p); err != nil {
368 http.Error(w, err.Error(), http.StatusInternalServerError)
369 return
370 }
371
372 w.WriteHeader(http.StatusOK)
373 }
374
375
376 func (server *WebUIServer) deletePresetByName(w http.ResponseWriter, name string) {
377 if err := server.metricsReaderWriter.DeletePreset(name); err != nil {
378 http.Error(w, err.Error(), http.StatusInternalServerError)
379 return
380 }
381
382 w.WriteHeader(http.StatusOK)
383 }
384