Files
sogoms/internal/config/config.go
Pierre 7e27f87d6f Initial commit - SOGOMS v1.0.0
- sogoctl: supervisor avec health checks et restart auto
- sogoway: gateway HTTP, auth JWT, routing par hostname
- sogoms-db: microservice MariaDB avec pool par application
- Protocol IPC Unix socket JSON length-prefixed
- Config YAML multi-application (prokov)
- Deploy script pour container Alpine gw3

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 19:09:00 +01:00

183 lines
4.5 KiB
Go

// 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
}