codice_amministratore} verso {$targetServerUrl}"); // 1. Prepara archivio per migrazione $migrationData = $amministratore->prepareForMigration(); if (!$migrationData['success']) { throw new \Exception('Errore preparazione migrazione: ' . $migrationData['error']); } // 2. Verifica connettività server target $targetHealth = static::checkServerHealth($targetServerUrl); if (!$targetHealth['success']) { throw new \Exception('Server target non raggiungibile: ' . $targetHealth['error']); } // 3. Trasferisce archivio al server target $transferResult = static::transferArchive($migrationData['zip_file'], $targetServerUrl, $amministratore); if (!$transferResult['success']) { throw new \Exception('Errore trasferimento archivio: ' . $transferResult['error']); } // 4. Aggiorna configurazione amministratore $amministratore->update([ 'server_database' => parse_url($targetServerUrl, PHP_URL_HOST), 'server_port' => parse_url($targetServerUrl, PHP_URL_PORT) ?: 3306, 'url_accesso' => $targetServerUrl, 'stato_sincronizzazione' => 'migrazione', 'ultimo_backup' => now() ]); // 5. Notifica server target per attivazione $activationResult = static::activateOnTargetServer($targetServerUrl, $amministratore); if ($activationResult['success']) { $amministratore->update(['stato_sincronizzazione' => 'attivo']); Log::info("Migrazione completata per amministratore {$amministratore->codice_amministratore}"); return [ 'success' => true, 'message' => 'Migrazione completata con successo', 'new_url' => $targetServerUrl, 'transfer_id' => $transferResult['transfer_id'] ?? null ]; } else { throw new \Exception('Errore attivazione su server target: ' . $activationResult['error']); } } catch (\Exception $e) { Log::error("Errore migrazione amministratore {$amministratore->codice_amministratore}: " . $e->getMessage()); // Ripristina stato precedente $amministratore->update(['stato_sincronizzazione' => 'errore']); return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Verifica salute e compatibilità di un server NetGesCon */ public static function checkServerHealth(string $serverUrl): array { try { $response = Http::timeout(10)->get("{$serverUrl}/api/health"); if (!$response->successful()) { throw new \Exception("Server risponde con codice {$response->status()}"); } $data = $response->json(); // Verifica versione compatibile $requiredVersion = config('app.min_version', '1.0.0'); if (version_compare($data['version'] ?? '0.0.0', $requiredVersion, '<')) { throw new \Exception("Versione server incompatibile: {$data['version']} < {$requiredVersion}"); } return [ 'success' => true, 'server_info' => $data, 'compatible' => true ]; } catch (\Exception $e) { return [ 'success' => false, 'error' => $e->getMessage(), 'compatible' => false ]; } } /** * Trasferisce archivio amministratore a server target */ private static function transferArchive(string $zipPath, string $targetServerUrl, Amministratore $amministratore): array { try { $response = Http::timeout(300) ->attach('archive', file_get_contents($zipPath), basename($zipPath)) ->post("{$targetServerUrl}/api/import-administrator", [ 'codice_amministratore' => $amministratore->codice_amministratore, 'source_server' => config('app.url'), 'migration_token' => static::generateMigrationToken($amministratore) ]); if (!$response->successful()) { throw new \Exception("Errore HTTP {$response->status()}: " . $response->body()); } return $response->json(); } catch (\Exception $e) { return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Attiva amministratore su server target */ private static function activateOnTargetServer(string $targetServerUrl, Amministratore $amministratore): array { try { $response = Http::timeout(60)->post("{$targetServerUrl}/api/activate-administrator", [ 'codice_amministratore' => $amministratore->codice_amministratore, 'activation_token' => static::generateMigrationToken($amministratore) ]); if (!$response->successful()) { throw new \Exception("Errore attivazione HTTP {$response->status()}: " . $response->body()); } return $response->json(); } catch (\Exception $e) { return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Genera token sicuro per migrazione */ private static function generateMigrationToken(Amministratore $amministratore): string { return hash('sha256', $amministratore->codice_amministratore . $amministratore->created_at . config('app.key')); } /** * Sincronizza dati tra server per amministratore distribuito */ public static function syncAdministratorData(Amministratore $amministratore): array { try { if (!$amministratore->server_database) { return ['success' => true, 'message' => 'Amministratore su server locale']; } $targetUrl = $amministratore->url_accesso; if (!$targetUrl) { throw new \Exception('URL server target non configurato'); } // Verifica stato server target $healthCheck = static::checkServerHealth($targetUrl); if (!$healthCheck['success']) { throw new \Exception('Server target non raggiungibile'); } // Invia richiesta di sincronizzazione $response = Http::timeout(30)->post("{$targetUrl}/api/sync-administrator", [ 'codice_amministratore' => $amministratore->codice_amministratore, 'last_sync' => $amministratore->updated_at, 'sync_token' => static::generateMigrationToken($amministratore) ]); if (!$response->successful()) { throw new \Exception("Errore sincronizzazione: {$response->status()}"); } $syncData = $response->json(); // Aggiorna timestamp ultima sincronizzazione $amministratore->touch(); return [ 'success' => true, 'synced_at' => now(), 'changes' => $syncData['changes'] ?? 0 ]; } catch (\Exception $e) { Log::error("Errore sincronizzazione amministratore {$amministratore->codice_amministratore}: " . $e->getMessage()); return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Ottiene statistiche distribuzione server */ public static function getDistributionStats(): array { $stats = [ 'total_administrators' => Amministratore::count(), 'local_administrators' => Amministratore::whereNull('server_database')->count(), 'distributed_administrators' => Amministratore::whereNotNull('server_database')->count(), 'servers' => [], 'status_distribution' => Amministratore::groupBy('stato_sincronizzazione') ->selectRaw('stato_sincronizzazione, count(*) as count') ->pluck('count', 'stato_sincronizzazione') ->toArray() ]; // Raggruppa per server $serverGroups = Amministratore::whereNotNull('server_database') ->groupBy('server_database') ->selectRaw('server_database, count(*) as administrators_count') ->get(); foreach ($serverGroups as $group) { $stats['servers'][] = [ 'server' => $group->server_database, 'administrators_count' => $group->administrators_count, 'health' => 'unknown' // TODO: implementare controllo salute periodico ]; } return $stats; } /** * Routing DNS intelligente per amministratori distribuiti */ public static function getAdministratorAccessUrl(string $codiceAmministratore): array { $amministratore = Amministratore::where('codice_amministratore', $codiceAmministratore)->first(); if (!$amministratore) { return [ 'success' => false, 'error' => 'Amministratore non trovato' ]; } // Se ha URL specifico, usalo if ($amministratore->url_accesso) { return [ 'success' => true, 'url' => $amministratore->url_accesso, 'server_type' => 'distributed', 'server' => $amministratore->server_database ]; } // Altrimenti è su server locale return [ 'success' => true, 'url' => config('app.url'), 'server_type' => 'local', 'server' => 'localhost' ]; } /** * Backup automatico distribuito */ public static function performDistributedBackup(Amministratore $amministratore): array { try { // Backup locale $localBackup = $amministratore->createDatabaseBackup(); if (!$localBackup['success']) { throw new \Exception('Errore backup locale: ' . $localBackup['error']); } // Se distribuito, backup anche remoto if ($amministratore->server_database && $amministratore->url_accesso) { $remoteBackup = static::triggerRemoteBackup($amministratore); return [ 'success' => true, 'local_backup' => $localBackup, 'remote_backup' => $remoteBackup ]; } return [ 'success' => true, 'local_backup' => $localBackup, 'remote_backup' => null ]; } catch (\Exception $e) { return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Trigger backup remoto */ private static function triggerRemoteBackup(Amministratore $amministratore): array { try { $response = Http::timeout(120)->post("{$amministratore->url_accesso}/api/backup-administrator", [ 'codice_amministratore' => $amministratore->codice_amministratore, 'backup_token' => static::generateMigrationToken($amministratore) ]); if (!$response->successful()) { throw new \Exception("Errore backup remoto: {$response->status()}"); } return $response->json(); } catch (\Exception $e) { return [ 'success' => false, 'error' => $e->getMessage() ]; } } }