modulesPath = app_path('Modules'); $this->loadActiveModules(); } /** * Carica lista moduli attivi dalla cache/database */ protected function loadActiveModules() { if (!$this->modulesTableExists()) { $this->activeModules = []; return; } $this->activeModules = Cache::remember('netgescon.active_modules', 3600, function () { try { return DB::table('modules')->where('status', 'active')->pluck('name')->toArray(); } catch (\Throwable $e) { Log::warning('ModuleManager: impossibile caricare moduli attivi', [ 'error' => $e->getMessage(), ]); return []; } }); } /** * Installa un nuovo modulo da file ZIP */ public function installModule($zipPath, $validateLicense = true) { try { Log::info("Inizio installazione modulo da: {$zipPath}"); // 1. Estrai e valida $extractPath = $this->extractModule($zipPath); $config = $this->validateModuleConfig($extractPath); // 2. Verifica licenza (se richiesta) if ($validateLicense && !$this->validateLicense($config)) { throw new \Exception("Licenza non valida per il modulo {$config['name']}"); } // 3. Verifica dipendenze $this->checkDependencies($config); // 4. Backup database $this->createBackup($config['name']); // 5. Esegui migrazioni $this->runMigrations($config['name']); // 6. Registra routes $this->registerRoutes($config['name']); // 7. Installa permessi $this->installPermissions($config); // 8. Aggiorna menu $this->updateMenuSystem($config); // 9. Copia assets $this->publishAssets($config['name']); // 10. Registra modulo nel database $this->registerModule($config); // 11. Pulisci cache $this->clearModuleCache(); Log::info("Modulo {$config['name']} installato con successo"); return [ 'success' => true, 'message' => "Modulo {$config['display_name']} installato con successo", 'module' => $config ]; } catch (\Exception $e) { Log::error("Errore installazione modulo: " . $e->getMessage()); // Rollback se necessario if (isset($config)) { $this->rollbackInstallation($config['name']); } return [ 'success' => false, 'message' => $e->getMessage() ]; } } /** * Disinstalla un modulo */ public function uninstallModule($moduleName, $keepData = false) { try { Log::info("Inizio disinstallazione modulo: {$moduleName}"); $config = $this->getModuleConfig($moduleName); // 1. Controlla dipendenze inverse $dependentModules = $this->getDependentModules($moduleName); if (!empty($dependentModules)) { throw new \Exception("Impossibile disinstallare: moduli dipendenti attivi: " . implode(', ', $dependentModules)); } // 2. Backup prima della disinstallazione if (!$keepData) { $this->createBackup($moduleName); } // 3. Disattiva modulo $this->deactivateModule($moduleName); // 4. Rimuovi routes $this->unregisterRoutes($moduleName); // 5. Rimuovi permessi $this->uninstallPermissions($config); // 6. Rimuovi menu $this->removeMenuItems($config); // 7. Esegui migrazioni rollback (se non si tengono i dati) if (!$keepData) { $this->rollbackMigrations($moduleName); } // 8. Rimuovi assets $this->removeAssets($moduleName); // 9. Rimuovi file modulo $this->removeModuleFiles($moduleName); // 10. Rimuovi dal database $this->unregisterModule($moduleName); // 11. Pulisci cache $this->clearModuleCache(); Log::info("Modulo {$moduleName} disinstallato con successo"); return [ 'success' => true, 'message' => "Modulo {$moduleName} disinstallato con successo" ]; } catch (\Exception $e) { Log::error("Errore disinstallazione modulo: " . $e->getMessage()); return [ 'success' => false, 'message' => $e->getMessage() ]; } } /** * Aggiorna un modulo esistente */ public function updateModule($moduleName, $zipPath) { try { Log::info("Inizio aggiornamento modulo: {$moduleName}"); $oldConfig = $this->getModuleConfig($moduleName); // 1. Backup completo $backupPath = $this->createFullBackup($moduleName); // 2. Estrai nuova versione $extractPath = $this->extractModule($zipPath); $newConfig = $this->validateModuleConfig($extractPath); // 3. Verifica compatibilità versioni if (!$this->isCompatibleVersion($oldConfig, $newConfig)) { throw new \Exception("Versione non compatibile"); } // 4. Disattiva temporaneamente $this->deactivateModule($moduleName); // 5. Aggiorna file $this->updateModuleFiles($moduleName, $extractPath); // 6. Esegui migrazioni di aggiornamento $this->runMigrations($moduleName); // 7. Aggiorna configurazione $this->updateModuleConfig($newConfig); // 8. Riattiva modulo $this->activateModule($moduleName); // 9. Pulisci cache $this->clearModuleCache(); Log::info("Modulo {$moduleName} aggiornato con successo"); return [ 'success' => true, 'message' => "Modulo {$moduleName} aggiornato alla versione {$newConfig['version']}", 'old_version' => $oldConfig['version'], 'new_version' => $newConfig['version'] ]; } catch (\Exception $e) { Log::error("Errore aggiornamento modulo: " . $e->getMessage()); // Ripristina backup se necessario if (isset($backupPath)) { $this->restoreBackup($moduleName, $backupPath); } return [ 'success' => false, 'message' => $e->getMessage() ]; } } /** * Ottieni lista moduli attivi */ public function getActiveModules() { return collect($this->activeModules)->map(function ($moduleName) { return $this->getModuleInfo($moduleName); }); } /** * Ottieni lista tutti i moduli (attivi e inattivi) */ public function getAllModules() { if (!$this->modulesTableExists()) { return collect(); } return DB::table('modules')->get()->map(function ($module) { $module->config = json_decode($module->config, true); return $module; }); } /** * Verifica licenza modulo (connessione server licenze) */ public function validateLicense($config) { if ($config['license'] === 'free') { return true; } // TODO: Implementare verifica licenza online $licenseServer = config('netgescon.license_server'); $licenseKey = config("modules.{$config['name']}.license_key"); // Chiamata API al server licenze // return $this->callLicenseServer($licenseServer, $config['name'], $licenseKey); // Per ora ritorna true (da implementare) return true; } /** * Attiva/disattiva un modulo */ public function toggleModule($moduleName) { if (!$this->modulesTableExists()) { throw new \Exception('Tabella modules non disponibile'); } $module = DB::table('modules')->where('name', $moduleName)->first(); if (!$module) { throw new \Exception("Modulo {$moduleName} non trovato"); } $newStatus = $module->status === 'active' ? 'inactive' : 'active'; DB::table('modules')->where('name', $moduleName)->update([ 'status' => $newStatus, 'updated_at' => now() ]); $this->clearModuleCache(); return $newStatus; } // ===================================================== // METODI PRIVATI DI SUPPORTO // ===================================================== private function extractModule($zipPath) { $zip = new ZipArchive; $extractPath = storage_path('app/modules/temp/' . uniqid()); if ($zip->open($zipPath) === TRUE) { $zip->extractTo($extractPath); $zip->close(); return $extractPath; } else { throw new \Exception("Impossibile estrarre il file ZIP del modulo"); } } private function validateModuleConfig($extractPath) { $configPath = $extractPath . '/module.json'; if (!File::exists($configPath)) { throw new \Exception("File module.json non trovato"); } $config = json_decode(File::get($configPath), true); if (!$config || !isset($config['name'], $config['version'])) { throw new \Exception("Configurazione modulo non valida"); } return $config; } private function checkDependencies($config) { if (!isset($config['dependencies'])) { return true; } foreach ($config['dependencies'] as $dependency) { if (!in_array($dependency, $this->activeModules)) { throw new \Exception("Dipendenza mancante: {$dependency}"); } } return true; } private function registerModule($config) { if (!$this->modulesTableExists()) { throw new \Exception('Tabella modules non disponibile'); } DB::table('modules')->updateOrInsert( ['name' => $config['name']], [ 'name' => $config['name'], 'display_name' => $config['display_name'], 'version' => $config['version'], 'status' => 'active', 'config' => json_encode($config), 'installed_at' => now(), 'updated_at' => now() ] ); } private function clearModuleCache() { Cache::forget('netgescon.active_modules'); Cache::forget('netgescon.modules_menu'); Cache::forget('netgescon.modules_permissions'); // Ricarica moduli attivi $this->loadActiveModules(); } private function getModuleConfig($moduleName) { $configPath = $this->modulesPath . "/{$moduleName}/module.json"; if (!File::exists($configPath)) { throw new \Exception("Configurazione modulo {$moduleName} non trovata"); } return json_decode(File::get($configPath), true); } private function getModuleInfo($moduleName) { $config = $this->getModuleConfig($moduleName); $dbInfo = null; if ($this->modulesTableExists()) { try { $dbInfo = DB::table('modules')->where('name', $moduleName)->first(); } catch (\Throwable $e) { Log::warning('ModuleManager: impossibile recuperare info modulo', [ 'module' => $moduleName, 'error' => $e->getMessage(), ]); } } return (object) array_merge($config, [ 'status' => $dbInfo->status ?? 'unknown', 'installed_at' => $dbInfo->installed_at ?? null, 'updated_at' => $dbInfo->updated_at ?? null ]); } private function modulesTableExists(): bool { try { return DB::connection()->getSchemaBuilder()->hasTable('modules'); } catch (\Throwable $e) { Log::notice('ModuleManager: schema builder non disponibile', [ 'error' => $e->getMessage(), ]); return false; } } // TODO: Implementare metodi rimanenti private function runMigrations($moduleName) { /* TODO */ } private function registerRoutes($moduleName) { /* TODO */ } private function installPermissions($config) { if (!isset($config['permissions']) || empty($config['permissions'])) { return; } $permissions = $config['permissions']; $createdPermissions = []; try { foreach ($permissions as $permission) { // Crea il permesso se non esiste $existingPermission = \Spatie\Permission\Models\Permission::where('name', $permission['name'])->first(); if (!$existingPermission) { $newPermission = \Spatie\Permission\Models\Permission::create([ 'name' => $permission['name'], 'guard_name' => $permission['guard_name'] ?? 'web', 'description' => $permission['description'] ?? null, ]); $createdPermissions[] = $newPermission->name; Log::info("Permesso creato: {$newPermission->name}"); } } // Assegna i permessi ai ruoli appropriati $this->assignPermissionsToRoles($permissions); Log::info("Installazione permessi completata", ['permissions' => $createdPermissions]); } catch (\Exception $e) { Log::error("Errore installazione permessi", ['error' => $e->getMessage()]); throw $e; } } /** * Assegna i permessi ai ruoli appropriati */ private function assignPermissionsToRoles($permissions) { // Ottieni i ruoli esistenti $adminRoles = ['admin', 'amministratore', 'super-admin']; foreach ($permissions as $permission) { $permName = $permission['name']; $assignToRoles = $permission['roles'] ?? $adminRoles; // Default: assegna agli admin foreach ($assignToRoles as $roleName) { $role = \Spatie\Permission\Models\Role::where('name', $roleName)->first(); if ($role) { // Assegna il permesso al ruolo se non ce l'ha già if (!$role->hasPermissionTo($permName)) { $role->givePermissionTo($permName); Log::info("Permesso {$permName} assegnato al ruolo {$roleName}"); } } } } } private function uninstallPermissions($config) { if (!isset($config['permissions']) || empty($config['permissions'])) { return; } try { foreach ($config['permissions'] as $permission) { $existingPermission = \Spatie\Permission\Models\Permission::where('name', $permission['name'])->first(); if ($existingPermission) { // Rimuovi il permesso da tutti i ruoli $existingPermission->roles()->detach(); // Elimina il permesso $existingPermission->delete(); Log::info("Permesso rimosso: {$permission['name']}"); } } } catch (\Exception $e) { Log::error("Errore rimozione permessi", ['error' => $e->getMessage()]); } } private function updateMenuSystem($config) { /* TODO */ } private function publishAssets($moduleName) { /* TODO */ } private function createBackup($moduleName) { /* TODO */ } private function rollbackInstallation($moduleName) { /* TODO */ } // ... altri metodi di supporto }