// Package httpx holds the tiny JSON helpers shared by the public and admin APIs. package httpx import ( "encoding/json" "io" "net/http" "strconv" ) type ErrorResponse struct { Error string `json:"error"` } func WriteJSON(w http.ResponseWriter, status int, v any) { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(v) } func OK(w http.ResponseWriter, v any) { WriteJSON(w, http.StatusOK, v) } func Created(w http.ResponseWriter, v any) { WriteJSON(w, http.StatusCreated, v) } func Error(w http.ResponseWriter, status int, msg string) { WriteJSON(w, status, ErrorResponse{Error: msg}) } func BadRequest(w http.ResponseWriter, msg string) { Error(w, http.StatusBadRequest, msg) } func NotFound(w http.ResponseWriter) { Error(w, http.StatusNotFound, "not found") } func Unauthorized(w http.ResponseWriter) { Error(w, http.StatusUnauthorized, "unauthorized") } func ServerError(w http.ResponseWriter, err error) { Error(w, http.StatusInternalServerError, err.Error()) } func Decode(r *http.Request, dst any) error { body, err := io.ReadAll(io.LimitReader(r.Body, 8<<20)) if err != nil { return err } if len(body) == 0 { return io.EOF } return json.Unmarshal(body, dst) } func QueryInt(r *http.Request, key string, def int) int { s := r.URL.Query().Get(key) if s == "" { return def } n, err := strconv.Atoi(s) if err != nil || n < 0 { return def } return n } func QueryString(r *http.Request, key string) string { return r.URL.Query().Get(key) }