fix(backend): report ingest correctness — propagate storage failure, 413 on oversize, global body cap (closes #23)
check / check (push) Failing after 1s
check / check (push) Failing after 1s
A buffer failure on POST /api/v1/reports now returns 500 instead of a
false {"status":"ok"}, so clients can retry. Decode errors are split:
an over-limit body returns 413 (via errors.As on *http.MaxBytesError),
malformed JSON stays 400. A new MaxBodyBytes middleware caps every
route (1 MiB default; rejects an oversized Content-Length up-front and
caps the read otherwise), replacing the per-route reader so the health
check and future routes are bounded too. The raw attacker-controlled
geo blob is no longer logged — only its byte length — and client_id and
timestamp are length-bounded before logging. A decodeJSON handler helper
is added per the HTTP server conventions. Panic recovery is now a local
middleware that routes the stack through slog as structured JSON rather
than chi's plain-text stderr. Error bodies still leak nothing internal.
Chosen failure code for a storage failure: 500, since a full buffer or
write error is server-side and retryable, not the client's fault.
Model: opus-4-8
This commit is contained in:
@@ -18,6 +18,13 @@ import (
|
||||
|
||||
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
|
||||
@@ -30,7 +37,7 @@ type Params struct {
|
||||
|
||||
// Handlers provides HTTP handler factories for all endpoints.
|
||||
type Handlers struct {
|
||||
buf *reportbuf.Buffer
|
||||
buf reportAppender
|
||||
hc *healthcheck.Healthcheck
|
||||
log *slog.Logger
|
||||
params *Params
|
||||
@@ -72,3 +79,15 @@ func (s *Handlers) respondJSON(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user