info('Iniciando copia de seguridad de la base de datos...'); $dbConfig = config('database.connections.mysql'); if (!$dbConfig) { $this->error('No se pudo cargar la configuración de la base de datos MySQL.'); return 1; } $host = $dbConfig['host'] ?? '127.0.0.1'; $port = $dbConfig['port'] ?? '3306'; $database = $dbConfig['database'] ?? 'lauck'; $username = $dbConfig['username'] ?? 'root'; $password = $dbConfig['password'] ?? ''; // Asegurarse de que el directorio de respaldos exista $backupDir = storage_path('app/backups'); if (!File::exists($backupDir)) { File::makeDirectory($backupDir, 0755, true); } // Nombre de los archivos $timestamp = now()->format('Y-m-d_H-i-s'); $sqlFile = "backup-{$database}-{$timestamp}.sql"; $sqlPath = $backupDir . '/' . $sqlFile; $zipFile = "backup-{$database}-{$timestamp}.zip"; $zipPath = $backupDir . '/' . $zipFile; // Buscar el ejecutable mysqldump $mysqldumpPath = 'mysqldump'; if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { // En Windows/XAMPP comúnmente está en C:\xampp\mysql\bin\mysqldump.exe $xamppPath = 'C:\\xampp\\mysql\\bin\\mysqldump.exe'; if (File::exists($xamppPath)) { $mysqldumpPath = $xamppPath; } } // Ejecutar mysqldump con Process // Usamos variables de entorno para la contraseña por seguridad $command = [ $mysqldumpPath, "--host={$host}", "--port={$port}", "--user={$username}", "--no-tablespaces", $database ]; // Inherit SystemRoot and PATH from the system environment to prevent Winsock 10106 errors on Windows $env = [ 'SystemRoot' => getenv('SystemRoot') ?: 'C:\\Windows', 'windir' => getenv('windir') ?: 'C:\\Windows', 'PATH' => getenv('PATH'), ]; if ($password !== '') { $env['MYSQL_PWD'] = $password; } $this->info("Ejecutando volcado a archivo temporal SQL..."); $process = new Process($command, null, $env); $process->setTimeout(300); // 5 minutos máximo try { $process->run(); if (!$process->isSuccessful()) { throw new \Exception(trim($process->getErrorOutput()) ?: 'Error desconocido al ejecutar mysqldump.'); } // Guardar salida en el archivo SQL File::put($sqlPath, $process->getOutput()); } catch (\Exception $e) { $this->error('Error durante el volcado de la base de datos: ' . $e->getMessage()); // Limpieza si quedó el archivo a medio hacer if (File::exists($sqlPath)) { File::delete($sqlPath); } return 1; } // Comprimir el archivo SQL a ZIP $this->info('Comprimiendo respaldo...'); $zip = new ZipArchive(); if ($zip->open($zipPath, ZipArchive::CREATE) === true) { $zip->addFile($sqlPath, $sqlFile); $zip->close(); // Eliminar el archivo SQL temporal File::delete($sqlPath); $this->info("Copia de seguridad guardada con éxito en: {$zipFile}"); } else { $this->error('No se pudo crear el archivo ZIP.'); return 1; } // Retención de respaldos (limpieza de archivos antiguos) $this->cleanupOldBackups($backupDir); return 0; } /** * Elimina respaldos antiguos que exceden el límite de días (30 días). */ protected function cleanupOldBackups($backupDir) { $this->info('Revisando si hay respaldos antiguos para limpiar...'); $days = 30; // Conservar los últimos 30 días de respaldos semanales $files = File::files($backupDir); foreach ($files as $file) { // Verificar que sea un archivo de backup if ($file->getExtension() === 'zip' && strpos($file->getFilename(), 'backup-') === 0) { $lastModified = $file->getMTime(); $ageInDays = (time() - $lastModified) / (24 * 60 * 60); if ($ageInDays > $days) { $this->warn("Eliminando respaldo antiguo: " . $file->getFilename()); File::delete($file->getPathname()); } } } $this->info('Limpieza finalizada.'); } }