Newwebshop/app/Core/ModuleRepository.php

625 lines
18 KiB
PHP

<?php
/**
* Copyright seit 2024 Webshop System
*
* Module-Repository für PrestaShop-Modul-Kompatibilität
*
* @author Webshop System
* @license GPL v3
*/
namespace App\Core;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Exception;
class ModuleRepository
{
private static $instance = null;
private $moduleManager;
private $eventDispatcher;
private $cache;
private $logger;
private $enabled = true;
private $repositories = [];
private $downloadPath;
private $tempPath;
private function __construct()
{
$this->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;
}
}