// Package handlers implements HTTP request handlers for the // netwatch-server API. package handlers import ( "context" "encoding/json" "log/slog" "net/http" "sneak.berlin/go/netwatch/internal/globals" "sneak.berlin/go/netwatch/internal/healthcheck" "sneak.berlin/go/netwatch/internal/logger" "sneak.berlin/go/netwatch/internal/reportbuf" "go.uber.org/fx" ) const jsonContentType = "application/json; charset=utf-8" // reportAppender is the subset of the report buffer the handlers // depend on. Defining it here keeps the storage failure path // exercisable with a stub in tests. type reportAppender interface { Append(v any) error } // Params defines the dependencies for Handlers. type Params struct { fx.In Buffer *reportbuf.Buffer Globals *globals.Globals Healthcheck *healthcheck.Healthcheck Logger *logger.Logger } // Handlers provides HTTP handler factories for all endpoints. type Handlers struct { buf reportAppender hc *healthcheck.Healthcheck log *slog.Logger params *Params } // New creates a Handlers instance. func New( lc fx.Lifecycle, params Params, ) (*Handlers, error) { s := new(Handlers) s.buf = params.Buffer s.params = ¶ms s.log = params.Logger.Get() s.hc = params.Healthcheck lc.Append(fx.Hook{ OnStart: func(_ context.Context) error { return nil }, }) return s, nil } func (s *Handlers) respondJSON( w http.ResponseWriter, _ *http.Request, data any, status int, ) { w.Header().Set("Content-Type", jsonContentType) w.WriteHeader(status) if data != nil { err := json.NewEncoder(w).Encode(data) if err != nil { s.log.Error("json encode error", "error", err) } } } // decodeJSON decodes the request body into v. The body is // expected to already be bounded by the body-size middleware, so // a caller can distinguish an over-limit body from malformed // JSON by testing the returned error for *http.MaxBytesError. func (s *Handlers) decodeJSON( _ http.ResponseWriter, r *http.Request, v any, ) error { return json.NewDecoder(r.Body).Decode(v) }