Introduce the Go backend (netwatch-server) with an HTTP API that accepts telemetry reports and persists them as zstd-compressed JSONL files. Reports are buffered in memory and flushed to disk when the buffer reaches 10 MiB or every 60 seconds.
83 lines
1.6 KiB
Go
83 lines
1.6 KiB
Go
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
)
|
|
|
|
const maxReportBodyBytes = 1 << 20 // 1 MiB
|
|
|
|
type reportSample struct {
|
|
T int64 `json:"t"`
|
|
Latency *int `json:"latency"`
|
|
Error *string `json:"error"`
|
|
}
|
|
|
|
type reportHost struct {
|
|
History []reportSample `json:"history"`
|
|
Name string `json:"name"`
|
|
Status string `json:"status"`
|
|
URL string `json:"url"`
|
|
}
|
|
|
|
type report struct {
|
|
ClientID string `json:"clientId"`
|
|
Geo json.RawMessage `json:"geo"`
|
|
Hosts []reportHost `json:"hosts"`
|
|
Timestamp string `json:"timestamp"`
|
|
}
|
|
|
|
// HandleReport returns a handler that accepts telemetry
|
|
// reports from NetWatch clients.
|
|
func (s *Handlers) HandleReport() http.HandlerFunc {
|
|
type response struct {
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
r.Body = http.MaxBytesReader(
|
|
w, r.Body, maxReportBodyBytes,
|
|
)
|
|
|
|
var rpt report
|
|
|
|
err := json.NewDecoder(r.Body).Decode(&rpt)
|
|
if err != nil {
|
|
s.log.Error("failed to decode report",
|
|
"error", err,
|
|
)
|
|
s.respondJSON(w, r,
|
|
&response{Status: "error"},
|
|
http.StatusBadRequest,
|
|
)
|
|
|
|
return
|
|
}
|
|
|
|
totalSamples := 0
|
|
for _, h := range rpt.Hosts {
|
|
totalSamples += len(h.History)
|
|
}
|
|
|
|
s.log.Info("report received",
|
|
"client_id", rpt.ClientID,
|
|
"timestamp", rpt.Timestamp,
|
|
"host_count", len(rpt.Hosts),
|
|
"total_samples", totalSamples,
|
|
"geo", string(rpt.Geo),
|
|
)
|
|
|
|
bufErr := s.buf.Append(rpt)
|
|
if bufErr != nil {
|
|
s.log.Error("failed to buffer report",
|
|
"error", bufErr,
|
|
)
|
|
}
|
|
|
|
s.respondJSON(w, r,
|
|
&response{Status: "ok"},
|
|
http.StatusOK,
|
|
)
|
|
}
|
|
}
|