976f5238f8
- internal .env loader (real env wins) + strictly validated config struct - ULID package: 48-bit ms timestamp + 80-bit crypto randomness, sortable - HTTP server with recovery/request-id/trusted-proxy/access-log middleware - /healthz, /readyz (pluggable checkers), /metricsz (counter registry) - multi-stage Dockerfile (alpine, non-root) + compose with healthchecks, app bound to 127.0.0.1:8787, mongo unpublished, graceful 15s shutdown Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
35 lines
1.0 KiB
Go
35 lines
1.0 KiB
Go
// Package httpx implements the application HTTP server: routing, middleware
|
|
// (recovery, request id, trusted-proxy client resolution, access logging),
|
|
// health endpoints, and shared JSON response helpers. Named httpx to avoid
|
|
// clashing with net/http; it lives in internal/http per the repository layout.
|
|
package httpx
|
|
|
|
import (
|
|
"encoding/json"
|
|
"log/slog"
|
|
"net/http"
|
|
)
|
|
|
|
// errorEnvelope is the consistent error body for every JSON error response:
|
|
// {"error":{"code":"...","message":"..."}}.
|
|
type errorEnvelope struct {
|
|
Error errorBody `json:"error"`
|
|
}
|
|
|
|
type errorBody struct {
|
|
Code string `json:"code"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
func writeJSON(w http.ResponseWriter, status int, v any) {
|
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
w.WriteHeader(status)
|
|
if err := json.NewEncoder(w).Encode(v); err != nil {
|
|
slog.Error("write json response", "err", err)
|
|
}
|
|
}
|
|
|
|
func writeError(w http.ResponseWriter, status int, code, message string) {
|
|
writeJSON(w, status, errorEnvelope{Error: errorBody{Code: code, Message: message}})
|
|
}
|