// Package config handles loading configuration from environment variables. // Configuration is loaded from .env files (for development) and environment variables (for production). package config import ( "fmt" // formatted error messages "os" // Standard library for reading environment variables "strings" "github.com/joho/godotenv" // Third-party library for loading .env files ) // Config holds all configuration options for the application. // Each field corresponds to either an environment variable or a default value. type Config struct { // Database connection settings DBHost string // Database server hostname or IP address DBPort string // Database server port (usually 3306 for MySQL/MariaDB) DBUser string // Database username DBPassword string // Database password (keep this secret!) DBName string // Database name (e.g., "mailserver") // Network settings for the web server Bind string // IP address to bind to (0.0.0.0 = all interfaces, 127.0.0.1 = localhost only) Port string // TCP port to listen on // Security settings JWTSecret string // Secret key for signing JWT tokens (keep this secret!) TrustedProxies []string // IPs of trusted reverse proxies (for X-Forwarded-For) // Mail server paths and settings MailDataDir string // Directory where mail is stored (e.g., /var/vmail) PostqueuePath string // Path to postqueue command (for mail queue operations) PostsuperPath string // Path to postsuper command (for mail queue operations) DovecotQuotaCmd string // Path to doveadm command (for quota operations) // System integration JournalctlPath string // Path to journalctl command (for reading system logs) // Rspamd settings (spam filter) RspamdAPI string // URL of the Rspamd web interface } // Load reads configuration from environment variables and .env files. // It returns a Config struct with all settings. // // Environment variables take precedence over .env file values. // .env file values take precedence over code defaults. // // The function looks for a .env file in the current directory. func Load() *Config { // Load .env file if it exists. // This is for local development convenience. // In production, use environment variables directly. // Try current dir first, then parent (for when running from backend/). if err := godotenv.Load(".env"); err != nil { godotenv.Load("../.env") } // Return a new Config struct with all values. // getEnv(key, default) returns the environment variable value, // or the default if the environment variable is not set. return &Config{ // Database settings // Default: localhost:3306 with mailadmin user DBHost: getEnv("DB_HOST", "localhost"), DBPort: getEnv("DB_PORT", "3306"), DBUser: getEnv("DB_USER", "mailadmin"), DBPassword: getEnv("DB_PASSWORD", ""), // No default - must be set! DBName: getEnv("DB_NAME", "mailserver"), // Server settings // Default: bind to all interfaces on port 8080 Bind: getEnv("BIND", "0.0.0.0"), Port: getEnv("PORT", "8080"), // Security // Default JWT secret is insecure - must be changed in production! JWTSecret: getEnv("JWT_SECRET", "change-this-secret-in-production"), // Mail server paths MailDataDir: getEnv("MAIL_DATA_DIR", "/var/vmail"), PostqueuePath: getEnv("POSTQUEUE_PATH", "/usr/sbin/postqueue"), PostsuperPath: getEnv("POSTSUPER_PATH", "/usr/sbin/postsuper"), DovecotQuotaCmd: getEnv("DOVECOT_QUOTA_CMD", "/usr/bin/doveadm"), JournalctlPath: getEnv("JOURNALCTL_PATH", "/usr/bin/journalctl"), // External services RspamdAPI: getEnv("RSPAMD_API", "http://127.0.0.1:11334"), // Trusted proxies (comma-separated IPs) TrustedProxies: parseTrustedProxies(getEnv("TRUSTED_PROXIES", "")), } } func parseTrustedProxies(s string) []string { if s == "" { return nil } parts := strings.Split(s, ",") result := make([]string, 0, len(parts)) for _, p := range parts { p = strings.TrimSpace(p) if p != "" { result = append(result, p) } } return result } func (c *Config) Validate() error { if len(c.JWTSecret) < 32 { return fmt.Errorf("JWT_SECRET must be at least 32 characters long") } if c.JWTSecret == "your-secret-key-at-least-32-chars" { return fmt.Errorf("JWT_SECRET must not be the default value") } return nil } // getEnv reads an environment variable or returns a default value. // key: the name of the environment variable. // defaultValue: the value to return if the environment variable is not set. func getEnv(key, defaultValue string) string { // Check if the environment variable is set and not empty. if value := os.Getenv(key); value != "" { return value } // Return the default value. return defaultValue }