package config import ( "os" "strconv" "github.com/joho/godotenv" "github.com/pelletier/go-toml/v2" ) // Config holds application configuration loaded from TOML type Config struct { Server ServerConfig `toml:"server"` MQTT MQTTConfig `toml:"mqtt"` Database DatabaseConfig `toml:"database"` } type ServerConfig struct { Address string `toml:"address"` Port int `toml:"port"` JWTSecret string `toml:"jwt_secret"` } type MQTTConfig struct { Broker string `toml:"broker"` ClientID string `toml:"client_id"` Username string `toml:"username"` Password string `toml:"password"` Topic string `toml:"topic"` } type DatabaseConfig struct { Host string `toml:"host"` Port int `toml:"port"` User string `toml:"user"` Password string `toml:"password"` Name string `toml:"name"` } // Load loads configuration from the given path. If path is empty, it tries // to load ./config.toml or falls back to environment defaults. func Load(path string) (*Config, error) { cfg := &Config{ Server: ServerConfig{ Address: "0.0.0.0", Port: 8080, JWTSecret: "secret", }, MQTT: MQTTConfig{ Broker: "tcp://localhost:1883", ClientID: "lambda-iot-core", Topic: "lambda/iot", }, Database: DatabaseConfig{ Host: "localhost", Port: 3306, User: "root", Name: "lambdaiot", }, } // try to load TOML if provided or present if path == "" { path = "config.toml" } if _, err := os.Stat(path); err == nil { f, err := os.ReadFile(path) if err != nil { return nil, err } if err := toml.Unmarshal(f, cfg); err != nil { return nil, err } } // load .env (if present) so env vars are populated for overrides _ = godotenv.Load() // environment overrides (common for Docker/.env) if v := os.Getenv("SERVER_ADDRESS"); v != "" { cfg.Server.Address = v } if v := os.Getenv("SERVER_PORT"); v != "" { if p, err := strconv.Atoi(v); err == nil { cfg.Server.Port = p } } if v := os.Getenv("JWT_SECRET"); v != "" { cfg.Server.JWTSecret = v } if v := os.Getenv("MQTT_BROKER"); v != "" { cfg.MQTT.Broker = v } if v := os.Getenv("MQTT_CLIENT_ID"); v != "" { cfg.MQTT.ClientID = v } if v := os.Getenv("MQTT_USERNAME"); v != "" { cfg.MQTT.Username = v } if v := os.Getenv("MQTT_PASSWORD"); v != "" { cfg.MQTT.Password = v } if v := os.Getenv("MQTT_TOPIC"); v != "" { cfg.MQTT.Topic = v } if v := os.Getenv("DB_HOST"); v != "" { cfg.Database.Host = v } if v := os.Getenv("DB_PORT"); v != "" { if p, err := strconv.Atoi(v); err == nil { cfg.Database.Port = p } } if v := os.Getenv("DB_USER"); v != "" { cfg.Database.User = v } if v := os.Getenv("DB_PASSWORD"); v != "" { cfg.Database.Password = v } if v := os.Getenv("DB_NAME"); v != "" { cfg.Database.Name = v } return cfg, nil }