- Added videoWorkflowRepository with methods to manage video and user interactions. - Introduced catalog_mapper for converting database models to protobuf representations. - Created domain_helpers for normalizing domain and ad format values. - Defined service interfaces for payment, account, notification, domain, ad template, player config, video, and user management. - Implemented OAuth helpers for generating state and caching keys. - Developed payment_proto_helpers for mapping payment-related models to protobuf. - Added service policy helpers to enforce plan requirements and user permissions. - Created user_mapper for converting user payloads to protobuf format. - Implemented value_helpers for handling various value conversions and nil checks. - Developed video_helpers for normalizing video statuses and managing storage types. - Created video_mapper for mapping video models to protobuf format. - Implemented render workflow for managing video creation and job processing.
100 lines
3.2 KiB
Go
100 lines
3.2 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type HealthStatus string
|
|
|
|
const (
|
|
HealthStatusHealthy HealthStatus = "healthy"
|
|
HealthStatusUnhealthy HealthStatus = "unhealthy"
|
|
HealthStatusDegraded HealthStatus = "degraded"
|
|
)
|
|
|
|
type ComponentHealth struct {
|
|
Status HealthStatus `json:"status"`
|
|
Message string `json:"message,omitempty"`
|
|
Latency string `json:"latency,omitempty"`
|
|
CheckedAt time.Time `json:"checked_at"`
|
|
}
|
|
|
|
type HealthReport struct {
|
|
Status HealthStatus `json:"status"`
|
|
Version string `json:"version"`
|
|
CheckedAt time.Time `json:"checked_at"`
|
|
Components map[string]ComponentHealth `json:"components"`
|
|
}
|
|
|
|
type HealthService struct {
|
|
db *gorm.DB
|
|
redis *redis.Client
|
|
version string
|
|
}
|
|
|
|
func NewHealthService(db *gorm.DB, redisClient *redis.Client, version string) *HealthService {
|
|
if version == "" {
|
|
version = "0.0.1"
|
|
}
|
|
return &HealthService{db: db, redis: redisClient, version: version}
|
|
}
|
|
|
|
func (s *HealthService) CheckHealth(ctx context.Context) *HealthReport {
|
|
report := &HealthReport{Status: HealthStatusHealthy, Version: s.version, CheckedAt: time.Now(), Components: map[string]ComponentHealth{}}
|
|
dbHealth := s.checkDatabase(ctx)
|
|
report.Components["database"] = dbHealth
|
|
if dbHealth.Status != HealthStatusHealthy {
|
|
report.Status = HealthStatusUnhealthy
|
|
}
|
|
redisHealth := s.checkRedis(ctx)
|
|
report.Components["redis"] = redisHealth
|
|
if redisHealth.Status != HealthStatusHealthy && report.Status == HealthStatusHealthy {
|
|
report.Status = HealthStatusDegraded
|
|
}
|
|
return report
|
|
}
|
|
|
|
func (s *HealthService) SimpleHealthCheck(ctx context.Context) (string, int) {
|
|
report := s.CheckHealth(ctx)
|
|
switch report.Status {
|
|
case HealthStatusHealthy:
|
|
return "ok", 200
|
|
case HealthStatusDegraded:
|
|
return "degraded", 200
|
|
default:
|
|
return "unhealthy", 503
|
|
}
|
|
}
|
|
|
|
func (s *HealthService) checkDatabase(ctx context.Context) ComponentHealth {
|
|
start := time.Now()
|
|
sqlDB, err := s.db.DB()
|
|
if err != nil {
|
|
return ComponentHealth{Status: HealthStatusUnhealthy, Message: fmt.Sprintf("failed to get database instance: %v", err), CheckedAt: time.Now()}
|
|
}
|
|
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
|
defer cancel()
|
|
if err := sqlDB.PingContext(ctx); err != nil {
|
|
return ComponentHealth{Status: HealthStatusUnhealthy, Message: fmt.Sprintf("database ping failed: %v", err), Latency: time.Since(start).String(), CheckedAt: time.Now()}
|
|
}
|
|
return ComponentHealth{Status: HealthStatusHealthy, Latency: time.Since(start).String(), CheckedAt: time.Now()}
|
|
}
|
|
|
|
func (s *HealthService) checkRedis(ctx context.Context) ComponentHealth {
|
|
start := time.Now()
|
|
if s.redis == nil {
|
|
return ComponentHealth{Status: HealthStatusUnhealthy, Message: "redis client not initialized", CheckedAt: time.Now()}
|
|
}
|
|
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
|
defer cancel()
|
|
if err := s.redis.Ping(ctx).Err(); err != nil {
|
|
return ComponentHealth{Status: HealthStatusUnhealthy, Message: fmt.Sprintf("redis ping failed: %v", err), Latency: time.Since(start).String(), CheckedAt: time.Now()}
|
|
}
|
|
return ComponentHealth{Status: HealthStatusHealthy, Latency: time.Since(start).String(), CheckedAt: time.Now()}
|
|
}
|