package api import ( "context" "fmt" "log/slog" "net/http" "time" "github.com/TopherMayor/unified-media-manager/internal/config" "github.com/TopherMayor/unified-media-manager/internal/db" "github.com/labstack/echo/v4" ) func healthLive(c echo.Context) error { return c.JSON(http.StatusOK, map[string]string{"status": "alive"}) } func healthReady(database *db.DB, cfg *config.Config) echo.HandlerFunc { return func(c echo.Context) error { ctx, cancel := context.WithTimeout(c.Request().Context(), 5*time.Second) defer cancel() checks := map[string]string{} allReady := true if err := database.Ping(ctx); err != nil { checks["database"] = "unhealthy: " + err.Error() allReady = false slog.Error("readiness check failed", "component", "database", "error", err) } else { checks["database"] = "healthy" } if err := checkHTTPService(ctx, cfg.QdrantURL+"/healthz"); err != nil { checks["qdrant"] = "unhealthy: " + err.Error() allReady = false slog.Error("readiness check failed", "component", "qdrant", "error", err) } else { checks["qdrant"] = "healthy" } if err := checkHTTPService(ctx, cfg.OllamaURL+"/api/version"); err != nil { checks["ollama"] = "unhealthy: " + err.Error() allReady = false slog.Error("readiness check failed", "component", "ollama", "error", err) } else { checks["ollama"] = "healthy" } status := "healthy" code := http.StatusOK if !allReady { status = "degraded" code = http.StatusServiceUnavailable } return c.JSON(code, map[string]interface{}{"status": status, "checks": checks}) } } func checkHTTPService(ctx context.Context, url string) error { client := &http.Client{Timeout: 3 * time.Second} req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return fmt.Errorf("build request: %w", err) } resp, err := client.Do(req) if err != nil { return fmt.Errorf("request failed: %w", err) } resp.Body.Close() if resp.StatusCode >= 500 { return fmt.Errorf("server returned %d", resp.StatusCode) } return nil }