*/ protected $fillable = [ 'user_id', 'nome', 'cognome', 'denominazione_studio', 'partita_iva', 'codice_fiscale_studio', 'indirizzo_studio', 'cap_studio', 'citta_studio', 'provincia_studio', 'telefono_studio', 'email_studio', 'pec_studio', 'codice_univoco', ]; /** * Get the user associated with the Amministratore. */ public function user(): BelongsTo { return $this->belongsTo(User::class, 'user_id'); } /** * Get the condomini for the Amministratore. */ public function stabili(): HasMany { return $this->hasMany(Stabile::class, 'amministratore_id', 'id'); } /** * Get the fornitori for the Amministratore. */ public function fornitori(): HasMany { return $this->hasMany(Fornitore::class, 'amministratore_id', 'id'); } protected static function boot() { parent::boot(); static::creating(function ($amministratore) { if (empty($amministratore->codice_univoco)) { do { $code = Str::upper(Str::random(8)); } while (self::where('codice_univoco', $code)->exists()); $amministratore->codice_univoco = $code; } }); // Auto-crea cartelle dopo la creazione static::created(function ($amministratore) { $amministratore->createFolderStructure(); }); // Aggiorna codice_amministratore per compatibilità static::creating(function ($amministratore) { if (empty($amministratore->codice_amministratore)) { $amministratore->codice_amministratore = $amministratore->codice_univoco ?? $amministratore->generateCodiceUnivoco(); } }); } /** * Crea la struttura di cartelle per l'amministratore */ public function createFolderStructure(): void { $basePath = "amministratori/{$this->codice_univoco}"; $folders = [ 'documenti/allegati', 'documenti/contratti', 'documenti/assemblee', 'documenti/preventivi', 'backup/database', 'backup/files', 'temp/upload', 'temp/processing', 'logs', 'exports', ]; foreach ($folders as $folder) { Storage::disk('local')->makeDirectory("{$basePath}/{$folder}"); } // Crea file README informativo $readme = "# Cartella Amministratore: {$this->nome_completo}\n\n"; $readme .= "**Codice**: {$this->codice_amministratore}\n"; $readme .= "**Creato**: " . $this->created_at->format('d/m/Y H:i') . "\n\n"; $readme .= "## Struttura Cartelle\n\n"; $readme .= "- `documenti/` - Documenti dell'amministratore\n"; $readme .= "- `backup/` - Backup automatici\n"; $readme .= "- `temp/` - File temporanei\n"; $readme .= "- `logs/` - Log specifici\n"; $readme .= "- `exports/` - Esportazioni dati\n"; Storage::disk('local')->put("{$basePath}/README.md", $readme); } /** * Ottieni il path della cartella dell'amministratore */ public function getFolderPath(): string { return "amministratori/{$this->codice_amministratore}"; } /** * Genera codice univoco alfanumerico 8 caratteri */ public function generateCodiceUnivoco(): string { do { $codice = 'ADM' . strtoupper(substr(str_shuffle('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'), 0, 5)); } while (self::where('codice_amministratore', $codice)->exists()); return $codice; } /** * Verifica se l'amministratore ha un database dedicato attivo */ public function hasDedicatedDatabase(): bool { return !empty($this->database_attivo); } /** * Ottiene il nome del database dedicato per questo amministratore */ public function getDatabaseName(): string { return $this->database_attivo ?: "netgescon_{$this->codice_amministratore}"; } /** * Ottiene il percorso fisico dell'archivio amministratore */ public function getArchivePath(): string { return storage_path("app/amministratori/{$this->codice_amministratore}"); } /** * Ottiene il percorso del backup database */ public function getDatabaseBackupPath(): string { return $this->getArchivePath() . '/backup/database'; } /** * Verifica se l'archivio fisico esiste */ public function archiveExists(): bool { return is_dir($this->getArchivePath()); } /** * Ottiene informazioni dettagliate sull'archivio */ public function getArchiveInfo(): array { $archivePath = $this->getArchivePath(); if (!$this->archiveExists()) { return [ 'exists' => false, 'path' => $archivePath, 'size' => 0, 'files_count' => 0, 'last_backup' => null ]; } $size = 0; $filesCount = 0; $lastBackup = null; // Calcola dimensione totale e numero file $iterator = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($archivePath) ); foreach ($iterator as $file) { if ($file->isFile()) { $size += $file->getSize(); $filesCount++; // Trova ultimo backup database if (str_contains($file->getPathname(), 'backup/database') && str_ends_with($file->getFilename(), '.sql')) { $backupTime = filemtime($file->getPathname()); if (!$lastBackup || $backupTime > $lastBackup) { $lastBackup = $backupTime; } } } } return [ 'exists' => true, 'path' => $archivePath, 'size' => $size, 'size_formatted' => $this->formatBytes($size), 'files_count' => $filesCount, 'last_backup' => $lastBackup ? date('Y-m-d H:i:s', $lastBackup) : null, 'directories' => [ 'documenti' => is_dir($archivePath . '/documenti'), 'backup' => is_dir($archivePath . '/backup'), 'temp' => is_dir($archivePath . '/temp'), 'logs' => is_dir($archivePath . '/logs'), 'exports' => is_dir($archivePath . '/exports'), ] ]; } /** * Formatta bytes in formato leggibile */ private function formatBytes(int $bytes, int $precision = 2): string { $units = ['B', 'KB', 'MB', 'GB', 'TB']; for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) { $bytes /= 1024; } return round($bytes, $precision) . ' ' . $units[$i]; } /** * Crea backup del database amministratore */ public function createDatabaseBackup(): array { try { if (!$this->hasDedicatedDatabase()) { throw new \Exception('Amministratore non ha database dedicato'); } $backupPath = $this->getDatabaseBackupPath(); if (!is_dir($backupPath)) { mkdir($backupPath, 0755, true); } $filename = "backup_" . $this->codice_amministratore . "_" . date('Y-m-d_H-i-s') . ".sql"; $fullPath = $backupPath . '/' . $filename; $dbName = $this->getDatabaseName(); $command = sprintf( 'mysqldump -h %s -u %s -p%s %s > %s', env('DB_HOST', '127.0.0.1'), env('DB_USERNAME'), env('DB_PASSWORD'), escapeshellarg($dbName), escapeshellarg($fullPath) ); exec($command, $output, $returnCode); if ($returnCode !== 0) { throw new \Exception('Errore durante backup database: ' . implode("\n", $output)); } return [ 'success' => true, 'filename' => $filename, 'path' => $fullPath, 'size' => filesize($fullPath), 'created_at' => date('Y-m-d H:i:s') ]; } catch (\Exception $e) { return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Prepara archivio per migrazione/trasferimento */ public function prepareForMigration(): array { try { // 1. Backup database $dbBackup = $this->createDatabaseBackup(); if (!$dbBackup['success']) { throw new \Exception('Errore backup database: ' . $dbBackup['error']); } // 2. Crea archivio ZIP dell'intera cartella $archivePath = $this->getArchivePath(); $zipFilename = "migration_" . $this->codice_amministratore . "_" . date('Y-m-d_H-i-s') . ".zip"; $zipPath = storage_path("app/migrations/{$zipFilename}"); if (!is_dir(dirname($zipPath))) { mkdir(dirname($zipPath), 0755, true); } $zip = new \ZipArchive(); if ($zip->open($zipPath, \ZipArchive::CREATE) !== TRUE) { throw new \Exception('Impossibile creare archivio ZIP'); } $iterator = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($archivePath) ); foreach ($iterator as $file) { if ($file->isFile()) { $relativePath = str_replace($archivePath . DIRECTORY_SEPARATOR, '', $file->getPathname()); $zip->addFile($file->getPathname(), $relativePath); } } $zip->close(); // 3. Crea file metadata per migrazione $metadata = [ 'amministratore' => [ 'codice' => $this->codice_amministratore, 'nome' => $this->nome, 'cognome' => $this->cognome, 'denominazione_studio' => $this->denominazione_studio, 'database_name' => $this->getDatabaseName(), ], 'migration' => [ 'created_at' => date('Y-m-d H:i:s'), 'source_server' => env('APP_URL'), 'database_backup' => $dbBackup['filename'], 'archive_size' => filesize($zipPath), 'files_count' => $this->getArchiveInfo()['files_count'], ], 'requirements' => [ 'php_version' => PHP_VERSION, 'laravel_version' => app()->version(), 'mysql_version' => DB::select('SELECT VERSION() as version')[0]->version, ] ]; $metadataPath = dirname($zipPath) . "/metadata_{$this->codice_amministratore}.json"; file_put_contents($metadataPath, json_encode($metadata, JSON_PRETTY_PRINT)); return [ 'success' => true, 'zip_file' => $zipPath, 'metadata_file' => $metadataPath, 'size' => filesize($zipPath), 'database_backup' => $dbBackup['filename'] ]; } catch (\Exception $e) { return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Attributo computed per nome completo */ public function getNomeCompletoAttribute(): string { return trim($this->nome . ' ' . $this->cognome); } }