Skip to content

Commit 9d52882

Browse files
FEATURE (disk usage & healthcheck): Add healthckeck API endpoint with disk usage stats
1 parent 4ab63cf commit 9d52882

File tree

18 files changed

+293
-5
lines changed

18 files changed

+293
-5
lines changed

backend/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ To generate swagger docs:
4949
5050
Swagger URL is:
5151

52-
> http://localhost:8080/api/v1/docs/swagger/index.html#/
52+
> http://localhost:4005/api/v1/docs/swagger/index.html#/
5353
5454
# Project structure
5555

backend/cmd/main.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import (
1616
"postgresus-backend/internal/downdetect"
1717
"postgresus-backend/internal/features/backups"
1818
"postgresus-backend/internal/features/databases"
19+
"postgresus-backend/internal/features/disk"
20+
"postgresus-backend/internal/features/healthcheck"
1921
"postgresus-backend/internal/features/notifiers"
2022
"postgresus-backend/internal/features/restores"
2123
"postgresus-backend/internal/features/storages"
@@ -127,6 +129,8 @@ func setUpRoutes(r *gin.Engine) {
127129
databaseController := databases.GetDatabaseController()
128130
backupController := backups.GetBackupController()
129131
restoreController := restores.GetRestoreController()
132+
healthcheckController := healthcheck.GetHealthcheckController()
133+
diskController := disk.GetDiskController()
130134

131135
downdetectContoller.RegisterRoutes(v1)
132136
userController.RegisterRoutes(v1)
@@ -135,6 +139,8 @@ func setUpRoutes(r *gin.Engine) {
135139
databaseController.RegisterRoutes(v1)
136140
backupController.RegisterRoutes(v1)
137141
restoreController.RegisterRoutes(v1)
142+
healthcheckController.RegisterRoutes(v1)
143+
diskController.RegisterRoutes(v1)
138144
}
139145

140146
func setUpDependencies() {

backend/go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ require (
2020
github.com/swaggo/swag v1.16.4
2121
github.com/testcontainers/testcontainers-go v0.37.0
2222
golang.org/x/crypto v0.39.0
23+
golang.org/x/sys v0.33.0
2324
gorm.io/driver/postgres v1.5.11
2425
gorm.io/gorm v1.26.1
2526
)
@@ -100,6 +101,7 @@ require (
100101
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
101102
github.com/rogpeppe/go-internal v1.14.1 // indirect
102103
github.com/rs/xid v1.6.0 // indirect
104+
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
103105
github.com/shirou/gopsutil/v4 v4.25.5 // indirect
104106
github.com/sirupsen/logrus v1.9.3 // indirect
105107
github.com/tinylib/msgp v1.3.0 // indirect
@@ -118,7 +120,6 @@ require (
118120
golang.org/x/arch v0.17.0 // indirect
119121
golang.org/x/net v0.40.0 // indirect
120122
golang.org/x/sync v0.15.0 // indirect
121-
golang.org/x/sys v0.33.0 // indirect
122123
golang.org/x/text v0.26.0 // indirect
123124
golang.org/x/tools v0.33.0 // indirect
124125
google.golang.org/protobuf v1.36.6 // indirect

backend/go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t
213213
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
214214
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
215215
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
216+
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
217+
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
216218
github.com/shirou/gopsutil/v4 v4.25.5 h1:rtd9piuSMGeU8g1RMXjZs9y9luK5BwtnG7dZaQUJAsc=
217219
github.com/shirou/gopsutil/v4 v4.25.5/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
218220
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package disk
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/gin-gonic/gin"
7+
)
8+
9+
type DiskController struct {
10+
diskService *DiskService
11+
}
12+
13+
func (c *DiskController) RegisterRoutes(router *gin.RouterGroup) {
14+
router.GET("/disk/usage", c.GetDiskUsage)
15+
}
16+
17+
// GetDiskUsage
18+
// @Summary Get disk usage information
19+
// @Description Returns information about disk space usage
20+
// @Tags disk
21+
// @Produce json
22+
// @Success 200 {object} DiskUsage
23+
// @Failure 500
24+
// @Router /disk/usage [get]
25+
func (c *DiskController) GetDiskUsage(ctx *gin.Context) {
26+
diskUsage, err := c.diskService.GetDiskUsage()
27+
if err != nil {
28+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
29+
return
30+
}
31+
32+
ctx.JSON(http.StatusOK, diskUsage)
33+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package disk
2+
3+
var (
4+
diskService *DiskService
5+
diskController *DiskController
6+
)
7+
8+
func init() {
9+
diskService = &DiskService{}
10+
11+
diskController = &DiskController{
12+
diskService,
13+
}
14+
}
15+
16+
func GetDiskService() *DiskService {
17+
return diskService
18+
}
19+
20+
func GetDiskController() *DiskController {
21+
return diskController
22+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package disk
2+
3+
type DiskUsage struct {
4+
Platform Platform `json:"platform"`
5+
TotalSpaceBytes int64 `json:"totalSpaceBytes"`
6+
UsedSpaceBytes int64 `json:"usedSpaceBytes"`
7+
FreeSpaceBytes int64 `json:"freeSpaceBytes"`
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package disk
2+
3+
type Platform string
4+
5+
const (
6+
PlatformLinux Platform = "linux"
7+
PlatformWindows Platform = "windows"
8+
)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package disk
2+
3+
import (
4+
"fmt"
5+
"runtime"
6+
7+
"github.com/shirou/gopsutil/v4/disk"
8+
)
9+
10+
type DiskService struct{}
11+
12+
func (s *DiskService) GetDiskUsage() (*DiskUsage, error) {
13+
platform := s.detectPlatform()
14+
15+
// Set path based on platform
16+
path := "/"
17+
if platform == PlatformWindows {
18+
path = "C:\\"
19+
}
20+
21+
diskUsage, err := disk.Usage(path)
22+
if err != nil {
23+
return nil, fmt.Errorf("failed to get disk usage for path %s: %w", path, err)
24+
}
25+
26+
return &DiskUsage{
27+
Platform: platform,
28+
TotalSpaceBytes: int64(diskUsage.Total),
29+
UsedSpaceBytes: int64(diskUsage.Used),
30+
FreeSpaceBytes: int64(diskUsage.Free),
31+
}, nil
32+
}
33+
34+
func (s *DiskService) detectPlatform() Platform {
35+
switch runtime.GOOS {
36+
case "windows":
37+
return PlatformWindows
38+
default:
39+
return PlatformLinux
40+
}
41+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package healthcheck
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/gin-gonic/gin"
7+
)
8+
9+
type HealthcheckController struct {
10+
healthcheckService *HealthcheckService
11+
}
12+
13+
func (c *HealthcheckController) RegisterRoutes(router *gin.RouterGroup) {
14+
router.GET("/health", c.CheckHealth)
15+
}
16+
17+
// CheckHealth
18+
// @Summary Check system health
19+
// @Description Check if the system is healthy by testing database connection
20+
// @Tags healthcheck
21+
// @Produce json
22+
// @Success 200 {object} HealthcheckResponse
23+
// @Failure 503 {object} HealthcheckResponse
24+
// @Router /health [get]
25+
func (c *HealthcheckController) CheckHealth(ctx *gin.Context) {
26+
err := c.healthcheckService.IsHealthy()
27+
28+
if err == nil {
29+
ctx.JSON(
30+
http.StatusOK,
31+
HealthcheckResponse{
32+
Status: "Application is healthy, internal DB working fine and disk usage is below 95%. You can connect downdetector to this endpoint",
33+
},
34+
)
35+
return
36+
}
37+
38+
ctx.JSON(http.StatusServiceUnavailable, HealthcheckResponse{Status: err.Error()})
39+
}

0 commit comments

Comments
 (0)