Files
Agent-System/internal/server/handlers_health.go
T
weichen b812d1d30f feat(dev): single-binary no-docker runtime with managed localpg
Embed web/dist, run events in-process, manage brew PG18+pgvector, add PI host sidecar and launch --no-docker; unify MiniMax-M2.7-highspeed pins and record dev acceptance.
2026-09-24 15:53:50 +00:00

105 lines
2.9 KiB
Go

package server
import (
"context"
"net/http"
"time"
)
func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) {
resp, code := s.buildHealthResponse(r.Context(), false)
writeJSONResponse(w, code, resp)
}
func (s *Server) handleReadiness(w http.ResponseWriter, r *http.Request) {
resp, code := s.buildHealthResponse(r.Context(), true)
writeJSONResponse(w, code, resp)
}
type healthSnapshot struct {
at time.Time
strict bool
resp HealthResponse
code int
ok bool
}
func (s *Server) buildHealthResponse(ctx context.Context, strict bool) (HealthResponse, int) {
const healthCacheTTL = 2 * time.Second
s.healthMu.Lock()
if s.healthCache.ok && s.healthCache.strict == strict && time.Since(s.healthCache.at) < healthCacheTTL {
resp, code := s.healthCache.resp, s.healthCache.code
s.healthMu.Unlock()
return resp, code
}
s.healthMu.Unlock()
dbCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
defer cancel()
dbReady := s.svc != nil && s.svc.DatabaseReady()
schemaVersion := int64(0)
if s.svc != nil {
if dbReady {
schemaVersion = s.svc.LiveSchemaVersion(dbCtx)
} else {
schemaVersion = s.svc.SchemaVersion()
}
}
schemaReady := schemaVersion > 0 || (s.cfg != nil && s.cfg.AllowInMemory)
taskStoreReady := s.rt != nil && s.rt.TaskStoreReady(dbCtx)
status := "ok"
code := http.StatusOK
if s.cfg != nil && s.cfg.ContextDbHost != "" && !s.cfg.AllowInMemory {
if !dbReady {
status = "degraded"
code = http.StatusServiceUnavailable
} else if strict && (!schemaReady || !taskStoreReady) {
status = "not_ready"
code = http.StatusServiceUnavailable
}
}
vaultIdentityOK, vaultConnected, vaultLagOK := s.vaultReady()
vaultConfigured := s.cfg != nil && s.cfg.VaultStateRoot != "" && s.cfg.VaultMasterKey != ""
if s.cfg != nil && s.cfg.ContextDbHost != "" && !s.cfg.AllowInMemory && dbReady && strict {
if vaultConfigured && (!vaultIdentityOK || !vaultConnected || !vaultLagOK) {
status = "not_ready"
code = http.StatusServiceUnavailable
}
}
kbReady := s.kb != nil && s.kb.Online()
eventsReady := true
if s.eventsAlive != nil {
eventsReady = s.eventsAlive()
if strict && !eventsReady && s.cfg != nil && s.cfg.ContextDbHost != "" && !s.cfg.AllowInMemory && dbReady {
status = "not_ready"
code = http.StatusServiceUnavailable
}
}
resp := HealthResponse{
Status: status,
Env: s.cfg.Env,
AgentBackend: s.cfg.AgentBackend,
Version: "v1.0",
SharedKBPort: s.cfg.KbDbPort,
KBReady: kbReady,
DatabaseReady: dbReady,
SchemaVersion: schemaVersion,
SchemaReady: schemaReady,
TaskStoreReady: taskStoreReady,
VaultIdentityOK: vaultIdentityOK,
VaultConnected: vaultConnected,
VaultLagOK: vaultLagOK,
EventsReady: eventsReady,
}
s.healthMu.Lock()
s.healthCache = healthSnapshot{at: time.Now(), strict: strict, resp: resp, code: code, ok: true}
s.healthMu.Unlock()
return resp, code
}