Enhance Configuration System with Environment Variable Priority Handling
- Updated the configuration loading mechanism to prioritize OS environment variables over .env file values, improving flexibility and security. - Added a new function, GetEnvWithPriority, to explicitly check the source of configuration values. - Revised documentation to reflect the new priority system, including detailed examples and usage instructions for mixed configuration approaches. - Enhanced error handling for .env file loading, ensuring graceful handling of missing files without disrupting the application flow.
This commit is contained in:
+34
-5
@@ -65,15 +65,24 @@ type RateLimitConfig struct {
|
||||
Burst int `env:"RATE_LIMIT_BURST"`
|
||||
}
|
||||
|
||||
// LoadConfig loads configuration from .env file
|
||||
// LoadConfig loads configuration with priority: OS environment variables > .env file
|
||||
// OS environment variables take precedence over .env file values
|
||||
func LoadConfig[T any](path string, config T) (T, error) {
|
||||
// Load .env file
|
||||
// First, try to load .env file (this will not override existing OS env vars)
|
||||
envPath := fmt.Sprintf("%s/.env", path)
|
||||
if err := gotenv.Load(envPath); err != nil {
|
||||
return config, fmt.Errorf("failed to load .env file from %s: %w", envPath, err)
|
||||
|
||||
// Check if .env file exists before trying to load it
|
||||
if _, err := os.Stat(envPath); err == nil {
|
||||
// Load .env file - gotenv.Load respects existing environment variables
|
||||
// and will not override them with values from the .env file
|
||||
if err := gotenv.Load(envPath); err != nil {
|
||||
return config, fmt.Errorf("failed to load .env file from %s: %w", envPath, err)
|
||||
}
|
||||
}
|
||||
// If .env file doesn't exist, we continue with OS environment variables only
|
||||
|
||||
// Parse configuration using reflection
|
||||
// This will use OS env vars first, then fall back to values loaded from .env
|
||||
if err := parseConfig(config); err != nil {
|
||||
return config, fmt.Errorf("failed to parse configuration: %w", err)
|
||||
}
|
||||
@@ -82,6 +91,9 @@ func LoadConfig[T any](path string, config T) (T, error) {
|
||||
}
|
||||
|
||||
// parseConfig uses reflection to parse environment variables into the config struct
|
||||
// Environment variables are read with the following priority:
|
||||
// 1. OS environment variables (highest priority)
|
||||
// 2. Values from .env file (loaded by gotenv.Load, lower priority)
|
||||
func parseConfig(config interface{}) error {
|
||||
v := reflect.ValueOf(config)
|
||||
if v.Kind() != reflect.Ptr {
|
||||
@@ -122,6 +134,7 @@ func parseStruct(v reflect.Value) error {
|
||||
}
|
||||
|
||||
// Get environment variable value
|
||||
// os.Getenv will return OS env vars first, then values loaded from .env
|
||||
envValue := os.Getenv(envTag)
|
||||
if envValue == "" {
|
||||
continue
|
||||
@@ -129,7 +142,7 @@ func parseStruct(v reflect.Value) error {
|
||||
|
||||
// Parse and set the field value
|
||||
if err := setFieldValue(field, envValue); err != nil {
|
||||
return fmt.Errorf("failed to set field %s: %w", fieldType.Name, err)
|
||||
return fmt.Errorf("failed to set field %s (env: %s): %w", fieldType.Name, envTag, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,6 +189,22 @@ func setFieldValue(field reflect.Value, value string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetEnvWithPriority gets environment variable with explicit priority handling
|
||||
// Returns the value and a boolean indicating the source (true = OS env, false = .env file)
|
||||
func GetEnvWithPriority(key string) (string, bool) {
|
||||
// Check if the value exists in OS environment
|
||||
if value, exists := os.LookupEnv(key); exists {
|
||||
return value, true // From OS environment
|
||||
}
|
||||
|
||||
// If not in OS environment, it might be from .env file
|
||||
if value := os.Getenv(key); value != "" {
|
||||
return value, false // From .env file
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Helper function to get environment variable with default value
|
||||
func getEnv(key, defaultValue string) string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
|
||||
Reference in New Issue
Block a user