veza/veza-backend-api/internal/config/reloader.go
2025-12-03 20:29:37 +01:00

149 lines
4.4 KiB
Go

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