add env files
This commit is contained in:
216
config.go
Normal file
216
config.go
Normal file
@@ -0,0 +1,216 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
type Environment string
|
||||
|
||||
const (
|
||||
Development Environment = "development"
|
||||
Staging Environment = "staging"
|
||||
Production Environment = "production"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Environment Environment
|
||||
DatabaseURL string
|
||||
JWTSecret string
|
||||
Port string
|
||||
GinMode string
|
||||
LogLevel string
|
||||
Debug bool
|
||||
}
|
||||
|
||||
// LoadConfig loads configuration with proper environment file precedence
|
||||
func LoadConfig() *Config {
|
||||
// Load environment files in priority order
|
||||
loadEnvironmentFiles()
|
||||
|
||||
// Get environment
|
||||
env := getEnvironment()
|
||||
|
||||
// Load configuration
|
||||
config := &Config{
|
||||
Environment: env,
|
||||
DatabaseURL: getEnv("DATABASE_URL", getDefaultDatabaseURL(env)),
|
||||
JWTSecret: getRequiredEnv("JWT_SECRET"),
|
||||
Port: getEnv("PORT", "8080"),
|
||||
GinMode: getGinMode(env),
|
||||
LogLevel: getLogLevel(env),
|
||||
Debug: env == Development,
|
||||
}
|
||||
|
||||
// Log configuration (without sensitive data)
|
||||
logConfig(config)
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// loadEnvironmentFiles loads environment files in priority order
|
||||
func loadEnvironmentFiles() {
|
||||
// Get environment first (from system env or default)
|
||||
env := getEnvironmentFromSystem()
|
||||
|
||||
log.Printf("🔍 Detected environment: %s", env)
|
||||
|
||||
// Define file loading order (later files override earlier ones)
|
||||
files := []string{
|
||||
".env", // Base configuration
|
||||
fmt.Sprintf(".env.%s", env), // Environment-specific
|
||||
}
|
||||
|
||||
// Load files in order
|
||||
for _, file := range files {
|
||||
if _, err := os.Stat(file); err == nil {
|
||||
if err := godotenv.Load(file); err != nil {
|
||||
log.Printf("⚠️ Warning: Could not load %s: %v", file, err)
|
||||
} else {
|
||||
log.Printf("✅ Loaded: %s", file)
|
||||
}
|
||||
} else {
|
||||
log.Printf("❌ Not found: %s", file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getEnvironmentFromSystem gets environment from system variables only
|
||||
func getEnvironmentFromSystem() string {
|
||||
// Check if ENVIRONMENT is already set
|
||||
if env := os.Getenv("ENVIRONMENT"); env != "" {
|
||||
return strings.ToLower(env)
|
||||
}
|
||||
|
||||
// Fallback detection
|
||||
if ginMode := os.Getenv("GIN_MODE"); ginMode == "release" {
|
||||
return "production"
|
||||
}
|
||||
|
||||
return "development"
|
||||
}
|
||||
|
||||
// getEnvironment gets the current environment
|
||||
func getEnvironment() Environment {
|
||||
env := strings.ToLower(getEnv("ENVIRONMENT", "development"))
|
||||
|
||||
switch env {
|
||||
case "development", "dev":
|
||||
return Development
|
||||
case "staging", "stage":
|
||||
return Staging
|
||||
case "production", "prod":
|
||||
return Production
|
||||
default:
|
||||
log.Printf("⚠️ Unknown environment '%s', defaulting to development", env)
|
||||
return Development
|
||||
}
|
||||
}
|
||||
|
||||
// getGinMode returns the appropriate Gin mode for the environment
|
||||
func getGinMode(env Environment) string {
|
||||
switch env {
|
||||
case Production, Staging:
|
||||
return "release"
|
||||
case Development:
|
||||
return "debug"
|
||||
default:
|
||||
return "debug"
|
||||
}
|
||||
}
|
||||
|
||||
// getLogLevel returns the appropriate log level for the environment
|
||||
func getLogLevel(env Environment) string {
|
||||
switch env {
|
||||
case Production:
|
||||
return "warn"
|
||||
case Staging:
|
||||
return "info"
|
||||
case Development:
|
||||
return "debug"
|
||||
default:
|
||||
return "debug"
|
||||
}
|
||||
}
|
||||
|
||||
// getDefaultDatabaseURL returns default database URL for environment
|
||||
func getDefaultDatabaseURL(env Environment) string {
|
||||
switch env {
|
||||
case Development:
|
||||
return "postgres://postgres:password@localhost:5432/counter_db?sslmode=disable"
|
||||
case Staging:
|
||||
return "postgres://postgres:password@postgres:5432/counter_db?sslmode=disable"
|
||||
case Production:
|
||||
return "postgres://postgres:password@postgres:5432/counter_db?sslmode=require"
|
||||
default:
|
||||
return "postgres://postgres:password@localhost:5432/counter_db?sslmode=disable"
|
||||
}
|
||||
}
|
||||
|
||||
// getEnv gets environment variable with default value
|
||||
func getEnv(key, defaultValue string) string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// getRequiredEnv gets required environment variable
|
||||
func getRequiredEnv(key string) string {
|
||||
value := os.Getenv(key)
|
||||
if value == "" {
|
||||
log.Fatalf("❌ Required environment variable %s is not set", key)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// logConfig logs configuration (without sensitive data)
|
||||
func logConfig(config *Config) {
|
||||
// Environment banner
|
||||
log.Printf("")
|
||||
log.Printf("╔══════════════════════════════════════════════════════════════╗")
|
||||
log.Printf("║ COUNTER APPLICATION ║")
|
||||
log.Printf("║ ║")
|
||||
log.Printf("║ 🌍 ENVIRONMENT: %-15s ║", strings.ToUpper(string(config.Environment)))
|
||||
log.Printf("║ 🚀 MODE: %-20s ║", config.GinMode)
|
||||
log.Printf("║ 🔧 DEBUG: %-20s ║", fmt.Sprintf("%t", config.Debug))
|
||||
log.Printf("║ 📊 LOG LEVEL: %-15s ║", config.LogLevel)
|
||||
log.Printf("║ 🌐 PORT: %-20s ║", config.Port)
|
||||
log.Printf("║ ║")
|
||||
log.Printf("║ 📁 Configuration Files Loaded: ║")
|
||||
log.Printf("║ • .env (base configuration) ║")
|
||||
log.Printf("║ • .env.%s (environment-specific) ║", config.Environment)
|
||||
log.Printf("║ ║")
|
||||
log.Printf("║ 🔐 Security: ║")
|
||||
log.Printf("║ • Database: %s ║", maskDatabaseURL(config.DatabaseURL))
|
||||
log.Printf("║ • JWT Secret: %s ║", maskSecret(config.JWTSecret))
|
||||
log.Printf("║ ║")
|
||||
log.Printf("╚══════════════════════════════════════════════════════════════╝")
|
||||
log.Printf("")
|
||||
}
|
||||
|
||||
// maskDatabaseURL masks sensitive parts of database URL
|
||||
func maskDatabaseURL(url string) string {
|
||||
// Simple masking - replace password with ***
|
||||
if strings.Contains(url, "://") {
|
||||
parts := strings.Split(url, "://")
|
||||
if len(parts) == 2 {
|
||||
// Replace password in connection string
|
||||
masked := strings.Replace(parts[1], ":", ":***@", 1)
|
||||
return parts[0] + "://" + masked
|
||||
}
|
||||
}
|
||||
return "***"
|
||||
}
|
||||
|
||||
// maskSecret masks JWT secret for logging
|
||||
func maskSecret(secret string) string {
|
||||
if len(secret) <= 8 {
|
||||
return "***"
|
||||
}
|
||||
return secret[:4] + "***" + secret[len(secret)-4:]
|
||||
}
|
||||
Reference in New Issue
Block a user