Files
counter/config.go
aovantsev ef352736f0
All checks were successful
continuous-integration/drone/push Build is passing
test
2025-10-10 19:26:15 +03:00

227 lines
7.1 KiB
Go

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
MetricsPort string
GinMode string
LogLevel string
Debug bool
LogDir string
LogVolume string
}
// 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"),
MetricsPort: getEnv("METRICS_PORT", "9090"),
GinMode: getGinMode(env),
LogLevel: getLogLevel(env),
Debug: env == Development,
LogDir: getEnv("LOG_DIR", "/app/logs"),
LogVolume: getEnv("LOG_VOLUME", "counter_logs"),
}
// 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()
// Use standard log for early initialization (before logger is ready)
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 "info" // Changed from "warn" to "info" to capture more logs
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) {
// Use standard log for configuration banner since logger might not be initialized yet
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("║ 📈 METRICS PORT: %-15s ║", config.MetricsPort)
log.Printf("║ 📝 LOG DIR: %-20s ║", config.LogDir)
log.Printf("║ 📦 LOG VOLUME: %-18s ║", config.LogVolume)
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:]
}