Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
package httpapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/descrybe/descrybe-v2/apps/api/internal/jobs"
|
||||
)
|
||||
|
||||
const healthServiceName = "api"
|
||||
|
||||
// dbPinger is satisfied by *pgxpool.Pool; kept narrow for unit tests.
|
||||
type dbPinger interface {
|
||||
Ping(ctx context.Context) error
|
||||
}
|
||||
|
||||
func (s *Server) handleHealthz(w http.ResponseWriter, _ *http.Request) {
|
||||
JSON(w, http.StatusOK, map[string]any{
|
||||
"status": "ok",
|
||||
"service": healthServiceName,
|
||||
"maintenance": s.Config.MaintenanceMode,
|
||||
"read_only": s.Config.ReadOnlyMode,
|
||||
"hypercare": s.Config.HypercareMode,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) handleReadyz(w http.ResponseWriter, r *http.Request) {
|
||||
var pinger dbPinger
|
||||
var prober jobs.HeartbeatQuerier
|
||||
if s.Pool != nil {
|
||||
pinger = s.Pool
|
||||
prober = s.Pool
|
||||
}
|
||||
s.writeReadyz(w, r, pinger, prober)
|
||||
}
|
||||
|
||||
func (s *Server) writeReadyz(w http.ResponseWriter, r *http.Request, pinger dbPinger, prober jobs.HeartbeatQuerier) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
|
||||
defer cancel()
|
||||
|
||||
ok, check, errMsg := databaseReady(ctx, pinger)
|
||||
checks := map[string]string{"database": check}
|
||||
body := map[string]any{
|
||||
"status": "ready",
|
||||
"service": healthServiceName,
|
||||
"maintenance": s.Config.MaintenanceMode,
|
||||
"read_only": s.Config.ReadOnlyMode,
|
||||
"hypercare": s.Config.HypercareMode,
|
||||
"checks": checks,
|
||||
}
|
||||
if !ok {
|
||||
body["status"] = "not_ready"
|
||||
body["error"] = errMsg
|
||||
JSON(w, http.StatusServiceUnavailable, body)
|
||||
return
|
||||
}
|
||||
|
||||
probe := jobs.ProbeWorkerReadiness(ctx, prober, jobs.DefaultHeartbeatStaleAfter)
|
||||
checks["worker"] = probe.WorkerCheck
|
||||
checks["queue"] = probe.QueueCheck
|
||||
body["queue_pending"] = probe.PendingJobs
|
||||
if probe.LastSeenAgeS >= 0 {
|
||||
body["worker_last_seen_age_s"] = probe.LastSeenAgeS
|
||||
}
|
||||
if !probe.OK {
|
||||
body["status"] = "not_ready"
|
||||
body["error"] = probe.ErrMsg
|
||||
if probe.Reason != "" {
|
||||
body["reason"] = probe.Reason
|
||||
}
|
||||
JSON(w, http.StatusServiceUnavailable, body)
|
||||
return
|
||||
}
|
||||
|
||||
JSON(w, http.StatusOK, body)
|
||||
}
|
||||
|
||||
// databaseReady pings Postgres for readiness. check is "ok", "unavailable", or "fail".
|
||||
// errMsg is empty when ok; never includes driver detail (safe for public probes).
|
||||
func databaseReady(ctx context.Context, p dbPinger) (ok bool, check string, errMsg string) {
|
||||
if p == nil {
|
||||
return false, "unavailable", "database pool unavailable"
|
||||
}
|
||||
if err := p.Ping(ctx); err != nil {
|
||||
return false, "fail", "database ping failed"
|
||||
}
|
||||
return true, "ok", ""
|
||||
}
|
||||
Reference in New Issue
Block a user