xtablo-source/backend/internal/web/handlers.go
Arthur Belleville 77e37cb21b
feat(07-01): embed.go + RunMigrations + HealthzHandler()/ReadyzHandler() split
- backend/embed.go: package assets with //go:embed all:static and //go:embed migrations
- backend/internal/db/migrate.go: RunMigrations using pgx/v5/stdlib bridge to goose.Up()
- backend/internal/web/handlers.go: HealthzHandler() no-arg liveness + ReadyzHandler(pinger) readiness
- backend/internal/web/handlers_test.go: TestHealthz_OK (no pinger), TestReadyz_OK, TestReadyz_Down added; TestHealthz_Down deleted
2026-05-15 18:14:26 +02:00

58 lines
1.8 KiB
Go

package web
import (
"context"
"encoding/json"
"net/http"
"time"
"backend/templates"
)
// HealthzHandler returns a pure liveness probe handler (D-12). It responds
// immediately with 200 + `{"status":"ok"}` and never contacts the database.
// Use /readyz for a DB-aware readiness probe.
func HealthzHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(map[string]string{
"status": "ok",
})
}
}
// ReadyzHandler returns an HTTP handler that probes the supplied Pinger with a
// 2-second timeout and responds per CONTEXT D-13: 200 + JSON
// `{"status":"ok","db":"ok"}` when reachable, 503 + JSON
// `{"status":"degraded","db":"down"}` otherwise.
func ReadyzHandler(pinger Pinger) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
defer cancel()
w.Header().Set("Content-Type", "application/json")
if err := pinger.Ping(ctx); err != nil {
w.WriteHeader(http.StatusServiceUnavailable)
_ = json.NewEncoder(w).Encode(map[string]string{
"status": "degraded",
"db": "down",
})
return
}
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(map[string]string{
"status": "ok",
"db": "ok",
})
}
}
// DemoTimeHandler renders the HTMX fragment for /demo/time. The `now` clock
// is injected so tests can substitute a deterministic time source; production
// passes time.Now.
func DemoTimeHandler(now func() time.Time) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
_ = templates.TimeFragment(now()).Render(r.Context(), w)
}
}