package config import ( "bufio" "fmt" "os" "strconv" "strings" ) // Config enthält alle Anwendungskonfigurationen type Config struct { // Server Konfiguration ServerPort string // Database Konfiguration DatabaseURL string DatabaseType string // Logging Konfiguration DebugMode bool LogLevel string // Environment Environment string DevTesting string // "yes" or "no", used to determine if we are in a test environment // Frontend URLs for CORS FrontendURL string // Frontend URL for CORS configuration // PDF Templates TemplatesDir string // Directory where PDF templates are stored ExportTempDir string // Directory for temporary PDF exports // Mail Configuration MailHost string // SMTP server host MailPort int // SMTP server port MailUsername string // SMTP username MailPassword string // SMTP password MailFrom string // Default sender email address } // Cfg ist die globale Konfigurationsvariable // Sie wird beim Programmstart automatisch geladen var Cfg *Config // init lädt die Konfiguration einmal beim Programmstart func init() { Cfg = LoadConfig() } // defaultConfig gibt die Standard-Konfiguration zurück func defaultConfig() *Config { return &Config{ ServerPort: "8180", DatabaseURL: "", DatabaseType: "mysql", DebugMode: false, LogLevel: "INFO", Environment: "production", DevTesting: "no", // Default to "no", can be overridden in tests FrontendURL: "http://localhost:5173", // Default frontend URL for development TemplatesDir: "./templates", // Default templates directory ExportTempDir: "./xporttemp", // Default export temp directory MailHost: "", // No default, must be configured MailPort: 465, // Default SMTP SSL port MailUsername: "", // No default, must be configured MailPassword: "", // No default, must be configured MailFrom: "", // No default, must be configured } } // LoadConfig lädt die Konfiguration aus Umgebungsvariablen func LoadConfig() *Config { // Lade .env-Datei falls vorhanden loadEnvFile() config := defaultConfig() // Debug: Zeige geladene Umgebungsvariablen fmt.Printf("DEBUG LoadConfig - ENVIRONMENT aus ENV: '%s'\n", os.Getenv("ENVIRONMENT")) fmt.Printf("DEBUG LoadConfig - TESTING aus ENV: '%s'\n", os.Getenv("DEVTESTING")) fmt.Printf("DEBUG LoadConfig - DATABASE_TYPE aus ENV: '%s'\n", os.Getenv("DATABASE_TYPE")) // Server Port if port := os.Getenv("API_PORT"); port != "" { config.ServerPort = port } if port := os.Getenv("SERVER_PORT"); port != "" { config.ServerPort = port } // Database if dbURL := os.Getenv("DATABASE_URL"); dbURL != "" { config.DatabaseURL = dbURL } else { host := os.Getenv("DB_HOST") port := os.Getenv("DB_PORT") user := os.Getenv("DB_USER") password := os.Getenv("DB_PASSWORD") dbName := os.Getenv("DB_NAME") if composed := buildDatabaseURL(host, port, user, password, dbName); composed != "" { config.DatabaseURL = composed } } if dbType := os.Getenv("DATABASE_TYPE"); dbType != "" { config.DatabaseType = strings.ToLower(dbType) } // Debug Mode if debug := os.Getenv("DEBUG"); debug != "" { config.DebugMode = strings.ToLower(debug) == "true" || debug == "1" } // Log Level if logLevel := os.Getenv("LOG_LEVEL"); logLevel != "" { config.LogLevel = strings.ToUpper(logLevel) } // Environment if env := os.Getenv("ENVIRONMENT"); env != "" { config.Environment = strings.ToLower(env) } if env := os.Getenv("GO_ENV"); env != "" { config.Environment = strings.ToLower(env) } // Automatisch Debug-Modus für Development-Environment aktivieren if config.Environment == "development" || config.Environment == "dev" { config.DebugMode = true if config.LogLevel == "INFO" { config.LogLevel = "DEBUG" } } // Testing in Development if testing := os.Getenv("DEVTESTING"); testing != "" { config.DevTesting = strings.ToLower(testing) fmt.Printf("DEBUG LoadConfig - DEVTESTING gefunden: '%s' -> DevTesting: '%s'\n", testing, config.DevTesting) } else { config.DevTesting = "no" // Default to "no" fmt.Printf("DEBUG LoadConfig - DEVTESTING nicht gefunden, setze DevTesting auf 'no'\n") } // Frontend URL if frontendURL := os.Getenv("BASE_URL"); frontendURL != "" { config.FrontendURL = frontendURL } // Templates Directory if templatesDir := os.Getenv("TEMPLATES_DIR"); templatesDir != "" { config.TemplatesDir = templatesDir } // Export Temp Directory if exportTempDir := os.Getenv("EXPORT_TEMP_DIR"); exportTempDir != "" { config.ExportTempDir = exportTempDir } // Mail Configuration if mailHost := os.Getenv("MAIL_HOST"); mailHost != "" { config.MailHost = mailHost } if mailPort := os.Getenv("MAIL_PORT"); mailPort != "" { if port, err := strconv.Atoi(mailPort); err == nil { config.MailPort = port } } if mailUsername := os.Getenv("MAIL_USERNAME"); mailUsername != "" { config.MailUsername = mailUsername } if mailPassword := os.Getenv("MAIL_PASSWORD"); mailPassword != "" { config.MailPassword = mailPassword } if mailFrom := os.Getenv("MAIL_FROM"); mailFrom != "" { config.MailFrom = mailFrom } else if config.MailUsername != "" { // Fallback: Verwende Username als From-Adresse config.MailFrom = config.MailUsername } fmt.Printf("DEBUG LoadConfig - Finale Config: Environment='%s', DevTesting='%s', DatabaseType='%s'\n Complete: %v\n", config.Environment, config.DevTesting, config.DatabaseType, config) return config } // loadEnvFile lädt eine .env-Datei falls vorhanden func loadEnvFile() { envFiles := []string{".env", ".env.local"} configFile := os.Getenv("CONFIG_FILE") if configFile != "" { envFiles = append(envFiles, configFile) } for _, envFile := range envFiles { if _, err := os.Stat(envFile); err == nil { fmt.Printf("DEBUG loadEnvFile - Lade .env-Datei: %s\n", envFile) loadEnvFileContent(envFile) } else { fmt.Printf("DEBUG loadEnvFile - .env-Datei nicht gefunden: %s\n", envFile) } } } // loadEnvFileContent lädt den Inhalt einer .env-Datei func loadEnvFileContent(filename string) { fmt.Printf("DEBUG loadEnvFileContent - Öffne Datei: %s\n", filename) file, err := os.Open(filename) if err != nil { fmt.Printf("DEBUG loadEnvFileContent - Fehler beim Öffnen von %s: %v\n", filename, err) return } defer file.Close() scanner := bufio.NewScanner(file) lineNum := 0 for scanner.Scan() { lineNum++ line := strings.TrimSpace(scanner.Text()) // Überspringe leere Zeilen und Kommentare if line == "" || strings.HasPrefix(line, "#") { continue } // Teile die Zeile in Key=Value auf parts := strings.SplitN(line, "=", 2) if len(parts) != 2 { continue } key := strings.TrimSpace(parts[0]) value := strings.TrimSpace(parts[1]) // Behandle Kommentare am Ende der Zeile (nur wenn nicht in Anführungszeichen) if !strings.HasPrefix(value, `"`) && !strings.HasPrefix(value, `'`) { // Suche nach Kommentar am Ende der Zeile if commentPos := strings.Index(value, "#"); commentPos > 0 { // Entferne Kommentar und Leerzeichen davor value = strings.TrimSpace(value[:commentPos]) } } // Entferne Anführungszeichen falls vorhanden value = strings.Trim(value, `"'`) fmt.Printf("DEBUG loadEnvFileContent - Zeile %d: %s='%s' (nach Kommentar-Behandlung)\n", lineNum, key, value) // Setze die Umgebungsvariable nur, wenn sie noch nicht gesetzt ist if os.Getenv(key) == "" { os.Setenv(key, value) fmt.Printf("DEBUG loadEnvFileContent - Setze ENV %s='%s'\n", key, value) } else { fmt.Printf("DEBUG loadEnvFileContent - ENV %s bereits gesetzt, überspringe\n", key) } } fmt.Printf("DEBUG loadEnvFileContent - Datei %s vollständig verarbeitet (%d Zeilen)\n", filename, lineNum) } // buildDatabaseURL composes a MySQL/MariaDB DSN from individual connection parts. // Returns an empty string if host, user, or dbName is missing. func buildDatabaseURL(host, port, user, password, dbName string) string { if host == "" || user == "" || dbName == "" { return "" } if port == "" { port = "3306" } return fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", user, password, host, port, dbName) } // IsDevelopment prüft, ob die Anwendung im Development-Modus läuft func (c *Config) IsDevelopment() bool { return c.Environment == "development" || c.Environment == "dev" } // IsProduction prüft, ob die Anwendung im Production-Modus läuft func (c *Config) IsProduction() bool { return c.Environment == "production" || c.Environment == "prod" } // GetServerAddress gibt die vollständige Server-Adresse zurück func (c *Config) GetServerAddress() string { return ":" + c.ServerPort } // GetBoolEnv ist eine Hilfsfunktion zum Laden von Boolean-Umgebungsvariablen func GetBoolEnv(key string, defaultValue bool) bool { if value := os.Getenv(key); value != "" { if parsed, err := strconv.ParseBool(value); err == nil { return parsed } return strings.ToLower(value) == "true" || value == "1" } return defaultValue } // GetIntEnv ist eine Hilfsfunktion zum Laden von Integer-Umgebungsvariablen func GetIntEnv(key string, defaultValue int) int { if value := os.Getenv(key); value != "" { if parsed, err := strconv.Atoi(value); err == nil { return parsed } } return defaultValue }