aurganize-backend/backend/internal/config/config.go

210 lines
6.0 KiB
Go

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