package controllers import ( "context" "net/http" "runtime" "time" "fotbal-club/internal/services" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // HealthController handles health check endpoints type HealthController struct { DB *gorm.DB } // HealthResponse represents health check response type HealthResponse struct { Status string `json:"status"` Timestamp time.Time `json:"timestamp"` Version string `json:"version,omitempty"` Checks map[string]CheckResult `json:"checks"` System SystemInfo `json:"system,omitempty"` } // CheckResult represents individual health check result type CheckResult struct { Status string `json:"status"` Message string `json:"message,omitempty"` Latency time.Duration `json:"latency_ms,omitempty"` Timestamp time.Time `json:"timestamp"` } // SystemInfo represents system resource information type SystemInfo struct { Goroutines int `json:"goroutines"` MemoryAlloc uint64 `json:"memory_alloc_mb"` MemoryTotal uint64 `json:"memory_total_mb"` MemorySys uint64 `json:"memory_sys_mb"` NumCPU int `json:"num_cpu"` GoVersion string `json:"go_version"` } // Liveness returns a simple liveness probe func (hc *HealthController) Liveness(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "status": "alive", "timestamp": time.Now(), }) } // Readiness returns detailed readiness probe func (hc *HealthController) Readiness(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second) defer cancel() checks := make(map[string]CheckResult) overallStatus := "healthy" // Check database dbCheck := hc.checkDatabase(ctx) checks["database"] = dbCheck if dbCheck.Status != "healthy" { overallStatus = "unhealthy" } // Check cache cacheCheck := hc.checkCache(ctx) checks["cache"] = cacheCheck if cacheCheck.Status != "degraded" && cacheCheck.Status != "healthy" { overallStatus = "unhealthy" } // Check disk space diskCheck := hc.checkDiskSpace() checks["disk"] = diskCheck if diskCheck.Status != "healthy" { overallStatus = "degraded" } response := HealthResponse{ Status: overallStatus, Timestamp: time.Now(), Checks: checks, } statusCode := http.StatusOK if overallStatus == "unhealthy" { statusCode = http.StatusServiceUnavailable } c.JSON(statusCode, response) } // Health returns comprehensive health information func (hc *HealthController) Health(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), 10*time.Second) defer cancel() checks := make(map[string]CheckResult) overallStatus := "healthy" // All checks dbCheck := hc.checkDatabase(ctx) checks["database"] = dbCheck if dbCheck.Status != "healthy" { overallStatus = "unhealthy" } cacheCheck := hc.checkCache(ctx) checks["cache"] = cacheCheck diskCheck := hc.checkDiskSpace() checks["disk"] = diskCheck // System info sysInfo := hc.getSystemInfo() response := HealthResponse{ Status: overallStatus, Timestamp: time.Now(), Version: "1.0.0", // Use actual version Checks: checks, System: sysInfo, } statusCode := http.StatusOK if overallStatus == "unhealthy" { statusCode = http.StatusServiceUnavailable } c.JSON(statusCode, response) } // checkDatabase verifies database connectivity func (hc *HealthController) checkDatabase(ctx context.Context) CheckResult { start := time.Now() sqlDB, err := hc.DB.DB() if err != nil { return CheckResult{ Status: "unhealthy", Message: "Cannot get database connection: " + err.Error(), Timestamp: time.Now(), } } err = sqlDB.PingContext(ctx) latency := time.Since(start) if err != nil { return CheckResult{ Status: "unhealthy", Message: "Database ping failed: " + err.Error(), Latency: latency, Timestamp: time.Now(), } } // Check connection pool stats stats := sqlDB.Stats() if stats.OpenConnections >= stats.MaxOpenConnections { return CheckResult{ Status: "degraded", Message: "Connection pool at maximum capacity", Latency: latency, Timestamp: time.Now(), } } return CheckResult{ Status: "healthy", Message: "Database connection successful", Latency: latency, Timestamp: time.Now(), } } // checkCache verifies cache service func (hc *HealthController) checkCache(ctx context.Context) CheckResult { start := time.Now() cache := services.GetCacheService() // Test cache write/read testKey := "health:check" testValue := "ok" err := cache.Set(testKey, testValue, 1*time.Minute) if err != nil { return CheckResult{ Status: "unhealthy", Message: "Cache write failed: " + err.Error(), Latency: time.Since(start), Timestamp: time.Now(), } } var result string err = cache.Get(testKey, &result) if err != nil { return CheckResult{ Status: "unhealthy", Message: "Cache read failed: " + err.Error(), Latency: time.Since(start), Timestamp: time.Now(), } } cache.Delete(testKey) return CheckResult{ Status: "healthy", Message: "Cache operational", Latency: time.Since(start), Timestamp: time.Now(), } } // checkDiskSpace checks available disk space func (hc *HealthController) checkDiskSpace() CheckResult { // This is a simplified check // In production, implement proper disk space monitoring return CheckResult{ Status: "healthy", Message: "Disk space sufficient", Timestamp: time.Now(), } } // getSystemInfo collects system resource information func (hc *HealthController) getSystemInfo() SystemInfo { var m runtime.MemStats runtime.ReadMemStats(&m) return SystemInfo{ Goroutines: runtime.NumGoroutine(), MemoryAlloc: m.Alloc / 1024 / 1024, // MB MemoryTotal: m.TotalAlloc / 1024 / 1024, // MB MemorySys: m.Sys / 1024 / 1024, // MB NumCPU: runtime.NumCPU(), GoVersion: runtime.Version(), } } // Metrics returns Prometheus-compatible metrics func (hc *HealthController) Metrics(c *gin.Context) { var m runtime.MemStats runtime.ReadMemStats(&m) // Simple text-based metrics metrics := "" metrics += "# HELP go_goroutines Number of goroutines\n" metrics += "# TYPE go_goroutines gauge\n" metrics += "go_goroutines " + string(rune(runtime.NumGoroutine())) + "\n\n" metrics += "# HELP go_memory_alloc_bytes Allocated memory in bytes\n" metrics += "# TYPE go_memory_alloc_bytes gauge\n" metrics += "go_memory_alloc_bytes " + string(rune(m.Alloc)) + "\n\n" c.String(http.StatusOK, metrics) }