Skip to content

Commit

Permalink
Merge pull request #16 from openfort-xyz/feat/health-endpoint
Browse files Browse the repository at this point in the history
feat: add health endpoint
  • Loading branch information
gllm-dev authored Dec 12, 2024
2 parents 25e6237 + ad98b44 commit 50c8df0
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v0.1.24]
### Added
- Health check endpoint

## [v0.1.23]
### Fixed
- Update crypto to v0.31.0 package because of CVE-2024-45337
Expand Down
11 changes: 11 additions & 0 deletions di/wire.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"go.openfort.xyz/shield/internal/adapters/repositories/sql/providerrepo"
"go.openfort.xyz/shield/internal/adapters/repositories/sql/sharerepo"
"go.openfort.xyz/shield/internal/adapters/repositories/sql/userrepo"
"go.openfort.xyz/shield/internal/applications/healthzapp"
"go.openfort.xyz/shield/internal/applications/projectapp"
"go.openfort.xyz/shield/internal/applications/shamirjob"
"go.openfort.xyz/shield/internal/applications/shareapp"
Expand Down Expand Up @@ -196,12 +197,22 @@ func ProvideIdentityFactory() (f factories.IdentityFactory, err error) {
return
}

func ProvideHealthzApplication() (a *healthzapp.Application, err error) {
wire.Build(
ProvideSQL,
healthzapp.New,
)

return
}

func ProvideRESTServer() (s *rest.Server, err error) {
wire.Build(
rest.New,
rest.GetConfigFromEnv,
ProvideShareApplication,
ProvideProjectApplication,
ProvideHealthzApplication,
ProvideUserService,
ProvideAuthenticationFactory,
ProvideIdentityFactory,
Expand Down
16 changes: 15 additions & 1 deletion di/wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 60 additions & 0 deletions internal/adapters/handlers/rest/healthzhdl/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package healthzhdl

import (
"encoding/json"
"errors"
"net/http"
"time"

"go.openfort.xyz/shield/internal/applications/healthzapp"
domainErrors "go.openfort.xyz/shield/internal/core/domain/errors"
)

type Handler struct {
app *healthzapp.Application
}

func New(app *healthzapp.Application) *Handler {
return &Handler{
app: app,
}
}

type Status struct {
Status string `json:"status"`
At string `json:"at"`
Checks []Check `json:"checks"`
}

type Check struct {
Name string `json:"name"`
Status string `json:"status"`
}

func (h *Handler) Healthz(w http.ResponseWriter, r *http.Request) {
status := Status{
Status: "healthy",
At: time.Now().UTC().Format(time.RFC3339),
Checks: []Check{},
}

err := h.app.Healthz(r.Context())
if err != nil {
status.Status = "unhealthy"
if errors.Is(err, domainErrors.ErrDatabaseUnavailable) {
status.Checks = append(status.Checks, Check{
Name: "database",
Status: "unhealthy",
})
}
} else {
status.Checks = append(status.Checks, Check{
Name: "database",
Status: "healthy",
})
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(status)
}
9 changes: 8 additions & 1 deletion internal/adapters/handlers/rest/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import (
"net/http"
"strings"

"go.openfort.xyz/shield/internal/adapters/handlers/rest/healthzhdl"
"go.openfort.xyz/shield/internal/applications/healthzapp"

"go.openfort.xyz/shield/internal/core/ports/factories"
"go.openfort.xyz/shield/internal/core/ports/services"

Expand All @@ -27,6 +30,7 @@ import (
type Server struct {
projectApp *projectapp.ProjectApplication
shareApp *shareapp.ShareApplication
healthzApp *healthzapp.Application
server *http.Server
logger *slog.Logger
config *Config
Expand All @@ -36,10 +40,11 @@ type Server struct {
}

// New creates a new REST server
func New(cfg *Config, projectApp *projectapp.ProjectApplication, shareApp *shareapp.ShareApplication, authenticationFactory factories.AuthenticationFactory, identityFactory factories.IdentityFactory, userService services.UserService) *Server {
func New(cfg *Config, projectApp *projectapp.ProjectApplication, shareApp *shareapp.ShareApplication, authenticationFactory factories.AuthenticationFactory, identityFactory factories.IdentityFactory, userService services.UserService, healthzApp *healthzapp.Application) *Server {
return &Server{
projectApp: projectApp,
shareApp: shareApp,
healthzApp: healthzApp,
server: new(http.Server),
logger: logger.New("rest_server"),
config: cfg,
Expand All @@ -51,6 +56,7 @@ func New(cfg *Config, projectApp *projectapp.ProjectApplication, shareApp *share

// Start starts the REST server
func (s *Server) Start(ctx context.Context) error {
healthzHdl := healthzhdl.New(s.healthzApp)
projectHdl := projecthdl.New(s.projectApp)
shareHdl := sharehdl.New(s.shareApp)
authMdw := authmdw.New(s.authenticationFactory, s.identityFactory, s.userService)
Expand All @@ -60,6 +66,7 @@ func (s *Server) Start(ctx context.Context) error {
r.Use(rateLimiterMdw.RateLimitMiddleware)
r.Use(requestmdw.RequestIDMiddleware)
r.Use(responsemdw.ResponseMiddleware)
r.HandleFunc("/healthz", healthzHdl.Healthz).Methods(http.MethodGet)
r.HandleFunc("/register", projectHdl.CreateProject).Methods(http.MethodPost)
p := r.PathPrefix("/project").Subrouter()
p.Use(authMdw.AuthenticateAPISecret)
Expand Down
37 changes: 37 additions & 0 deletions internal/applications/healthzapp/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package healthzapp

import (
"context"
"log/slog"

"go.openfort.xyz/shield/internal/adapters/repositories/sql"
"go.openfort.xyz/shield/internal/core/domain/errors"
"go.openfort.xyz/shield/pkg/logger"
)

type Application struct {
db *sql.Client
logger *slog.Logger
}

func New(db *sql.Client) *Application {
return &Application{
db: db,
logger: logger.New("health_application"),
}
}

func (a *Application) Healthz(ctx context.Context) error {
db, err := a.db.DB.DB()
if err != nil {
a.logger.ErrorContext(ctx, "failed to get database connection", logger.Error(err))
return errors.ErrDatabaseUnavailable
}

if err = db.PingContext(ctx); err != nil {
a.logger.ErrorContext(ctx, "failed to ping database", logger.Error(err))
return errors.ErrDatabaseUnavailable
}

return nil
}
7 changes: 7 additions & 0 deletions internal/core/domain/errors/infra.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package errors

import "errors"

var (
ErrDatabaseUnavailable = errors.New("database unavailable")
)

0 comments on commit 50c8df0

Please sign in to comment.