moduleManager = ModuleManager::getInstance(); $this->moduleRepository = ModuleRepository::getInstance(); $this->eventDispatcher = EventDispatcher::getInstance(); $this->cache = Cache::getInstance(); $this->logger = Logger::getInstance(); $this->loadSettings(); } /** * Singleton-Instanz abrufen */ public static function getInstance() { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } /** * Einstellungen laden */ private function loadSettings() { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $stmt = $conn->prepare(' SELECT setting_key, setting_value FROM ws_auto_update_settings WHERE active = 1 '); $stmt->execute(); $settings = $stmt->fetchAllAssociative(); foreach ($settings as $setting) { switch ($setting['setting_key']) { case 'enabled': $this->enabled = (bool)$setting['setting_value']; break; case 'check_interval': $this->checkInterval = (int)$setting['setting_value']; break; case 'auto_install': $this->autoInstall = (bool)$setting['setting_value']; break; case 'notify_email': $this->notifyEmail = $setting['setting_value']; break; } } } catch (Exception $e) { $this->logger->error('Auto-Update-Einstellungen laden Fehler', [ 'error' => $e->getMessage() ]); } } /** * Update-Check für alle Module */ public function checkForUpdates() { if (!$this->enabled) { return []; } $this->logger->info('Update-Check gestartet'); $updates = []; $modules = $this->moduleManager->getAllModules(); foreach ($modules as $moduleName => $module) { try { $update = $this->checkModuleUpdate($moduleName, $module); if ($update) { $updates[$moduleName] = $update; } } catch (\Exception $e) { $this->logger->error('Update-Check Fehler für Modul', [ 'module_name' => $moduleName, 'error' => $e->getMessage() ]); } } // Updates in Datenbank speichern $this->saveUpdateResults($updates); // Benachrichtigung senden falls Updates verfügbar if (!empty($updates) && $this->notifyEmail) { $this->sendUpdateNotification($updates); } // Auto-Installation falls aktiviert if ($this->autoInstall && !empty($updates)) { $this->autoInstallUpdates($updates); } $this->logger->info('Update-Check abgeschlossen', [ 'updates_found' => count($updates) ]); return $updates; } /** * Update-Check für einzelnes Modul */ public function checkModuleUpdate($moduleName, $module) { $currentVersion = $module['version'] ?? '1.0.0'; // Repository-Details abrufen $repositoryDetails = $this->getModuleRepositoryDetails($moduleName); if (!$repositoryDetails) { return null; } $latestVersion = $repositoryDetails['latest_version'] ?? '1.0.0'; // Version vergleichen if (version_compare($latestVersion, $currentVersion, '>')) { return [ 'module_name' => $moduleName, 'current_version' => $currentVersion, 'latest_version' => $latestVersion, 'repository' => $repositoryDetails['repository'] ?? 'official', 'changelog' => $repositoryDetails['changelog'] ?? '', 'download_url' => $repositoryDetails['download_url'] ?? '', 'release_date' => $repositoryDetails['release_date'] ?? '', 'priority' => $repositoryDetails['priority'] ?? 'normal' ]; } return null; } /** * Repository-Details für Modul abrufen */ private function getModuleRepositoryDetails($moduleName) { $repositories = $this->moduleRepository->getRepositories(); foreach ($repositories as $repositoryId => $repository) { if (!$repository['enabled']) { continue; } try { $details = $this->moduleRepository->getModuleDetails($moduleName, $repositoryId); if ($details) { $details['repository'] = $repositoryId; return $details; } } catch (\Exception $e) { $this->logger->error('Repository-Details Fehler', [ 'module_name' => $moduleName, 'repository_id' => $repositoryId, 'error' => $e->getMessage() ]); } } return null; } /** * Update installieren */ public function installUpdate($moduleName, $version = null) { try { // Event auslösen $this->eventDispatcher->dispatch('module.update.before', [ 'module_name' => $moduleName, 'version' => $version ]); // Backup erstellen $backup = $this->createModuleBackup($moduleName); // Update installieren $result = $this->moduleRepository->installModuleFromRepository($moduleName, $version); if ($result) { // Update-Status in Datenbank speichern $this->saveUpdateInstallation($moduleName, $version); // Event auslösen $this->eventDispatcher->dispatch('module.update.after', [ 'module_name' => $moduleName, 'version' => $version, 'backup' => $backup ]); $this->logger->info('Update installiert', [ 'module_name' => $moduleName, 'version' => $version, 'backup' => $backup ]); return true; } else { // Rollback falls Update fehlschlägt $this->restoreModuleBackup($moduleName, $backup); throw new \Exception('Update-Installation fehlgeschlagen'); } } catch (\Exception $e) { $this->logger->error('Update-Installation Fehler', [ 'module_name' => $moduleName, 'version' => $version, 'error' => $e->getMessage() ]); throw $e; } } /** * Modul-Backup erstellen */ private function createModuleBackup($moduleName) { $modulesDir = __DIR__ . '/../../../modules/'; $backupDir = __DIR__ . '/../../../backups/modules/'; if (!is_dir($backupDir)) { mkdir($backupDir, 0755, true); } $moduleDir = $modulesDir . $moduleName; $backupPath = $backupDir . $moduleName . '_backup_' . date('Y-m-d_H-i-s') . '.zip'; if (!is_dir($moduleDir)) { return null; } $zip = new \ZipArchive(); if ($zip->open($backupPath, \ZipArchive::CREATE) !== true) { return null; } $this->addDirectoryToZip($zip, $moduleDir, $moduleName); $zip->close(); return $backupPath; } /** * Verzeichnis zu ZIP hinzufügen */ private function addDirectoryToZip($zip, $dir, $basePath) { $files = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($dir), \RecursiveIteratorIterator::LEAVES_ONLY ); foreach ($files as $file) { if (!$file->isDir()) { $filePath = $file->getRealPath(); $relativePath = $basePath . '/' . substr($filePath, strlen($dir) + 1); $zip->addFile($filePath, $relativePath); } } } /** * Modul-Backup wiederherstellen */ private function restoreModuleBackup($moduleName, $backupPath) { if (!$backupPath || !file_exists($backupPath)) { return false; } $modulesDir = __DIR__ . '/../../../modules/'; $moduleDir = $modulesDir . $moduleName; // Aktuelles Modul entfernen if (is_dir($moduleDir)) { $this->removeDirectory($moduleDir); } // Backup entpacken $zip = new \ZipArchive(); if ($zip->open($backupPath) !== true) { return false; } $zip->extractTo($modulesDir); $zip->close(); return true; } /** * Verzeichnis rekursiv löschen */ private function removeDirectory($dir) { if (!is_dir($dir)) { return false; } $files = array_diff(scandir($dir), ['.', '..']); foreach ($files as $file) { $path = $dir . '/' . $file; if (is_dir($path)) { $this->removeDirectory($path); } else { unlink($path); } } return rmdir($dir); } /** * Auto-Installation von Updates */ private function autoInstallUpdates($updates) { foreach ($updates as $moduleName => $update) { try { // Nur automatische Updates für normale Priorität if ($update['priority'] === 'normal') { $this->installUpdate($moduleName, $update['latest_version']); $this->logger->info('Auto-Update installiert', [ 'module_name' => $moduleName, 'version' => $update['latest_version'] ]); } } catch (\Exception $e) { $this->logger->error('Auto-Update Fehler', [ 'module_name' => $moduleName, 'error' => $e->getMessage() ]); } } } /** * Update-Benachrichtigung senden */ private function sendUpdateNotification($updates) { if (empty($this->notifyEmail)) { return; } $subject = 'Module-Updates verfügbar - Webshop System'; $message = "Hallo,\n\n"; $message .= "Es sind Updates für folgende Module verfügbar:\n\n"; foreach ($updates as $moduleName => $update) { $message .= "- {$moduleName}: {$update['current_version']} → {$update['latest_version']}\n"; } $message .= "\nSie können die Updates im Admin-Bereich installieren.\n\n"; $message .= "Mit freundlichen Grüßen\nWebshop System"; $headers = [ 'From: noreply@webshop-system.com', 'Content-Type: text/plain; charset=UTF-8' ]; mail($this->notifyEmail, $subject, $message, implode("\r\n", $headers)); $this->logger->info('Update-Benachrichtigung gesendet', [ 'email' => $this->notifyEmail, 'updates_count' => count($updates) ]); } /** * Update-Ergebnisse in Datenbank speichern */ private function saveUpdateResults($updates) { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); // Alte Update-Checks löschen $stmt = $conn->prepare('DELETE FROM ws_auto_updates WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY)'); $stmt->execute(); // Neue Updates speichern $stmt = $conn->prepare(' INSERT INTO ws_auto_updates ( module_name, current_version, latest_version, repository, changelog, download_url, release_date, priority, created_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW()) '); foreach ($updates as $moduleName => $update) { $stmt->execute([ $moduleName, $update['current_version'], $update['latest_version'], $update['repository'], $update['changelog'], $update['download_url'], $update['release_date'], $update['priority'] ]); } } catch (Exception $e) { $this->logger->error('Update-Ergebnisse speichern Fehler', [ 'error' => $e->getMessage() ]); } } /** * Update-Installation in Datenbank speichern */ private function saveUpdateInstallation($moduleName, $version) { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $stmt = $conn->prepare(' INSERT INTO ws_update_installations ( module_name, version, installed_at ) VALUES (?, ?, NOW()) '); $stmt->execute([$moduleName, $version]); } catch (Exception $e) { $this->logger->error('Update-Installation speichern Fehler', [ 'error' => $e->getMessage() ]); } } /** * Verfügbare Updates abrufen */ public function getAvailableUpdates() { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $stmt = $conn->prepare(' SELECT * FROM ws_auto_updates WHERE created_at > DATE_SUB(NOW(), INTERVAL 7 DAY) ORDER BY priority DESC, created_at DESC '); $stmt->execute(); return $stmt->fetchAllAssociative(); } catch (Exception $e) { $this->logger->error('Verfügbare Updates abrufen Fehler', [ 'error' => $e->getMessage() ]); return []; } } /** * Update-Historie abrufen */ public function getUpdateHistory($moduleName = null) { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); $sql = 'SELECT * FROM ws_update_installations'; $params = []; if ($moduleName) { $sql .= ' WHERE module_name = ?'; $params[] = $moduleName; } $sql .= ' ORDER BY installed_at DESC'; $stmt = $conn->prepare($sql); $stmt->execute($params); return $stmt->fetchAllAssociative(); } catch (Exception $e) { $this->logger->error('Update-Historie abrufen Fehler', [ 'error' => $e->getMessage() ]); return []; } } /** * Einstellungen speichern */ public function saveSettings($settings) { try { $conn = DriverManager::getConnection([ 'url' => getenv('DATABASE_URL') ?: 'mysql://root:password@localhost/webshop' ]); foreach ($settings as $key => $value) { $stmt = $conn->prepare(' INSERT INTO ws_auto_update_settings ( setting_key, setting_value, active, updated_at ) VALUES (?, ?, 1, NOW()) ON DUPLICATE KEY UPDATE setting_value = ?, updated_at = NOW() '); $stmt->execute([$key, $value, $value]); } // Einstellungen neu laden $this->loadSettings(); $this->logger->info('Auto-Update-Einstellungen gespeichert', [ 'settings' => $settings ]); return true; } catch (Exception $e) { $this->logger->error('Auto-Update-Einstellungen speichern Fehler', [ 'error' => $e->getMessage() ]); return false; } } /** * Auto-Update-System aktivieren/deaktivieren */ public function setEnabled($enabled) { $this->enabled = $enabled; return $this; } /** * Auto-Update-System Status prüfen */ public function isEnabled() { return $this->enabled; } /** * Check-Intervall setzen */ public function setCheckInterval($interval) { $this->checkInterval = $interval; return $this; } /** * Check-Intervall abrufen */ public function getCheckInterval() { return $this->checkInterval; } /** * Auto-Installation setzen */ public function setAutoInstall($autoInstall) { $this->autoInstall = $autoInstall; return $this; } /** * Auto-Installation Status prüfen */ public function isAutoInstallEnabled() { return $this->autoInstall; } /** * Benachrichtigungs-E-Mail setzen */ public function setNotifyEmail($email) { $this->notifyEmail = $email; return $this; } /** * Benachrichtigungs-E-Mail abrufen */ public function getNotifyEmail() { return $this->notifyEmail; } }