552 lines
16 KiB
PHP
552 lines
16 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Illuminate\Support\Facades\File;
|
|
use Illuminate\Support\Facades\Route;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\Log;
|
|
use ZipArchive;
|
|
|
|
/**
|
|
* NetGescon Module Manager
|
|
*
|
|
* Gestisce installazione, disinstallazione e aggiornamento moduli
|
|
* Supporta sistema hook, permessi e menu dinamici
|
|
*/
|
|
class ModuleManager
|
|
{
|
|
protected $modulesPath;
|
|
protected $activeModules = [];
|
|
|
|
public function __construct()
|
|
{
|
|
$this->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
|
|
}
|