moduleManager = ModuleManager::getInstance(); $this->eventDispatcher = EventDispatcher::getInstance(); $this->cache = Cache::getInstance(); $this->logger = Logger::getInstance(); $this->downloadPath = __DIR__ . '/../../../downloads/modules/'; $this->tempPath = __DIR__ . '/../../../temp/modules/'; // Verzeichnisse erstellen if (!is_dir($this->downloadPath)) { mkdir($this->downloadPath, 0755, true); } if (!is_dir($this->tempPath)) { mkdir($this->tempPath, 0755, true); } $this->loadRepositories(); } /** * Singleton-Instanz abrufen */ public static function getInstance() { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } /** * Repositories laden */ private function loadRepositories() { $this->repositories = [ 'official' => [ 'name' => 'Offizielles Repository', 'url' => 'https://repository.webshop-system.com/official', 'type' => 'official', 'enabled' => true ], 'community' => [ 'name' => 'Community Repository', 'url' => 'https://repository.webshop-system.com/community', 'type' => 'community', 'enabled' => true ], 'custom' => [ 'name' => 'Custom Repository', 'url' => getenv('CUSTOM_REPOSITORY_URL') ?: '', 'type' => 'custom', 'enabled' => !empty(getenv('CUSTOM_REPOSITORY_URL')) ] ]; } /** * Module-Liste aus Repository abrufen */ public function getModulesFromRepository($repositoryId = 'official', $filters = []) { $cacheKey = 'repository_modules_' . $repositoryId . '_' . md5(serialize($filters)); // Cache prüfen $cached = $this->cache->get($cacheKey); if ($cached !== null) { return $cached; } if (!isset($this->repositories[$repositoryId])) { return []; } $repository = $this->repositories[$repositoryId]; try { $modules = $this->fetchModulesFromRepository($repository, $filters); // Cache setzen (1 Stunde) $this->cache->set($cacheKey, $modules, 3600); return $modules; } catch (\Exception $e) { $this->logger->error('Repository-Fehler', [ 'repository_id' => $repositoryId, 'error' => $e->getMessage() ]); return []; } } /** * Module aus Repository abrufen */ private function fetchModulesFromRepository($repository, $filters) { $url = $repository['url'] . '/api/modules'; // Filter als Query-Parameter hinzufügen if (!empty($filters)) { $url .= '?' . http_build_query($filters); } $context = stream_context_create([ 'http' => [ 'method' => 'GET', 'header' => [ 'User-Agent: Webshop-System/1.0', 'Accept: application/json' ], 'timeout' => 30 ] ]); $response = file_get_contents($url, false, $context); if ($response === false) { throw new \Exception('Repository nicht erreichbar'); } $data = json_decode($response, true); if (!$data || !isset($data['modules'])) { throw new \Exception('Ungültige Repository-Antwort'); } return $data['modules']; } /** * Modul-Details aus Repository abrufen */ public function getModuleDetails($moduleName, $repositoryId = 'official') { $cacheKey = 'repository_module_details_' . $repositoryId . '_' . $moduleName; // Cache prüfen $cached = $this->cache->get($cacheKey); if ($cached !== null) { return $cached; } if (!isset($this->repositories[$repositoryId])) { return null; } $repository = $this->repositories[$repositoryId]; try { $url = $repository['url'] . '/api/modules/' . urlencode($moduleName); $context = stream_context_create([ 'http' => [ 'method' => 'GET', 'header' => [ 'User-Agent: Webshop-System/1.0', 'Accept: application/json' ], 'timeout' => 30 ] ]); $response = file_get_contents($url, false, $context); if ($response === false) { throw new \Exception('Repository nicht erreichbar'); } $data = json_decode($response, true); if (!$data || !isset($data['module'])) { throw new \Exception('Modul nicht gefunden'); } // Cache setzen (1 Stunde) $this->cache->set($cacheKey, $data['module'], 3600); return $data['module']; } catch (\Exception $e) { $this->logger->error('Repository-Modul-Details Fehler', [ 'repository_id' => $repositoryId, 'module_name' => $moduleName, 'error' => $e->getMessage() ]); return null; } } /** * Modul aus Repository herunterladen */ public function downloadModule($moduleName, $version = null, $repositoryId = 'official') { if (!isset($this->repositories[$repositoryId])) { throw new \Exception('Repository nicht gefunden'); } $repository = $this->repositories[$repositoryId]; try { // Modul-Details abrufen $moduleDetails = $this->getModuleDetails($moduleName, $repositoryId); if (!$moduleDetails) { throw new \Exception('Modul nicht gefunden'); } // Version bestimmen if (!$version) { $version = $moduleDetails['latest_version']; } // Download-URL erstellen $downloadUrl = $repository['url'] . '/download/' . urlencode($moduleName) . '/' . urlencode($version); // Datei herunterladen $tempFile = $this->tempPath . $moduleName . '_' . $version . '.zip'; $context = stream_context_create([ 'http' => [ 'method' => 'GET', 'header' => [ 'User-Agent: Webshop-System/1.0' ], 'timeout' => 300 // 5 Minuten für Download ] ]); $downloadResult = file_put_contents($tempFile, file_get_contents($downloadUrl, false, $context)); if ($downloadResult === false) { throw new \Exception('Download fehlgeschlagen'); } // Datei validieren if (!$this->validateModuleFile($tempFile)) { unlink($tempFile); throw new \Exception('Ungültige Modul-Datei'); } // Event auslösen $this->eventDispatcher->dispatch('module.download', [ 'module_name' => $moduleName, 'version' => $version, 'repository_id' => $repositoryId, 'file_path' => $tempFile ]); $this->logger->info('Modul heruntergeladen', [ 'module_name' => $moduleName, 'version' => $version, 'repository_id' => $repositoryId, 'file_size' => filesize($tempFile) ]); return $tempFile; } catch (\Exception $e) { $this->logger->error('Modul-Download Fehler', [ 'module_name' => $moduleName, 'version' => $version, 'repository_id' => $repositoryId, 'error' => $e->getMessage() ]); throw $e; } } /** * Modul-Datei validieren */ private function validateModuleFile($filePath) { if (!file_exists($filePath)) { return false; } $zip = new \ZipArchive(); if ($zip->open($filePath) !== true) { return false; } // Mindestanforderungen prüfen $requiredFiles = ['module.json', 'Module.php']; $hasRequiredFiles = true; foreach ($requiredFiles as $requiredFile) { if ($zip->locateName($requiredFile) === false) { $hasRequiredFiles = false; break; } } $zip->close(); return $hasRequiredFiles; } /** * Modul aus Repository installieren */ public function installModuleFromRepository($moduleName, $version = null, $repositoryId = 'official') { try { // Event auslösen $this->eventDispatcher->dispatch('module.install.repository.before', [ 'module_name' => $moduleName, 'version' => $version, 'repository_id' => $repositoryId ]); // Modul herunterladen $tempFile = $this->downloadModule($moduleName, $version, $repositoryId); // Modul installieren $result = $this->installModuleFromFile($tempFile); // Temporäre Datei löschen if (file_exists($tempFile)) { unlink($tempFile); } if ($result) { // Event auslösen $this->eventDispatcher->dispatch('module.install.repository.after', [ 'module_name' => $moduleName, 'version' => $version, 'repository_id' => $repositoryId, 'result' => $result ]); $this->logger->info('Modul aus Repository installiert', [ 'module_name' => $moduleName, 'version' => $version, 'repository_id' => $repositoryId ]); return $result; } else { throw new \Exception('Modul-Installation fehlgeschlagen'); } } catch (\Exception $e) { $this->logger->error('Repository-Modul-Installation Fehler', [ 'module_name' => $moduleName, 'version' => $version, 'repository_id' => $repositoryId, 'error' => $e->getMessage() ]); throw $e; } } /** * Modul aus Datei installieren */ private function installModuleFromFile($filePath) { $modulesDir = __DIR__ . '/../../../modules/'; if (!is_dir($modulesDir)) { mkdir($modulesDir, 0755, true); } $zip = new \ZipArchive(); if ($zip->open($filePath) !== true) { return false; } // Modul-Name aus ZIP extrahieren $moduleName = null; $configContent = $zip->getFromName('module.json'); if ($configContent) { $config = json_decode($configContent, true); $moduleName = $config['name'] ?? null; } if (!$moduleName) { $zip->close(); return false; } // Modul-Verzeichnis erstellen $moduleDir = $modulesDir . $moduleName; if (is_dir($moduleDir)) { // Bestehendes Modul sichern $backupDir = $moduleDir . '_backup_' . date('Y-m-d_H-i-s'); rename($moduleDir, $backupDir); } // ZIP entpacken $zip->extractTo($moduleDir); $zip->close(); // Modul registrieren if (file_exists($moduleDir . '/module.json')) { $config = json_decode(file_get_contents($moduleDir . '/module.json'), true); return $this->moduleManager->registerModule($moduleName, $config); } return false; } /** * Repository-Liste abrufen */ public function getRepositories() { return $this->repositories; } /** * Repository hinzufügen */ public function addRepository($id, $config) { $this->repositories[$id] = array_merge($config, [ 'id' => $id, 'enabled' => $config['enabled'] ?? true ]); // Cache invalidieren $this->cache->delete('repository_modules_' . $id . '_*'); $this->logger->info('Repository hinzugefügt', [ 'repository_id' => $id, 'config' => $config ]); return $this; } /** * Repository entfernen */ public function removeRepository($id) { if (isset($this->repositories[$id])) { unset($this->repositories[$id]); // Cache invalidieren $this->cache->delete('repository_modules_' . $id . '_*'); $this->logger->info('Repository entfernt', [ 'repository_id' => $id ]); } return $this; } /** * Repository aktivieren/deaktivieren */ public function setRepositoryEnabled($id, $enabled) { if (isset($this->repositories[$id])) { $this->repositories[$id]['enabled'] = $enabled; $this->logger->info('Repository-Status geändert', [ 'repository_id' => $id, 'enabled' => $enabled ]); } return $this; } /** * Repository-Status prüfen */ public function checkRepositoryStatus($repositoryId) { if (!isset($this->repositories[$repositoryId])) { return false; } $repository = $this->repositories[$repositoryId]; try { $url = $repository['url'] . '/api/status'; $context = stream_context_create([ 'http' => [ 'method' => 'GET', 'header' => [ 'User-Agent: Webshop-System/1.0', 'Accept: application/json' ], 'timeout' => 10 ] ]); $response = file_get_contents($url, false, $context); if ($response === false) { return false; } $data = json_decode($response, true); return isset($data['status']) && $data['status'] === 'ok'; } catch (\Exception $e) { return false; } } /** * Repository-Statistiken abrufen */ public function getRepositoryStatistics() { $statistics = []; foreach ($this->repositories as $id => $repository) { $statistics[$id] = [ 'name' => $repository['name'], 'type' => $repository['type'], 'enabled' => $repository['enabled'], 'status' => $this->checkRepositoryStatus($id), 'modules_count' => 0 ]; if ($repository['enabled']) { try { $modules = $this->getModulesFromRepository($id); $statistics[$id]['modules_count'] = count($modules); } catch (\Exception $e) { $statistics[$id]['error'] = $e->getMessage(); } } } return $statistics; } /** * Cache invalidieren */ public function invalidateCache($repositoryId = null) { if ($repositoryId) { $this->cache->delete('repository_modules_' . $repositoryId . '_*'); $this->cache->delete('repository_module_details_' . $repositoryId . '_*'); } else { $this->cache->delete('repository_*'); } return $this; } /** * Repository-System aktivieren/deaktivieren */ public function setEnabled($enabled) { $this->enabled = $enabled; return $this; } /** * Repository-System Status prüfen */ public function isEnabled() { return $this->enabled; } /** * Download-Pfad abrufen */ public function getDownloadPath() { return $this->downloadPath; } /** * Temp-Pfad abrufen */ public function getTempPath() { return $this->tempPath; } }