netgescon-day0/app/Services/MigrationService.php

993 lines
38 KiB
PHP
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Services;
use App\Models\Amministratore;
use App\Models\Stabile;
use Exception;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use ZipArchive;
class MigrationService
{
protected DatabaseManagerService $dbManager;
protected TenantArchivePathService $tenantArchivePath;
public function __construct(DatabaseManagerService $dbManager, TenantArchivePathService $tenantArchivePath)
{
$this->dbManager = $dbManager;
$this->tenantArchivePath = $tenantArchivePath;
}
/**
* Prepara migrazione completa amministratore
*/
public function prepareAmministratoreMigration(Amministratore $amministratore): array
{
try {
$migrationId = "migration_admin_{$amministratore->codice_univoco}_" . date('Y-m-d_H-i-s');
$migrationPath = storage_path("app/migrations/{$migrationId}");
if (! is_dir($migrationPath)) {
mkdir($migrationPath, 0755, true);
}
$results = [
'migration_id' => $migrationId,
'amministratore' => $amministratore->codice_univoco,
'created_at' => date('Y-m-d H:i:s'),
'components' => [],
];
// 1. Backup database amministratore
$adminDbBackup = $this->dbManager->backupAmministratoreDatabase($amministratore);
if (! $adminDbBackup['success']) {
throw new Exception('Errore backup database amministratore: ' . $adminDbBackup['error']);
}
$results['components']['admin_database'] = $adminDbBackup;
// 2. Backup database per ogni stabile
$stabiliBackups = [];
foreach ($amministratore->stabili as $stabile) {
if ($stabile->database_dedicato) {
$stabileBackup = $this->dbManager->backupStabileDatabase($stabile);
if ($stabileBackup['success']) {
$stabiliBackups[$stabile->codice_stabile] = $stabileBackup;
}
}
}
$results['components']['stabili_databases'] = $stabiliBackups;
// 3. Archivio file sistema
$archiveResult = $this->createFileSystemArchive($amministratore, $migrationPath);
$results['components']['filesystem_archive'] = $archiveResult;
// 4. Metadata e configurazioni
$metadataResult = $this->createMigrationMetadata($amministratore, $migrationPath);
$results['components']['metadata'] = $metadataResult;
// 5. Script di ricostruzione
$scriptsResult = $this->createMigrationScripts($amministratore, $migrationPath);
$results['components']['scripts'] = $scriptsResult;
// 6. Verifica integrità
$verificationResult = $this->verifyMigrationIntegrity($migrationPath);
$results['components']['verification'] = $verificationResult;
// 7. Crea package finale
$packageResult = $this->createMigrationPackage($migrationId, $migrationPath);
$results['package'] = $packageResult;
return [
'success' => true,
'migration' => $results,
];
} catch (Exception $e) {
return [
'success' => false,
'error' => $e->getMessage(),
];
}
}
/**
* Prepara migrazione singolo stabile
*/
public function prepareStabileMigration(Stabile $stabile): array
{
try {
$migrationId = "migration_stabile_{$stabile->codice_stabile}_" . date('Y-m-d_H-i-s');
$migrationPath = storage_path("app/migrations/{$migrationId}");
if (! is_dir($migrationPath)) {
mkdir($migrationPath, 0755, true);
}
$results = [
'migration_id' => $migrationId,
'stabile' => $stabile->codice_stabile,
'amministratore' => $stabile->amministratore->codice_univoco,
'created_at' => date('Y-m-d H:i:s'),
'components' => [],
];
// 1. Backup database stabile
if ($stabile->database_dedicato) {
$stabileDbBackup = $this->dbManager->backupStabileDatabase($stabile);
if (! $stabileDbBackup['success']) {
throw new Exception('Errore backup database stabile: ' . $stabileDbBackup['error']);
}
$results['components']['stabile_database'] = $stabileDbBackup;
}
// 2. Archivio file stabile
$archiveResult = $this->createStabileFileArchive($stabile, $migrationPath);
$results['components']['filesystem_archive'] = $archiveResult;
// 3. Metadata stabile
$metadataResult = $this->createStabileMetadata($stabile, $migrationPath);
$results['components']['metadata'] = $metadataResult;
// 4. Package finale
$packageResult = $this->createMigrationPackage($migrationId, $migrationPath);
$results['package'] = $packageResult;
return [
'success' => true,
'migration' => $results,
];
} catch (Exception $e) {
return [
'success' => false,
'error' => $e->getMessage(),
];
}
}
/**
* Crea archivio completo file system amministratore
*/
private function createFileSystemArchive(Amministratore $amministratore, string $migrationPath): array
{
try {
$sourcePath = $this->tenantArchivePath->amministratoreAbsolutePath($amministratore);
$archiveName = "filesystem_admin_{$amministratore->codice_univoco}.zip";
$archivePath = $migrationPath . '/' . $archiveName;
if (! is_dir($sourcePath)) {
throw new Exception("Cartella amministratore non trovata: {$sourcePath}");
}
$zip = new ZipArchive();
if ($zip->open($archivePath, ZipArchive::CREATE) !== true) {
throw new Exception('Impossibile creare archivio filesystem');
}
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($sourcePath, \RecursiveDirectoryIterator::SKIP_DOTS)
);
foreach ($iterator as $file) {
if ($file->isFile()) {
$relativePath = str_replace($sourcePath . DIRECTORY_SEPARATOR, '', $file->getPathname());
$zip->addFile($file->getPathname(), $relativePath);
}
}
$zip->close();
return [
'success' => true,
'archive_name' => $archiveName,
'archive_path' => $archivePath,
'archive_size' => filesize($archivePath),
'files_count' => $zip->numFiles ?? 0,
];
} catch (Exception $e) {
return [
'success' => false,
'error' => $e->getMessage(),
];
}
}
/**
* Crea archivio file singolo stabile
*/
private function createStabileFileArchive(Stabile $stabile, string $migrationPath): array
{
try {
$sourcePath = $this->tenantArchivePath->stabileAbsolutePath($stabile);
$archiveName = "filesystem_stabile_{$stabile->codice_stabile}.zip";
$archivePath = $migrationPath . '/' . $archiveName;
if (! is_dir($sourcePath)) {
// Crea struttura base se non esiste
$this->createStabileFolderStructure($stabile);
}
$zip = new ZipArchive();
if ($zip->open($archivePath, ZipArchive::CREATE) !== true) {
throw new Exception('Impossibile creare archivio filesystem stabile');
}
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($sourcePath, \RecursiveDirectoryIterator::SKIP_DOTS)
);
foreach ($iterator as $file) {
if ($file->isFile()) {
$relativePath = str_replace($sourcePath . DIRECTORY_SEPARATOR, '', $file->getPathname());
$zip->addFile($file->getPathname(), $relativePath);
}
}
$zip->close();
return [
'success' => true,
'archive_name' => $archiveName,
'archive_path' => $archivePath,
'archive_size' => filesize($archivePath),
'files_count' => $zip->numFiles ?? 0,
];
} catch (Exception $e) {
return [
'success' => false,
'error' => $e->getMessage(),
];
}
}
/**
* Crea metadata completi per migrazione
*/
private function createMigrationMetadata(Amministratore $amministratore, string $migrationPath): array
{
try {
$metadata = [
'migration' => [
'type' => 'amministratore_completo',
'version' => '1.0.0',
'created_at' => date('Y-m-d H:i:s'),
'source_server' => env('APP_URL'),
'netgescon_version' => '2.1.0',
],
'amministratore' => [
'id' => $amministratore->id,
'codice_univoco' => $amministratore->codice_univoco,
'nome' => $amministratore->nome,
'cognome' => $amministratore->cognome,
'denominazione_studio' => $amministratore->denominazione_studio,
'partita_iva' => $amministratore->partita_iva,
'codice_fiscale_studio' => $amministratore->codice_fiscale_studio,
'email_studio' => $amministratore->email_studio,
'database_principale' => $amministratore->getDatabaseName(),
'created_at' => $amministratore->created_at->toISOString(),
],
'stabili' => [],
'databases' => [
'principale' => $amministratore->getDatabaseName(),
'stabili' => [],
],
'requirements' => [
'php_version' => PHP_VERSION,
'laravel_version' => app()->version(),
'mysql_version' => DB::select('SELECT VERSION() as version')[0]->version,
'required_extensions' => [
'zip',
'pdo_mysql',
'openssl',
'fileinfo',
'mbstring',
],
'min_disk_space_mb' => $this->calculateRequiredSpace($amministratore),
],
'structure' => [
'folders' => $this->analyzeFolderStructure($amministratore),
'databases' => $this->analyzeDatabaseStructure($amministratore),
],
];
// Aggiungi info per ogni stabile
foreach ($amministratore->stabili as $stabile) {
$stabileInfo = [
'id' => $stabile->id,
'codice_stabile' => $stabile->codice_stabile,
'denominazione' => $stabile->denominazione,
'indirizzo' => $stabile->indirizzo . ', ' . $stabile->citta,
'database_dedicato' => $stabile->database_dedicato,
'created_at' => $stabile->created_at->toISOString(),
];
$metadata['stabili'][] = $stabileInfo;
if ($stabile->database_dedicato) {
$metadata['databases']['stabili'][] = $stabile->database_dedicato;
}
}
$metadataPath = $migrationPath . '/metadata.json';
file_put_contents($metadataPath, json_encode($metadata, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
return [
'success' => true,
'metadata_file' => $metadataPath,
'stabili_count' => count($metadata['stabili']),
'databases_count' => count($metadata['databases']['stabili']) + 1,
];
} catch (Exception $e) {
return [
'success' => false,
'error' => $e->getMessage(),
];
}
}
/**
* Crea metadata per singolo stabile
*/
private function createStabileMetadata(Stabile $stabile, string $migrationPath): array
{
try {
$metadata = [
'migration' => [
'type' => 'stabile_singolo',
'version' => '1.0.0',
'created_at' => date('Y-m-d H:i:s'),
'source_server' => env('APP_URL'),
],
'stabile' => [
'id' => $stabile->id,
'codice_stabile' => $stabile->codice_stabile,
'denominazione' => $stabile->denominazione,
'indirizzo' => $stabile->indirizzo,
'cap' => $stabile->cap,
'citta' => $stabile->citta,
'provincia' => $stabile->provincia,
'database_dedicato' => $stabile->database_dedicato,
'created_at' => $stabile->created_at->toISOString(),
],
'amministratore_origine' => [
'codice_univoco' => $stabile->amministratore->codice_univoco,
'nome_completo' => $stabile->amministratore->nome_completo,
'denominazione_studio' => $stabile->amministratore->denominazione_studio,
],
'requirements' => [
'target_admin_code' => 'required',
'php_version' => PHP_VERSION,
'mysql_version' => DB::select('SELECT VERSION() as version')[0]->version,
],
];
$metadataPath = $migrationPath . '/stabile_metadata.json';
file_put_contents($metadataPath, json_encode($metadata, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
return [
'success' => true,
'metadata_file' => $metadataPath,
];
} catch (Exception $e) {
return [
'success' => false,
'error' => $e->getMessage(),
];
}
}
/**
* Crea script di ricostruzione ambiente
*/
private function createMigrationScripts(Amministratore $amministratore, string $migrationPath): array
{
try {
$scriptsPath = $migrationPath . '/scripts';
if (! is_dir($scriptsPath)) {
mkdir($scriptsPath, 0755, true);
}
// 1. Script restore database
$restoreDbScript = $this->createDatabaseRestoreScript($amministratore);
file_put_contents($scriptsPath . '/restore_databases.sh', $restoreDbScript);
// 2. Script restore filesystem
$restoreFsScript = $this->createFilesystemRestoreScript($amministratore);
file_put_contents($scriptsPath . '/restore_filesystem.sh', $restoreFsScript);
// 3. Script verifica post-migrazione
$verifyScript = $this->createVerificationScript($amministratore);
file_put_contents($scriptsPath . '/verify_migration.sh', $verifyScript);
// 4. Script PHP per ricostruzione record
$phpScript = $this->createPhpRestoreScript($amministratore);
file_put_contents($scriptsPath . '/restore_records.php', $phpScript);
// 5. README istruzioni
$readme = $this->createMigrationReadme($amministratore);
file_put_contents($scriptsPath . '/README.md', $readme);
// Rendi script eseguibili
chmod($scriptsPath . '/restore_databases.sh', 0755);
chmod($scriptsPath . '/restore_filesystem.sh', 0755);
chmod($scriptsPath . '/verify_migration.sh', 0755);
return [
'success' => true,
'scripts_path' => $scriptsPath,
'scripts_created' => 5,
];
} catch (Exception $e) {
return [
'success' => false,
'error' => $e->getMessage(),
];
}
}
/**
* Crea script restore database
*/
private function createDatabaseRestoreScript(Amministratore $amministratore): string
{
$script = "#!/bin/bash\n\n";
$script .= "# Script restore database per amministratore {$amministratore->codice_univoco}\n";
$script .= "# Generato automaticamente da NetGescon\n\n";
$script .= "set -e\n\n";
$script .= "# Configurazione\n";
$script .= "ADMIN_CODE=\"{$amministratore->codice_univoco}\"\n";
$script .= "ADMIN_DB=\"netgescon_admin_{$amministratore->codice_univoco}\"\n";
$script .= "MYSQL_USER=\"\${MYSQL_USER:-root}\"\n";
$script .= "MYSQL_PASS=\"\${MYSQL_PASS}\"\n";
$script .= "MYSQL_HOST=\"\${MYSQL_HOST:-localhost}\"\n\n";
$script .= "echo \"🚀 Inizio restore database amministratore \$ADMIN_CODE\"\n\n";
$script .= "# Crea database amministratore\n";
$script .= "echo \"📦 Creazione database \$ADMIN_DB\"\n";
$script .= "mysql -h\$MYSQL_HOST -u\$MYSQL_USER -p\$MYSQL_PASS -e \"CREATE DATABASE IF NOT EXISTS \\\`\$ADMIN_DB\\\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci\"\n\n";
$script .= "# Restore database amministratore\n";
$script .= "if [ -f \"../database_backups/backup_admin_\${ADMIN_CODE}_*.sql\" ]; then\n";
$script .= " ADMIN_BACKUP=\$(ls ../database_backups/backup_admin_\${ADMIN_CODE}_*.sql | head -1)\n";
$script .= " echo \"📥 Restore database amministratore da \$ADMIN_BACKUP\"\n";
$script .= " mysql -h\$MYSQL_HOST -u\$MYSQL_USER -p\$MYSQL_PASS \$ADMIN_DB < \$ADMIN_BACKUP\n";
$script .= " echo \"✅ Database amministratore ripristinato\"\n";
$script .= "else\n";
$script .= " echo \"❌ Backup database amministratore non trovato\"\n";
$script .= " exit 1\n";
$script .= "fi\n\n";
// Aggiungi restore per ogni stabile
foreach ($amministratore->stabili as $stabile) {
if ($stabile->database_dedicato) {
$script .= "# Restore database stabile {$stabile->codice_stabile}\n";
$script .= "STABILE_DB=\"{$stabile->database_dedicato}\"\n";
$script .= "echo \"📦 Creazione database stabile \$STABILE_DB\"\n";
$script .= "mysql -h\$MYSQL_HOST -u\$MYSQL_USER -p\$MYSQL_PASS -e \"CREATE DATABASE IF NOT EXISTS \\\`\$STABILE_DB\\\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci\"\n";
$script .= "if [ -f \"../database_backups/backup_stabile_{$stabile->codice_stabile}_*.sql\" ]; then\n";
$script .= " STABILE_BACKUP=\$(ls ../database_backups/backup_stabile_{$stabile->codice_stabile}_*.sql | head -1)\n";
$script .= " echo \"📥 Restore database stabile da \$STABILE_BACKUP\"\n";
$script .= " mysql -h\$MYSQL_HOST -u\$MYSQL_USER -p\$MYSQL_PASS \$STABILE_DB < \$STABILE_BACKUP\n";
$script .= " echo \"✅ Database stabile {$stabile->codice_stabile} ripristinato\"\n";
$script .= "else\n";
$script .= " echo \"⚠️ Backup database stabile {$stabile->codice_stabile} non trovato\"\n";
$script .= "fi\n\n";
}
}
$script .= "echo \"🎉 Restore database completato!\"\n";
return $script;
}
/**
* Crea script restore filesystem
*/
private function createFilesystemRestoreScript(Amministratore $amministratore): string
{
$script = "#!/bin/bash\n\n";
$script .= "# Script restore filesystem per amministratore {$amministratore->codice_univoco}\n\n";
$script .= "set -e\n\n";
$script .= "ADMIN_CODE=\"{$amministratore->codice_univoco}\"\n";
$script .= "TARGET_PATH=\"\${TARGET_PATH:-/var/www/netgescon/storage/app/amministratori}\"\n\n";
$script .= "echo \"🚀 Inizio restore filesystem amministratore \$ADMIN_CODE\"\n\n";
$script .= "# Crea directory target\n";
$script .= "mkdir -p \"\$TARGET_PATH/\$ADMIN_CODE\"\n\n";
$script .= "# Estrai archivio filesystem\n";
$script .= "if [ -f \"../filesystem_admin_\${ADMIN_CODE}.zip\" ]; then\n";
$script .= " echo \"📥 Estrazione archivio filesystem\"\n";
$script .= " cd \"\$TARGET_PATH/\$ADMIN_CODE\"\n";
$script .= " unzip -q \"../../../migrations/\$(basename \$PWD)/filesystem_admin_\${ADMIN_CODE}.zip\"\n";
$script .= " echo \"✅ Filesystem ripristinato in \$TARGET_PATH/\$ADMIN_CODE\"\n";
$script .= "else\n";
$script .= " echo \"❌ Archivio filesystem non trovato\"\n";
$script .= " exit 1\n";
$script .= "fi\n\n";
$script .= "# Imposta permessi\n";
$script .= "echo \"🔧 Impostazione permessi\"\n";
$script .= "chown -R www-data:www-data \"\$TARGET_PATH/\$ADMIN_CODE\"\n";
$script .= "find \"\$TARGET_PATH/\$ADMIN_CODE\" -type d -exec chmod 755 {} \\;\n";
$script .= "find \"\$TARGET_PATH/\$ADMIN_CODE\" -type f -exec chmod 644 {} \\;\n\n";
$script .= "echo \"🎉 Restore filesystem completato!\"\n";
return $script;
}
/**
* Crea script verifica post-migrazione
*/
private function createVerificationScript(Amministratore $amministratore): string
{
$script = "#!/bin/bash\n\n";
$script .= "# Script verifica migrazione per amministratore {$amministratore->codice_univoco}\n\n";
$script .= "ADMIN_CODE=\"{$amministratore->codice_univoco}\"\n";
$script .= "ADMIN_DB=\"netgescon_admin_{$amministratore->codice_univoco}\"\n";
$script .= "MYSQL_USER=\"\${MYSQL_USER:-root}\"\n";
$script .= "MYSQL_PASS=\"\${MYSQL_PASS}\"\n";
$script .= "MYSQL_HOST=\"\${MYSQL_HOST:-localhost}\"\n\n";
$script .= "echo \"🔍 Verifica migrazione amministratore \$ADMIN_CODE\"\n\n";
$script .= "# Verifica database\n";
$script .= "echo \"📊 Verifica database...\"\n";
$script .= "DB_EXISTS=\$(mysql -h\$MYSQL_HOST -u\$MYSQL_USER -p\$MYSQL_PASS -e \"SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='\$ADMIN_DB'\" | grep -v SCHEMA_NAME | wc -l)\n";
$script .= "if [ \$DB_EXISTS -eq 1 ]; then\n";
$script .= " echo \"✅ Database amministratore presente\"\n";
$script .= " TABLE_COUNT=\$(mysql -h\$MYSQL_HOST -u\$MYSQL_USER -p\$MYSQL_PASS -e \"SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='\$ADMIN_DB'\" | tail -1)\n";
$script .= " echo \"📋 Tabelle trovate: \$TABLE_COUNT\"\n";
$script .= "else\n";
$script .= " echo \"❌ Database amministratore mancante\"\n";
$script .= "fi\n\n";
$script .= "# Verifica filesystem\n";
$script .= "echo \"📁 Verifica filesystem...\"\n";
$script .= "ADMIN_PATH=\"/var/www/netgescon/storage/app/amministratori/\$ADMIN_CODE\"\n";
$script .= "if [ -d \"\$ADMIN_PATH\" ]; then\n";
$script .= " echo \"✅ Cartella amministratore presente\"\n";
$script .= " FILE_COUNT=\$(find \"\$ADMIN_PATH\" -type f | wc -l)\n";
$script .= " echo \"📄 File trovati: \$FILE_COUNT\"\n";
$script .= "else\n";
$script .= " echo \"❌ Cartella amministratore mancante\"\n";
$script .= "fi\n\n";
$script .= "echo \"🎯 Verifica completata!\"\n";
return $script;
}
/**
* Crea script PHP per restore record
*/
private function createPhpRestoreScript(Amministratore $amministratore): string
{
return "<?php
/*
* Script PHP per restore record amministratore {$amministratore->codice_univoco}
* Da eseguire dopo restore database e filesystem
*/
require_once __DIR__ . '/../../../../vendor/autoload.php';
use Illuminate\\Support\\Facades\\DB;
use App\\Models\\Amministratore;
use App\\Models\\User;
// Carica configurazione Laravel
\$app = require_once __DIR__ . '/../../../../bootstrap/app.php';
\$app->make(Illuminate\\Contracts\\Console\\Kernel::class)->bootstrap();
try {
echo \"🚀 Inizio restore record amministratore {$amministratore->codice_univoco}\\n\";
// Leggi metadata
\$metadata = json_decode(file_get_contents(__DIR__ . '/../metadata.json'), true);
\$adminData = \$metadata['amministratore'];
// Verifica se amministratore esiste già
\$esistente = Amministratore::where('codice_univoco', '{$amministratore->codice_univoco}')->first();
if (\$esistente) {
echo \"⚠️ Amministratore {$amministratore->codice_univoco} già presente, aggiorno record...\\n\";
\$esistente->update([
'database_attivo' => \$adminData['database_principale']
]);
\$amministratore = \$esistente;
} else {
echo \" Creazione nuovo record amministratore...\\n\";
// Crea/trova utente
\$user = User::where('email', \$adminData['email_studio'])->first();
if (!\$user) {
\$user = User::create([
'name' => \$adminData['nome'] . ' ' . \$adminData['cognome'],
'email' => \$adminData['email_studio'],
'password' => bcrypt('temp_password_' . uniqid()),
'email_verified_at' => now(),
]);
}
// Crea amministratore
\$amministratore = Amministratore::create([
'user_id' => \$user->id,
'nome' => \$adminData['nome'],
'cognome' => \$adminData['cognome'],
'denominazione_studio' => \$adminData['denominazione_studio'],
'partita_iva' => \$adminData['partita_iva'],
'codice_fiscale_studio' => \$adminData['codice_fiscale_studio'],
'email_studio' => \$adminData['email_studio'],
'codice_univoco' => '{$amministratore->codice_univoco}',
'database_attivo' => \$adminData['database_principale']
]);
}
echo \"✅ Record amministratore ripristinato (ID: {\$amministratore->id})\\n\";
// Restore stabili
foreach (\$metadata['stabili'] as \$stabileData) {
\$stabile = \$amministratore->stabili()->where('codice_stabile', \$stabileData['codice_stabile'])->first();
if (!\$stabile) {
\$stabile = \$amministratore->stabili()->create([
'codice_stabile' => \$stabileData['codice_stabile'],
'denominazione' => \$stabileData['denominazione'],
'indirizzo' => 'Da aggiornare',
'cap' => '00000',
'citta' => 'Da aggiornare',
'provincia' => 'XX',
'database_dedicato' => \$stabileData['database_dedicato']
]);
echo \" Stabile {\$stabileData['codice_stabile']} creato\\n\";
} else {
\$stabile->update(['database_dedicato' => \$stabileData['database_dedicato']]);
echo \"🔄 Stabile {\$stabileData['codice_stabile']} aggiornato\\n\";
}
}
echo \"🎉 Restore record completato!\\n\";
} catch (Exception \$e) {
echo \"❌ Errore durante restore: \" . \$e->getMessage() . \"\\n\";
exit(1);
}
";
}
/**
* Crea README istruzioni migrazione
*/
private function createMigrationReadme(Amministratore $amministratore): string
{
return "# 📦 Migrazione Amministratore {$amministratore->codice_univoco}
## 📋 Informazioni Migrazione
- **Amministratore**: {$amministratore->nome_completo}
- **Studio**: {$amministratore->denominazione_studio}
- **Codice**: {$amministratore->codice_univoco}
- **Data Migrazione**: " . date('d/m/Y H:i') . "
- **Server Origine**: " . env('APP_URL') . "
## 🎯 Contenuto Package
- Database amministratore principale
- Database dedicati per ogni stabile
- Archivio completo filesystem
- Script automatici di restore
- Metadata e configurazioni
## 🚀 Procedura di Restore
### 1⃣ Prerequisiti
```bash
# MySQL server attivo
# PHP 8.1+ con estensioni: zip, pdo_mysql, openssl
# Spazio disco sufficiente (vedere metadata.json)
# Accesso directory NetGescon
```
### 2⃣ Configurazione Environment
```bash
export MYSQL_USER=\"root\"
export MYSQL_PASS=\"password\"
export MYSQL_HOST=\"localhost\"
export TARGET_PATH=\"/var/www/netgescon/storage/app/amministratori\"
```
### 3⃣ Esecuzione Restore
```bash
# 1. Restore database
cd scripts/
chmod +x *.sh
./restore_databases.sh
# 2. Restore filesystem
./restore_filesystem.sh
# 3. Restore record Laravel
php restore_records.php
# 4. Verifica migrazione
./verify_migration.sh
```
## ⚠️ Note Importanti
### 🔐 Sicurezza
- Modificare password temporanee dopo restore
- Verificare permessi filesystem
- Testare connessioni database
### 🔄 Post-Migrazione
- Aggiornare configurazioni server-specific
- Verificare path assoluti
- Testare funzionalità complete
- Aggiornare backup automatici
### 🆘 Troubleshooting
- Verificare log errori in `/var/log/mysql/`
- Controllare permessi `www-data`
- Verificare spazio disco disponibile
- Consultare `verify_migration.sh`
## 📊 Statistiche Migrazione
- **Stabili Inclusi**: " . $amministratore->stabili->count() . "
- **Database**: " . ($amministratore->stabili->where('database_dedicato', '!=', null)->count() + 1) . "
- **Spazio Richiesto**: Vedere metadata.json
---
*Package generato automaticamente da NetGescon v2.1.0*
";
}
/**
* Verifica integrità migrazione
*/
private function verifyMigrationIntegrity(string $migrationPath): array
{
try {
$checks = [
'metadata_exists' => file_exists($migrationPath . '/metadata.json'),
'scripts_exists' => is_dir($migrationPath . '/scripts'),
'database_backups' => 0,
'filesystem_archives' => 0,
];
// Conta backup database
$files = glob($migrationPath . '/backup_*.sql');
$checks['database_backups'] = count($files);
// Conta archivi filesystem
$archives = glob($migrationPath . '/filesystem_*.zip');
$checks['filesystem_archives'] = count($archives);
$checks['integrity_ok'] = $checks['metadata_exists'] &&
$checks['scripts_exists'] &&
$checks['database_backups'] > 0;
return [
'success' => true,
'checks' => $checks,
];
} catch (Exception $e) {
return [
'success' => false,
'error' => $e->getMessage(),
];
}
}
/**
* Crea package finale migrazione
*/
private function createMigrationPackage(string $migrationId, string $migrationPath): array
{
try {
$packagePath = storage_path("app/migrations/{$migrationId}.zip");
$zip = new ZipArchive();
if ($zip->open($packagePath, ZipArchive::CREATE) !== true) {
throw new Exception('Impossibile creare package migrazione');
}
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($migrationPath, \RecursiveDirectoryIterator::SKIP_DOTS)
);
foreach ($iterator as $file) {
if ($file->isFile()) {
$relativePath = str_replace($migrationPath . DIRECTORY_SEPARATOR, '', $file->getPathname());
$zip->addFile($file->getPathname(), $relativePath);
}
}
$zip->close();
// Rimuovi cartella temporanea
$this->deleteDirectory($migrationPath);
return [
'success' => true,
'package_path' => $packagePath,
'package_size' => filesize($packagePath),
'download_url' => url('admin/migrations/download/' . basename($packagePath)),
];
} catch (Exception $e) {
return [
'success' => false,
'error' => $e->getMessage(),
];
}
}
/**
* Calcola spazio richiesto
*/
private function calculateRequiredSpace(Amministratore $amministratore): int
{
$totalSize = 0;
// Dimensione archivio amministratore
$adminPath = $this->tenantArchivePath->amministratoreAbsolutePath($amministratore);
if (is_dir($adminPath)) {
$totalSize += $this->getDirectorySize($adminPath);
}
// Aggiungi overhead per backup database (stimato)
$totalSize += 50 * 1024 * 1024; // 50MB stimati per backup
return intval($totalSize / (1024 * 1024)); // Converti in MB
}
/**
* Analizza struttura cartelle
*/
private function analyzeFolderStructure(Amministratore $amministratore): array
{
$adminPath = $this->tenantArchivePath->amministratoreAbsolutePath($amministratore);
if (! is_dir($adminPath)) {
return ['exists' => false];
}
return [
'exists' => true,
'path' => $adminPath,
'size' => $this->getDirectorySize($adminPath),
'files_count' => $this->countFiles($adminPath),
'directories' => $this->listDirectories($adminPath),
];
}
/**
* Analizza struttura database
*/
private function analyzeDatabaseStructure(Amministratore $amministratore): array
{
$structure = [
'amministratore' => [
'database' => $amministratore->getDatabaseName(),
'exists' => false,
'tables' => [],
],
'stabili' => [],
];
// Verifica database amministratore
try {
$tables = DB::select("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = ?", [$amministratore->getDatabaseName()]);
$structure['amministratore']['exists'] = count($tables) > 0;
$structure['amministratore']['tables'] = array_column($tables, 'TABLE_NAME');
} catch (Exception $e) {
// Database non esiste
}
// Verifica database stabili
foreach ($amministratore->stabili as $stabile) {
if ($stabile->database_dedicato) {
$stabileInfo = [
'codice' => $stabile->codice_stabile,
'database' => $stabile->database_dedicato,
'exists' => false,
'tables' => [],
];
try {
$tables = DB::select("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = ?", [$stabile->database_dedicato]);
$stabileInfo['exists'] = count($tables) > 0;
$stabileInfo['tables'] = array_column($tables, 'TABLE_NAME');
} catch (Exception $e) {
// Database non esiste
}
$structure['stabili'][] = $stabileInfo;
}
}
return $structure;
}
/**
* Utilities
*/
private function getDirectorySize(string $path): int
{
$size = 0;
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path));
foreach ($iterator as $file) {
if ($file->isFile()) {
$size += $file->getSize();
}
}
return $size;
}
private function countFiles(string $path): int
{
$count = 0;
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path));
foreach ($iterator as $file) {
if ($file->isFile()) {
$count++;
}
}
return $count;
}
private function listDirectories(string $path): array
{
$directories = [];
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path),
\RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $file) {
if ($file->isDir() && ! in_array($file->getFilename(), ['.', '..'])) {
$relativePath = str_replace($path . DIRECTORY_SEPARATOR, '', $file->getPathname());
$directories[] = $relativePath;
}
}
return $directories;
}
private function deleteDirectory(string $path): bool
{
if (! is_dir($path)) {
return false;
}
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($iterator as $file) {
if ($file->isDir()) {
rmdir($file->getPathname());
} else {
unlink($file->getPathname());
}
}
return rmdir($path);
}
}