## Backend Changes - Add WebhookService for sending alert notifications via HTTP webhooks - Implement HMAC-SHA256 signature for webhook payload authentication - Add webhook configuration API endpoints and settings - Integrate webhook calls into OpsAlertEvaluatorService - Fix routes/common.go string conversion (use strconv.Itoa) - Add comprehensive webhook service tests ## Frontend Changes - Add webhook notification configuration UI in OpsSettingsDialog - Add WebhookNotificationConfig types and API functions - Add i18n translations for webhook features (zh/en) - Refactor DataManagementView.vue into modular components: - PostgresProfilesCard.vue (356 lines) - RedisProfilesCard.vue (331 lines) - S3ProfilesCard.vue (363 lines) - BackupJobsCard.vue (216 lines) - DataManagementView.vue (94 lines) - Add OpsSettingsDialog component tests ## Testing - All backend tests pass - All frontend tests pass - Webhook service tests cover signature, HTTP, timeout, error handling
131 lines
3.3 KiB
Go
131 lines
3.3 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"time"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
)
|
|
|
|
// HealthChecker provides health check functionality for dependencies
|
|
type HealthChecker struct {
|
|
db *sql.DB
|
|
redisClient *redis.Client
|
|
}
|
|
|
|
// NewHealthChecker creates a new health checker instance
|
|
func NewHealthChecker(db *sql.DB, redisClient *redis.Client) *HealthChecker {
|
|
return &HealthChecker{
|
|
db: db,
|
|
redisClient: redisClient,
|
|
}
|
|
}
|
|
|
|
// CheckDatabase checks if the database connection is healthy
|
|
func (h *HealthChecker) CheckDatabase() bool {
|
|
if h.db == nil {
|
|
return false
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
|
defer cancel()
|
|
|
|
var result int
|
|
err := h.db.QueryRowContext(ctx, "SELECT 1").Scan(&result)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
return result == 1
|
|
}
|
|
|
|
// CheckRedis checks if the Redis connection is healthy
|
|
func (h *HealthChecker) CheckRedis() bool {
|
|
if h.redisClient == nil {
|
|
return false
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
|
defer cancel()
|
|
|
|
err := h.redisClient.Ping(ctx).Err()
|
|
return err == nil
|
|
}
|
|
|
|
// GetDatabaseStats returns database connection statistics
|
|
func (h *HealthChecker) GetDatabaseStats() (active, idle int) {
|
|
if h.db == nil {
|
|
return 0, 0
|
|
}
|
|
stats := h.db.Stats()
|
|
return stats.InUse, stats.Idle
|
|
}
|
|
|
|
// GetRedisStats returns Redis connection statistics
|
|
func (h *HealthChecker) GetRedisStats() (total, idle int) {
|
|
if h.redisClient == nil {
|
|
return 0, 0
|
|
}
|
|
stats := h.redisClient.PoolStats()
|
|
if stats == nil {
|
|
return 0, 0
|
|
}
|
|
return int(stats.TotalConns), int(stats.IdleConns)
|
|
}
|
|
|
|
// HealthStatus represents the health status of all dependencies
|
|
type HealthStatus struct {
|
|
Database DatabaseHealth `json:"database"`
|
|
Redis RedisHealth `json:"redis"`
|
|
}
|
|
|
|
// DatabaseHealth represents database health information
|
|
type DatabaseHealth struct {
|
|
Healthy bool `json:"healthy"`
|
|
ActiveConns int `json:"active_connections"`
|
|
IdleConns int `json:"idle_connections"`
|
|
OpenConns int `json:"open_connections"`
|
|
MaxOpenConns int `json:"max_open_connections"`
|
|
WaitCount int64 `json:"wait_count"`
|
|
MaxIdleClosed int64 `json:"max_idle_closed"`
|
|
MaxLifetimeClosed int64 `json:"max_lifetime_closed"`
|
|
}
|
|
|
|
// RedisHealth represents Redis health information
|
|
type RedisHealth struct {
|
|
Healthy bool `json:"healthy"`
|
|
TotalConns int `json:"total_connections"`
|
|
IdleConns int `json:"idle_connections"`
|
|
}
|
|
|
|
// GetHealthStatus returns comprehensive health status
|
|
func (h *HealthChecker) GetHealthStatus() HealthStatus {
|
|
status := HealthStatus{}
|
|
|
|
// Database health
|
|
status.Database.Healthy = h.CheckDatabase()
|
|
if h.db != nil {
|
|
stats := h.db.Stats()
|
|
status.Database.ActiveConns = stats.InUse
|
|
status.Database.IdleConns = stats.Idle
|
|
status.Database.OpenConns = stats.OpenConnections
|
|
status.Database.MaxOpenConns = stats.MaxOpenConnections
|
|
status.Database.WaitCount = stats.WaitCount
|
|
status.Database.MaxIdleClosed = stats.MaxIdleClosed
|
|
status.Database.MaxLifetimeClosed = stats.MaxLifetimeClosed
|
|
}
|
|
|
|
// Redis health
|
|
status.Redis.Healthy = h.CheckRedis()
|
|
if h.redisClient != nil {
|
|
stats := h.redisClient.PoolStats()
|
|
if stats != nil {
|
|
status.Redis.TotalConns = int(stats.TotalConns)
|
|
status.Redis.IdleConns = int(stats.IdleConns)
|
|
}
|
|
}
|
|
|
|
return status
|
|
}
|