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>
68 lines
1.8 KiB
Go
68 lines
1.8 KiB
Go
package httpx
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"runtime"
|
|
"time"
|
|
)
|
|
|
|
// ReadinessCheck is a named dependency probe run by /readyz. Required checks
|
|
// (Mongo) gate readiness; optional ones (atomizer, work performer) are
|
|
// reported but non-fatal, per §6.
|
|
type ReadinessCheck struct {
|
|
Name string
|
|
Required bool
|
|
Probe func(ctx context.Context) error
|
|
}
|
|
|
|
// AddReadinessCheck registers a probe; later phases plug in Mongo and the
|
|
// external services here.
|
|
func (s *Server) AddReadinessCheck(c ReadinessCheck) {
|
|
s.checks = append(s.checks, c)
|
|
}
|
|
|
|
func (s *Server) handleHealthz(w http.ResponseWriter, _ *http.Request) {
|
|
writeJSON(w, http.StatusOK, map[string]string{"status": "ok"})
|
|
}
|
|
|
|
type checkResult struct {
|
|
OK bool `json:"ok"`
|
|
Required bool `json:"required"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
func (s *Server) handleReadyz(w http.ResponseWriter, r *http.Request) {
|
|
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
status := "ok"
|
|
httpStatus := http.StatusOK
|
|
results := make(map[string]checkResult, len(s.checks))
|
|
for _, c := range s.checks {
|
|
res := checkResult{OK: true, Required: c.Required}
|
|
if err := c.Probe(ctx); err != nil {
|
|
res.OK = false
|
|
res.Error = err.Error()
|
|
if c.Required {
|
|
status = "unavailable"
|
|
httpStatus = http.StatusServiceUnavailable
|
|
} else if status == "ok" {
|
|
status = "degraded"
|
|
}
|
|
}
|
|
results[c.Name] = res
|
|
}
|
|
writeJSON(w, httpStatus, map[string]any{"status": status, "checks": results})
|
|
}
|
|
|
|
func (s *Server) handleMetricsz(w http.ResponseWriter, _ *http.Request) {
|
|
counters, gauges := s.metrics.Snapshot()
|
|
writeJSON(w, http.StatusOK, map[string]any{
|
|
"uptimeSec": int64(time.Since(s.startedAt).Seconds()),
|
|
"goroutines": runtime.NumGoroutine(),
|
|
"counters": counters,
|
|
"gauges": gauges,
|
|
})
|
|
}
|