fix(backend): report ingest correctness — propagate storage failure, 413 on oversize, global body cap (closes #23)
check / check (push) Successful in 45s

A buffer failure on POST /api/v1/reports now returns 500 instead of a
false `ok`: the failure is server-side and a client can retry. An
over-limit body returns 413 (errors.As on `*http.MaxBytesError`);
malformed JSON stays 400. A MaxBodyBytes middleware (1 MiB) caps every
route; a route group can only lower that limit. The raw geo blob is no
longer logged, only its length; client_id, timestamp and decode error
text are length-bounded before logging. A decodeJSON handler helper is
added. Panic recovery routes the stack through slog as structured
JSON. Writing a report file now returns its error, so a failed final
flush fails the stop and the process exits non-zero.

Model: opus-5-5
This commit is contained in:
2026-09-28 18:05:57 +00:00
parent 7a1ee6e5a8
commit 8cdc68e5ba
13 changed files with 737 additions and 55 deletions
+20 -1
View File
@@ -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)
}