package httpserver import ( "context" "encoding/json" "net/http" "time" "solopm.com/solopm-server/internal/config" ) // DBPinger is the subset of *pgxpool.Pool used for health checks. type DBPinger interface { Ping(ctx context.Context) error } type healthResponse struct { Status string `json:"status"` Version string `json:"version"` Database string `json:"database"` Meilisearch string `json:"meilisearch"` SMTP string `json:"smtp"` } func healthzHandler(cfg *config.Config, db DBPinger, version string) http.HandlerFunc { httpClient := &http.Client{Timeout: 2 * time.Second} return func(w http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second) defer cancel() resp := healthResponse{ Status: "ok", Version: version, Meilisearch: "absent", SMTP: "absent", } if err := db.Ping(ctx); err != nil { resp.Status = "error" resp.Database = "unreachable" } else { resp.Database = "ok" } if cfg.SearchEnabled() { req, err := http.NewRequestWithContext(ctx, http.MethodGet, cfg.MeiliURL+"/health", nil) if err == nil { if hr, err := httpClient.Do(req); err == nil { _ = hr.Body.Close() if hr.StatusCode == http.StatusOK { resp.Meilisearch = "ok" } else { resp.Meilisearch = "unreachable" } } else { resp.Meilisearch = "unreachable" } } } if cfg.EmailEnabled() { resp.SMTP = "ok" } status := http.StatusOK if resp.Status != "ok" { status = http.StatusServiceUnavailable } w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(resp) } }