From 2c5b1c1dd6b4d5547a3e9f3d6ab28ed15a00b4c2 Mon Sep 17 00:00:00 2001 From: ITQ Date: Tue, 11 Nov 2025 21:39:01 +0300 Subject: [PATCH] feat: added healthcheck --- internal/handler/http/health.go | 63 +++++++++++++++++++++++++++++++++ internal/server/server.go | 7 +++- 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 internal/handler/http/health.go diff --git a/internal/handler/http/health.go b/internal/handler/http/health.go new file mode 100644 index 0000000..498c6f2 --- /dev/null +++ b/internal/handler/http/health.go @@ -0,0 +1,63 @@ +package http + +import ( + "context" + "encoding/json" + "net/http" + "time" + + "github.com/jmoiron/sqlx" + "github.com/redis/go-redis/v9" +) + +type HealthHandler struct { + DB *sqlx.DB + Redis *redis.Client +} + +func NewHealthHandler(db *sqlx.DB, redisDB *redis.Client) *HealthHandler { + return &HealthHandler{ + DB: db, + Redis: redisDB, + } +} + +type HealthResponse struct { + Status string `json:"status"` + Details map[string]string `json:"details"` +} + +func (h *HealthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second) + defer cancel() + + details := map[string]string{} + + if err := h.DB.PingContext(ctx); err != nil { + details["postgres"] = "unhealthy: " + err.Error() + } else { + details["postgres"] = "ok" + } + + if err := h.Redis.Ping(ctx).Err(); err != nil { + details["redis"] = "unhealthy: " + err.Error() + } else { + details["redis"] = "ok" + } + + status := "ok" + for _, v := range details { + if v != "ok" { + status = "unhealthy" + break + } + } + + resp := HealthResponse{Status: status, Details: details} + + w.Header().Set("Content-Type", "application/json") + if status != "ok" { + w.WriteHeader(http.StatusServiceUnavailable) + } + _ = json.NewEncoder(w).Encode(resp) +} diff --git a/internal/server/server.go b/internal/server/server.go index 1cfbbac..2d8e380 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -11,6 +11,7 @@ import ( "orderservice/internal/interceptor" grpcHandlers "orderservice/internal/handler/grpc" + httpHandlers "orderservice/internal/handler/http" orderPostgresRepo "orderservice/internal/repository/postgres" "orderservice/internal/service" @@ -57,8 +58,12 @@ func runHTTPHandler(s *Server, grpcServerEndpoint *string) error { return err } + mux := http.NewServeMux() + mux.Handle("/healthz", httpHandlers.NewHealthHandler(s.db, s.redisDB)) + mux.Handle("/", gwmux) + addr := fmt.Sprintf(":%d", s.config.HTTPPort) - return http.ListenAndServe(addr, gwmux) + return http.ListenAndServe(addr, mux) } func getDatabase(cfg config.Config) (*sqlx.DB, error) {