package config import ( "fmt" "os" "strconv" "time" "github.com/joho/godotenv" ) type Config struct { Server ServerConfig Database DatabaseConfig JWT JWTConfig Redis RedisConfig NATS NATSConfig Storage StorageConfig } // ServerConfig type holds the information about the http server settings type ServerConfig struct { Port string // HTTP port to listen on Environment string // can be development, staging, production ReadTimeout time.Duration // Max time to read request WriteTimeout time.Duration // Max time to write response } // DatabaseConfig contains postgresSQL connection settings type DatabaseConfig struct { Host string // Database host Port string // Database port User string // Database user Password string // Database password DBName string // Database name SSLMode string // SSL mode : disable, require, verify-full ? not sure what this field is set for MaxOpenConns int // Maximum open connections MaxIdleConns int // Maximum idle connections ConnMaxLifetime time.Duration // Maximum connection lifetime } // JWT Config contains JWT token settings type JWTConfig struct { AccessSecret string // Secret for access tokens RefreshSecret string // Secret for refresh tokens AccessExpiry time.Duration // Accees token expiry (15 minutes) RefreshExpiry time.Duration // Refresh token expiry (7 days) Issuer string // Token issuer claim } type RedisConfig struct { Host string // Redis host Port string // Redis port Password string // Redis password (set to empty if no auth is set) DB int // Redis database number } // NATSConfig contains NATS messaging settings type NATSConfig struct { URL string // NATS server URL } // StorageCongfig contains MinIO (s3) settings type StorageConfig struct { Endpoint string // MinIO endpoint AccessKeyID string // Access key SecretAccessKey string // Secret key BucketName string // Bucket name UseSSL bool // User HTTPS } func Load() (*Config, error) { if os.Getenv("APP_ENV") != "production" { if err := godotenv.Load(); err != nil { fmt.Println("Warning: .env file not found, using environment variables") } } cfg := &Config{ Server: ServerConfig{ Port: getEnv("SERVER_PORT", "8080"), Environment: getEnv("APP_ENV", "development"), ReadTimeout: parseDuration(getEnv("SERVER_READ_TIMEOUT", "10s")), WriteTimeout: parseDuration(getEnv("SERVER_WRITE_TIMEOUT", "10s")), }, Database: DatabaseConfig{ Host: getEnv("DB_HOST", "localhost"), Port: getEnv("DB_PORT", "5432"), User: getEnv("DB_USER", "aurganize"), Password: getEnv("DB_PASSWORD", ""), DBName: getEnv("DB_NAME", "aruganize_db_1"), SSLMode: getEnv("DB_SSLMODE", "disable"), MaxOpenConns: parseInt(getEnv("DB_MAX_OPEN_CONNECTIONS", "25")), MaxIdleConns: parseInt(getEnv("DB_MAX_IDLE_CONNECTIONS", "5")), ConnMaxLifetime: parseDuration(getEnv("DB_CONNECTION_MAX_LIFETIME", "5m")), }, JWT: JWTConfig{ AccessSecret: getEnv("JWT_ACCESS_SECRET", ""), RefreshSecret: getEnv("JWT_REFRESH_SECRET", ""), AccessExpiry: parseDuration(getEnv("JWT_ACCESS_EXPIRY", "15m")), RefreshExpiry: parseDuration(getEnv("JWT_REFRESH_EXPIRY", "168h")), Issuer: getEnv("JWT_ISSUER", "aurganize-v62"), }, Redis: RedisConfig{ Host: getEnv("REDIST_HOST", "localhost"), Port: getEnv("REDIS_PORT", "6379"), Password: getEnv("REDIS_PASSWORD", ""), DB: parseInt(getEnv("REDIS_DB", "0")), }, NATS: NATSConfig{ URL: getEnv("NATS_URL", "nats://localhost:4222"), }, Storage: StorageConfig{ Endpoint: getEnv("MINIO_ENDPOINT", "localhost:9000"), AccessKeyID: getEnv("MINIO_ACCESS_KEY", "minioadmin"), SecretAccessKey: getEnv("MINIO_SECRET_KEY", "miniosecretkey"), BucketName: getEnv("MINIO_BUCKET", "aurganize_bucket_1"), UseSSL: parseBool(getEnv("MINIO_USE_SSL", "false")), }, } if err := cfg.Validate(); err != nil { return nil, fmt.Errorf("configuration validation failure [%w]", err) } return cfg, nil } // Validate checks if all required configuration is present and valid func (c *Config) Validate() error { // Database password required in production if c.Database.Password == "" && c.Server.Environment == "production" { return fmt.Errorf("DB_PASSWORD is required in production") } // JWT secrets are required always if c.JWT.AccessSecret == "" { return fmt.Errorf("JWT_ACCESS_SECRET is required") } if c.JWT.RefreshSecret == "" { return fmt.Errorf("JWT_REFRESH_SECRET is required") } // JWT secrets should be different if c.JWT.AccessSecret == c.JWT.RefreshSecret { return fmt.Errorf("JWT_ACCESS_SECRET and JWT_REFRESH_SECRET must be different") } validEnvs := map[string]bool{ "development": true, "test": true, "staging": true, "UAT": true, "production": true, } if !validEnvs[c.Server.Environment] { return fmt.Errorf("invalid environment configured in enviroment") } return nil } // Helper Functions func getEnv(key, defaultValue string) string { if value := os.Getenv(key); value != "" { return value } return defaultValue } func parseDuration(s string) time.Duration { d, err := time.ParseDuration(s) if err != nil { return 0 } return d } func parseInt(s string) int { i, err := strconv.Atoi(s) if err != nil { return 0 } return i } func parseBool(s string) bool { b, err := strconv.ParseBool(s) if err != nil { return false } return b } // DatabaseDSN returns the PostgreSQL connection string func (c *Config) DatabaseDSN() string { return fmt.Sprintf( "host=%s post %s user=%s password=%s dbname=%s sslmode=%s", c.Database.Host, c.Database.Port, c.Database.User, c.Database.Password, c.Database.DBName, c.Database.SSLMode, ) } // RedisDSN returns the Redis connection string func (c *Config) RedisDSN() string { return fmt.Sprintf("%s:%s", c.Redis.Host, c.Redis.Port) }