package health import ( "context" "database/sql" "encoding/json" "log/slog" "net/http" "time" ) type Checker struct { db *sql.DB logger *slog.Logger } func New(db *sql.DB, logger *slog.Logger) *Checker { return &Checker{ db: db, logger: logger, } } type HealthStatus struct { Status string `json:"status"` Checks map[string]string `json:"checks,omitempty"` } // LivenessHandler checks if the application is running // This is a simple check that always returns OK if the handler is reached func (h *Checker) LivenessHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _ = json.NewEncoder(w).Encode(HealthStatus{ Status: "UP", }) } // ReadinessHandler checks if the application is ready to accept traffic // This checks database connectivity and other critical dependencies func (h *Checker) ReadinessHandler(w http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second) defer cancel() checks := make(map[string]string) allHealthy := true // Check database connectivity if err := h.db.PingContext(ctx); err != nil { h.logger.With("error", err).Warn("database health check failed") checks["database"] = "DOWN" allHealthy = false } else { checks["database"] = "UP" } status := HealthStatus{ Status: "UP", Checks: checks, } if !allHealthy { status.Status = "DOWN" w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusServiceUnavailable) _ = json.NewEncoder(w).Encode(status) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _ = json.NewEncoder(w).Encode(status) }