160 lines
5.3 KiB
PHP
160 lines
5.3 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use Illuminate\Console\Command;
|
|
use Symfony\Component\Process\Process;
|
|
use Symfony\Component\Process\Exception\ProcessFailedException;
|
|
use Illuminate\Support\Facades\File;
|
|
use ZipArchive;
|
|
|
|
class BackupDatabase extends Command
|
|
{
|
|
/**
|
|
* The name and signature of the console command.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $signature = 'db:backup';
|
|
|
|
/**
|
|
* The console command description.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $description = 'Crea una copia de seguridad comprimida de la base de datos MySQL y elimina respaldos antiguos.';
|
|
|
|
/**
|
|
* Execute the console command.
|
|
*/
|
|
public function handle()
|
|
{
|
|
$this->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.');
|
|
}
|
|
}
|