Ctrl K
ComunidadGuías…

configs/config.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// Package configs provides configuration management for ActaLog
package configs

import (
	"fmt"
	"os"
	"strconv"
	"time"
)

// Config holds all configuration for the application
type Config struct {
	Server    ServerConfig
	Database  DatabaseConfig
	JWT       JWTConfig
	App       AppConfig
	Logging   LoggingConfig
	Email     EmailConfig
	Security  SecurityConfig
	Scheduler SchedulerConfig
}

// SchedulerConfig holds scheduler configuration
type SchedulerConfig struct {
	Enabled       bool          // Enable in-app scheduler
	Interval      time.Duration // How often to run the materializer
	DaysAhead     int           // Number of days ahead to materialize sessions
	RunOnStartup  bool          // Run materialization immediately on startup
}

// ServerConfig holds server-related configuration
type ServerConfig struct {
	Host         string
	Port         int
	ReadTimeout  time.Duration
	WriteTimeout time.Duration
	IdleTimeout  time.Duration
}

// DatabaseConfig holds database connection configuration
type DatabaseConfig struct {
	Driver   string // sqlite3, postgres, mysql
	Host     string
	Port     int
	User     string
	Password string
	Database string
	SSLMode  string
	Schema   string // PostgreSQL schema (default: public)

	// Connection pooling (PostgreSQL and MySQL only)
	MaxOpenConns    int           // Maximum number of open connections
	MaxIdleConns    int           // Maximum number of idle connections
	ConnMaxLifetime time.Duration // Maximum connection lifetime
}

// JWTConfig holds JWT authentication configuration
type JWTConfig struct {
	SecretKey            string
	ExpirationTime       time.Duration
	RefreshTokenDuration time.Duration
	Issuer               string
}

// AppConfig holds application-specific configuration
type AppConfig struct {
	Name              string
	Environment       string // development, staging, production
	LogLevel          string // debug, info, warn, error
	CORSOrigins       []string
	AllowRegistration bool   // Allow new user registration after first user
	LogoVariant       string // Logo variant: "logo" (default) or "betalogo"
}

// LoggingConfig holds logging configuration
type LoggingConfig struct {
	Level      string // debug, info, warn, error
	Format     string // text (default) or json
	EnableFile bool   // Enable file logging
	FilePath   string // Path to log file
	MaxSizeMB  int    // Max file size in MB before rotation
	MaxBackups int    // Number of old log files to keep
}

// EmailConfig holds email/SMTP configuration
type EmailConfig struct {
	SMTPHost            string // SMTP server host
	SMTPPort            int    // SMTP server port (587 for STARTTLS, 465 for TLS, 25 for plain)
	SMTPUser            string // SMTP username
	SMTPPassword        string // SMTP password
	FromAddress         string // From email address
	FromName            string // From name
	Enabled             bool   // Enable email sending
	RequireVerification bool   // Require email verification for new users
}

// SecurityConfig holds security-related configuration
type SecurityConfig struct {
	MaxLoginAttempts       int           // Max failed login attempts before lockout
	AccountLockoutDuration time.Duration // How long to lock account after max attempts

	// Password strength (for future use)
	MinPasswordLength        int  // Minimum password length
	RequirePasswordUppercase bool // Require at least one uppercase letter
	RequirePasswordLowercase bool // Require at least one lowercase letter
	RequirePasswordNumber    bool // Require at least one number
	RequirePasswordSpecial   bool // Require at least one special character
}

// Load loads configuration from environment variables with sensible defaults
func Load() (*Config, error) {
	cfg := &Config{
		Server: ServerConfig{
			Host:         getEnv("SERVER_HOST", "0.0.0.0"),
			Port:         getEnvInt("SERVER_PORT", 8080),
			ReadTimeout:  getEnvDuration("SERVER_READ_TIMEOUT", 30*time.Second),
			WriteTimeout: getEnvDuration("SERVER_WRITE_TIMEOUT", 60*time.Second),
			IdleTimeout:  getEnvDuration("SERVER_IDLE_TIMEOUT", 60*time.Second),
		},
		Database: DatabaseConfig{
			Driver:          getEnv("DB_DRIVER", "sqlite3"),
			Host:            getEnv("DB_HOST", "localhost"),
			Port:            getEnvInt("DB_PORT", 5432),
			User:            getEnv("DB_USER", "actalog"),
			Password:        getEnv("DB_PASSWORD", ""),
			Database:        getEnv("DB_NAME", "actalog.db"),
			SSLMode:         getEnv("DB_SSLMODE", "disable"),
			Schema:          getEnv("DB_SCHEMA", "public"), // PostgreSQL schema
			MaxOpenConns:    getEnvInt("DB_MAX_OPEN_CONNS", 25),
			MaxIdleConns:    getEnvInt("DB_MAX_IDLE_CONNS", 5),
			ConnMaxLifetime: getEnvDuration("DB_CONN_MAX_LIFETIME", 5*time.Minute),
		},
		JWT: JWTConfig{
			SecretKey:            getEnv("JWT_SECRET", ""), // Must be set in production
			ExpirationTime:       getEnvDuration("JWT_EXPIRATION", 24*time.Hour),
			RefreshTokenDuration: getEnvDuration("JWT_REFRESH_DURATION", 30*24*time.Hour), // 30 days
			Issuer:               getEnv("JWT_ISSUER", "actalog"),
		},
		App: AppConfig{
			Name:              "ActaLog",
			Environment:       getEnv("APP_ENV", "development"),
			LogLevel:          getEnv("LOG_LEVEL", "info"),
			CORSOrigins:       getEnvSlice("CORS_ORIGINS", []string{"http://localhost:8080", "http://localhost:3000"}),
			AllowRegistration: getEnvBool("ALLOW_REGISTRATION", true), // Allow by default in development
			LogoVariant:       getEnv("LOGO_VARIANT", "logo"),
		},
		Logging: LoggingConfig{
			Level:      getEnv("LOG_LEVEL", "info"),
			Format:     getEnv("LOG_FORMAT", "text"), // text (default) or json
			EnableFile: getEnvBool("LOG_FILE_ENABLED", false),
			FilePath:   getEnv("LOG_FILE_PATH", ""),       // Empty = auto-detect (./logs/actalog.log)
			MaxSizeMB:  getEnvInt("LOG_MAX_SIZE_MB", 100), // 100MB default
			MaxBackups: getEnvInt("LOG_MAX_BACKUPS", 3),   // Keep 3 old files
		},
		Email: EmailConfig{
			SMTPHost:            getEnv("SMTP_HOST", ""),
			SMTPPort:            getEnvInt("SMTP_PORT", 587), // Default to STARTTLS port
			SMTPUser:            getEnv("SMTP_USER", ""),
			SMTPPassword:        getEnv("SMTP_PASSWORD", ""),
			FromAddress:         getEnv("EMAIL_FROM", ""),
			FromName:            getEnv("EMAIL_FROM_NAME", "ActaLog"),
			Enabled:             getEnvBool("EMAIL_ENABLED", false),              // Disabled by default
			RequireVerification: getEnvBool("REQUIRE_EMAIL_VERIFICATION", false), // Disabled by default for testing
		},
		Security: SecurityConfig{
			MaxLoginAttempts:       getEnvInt("MAX_LOGIN_ATTEMPTS", 5),
			AccountLockoutDuration: getEnvDuration("ACCOUNT_LOCKOUT_DURATION", 15*time.Minute),

			// Password strength (future use)
			MinPasswordLength:        getEnvInt("MIN_PASSWORD_LENGTH", 8),
			RequirePasswordUppercase: getEnvBool("REQUIRE_PASSWORD_UPPERCASE", false),
			RequirePasswordLowercase: getEnvBool("REQUIRE_PASSWORD_LOWERCASE", false),
			RequirePasswordNumber:    getEnvBool("REQUIRE_PASSWORD_NUMBER", false),
			RequirePasswordSpecial:   getEnvBool("REQUIRE_PASSWORD_SPECIAL", false),
		},
		Scheduler: SchedulerConfig{
			Enabled:      getEnvBool("SCHEDULER_ENABLED", false),                   // Disabled by default
			Interval:     getEnvDuration("SCHEDULER_INTERVAL", 6*time.Hour),        // Run every 6 hours
			DaysAhead:    getEnvInt("SCHEDULER_DAYS_AHEAD", 14),                    // Materialize 14 days ahead
			RunOnStartup: getEnvBool("SCHEDULER_RUN_ON_STARTUP", true),             // Run on startup when enabled
		},
	}

	// Validate critical configuration
	if cfg.App.Environment == "production" && cfg.JWT.SecretKey == "" {
		return nil, fmt.Errorf("JWT_SECRET must be set in production environment")
	}

	return cfg, nil
}

// Helper functions for environment variable parsing

func getEnv(key, defaultValue string) string {
	if value := os.Getenv(key); value != "" {
		return value
	}
	return defaultValue
}

func getEnvInt(key string, defaultValue int) int {
	if value := os.Getenv(key); value != "" {
		if intVal, err := strconv.Atoi(value); err == nil {
			return intVal
		}
	}
	return defaultValue
}

func getEnvDuration(key string, defaultValue time.Duration) time.Duration {
	if value := os.Getenv(key); value != "" {
		if duration, err := time.ParseDuration(value); err == nil {
			return duration
		}
	}
	return defaultValue
}

func getEnvSlice(key string, defaultValue []string) []string {
	if value := os.Getenv(key); value != "" {
		// Simple comma-separated parsing
		return []string{value}
	}
	return defaultValue
}

func getEnvBool(key string, defaultValue bool) bool {
	if value := os.Getenv(key); value != "" {
		return value == "true" || value == "1" || value == "yes"
	}
	return defaultValue
}