package providers import ( "encoding/json" "os" "path/filepath" "sync" ) // ACMEProvider Interface für ACME-basierte Certificate Authorities type ACMEProvider interface { // GetName gibt den Namen des ACME-Providers zurück GetName() string // GetDisplayName gibt den Anzeigenamen zurück GetDisplayName() string // GetDescription gibt eine Beschreibung zurück GetDescription() string // GetDirectoryURL gibt die ACME Directory URL zurück GetDirectoryURL() string // GetRenewalInfoURL gibt die RenewalInfo API URL zurück (optional) GetRenewalInfoURL() string // ValidateConfig validiert die Konfiguration ValidateConfig(settings map[string]interface{}) error // TestConnection testet die Verbindung zum ACME-Server TestConnection(settings map[string]interface{}) error // GetRequiredSettings gibt die erforderlichen Einstellungen zurück GetRequiredSettings() []SettingField } // ACMEProviderConfig enthält die Konfiguration eines ACME-Providers type ACMEProviderConfig struct { Enabled bool `json:"enabled"` Settings map[string]interface{} `json:"settings"` } // ACMEProviderManager verwaltet alle ACME-Provider type ACMEProviderManager struct { providers map[string]ACMEProvider configs map[string]*ACMEProviderConfig configDir string mu sync.RWMutex } var acmeManager *ACMEProviderManager var acmeOnce sync.Once // GetACMEManager gibt die Singleton-Instanz des ACMEProviderManagers zurück func GetACMEManager() *ACMEProviderManager { acmeOnce.Do(func() { acmeManager = &ACMEProviderManager{ providers: make(map[string]ACMEProvider), configs: make(map[string]*ACMEProviderConfig), configDir: "./config/providers", } acmeManager.loadAllConfigs() }) return acmeManager } // RegisterACMEProvider registriert einen neuen ACME-Provider func (pm *ACMEProviderManager) RegisterACMEProvider(provider ACMEProvider) { pm.mu.Lock() defer pm.mu.Unlock() providerID := provider.GetName() pm.providers[providerID] = provider // Lade Konfiguration falls vorhanden if pm.configs[providerID] == nil { pm.configs[providerID] = &ACMEProviderConfig{ Enabled: false, Settings: make(map[string]interface{}), } } } // GetACMEProvider gibt einen ACME-Provider zurück func (pm *ACMEProviderManager) GetACMEProvider(id string) (ACMEProvider, bool) { pm.mu.RLock() defer pm.mu.RUnlock() provider, exists := pm.providers[id] return provider, exists } // GetAllACMEProviders gibt alle registrierten ACME-Provider zurück func (pm *ACMEProviderManager) GetAllACMEProviders() map[string]ACMEProvider { pm.mu.RLock() defer pm.mu.RUnlock() result := make(map[string]ACMEProvider) for id, provider := range pm.providers { result[id] = provider } return result } // GetACMEProviderConfig gibt die Konfiguration eines ACME-Providers zurück func (pm *ACMEProviderManager) GetACMEProviderConfig(id string) (*ACMEProviderConfig, error) { pm.mu.RLock() defer pm.mu.RUnlock() config, exists := pm.configs[id] if !exists { return &ACMEProviderConfig{ Enabled: false, Settings: make(map[string]interface{}), }, nil } return config, nil } // SetACMEProviderEnabled aktiviert/deaktiviert einen ACME-Provider func (pm *ACMEProviderManager) SetACMEProviderEnabled(id string, enabled bool) error { pm.mu.Lock() defer pm.mu.Unlock() if pm.configs[id] == nil { pm.configs[id] = &ACMEProviderConfig{ Enabled: enabled, Settings: make(map[string]interface{}), } } else { pm.configs[id].Enabled = enabled } return pm.saveConfig(id, pm.configs[id]) } // loadAllConfigs lädt alle Konfigurationsdateien func (pm *ACMEProviderManager) loadAllConfigs() { // Stelle sicher, dass das Verzeichnis existiert os.MkdirAll(pm.configDir, 0755) // Lade alle JSON-Dateien im Konfigurationsverzeichnis files, err := filepath.Glob(filepath.Join(pm.configDir, "*.json")) if err != nil { return } for _, file := range files { id := filepath.Base(file[:len(file)-5]) // Entferne .json // Nur ACME-Provider-Konfigurationen laden (beginnen mit "letsencrypt") if id == "letsencrypt-production" || id == "letsencrypt-staging" { config, err := pm.loadConfig(id) if err == nil { pm.configs[id] = config } } } } // loadConfig lädt eine Konfigurationsdatei func (pm *ACMEProviderManager) loadConfig(id string) (*ACMEProviderConfig, error) { filePath := filepath.Join(pm.configDir, id+".json") data, err := os.ReadFile(filePath) if err != nil { return nil, err } var config ACMEProviderConfig if err := json.Unmarshal(data, &config); err != nil { return nil, err } return &config, nil } // saveConfig speichert eine Konfiguration in eine Datei func (pm *ACMEProviderManager) saveConfig(id string, config *ACMEProviderConfig) error { // Stelle sicher, dass das Verzeichnis existiert os.MkdirAll(pm.configDir, 0755) filePath := filepath.Join(pm.configDir, id+".json") data, err := json.MarshalIndent(config, "", " ") if err != nil { return err } return os.WriteFile(filePath, data, 0644) }