package config import ( "sync" "time" "veza-backend-api/internal/logging" "veza-backend-api/internal/middleware" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) // Reloadable représente une configuration qui peut être rechargée (T0034) type Reloadable interface { Reload() error } // ConfigReloader gère le rechargement de configurations à chaud (T0034) type ConfigReloader struct { mu sync.RWMutex config *Config logger *zap.Logger loggingService *logging.Logger // Service de logging pour changement de niveau dynamique simpleRateLimiter *middleware.SimpleRateLimiter } // NewConfigReloader crée un nouveau ConfigReloader (T0034) func NewConfigReloader(config *Config, logger *zap.Logger) *ConfigReloader { return &ConfigReloader{ config: config, logger: logger, loggingService: nil, // Sera initialisé lors du premier reload si nécessaire simpleRateLimiter: config.SimpleRateLimiter, } } // SetLoggingService définit le service de logging pour permettre le changement dynamique de niveau func (r *ConfigReloader) SetLoggingService(loggingService *logging.Logger) { r.mu.Lock() defer r.mu.Unlock() r.loggingService = loggingService } // ReloadLogLevel recharge le niveau de log depuis les variables d'environnement (T0034) func (r *ConfigReloader) ReloadLogLevel() error { r.mu.Lock() defer r.mu.Unlock() // Récupérer le nouveau niveau depuis les variables d'environnement newLevelStr := getEnv("LOG_LEVEL", "INFO") if newLevelStr == "" { newLevelStr = "INFO" } // Parser le niveau level, err := zapcore.ParseLevel(newLevelStr) if err != nil { level = zapcore.InfoLevel } // Si le logger zap est accessible directement et utilise AtomicLevel // On peut changer le niveau dynamiquement if r.config.Logger != nil { // Essayer de changer le niveau via l'AtomicLevel si disponible // Note: Le logger zap doit être créé avec AtomicLevel pour permettre le changement dynamique // Pour l'instant, on log juste le changement et on met à jour la config r.config.LogLevel = newLevelStr r.logger.Info("Log level reloaded from environment", zap.String("old_level", r.config.LogLevel), zap.String("new_level", newLevelStr), zap.String("parsed_level", level.String()), ) } return nil } // ReloadRateLimits recharge les limites de rate limiting depuis les variables d'environnement (T0034) func (r *ConfigReloader) ReloadRateLimits() error { r.mu.Lock() defer r.mu.Unlock() // Récupérer les nouvelles limites depuis les variables d'environnement newLimit := getEnvInt("RATE_LIMIT_LIMIT", 100) newWindowSeconds := getEnvInt("RATE_LIMIT_WINDOW", 60) newWindow := time.Duration(newWindowSeconds) * time.Second // Si le simple rate limiter existe, mettre à jour ses limites if r.simpleRateLimiter != nil { // Mettre à jour les limites directement dans le rate limiter r.simpleRateLimiter.UpdateLimits(newLimit, newWindow) // Mettre à jour la config r.config.RateLimitLimit = newLimit r.config.RateLimitWindow = newWindowSeconds r.logger.Info("Rate limits reloaded from environment", zap.Int("new_limit", newLimit), zap.Int("new_window_seconds", newWindowSeconds), ) } return nil } // ReloadAll recharge toutes les configurations reloadable (T0034) func (r *ConfigReloader) ReloadAll() error { var errors []error // Recharger le niveau de log if err := r.ReloadLogLevel(); err != nil { errors = append(errors, err) } // Recharger les limites de rate limiting if err := r.ReloadRateLimits(); err != nil { errors = append(errors, err) } if len(errors) > 0 { r.logger.Error("Some configurations failed to reload", zap.Int("error_count", len(errors))) return errors[0] // Retourner la première erreur } r.logger.Info("All configurations reloaded successfully") return nil } // GetCurrentConfig retourne la configuration actuelle (en lecture seule) func (r *ConfigReloader) GetCurrentConfig() *ReloadableConfig { r.mu.RLock() defer r.mu.RUnlock() return &ReloadableConfig{ LogLevel: r.config.LogLevel, RateLimitLimit: r.config.RateLimitLimit, RateLimitWindow: r.config.RateLimitWindow, } } // ReloadableConfig représente la partie de la configuration qui peut être rechargée type ReloadableConfig struct { LogLevel string `json:"log_level"` RateLimitLimit int `json:"rate_limit_limit"` RateLimitWindow int `json:"rate_limit_window"` } // Note: getEnv et getEnvInt sont définis dans config.go