// Package config gère le chargement des configurations YAML. package config import ( "fmt" "os" "path/filepath" "strings" "sync" "gopkg.in/yaml.v3" ) // AppConfig représente la configuration d'une application cliente. type AppConfig struct { App string `yaml:"app"` Version string `yaml:"version"` BasePath string `yaml:"base_path"` Hosts []string `yaml:"hosts"` Database Database `yaml:"database"` Auth Auth `yaml:"auth"` Routes []Route `yaml:"routes"` } // Auth contient la configuration d'authentification. type Auth struct { JWTSecretFile string `yaml:"jwt_secret_file"` JWTExpiry string `yaml:"jwt_expiry"` jwtSecret string // Chargé depuis le fichier } // JWTSecret retourne le secret JWT (chargé depuis le fichier). func (a *Auth) JWTSecret() string { return a.jwtSecret } // Database contient la configuration de connexion à la base de données. type Database struct { Host string `yaml:"host"` Port int `yaml:"port"` User string `yaml:"user"` PasswordFile string `yaml:"password_file"` Name string `yaml:"name"` password string // Chargé depuis le fichier } // Password retourne le mot de passe (chargé depuis le fichier). func (d *Database) Password() string { return d.password } // DSN retourne la chaîne de connexion MySQL/MariaDB. func (d *Database) DSN() string { return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=true&charset=utf8mb4", d.User, d.password, d.Host, d.Port, d.Name) } // Route représente une route API. type Route struct { Path string `yaml:"path"` Method string `yaml:"method"` Scenario string `yaml:"scenario"` Auth *bool `yaml:"auth,omitempty"` } // Registry stocke les configurations des applications. type Registry struct { configDir string apps map[string]*AppConfig // Par app_id byHost map[string]*AppConfig // Par hostname mu sync.RWMutex } // NewRegistry crée un nouveau registre de configurations. func NewRegistry(configDir string) *Registry { return &Registry{ configDir: configDir, apps: make(map[string]*AppConfig), byHost: make(map[string]*AppConfig), } } // Load charge toutes les configurations depuis le répertoire routes. func (r *Registry) Load() error { r.mu.Lock() defer r.mu.Unlock() routesDir := filepath.Join(r.configDir, "routes") entries, err := os.ReadDir(routesDir) if err != nil { return fmt.Errorf("read routes dir: %w", err) } for _, entry := range entries { if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".yaml") { continue } path := filepath.Join(routesDir, entry.Name()) cfg, err := r.loadAppConfig(path) if err != nil { return fmt.Errorf("load %s: %w", entry.Name(), err) } r.apps[cfg.App] = cfg for _, host := range cfg.Hosts { r.byHost[host] = cfg } } return nil } // loadAppConfig charge une configuration d'application depuis un fichier YAML. func (r *Registry) loadAppConfig(path string) (*AppConfig, error) { data, err := os.ReadFile(path) if err != nil { return nil, err } var cfg AppConfig if err := yaml.Unmarshal(data, &cfg); err != nil { return nil, err } // Charger le mot de passe DB depuis le fichier if cfg.Database.PasswordFile != "" { passData, err := os.ReadFile(cfg.Database.PasswordFile) if err != nil { return nil, fmt.Errorf("read db password file: %w", err) } cfg.Database.password = strings.TrimSpace(string(passData)) } // Charger le secret JWT depuis le fichier if cfg.Auth.JWTSecretFile != "" { secretData, err := os.ReadFile(cfg.Auth.JWTSecretFile) if err != nil { return nil, fmt.Errorf("read jwt secret file: %w", err) } cfg.Auth.jwtSecret = strings.TrimSpace(string(secretData)) } // Port DB par défaut if cfg.Database.Port == 0 { cfg.Database.Port = 3306 } // Expiry JWT par défaut if cfg.Auth.JWTExpiry == "" { cfg.Auth.JWTExpiry = "24h" } return &cfg, nil } // GetByApp retourne la configuration d'une application par son ID. func (r *Registry) GetByApp(appID string) (*AppConfig, bool) { r.mu.RLock() defer r.mu.RUnlock() cfg, ok := r.apps[appID] return cfg, ok } // GetByHost retourne la configuration d'une application par son hostname. func (r *Registry) GetByHost(host string) (*AppConfig, bool) { r.mu.RLock() defer r.mu.RUnlock() cfg, ok := r.byHost[host] return cfg, ok } // Apps retourne la liste des IDs d'applications chargées. func (r *Registry) Apps() []string { r.mu.RLock() defer r.mu.RUnlock() apps := make([]string, 0, len(r.apps)) for app := range r.apps { apps = append(apps, app) } return apps }