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.
105 lines
2.9 KiB
Go
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
|
|
}
|