package config import ( "fmt" "os" "github.com/joho/godotenv" ) // Config holds all configuration for the service type Config struct { // Server Port string Environment string // Database DatabaseURL string // JWT JWTSecret string JWTRefreshSecret string // CORS AllowedOrigins []string // Rate Limiting RateLimitRequests int RateLimitWindow int // in seconds // BreakPilot Integration BreakPilotAPIURL string FrontendURL string // SMTP Email Configuration SMTPHost string SMTPPort int SMTPUsername string SMTPPassword string SMTPFromName string SMTPFromAddr string // Consent Settings ConsentDeadlineDays int ConsentReminderEnabled bool // VAPID Keys for Web Push VAPIDPublicKey string VAPIDPrivateKey string // Matrix (Synapse) Configuration MatrixHomeserverURL string MatrixAccessToken string MatrixServerName string MatrixEnabled bool // Jitsi Configuration JitsiBaseURL string JitsiAppID string JitsiAppSecret string JitsiEnabled bool } // Load loads configuration from environment variables func Load() (*Config, error) { // Load .env file if exists (for development) _ = godotenv.Load() cfg := &Config{ Port: getEnv("PORT", "8080"), Environment: getEnv("ENVIRONMENT", "development"), DatabaseURL: getEnv("DATABASE_URL", ""), JWTSecret: getEnv("JWT_SECRET", ""), JWTRefreshSecret: getEnv("JWT_REFRESH_SECRET", ""), RateLimitRequests: getEnvInt("RATE_LIMIT_REQUESTS", 100), RateLimitWindow: getEnvInt("RATE_LIMIT_WINDOW", 60), BreakPilotAPIURL: getEnv("BREAKPILOT_API_URL", "http://localhost:8000"), FrontendURL: getEnv("FRONTEND_URL", "http://localhost:8000"), // SMTP Configuration SMTPHost: getEnv("SMTP_HOST", ""), SMTPPort: getEnvInt("SMTP_PORT", 587), SMTPUsername: getEnv("SMTP_USERNAME", ""), SMTPPassword: getEnv("SMTP_PASSWORD", ""), SMTPFromName: getEnv("SMTP_FROM_NAME", "BreakPilot"), SMTPFromAddr: getEnv("SMTP_FROM_ADDR", "noreply@breakpilot.app"), // Consent Settings ConsentDeadlineDays: getEnvInt("CONSENT_DEADLINE_DAYS", 30), ConsentReminderEnabled: getEnvBool("CONSENT_REMINDER_ENABLED", true), // VAPID Keys VAPIDPublicKey: getEnv("VAPID_PUBLIC_KEY", ""), VAPIDPrivateKey: getEnv("VAPID_PRIVATE_KEY", ""), // Matrix Configuration MatrixHomeserverURL: getEnv("MATRIX_HOMESERVER_URL", "http://synapse:8008"), MatrixAccessToken: getEnv("MATRIX_ACCESS_TOKEN", ""), MatrixServerName: getEnv("MATRIX_SERVER_NAME", "breakpilot.local"), MatrixEnabled: getEnvBool("MATRIX_ENABLED", true), // Jitsi Configuration JitsiBaseURL: getEnv("JITSI_BASE_URL", "http://localhost:8443"), JitsiAppID: getEnv("JITSI_APP_ID", "breakpilot"), JitsiAppSecret: getEnv("JITSI_APP_SECRET", ""), JitsiEnabled: getEnvBool("JITSI_ENABLED", true), } // Parse allowed origins originsStr := getEnv("ALLOWED_ORIGINS", "http://localhost:3000,http://localhost:8000") cfg.AllowedOrigins = parseCommaSeparated(originsStr) // Validate required fields if cfg.DatabaseURL == "" { return nil, fmt.Errorf("DATABASE_URL is required") } if cfg.JWTSecret == "" { return nil, fmt.Errorf("JWT_SECRET is required") } return cfg, nil } 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 != "" { var result int fmt.Sscanf(value, "%d", &result) return result } return defaultValue } func getEnvBool(key string, defaultValue bool) bool { if value := os.Getenv(key); value != "" { return value == "true" || value == "1" || value == "yes" } return defaultValue } func parseCommaSeparated(s string) []string { if s == "" { return []string{} } var result []string start := 0 for i := 0; i <= len(s); i++ { if i == len(s) || s[i] == ',' { item := s[start:i] // Trim whitespace for len(item) > 0 && item[0] == ' ' { item = item[1:] } for len(item) > 0 && item[len(item)-1] == ' ' { item = item[:len(item)-1] } if item != "" { result = append(result, item) } start = i + 1 } } return result }