Files
go-caatsm/internal/infra/config/config.go
T

170 lines
4.4 KiB
Go

package config
import (
"fmt"
"os"
"strings"
"time"
"github.com/knadh/koanf/v2"
"github.com/knadh/koanf/parsers/toml"
"github.com/knadh/koanf/providers/file"
envprovider "github.com/knadh/koanf/providers/env"
)
// Config holds all application configuration
type Config struct {
NATS NATSConfig `koanf:"nats"`
Postgres PostgresConfig `koanf:"postgres"`
App AppConfig `koanf:"app"`
Log LogConfig `koanf:"log"`
Publisher PublisherConfig `koanf:"publisher"`
// Legacy fields for backward compatibility during migration
Subscription SubscriptionConfig `koanf:"subscription"`
Timeouts TimeoutsConfig `koanf:"timeouts"`
}
// NATSConfig holds NATS/JetStream configuration
type NATSConfig struct {
URL string `koanf:"url"`
Stream string `koanf:"stream"`
Consumer string `koanf:"consumer"`
// Legacy fields
Client string `koanf:"client"`
Cluster string `koanf:"cluster"`
}
// PostgresConfig holds PostgreSQL configuration
type PostgresConfig struct {
URL string `koanf:"url"`
MaxConns int32 `koanf:"max_conns"`
MinConns int32 `koanf:"min_conns"`
}
// AppConfig holds application-level configuration
type AppConfig struct {
BatchSize int `koanf:"batch_size"`
BatchTimeout time.Duration `koanf:"batch_timeout"`
}
// LogConfig holds logging configuration
type LogConfig struct {
Level string `koanf:"level"`
Format string `koanf:"format"` // json or console
}
// PublisherConfig holds publisher configuration
type PublisherConfig struct {
Topic string `koanf:"topic"`
}
// SubscriptionConfig holds subscription configuration (legacy)
type SubscriptionConfig struct {
Topic string `koanf:"topic"`
QueueGroup string `koanf:"queue_group"`
}
// TimeoutsConfig holds timeout configuration (legacy)
type TimeoutsConfig struct {
Server time.Duration `koanf:"server"`
ReconnectWait time.Duration `koanf:"reconnect_wait"`
Close time.Duration `koanf:"close"`
AckWait time.Duration `koanf:"ack_wait"`
}
// LoadConfig loads configuration from file and environment variables
func LoadConfig() (*Config, error) {
k := koanf.New(".")
// Determine environment
env := os.Getenv("GO_ENV")
if env == "" {
env = "dev"
}
// Load from TOML file
configFile := fmt.Sprintf("configs/config.%s.toml", env)
if err := k.Load(file.Provider(configFile), toml.Parser()); err != nil {
return nil, fmt.Errorf("error loading config file '%s': %w", configFile, err)
}
// Load from environment variables with CAATSM_ prefix
envProvider := envprovider.Provider("CAATSM_", ".", func(s string) string {
// Convert CAATSM_NATS_URL to nats.url
s = strings.TrimPrefix(s, "CAATSM_")
return strings.ToLower(strings.ReplaceAll(s, "_", "."))
})
if err := k.Load(envProvider, nil); err != nil {
// Environment variables are optional, so we don't fail if they're not present
// This allows the config to work with just the file
}
// Unmarshal into Config struct
var cfg Config
if err := k.Unmarshal("", &cfg); err != nil {
return nil, fmt.Errorf("error unmarshaling config: %w", err)
}
// Set defaults
if cfg.App.BatchSize == 0 {
cfg.App.BatchSize = 50
}
if cfg.App.BatchTimeout == 0 {
cfg.App.BatchTimeout = 2 * time.Second
}
if cfg.Postgres.MaxConns == 0 {
cfg.Postgres.MaxConns = 10
}
if cfg.Postgres.MinConns == 0 {
cfg.Postgres.MinConns = 2
}
if cfg.Log.Level == "" {
cfg.Log.Level = "info"
}
if cfg.Log.Format == "" {
cfg.Log.Format = "json"
}
if cfg.NATS.Stream == "" {
cfg.NATS.Stream = "TELEGRAM"
}
if cfg.NATS.Consumer == "" {
cfg.NATS.Consumer = "telegram-consumer"
}
// Validate configuration
if err := cfg.Validate(); err != nil {
return nil, fmt.Errorf("config validation failed: %w", err)
}
return &cfg, nil
}
// Validate validates the configuration
func (c *Config) Validate() error {
if c.NATS.URL == "" {
return fmt.Errorf("nats.url is required")
}
if c.Subscription.Topic == "" && c.NATS.Stream == "" {
return fmt.Errorf("subscription.topic or nats.stream is required")
}
if c.Publisher.Topic == "" {
return fmt.Errorf("publisher.topic is required")
}
if c.Postgres.URL == "" {
return fmt.Errorf("postgres.url is required")
}
if c.App.BatchSize <= 0 {
return fmt.Errorf("app.batch_size must be greater than 0")
}
if c.App.BatchTimeout <= 0 {
return fmt.Errorf("app.batch_timeout must be greater than 0")
}
return nil
}
// ProvideConfig is a Wire provider function
func ProvideConfig() (*Config, error) {
return LoadConfig()
}