diff --git a/config/payouts.properties b/config/payouts.properties index 6edb3d6..7786e64 100644 --- a/config/payouts.properties +++ b/config/payouts.properties @@ -7,11 +7,6 @@ Server.Tls.Enabled = false Server.Tls.CertFile = Server.Tls.KeyFile = -Socket.MaxHttpBufferSize = 2097152 -Socket.PingInterval = 25s -Socket.PingTimeout = 20s -Socket.Debug = false - # Prometheus settings Metrics.Endpoint = /metrics Metrics.HistogramBuckets = 0.001,0.002,0.005,0.01,0.025,0.05,0.1,0.25,0.5,1,2.5,5,10 @@ -64,4 +59,4 @@ YooKassa.ApiBaseSecret = YooKassa.ApiPaymentKey = YooKassa.ApiPaymentSecret = # Timeout to process yookassa callback -YooKassa.CallbackProcessTimeout = 1s \ No newline at end of file +YooKassa.CallbackProcessTimeout = 1s diff --git a/internal/api/health/health_handler.go b/internal/api/health/health_handler.go new file mode 100644 index 0000000..140a64d --- /dev/null +++ b/internal/api/health/health_handler.go @@ -0,0 +1,46 @@ +package health + +import ( + "encoding/json" + "fmt" + "log/slog" + "net/http" + + "payouts/internal/service/database" +) + +// Route health route +const Route = "/health" + +// New constructs a new health Handler. +func New(dbService database.Service) (Handler, error) { + return &handler{ + dbService: dbService, + }, nil +} + +type handler struct { + dbService database.Service +} + +// HealthHandler handles the health check requests +func (h *handler) Health(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + w.Header().Set("Content-Type", "application/json") + + status := map[string]any{} + + // Check database connection + err := h.dbService.HealthCheck() + if err != nil { + w.WriteHeader(http.StatusServiceUnavailable) + status["Error"] = fmt.Sprintf("%v", err) + slog.Error("Health check failed", slog.String("error", status["Error"].(string))) + } else { + w.WriteHeader(http.StatusOK) + } + status["OK"] = (err == nil) + + encoder := json.NewEncoder(w) + encoder.Encode(status) +} diff --git a/internal/api/health/module.go b/internal/api/health/module.go new file mode 100644 index 0000000..74a6041 --- /dev/null +++ b/internal/api/health/module.go @@ -0,0 +1,16 @@ +package health + +import ( + "net/http" + + "go.uber.org/fx" +) + +var Module = fx.Options( + fx.Provide(New), +) + +// Handler health handler interface +type Handler interface { + Health(http.ResponseWriter, *http.Request) +} diff --git a/internal/api/module.go b/internal/api/module.go index 904ffb3..0d6dcf1 100644 --- a/internal/api/module.go +++ b/internal/api/module.go @@ -11,6 +11,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" "go.uber.org/fx" + "payouts/internal/api/health" "payouts/internal/api/payout" "payouts/internal/api/user" "payouts/internal/api/version" @@ -21,6 +22,7 @@ import ( // Module is a fx module var Module = fx.Options( user.Module, + health.Module, payout.Module, version.Module, monitoring.Module, @@ -40,6 +42,7 @@ type Params struct { PayoutHandler payout.Handler UserHandler user.Handler Version version.Handler + HealthHandler health.Handler Metrics monitoring.Metrics } @@ -50,7 +53,10 @@ func RegisterRoutes(p Params, lc fx.Lifecycle) { router := mux.NewRouter() router.StrictSlash(true) + // Version endpoint router.HandleFunc(version.Route, p.Version.VersionHandler).Methods(http.MethodGet) + // Health check endpoint + router.HandleFunc(health.Route, p.HealthHandler.Health).Methods(http.MethodGet) if p.AppConfig.Server.EnablePProfEndpoints { router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) diff --git a/internal/api/version/handler.go b/internal/api/version/version_handler.go similarity index 71% rename from internal/api/version/handler.go rename to internal/api/version/version_handler.go index 0cc3e4b..96e4bdb 100644 --- a/internal/api/version/handler.go +++ b/internal/api/version/version_handler.go @@ -3,6 +3,7 @@ package version import ( "io" "net/http" + "runtime/debug" "go.uber.org/fx" @@ -39,7 +40,20 @@ type handler struct { func (h *handler) VersionHandler(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() + ver := h.version + if ver == "unknown" { + buildInfo, ok := debug.ReadBuildInfo() + if ok { + for _, setting := range buildInfo.Settings { + if setting.Key == "vcs.revision" { + ver = ver + "-" + setting.Value + break + } + } + } + } + w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) - io.WriteString(w, h.version) + io.WriteString(w, ver+"\n") } diff --git a/internal/service/database/db_service.go b/internal/service/database/db_service.go index 80de256..9ee8ad1 100644 --- a/internal/service/database/db_service.go +++ b/internal/service/database/db_service.go @@ -109,3 +109,17 @@ func (d *dbService) UpdatePayoutByPayoutID(payoutId string, updateModel orm.Payo p := d.getParams(opts...) return gorm.G[orm.Payout](d.db).Where("payout_id = ?", payoutId).Updates(p.ctx, updateModel) } + +// HealthCheck implements [Service]. +func (d *dbService) HealthCheck() error { + if d.db == nil { + return errors.New("database connection is nil") + } + + db, err := d.db.DB() + if err != nil { + return err + } + + return db.Ping() +} diff --git a/internal/service/database/module.go b/internal/service/database/module.go index a5eceb2..00e2bfd 100644 --- a/internal/service/database/module.go +++ b/internal/service/database/module.go @@ -31,6 +31,7 @@ type Service interface { CreatePayout(payoutModel *orm.Payout, opts ...Optional) error UpdatePayoutById(id uint, updateModel orm.Payout, opts ...Optional) (int, error) UpdatePayoutByPayoutID(payoutId string, updateModel orm.Payout, opts ...Optional) (int, error) + HealthCheck() error } // Params represents the module input params diff --git a/internal/service/monitoring/config/metrics.go b/internal/service/monitoring/config/metrics.go index 761aa29..4877c8b 100644 --- a/internal/service/monitoring/config/metrics.go +++ b/internal/service/monitoring/config/metrics.go @@ -1,10 +1,3 @@ -/* - * Copyright (c) New Cloud Technologies, Ltd., 2013-2026 - * - * You can not use the contents of the file in any way without New Cloud Technologies Ltd. written permission. - * To obtain such a permit, you should contact New Cloud Technologies, Ltd. at https://myoffice.ru/contacts/ - */ - package config import ( diff --git a/internal/version/version.txt b/internal/version/version.txt index 3546645..87edf79 100644 --- a/internal/version/version.txt +++ b/internal/version/version.txt @@ -1 +1 @@ -unknown +unknown \ No newline at end of file