// Package healthcheck provides a service that reports // application health, uptime, and version information. package healthcheck import ( "context" "log/slog" "time" "sneak.berlin/go/netwatch/internal/config" "sneak.berlin/go/netwatch/internal/globals" "sneak.berlin/go/netwatch/internal/logger" "go.uber.org/fx" ) // Params defines the dependencies for Healthcheck. type Params struct { fx.In Config *config.Config Globals *globals.Globals Logger *logger.Logger } // Healthcheck tracks startup time and builds health responses. type Healthcheck struct { StartupTime time.Time log *slog.Logger params *Params } // Response is the JSON payload returned by the health check // endpoint. type Response struct { Appname string `json:"appname"` Now string `json:"now"` Status string `json:"status"` UptimeHuman string `json:"uptimeHuman"` UptimeSeconds int64 `json:"uptimeSeconds"` Version string `json:"version"` } // New creates a Healthcheck, recording startup time via an // fx lifecycle hook. func New( lc fx.Lifecycle, params Params, ) (*Healthcheck, error) { s := new(Healthcheck) s.params = ¶ms s.log = params.Logger.Get() lc.Append(fx.Hook{ OnStart: func(_ context.Context) error { s.StartupTime = time.Now().UTC() return nil }, OnStop: func(_ context.Context) error { return nil }, }) return s, nil } // Check returns the current health status of the application. func (s *Healthcheck) Check() *Response { return &Response{ Appname: s.params.Globals.Appname, Now: time.Now().UTC().Format(time.RFC3339Nano), Status: "ok", UptimeHuman: s.uptime().String(), UptimeSeconds: int64(s.uptime().Seconds()), Version: s.params.Globals.Version, } } func (s *Healthcheck) uptime() time.Duration { return time.Since(s.StartupTime) }