Implementada logica para entornos dev/prod

This commit is contained in:
imnavajas
2025-06-12 14:57:48 +02:00
parent b0bceb20bc
commit 8288219872
7 changed files with 511 additions and 117 deletions

View File

@ -19,51 +19,141 @@ class Backups extends BaseController
{
helper('filesystem');
$entries = $this->backupModel->orderBy('created_at', 'DESC')->findAll();
$entorno = getenv('SK_ENVIRONMENT');
$backups = [];
foreach ($entries as $entry) {
$file = $entry['filename'];
$localPath = $entry['path_local'];
$remotePath = $entry['path_remote'];
$isLocal = $localPath && file_exists($localPath);
$isRemote = !empty($remotePath);
if ($entorno === 'development') {
// Leer archivos directamente del disco en entorno de desarrollo
$backups = [];
if ($isLocal) {
$fecha = date('Y-m-d H:i', filemtime($localPath));
$tamano = filesize($localPath);
$tamanoFmt = number_format($tamano / 1024, 2) . ' KB';
// === 1. Backups locales ===
$localDir = WRITEPATH . 'backups/';
$localFiles = get_filenames($localDir);
// Actualizar la BD si ha cambiado
if ($entry['size'] != $tamano) {
$this->backupModel->update($entry['id'], [
'size' => $tamano,
]);
foreach ($localFiles as $file) {
if (!str_ends_with($file, '.zip') || !str_starts_with($file, 'backup_dev_')) {
continue;
}
} else {
$fecha = $entry['created_at'] ?? '-';
$tamano = $entry['size'] ?? null;
$tamanoFmt = $tamano ? number_format($tamano / 1024, 2) . ' KB' : '-';
$localPath = $localDir . $file;
$fecha = date('Y-m-d H:i', filemtime($localPath));
$tamano = filesize($localPath);
$tamanoFmt = $tamano > 1048576
? number_format($tamano / 1048576, 2) . ' MB'
: number_format($tamano / 1024, 2) . ' KB';
$backups[$file] = [
'id' => null,
'filename' => $file,
'fecha' => $fecha,
'tamano' => $tamanoFmt,
'local' => true,
'remoto' => false,
];
}
$backups[] = [
'id' => $entry['id'],
'filename' => $file,
'fecha' => $fecha,
'tamano' => $tamanoFmt,
'local' => $isLocal,
'remoto' => $isRemote,
];
// === 2. Backups remotos en SFTP ===
$sftpHost = getenv('HIDRIVE_HOST');
$sftpUser = getenv('HIDRIVE_USER');
$sftpPass = getenv('HIDRIVE_PASS');
$remoteDir = '/users/erp2019/backups_erp/';
$sftp = new SFTP($sftpHost);
if ($sftp->login($sftpUser, $sftpPass)) {
$remoteFiles = $sftp->nlist($remoteDir);
foreach ($remoteFiles as $file) {
if (!str_ends_with($file, '.zip') || !str_starts_with($file, 'backup_')) {
continue;
}
// Verificar si ya se cargó como local
$alreadyLocal = isset($backups[$file]);
// Obtener info de archivo remoto
$stat = $sftp->stat($remoteDir . $file);
$fecha = isset($stat['mtime']) ? date('Y-m-d H:i', $stat['mtime']) : '-';
$tamano = $stat['size'] ?? null;
$tamanoFmt = $tamano > 1048576
? number_format($tamano / 1048576, 2) . ' MB'
: number_format($tamano / 1024, 2) . ' KB';
$backups[$file] = [
'id' => null,
'filename' => $file,
'fecha' => $fecha,
'tamano' => $tamanoFmt,
'local' => $alreadyLocal,
'remoto' => true,
];
}
} else {
// Opcional: mostrar un error o dejarlo pasar silenciosamente
session()->setFlashdata('warning', 'No se pudo conectar al servidor SFTP para obtener backups remotos.');
}
// Convertir a lista (por si se usó índice por filename)
$backups = array_values($backups);
} else {
// En producción: seguir usando la base de datos
$entries = $this->backupModel->orderBy('created_at', 'DESC')->findAll();
foreach ($entries as $entry) {
$file = $entry['filename'];
if (!str_ends_with($file, '.zip')) {
continue;
}
$localPath = $entry['path_local'];
$remotePath = $entry['path_remote'];
$isLocal = $localPath && file_exists($localPath);
$isRemote = !empty($remotePath);
if ($isLocal) {
$fecha = date('Y-m-d H:i', filemtime($localPath));
$tamano = filesize($localPath);
$tamanoFmt = $tamano > 1048576
? number_format($tamano / 1048576, 2) . ' MB'
: number_format($tamano / 1024, 2) . ' KB';
if ($entry['size'] != $tamano) {
$this->backupModel->update($entry['id'], [
'size' => $tamano,
]);
}
} else {
$fecha = $entry['created_at'] ?? '-';
$tamano = $entry['size'] ?? null;
$tamanoFmt = $tamano > 1048576
? number_format($tamano / 1048576, 2) . ' MB'
: number_format($tamano / 1024, 2) . ' KB';
}
$backups[] = [
'id' => $entry['id'],
'filename' => $file,
'fecha' => $fecha,
'tamano' => $tamanoFmt,
'local' => $isLocal,
'remoto' => $isRemote,
];
}
}
return view('themes/vuexy/form/backups/backupList', ['backups' => $backups]);
}
public function create()
{
if (getenv('SK_ENVIRONMENT') !== 'production') {
return redirect()->to(route_to('backupsList'))->with('error', 'No permitido en entorno de desarrollo.');
}
helper('filesystem');
$timestamp = date('Ymd_His');
@ -78,7 +168,8 @@ class Backups extends BaseController
$password = $dbConfig['password'];
$database = $dbConfig['database'];
$command = "mysqldump -h {$host} -u{$username} -p'{$password}' {$database} > {$sqlPath}";
$command = "mysqldump -h {$host} -u" . escapeshellarg($username) . " -p'" . addslashes($password) . "' {$database} > {$sqlPath}";
system($command, $retval);
if ($retval !== 0) {
@ -116,9 +207,175 @@ class Backups extends BaseController
'status' => 'subido'
]);
return redirect()->to(route_to('backupsList'))->with('message', 'Backup creado, comprimido y enviado.');
return redirect()->to(route_to('backupsList'))->with('message', 'Backup del entorno de produccion creado, comprimido y enviado al remoto.');
}
public function createDevelopment()
{
if (getenv('SK_ENVIRONMENT') !== 'development') {
return redirect()->to(route_to('backupsList'))->with('error', 'Esta acción solo está permitida en desarrollo.');
}
helper('filesystem');
$timestamp = date('Ymd_His');
$sqlFilename = "backup_dev_{$timestamp}.sql";
$zipFilename = "backup_dev_{$timestamp}.zip";
$sqlPath = WRITEPATH . 'backups/' . $sqlFilename;
$zipPath = WRITEPATH . 'backups/' . $zipFilename;
$dbConfig = config('Database')->default;
$host = $dbConfig['hostname'];
$username = $dbConfig['username'];
$password = $dbConfig['password'];
$database = $dbConfig['database'];
$command = "mysqldump -h {$host} -u{$username} -p'{$password}' {$database} > {$sqlPath}";
system($command, $retval);
if ($retval !== 0) {
throw new \RuntimeException("Error al crear el backup local.");
}
$zip = new \ZipArchive();
if ($zip->open($zipPath, \ZipArchive::CREATE) === TRUE) {
$zip->addFile($sqlPath, $sqlFilename);
$zip->close();
unlink($sqlPath);
} else {
throw new \RuntimeException("Error al comprimir el backup local.");
}
// Ya no insertamos en la base de datos; el archivo queda en disco y se listará dinámicamente
return redirect()->to(route_to('backupsList'))->with('message', 'Backup local del entorno de desarrollo creado.');
}
public function download($filename)
{
helper('filesystem');
$entorno = getenv('SK_ENVIRONMENT');
$backup = $this->backupModel->where('filename', $filename)->first();
// 1. Si hay entrada en la base de datos
if ($backup) {
$localPath = $backup['path_local'];
$remotePath = $backup['path_remote'];
if ($localPath && file_exists($localPath)) {
return $this->response->download($localPath, null)->setFileName($filename);
}
if (!empty($remotePath)) {
$sftpHost = getenv('HIDRIVE_HOST');
$sftpUser = getenv('HIDRIVE_USER');
$sftpPass = getenv('HIDRIVE_PASS');
$sftp = new SFTP($sftpHost);
if (!$sftp->login($sftpUser, $sftpPass)) {
return redirect()->to(route_to('backupsList'))->with('error', 'No se pudo conectar al servidor SFTP.');
}
$fileContents = $sftp->get($remotePath);
if ($fileContents === false) {
return redirect()->to(route_to('backupsList'))->with('error', 'Error al descargar desde SFTP.');
}
$newLocalPath = WRITEPATH . 'backups/' . $filename;
write_file($newLocalPath, $fileContents);
// Omitimos update() si estamos en desarrollo sin base de datos
if ($entorno === 'production') {
$this->backupModel->update($backup['id'], ['path_local' => $newLocalPath]);
}
return $this->response->download($newLocalPath, null)->setFileName($filename);
}
return redirect()->to(route_to('backupsList'))->with('error', 'No se pudo encontrar el archivo ni local ni remoto.');
}
// 2. Si NO hay entrada en la BD y estamos en desarrollo
if ($entorno === 'development') {
$localPath = WRITEPATH . 'backups/' . $filename;
if (file_exists($localPath)) {
return $this->response->download($localPath, null)->setFileName($filename);
}
// También se puede intentar buscar en el SFTP si quieres
$sftpHost = getenv('HIDRIVE_HOST');
$sftpUser = getenv('HIDRIVE_USER');
$sftpPass = getenv('HIDRIVE_PASS');
$remotePath = '/users/erp2019/backups_erp/' . $filename;
$sftp = new SFTP($sftpHost);
if ($sftp->login($sftpUser, $sftpPass)) {
$fileContents = $sftp->get($remotePath);
if ($fileContents !== false) {
$newLocalPath = WRITEPATH . 'backups/' . $filename;
write_file($newLocalPath, $fileContents);
return $this->response->download($newLocalPath, null)->setFileName($filename);
}
}
return redirect()->to(route_to('backupsList'))->with('error', 'Archivo no encontrado local ni remoto (sin base de datos).');
}
return redirect()->to(route_to('backupsList'))->with('error', 'Backup no encontrado.');
}
public function deleteLocal($id)
{
$entorno = getenv('SK_ENVIRONMENT');
$backup = $this->backupModel->find($id);
if (!$backup) {
return redirect()->to(route_to('backupsList'))->with('error', 'Backup no encontrado en la base de datos.');
}
$localPath = $backup['path_local'];
// Si existe el archivo, intentamos borrarlo
if ($localPath && file_exists($localPath)) {
unlink($localPath);
}
if ($entorno === 'production') {
// Solo desvincular el archivo local
$this->backupModel->update($id, ['path_local' => null]);
return redirect()->to(route_to('backupsList'))->with('message', 'Archivo local eliminado (registro conservado).');
} else {
// Eliminar completamente en desarrollo
$this->backupModel->delete($id);
return redirect()->to(route_to('backupsList'))->with('message', 'Backup de desarrollo eliminado completamente.');
}
}
public function deleteLocalDevelopment($filename)
{
$entorno = getenv('SK_ENVIRONMENT');
if ($entorno !== 'development') {
return redirect()->to(route_to('backupsList'))->with('error', 'Solo permitido en desarrollo.');
}
$path = WRITEPATH . 'backups/' . $filename;
if (file_exists($path)) {
unlink($path);
return redirect()->to(route_to('backupsList'))->with('message', "Archivo '$filename' eliminado del sistema de archivos.");
}
return redirect()->to(route_to('backupsList'))->with('error', "Archivo '$filename' no encontrado en el sistema.");
}
public function restoreLocal($file)
{
$path = WRITEPATH . 'backups/' . $file;
@ -126,7 +383,7 @@ class Backups extends BaseController
throw new \CodeIgniter\Exceptions\PageNotFoundException("Backup no encontrado.");
}
$zip = new ZipArchive();
$zip = new \ZipArchive();
if ($zip->open($path) === TRUE) {
$extractPath = WRITEPATH . 'backups/tmp_restore/';
if (!is_dir($extractPath)) {
@ -140,31 +397,32 @@ class Backups extends BaseController
throw new \RuntimeException("No se encontró ningún archivo .sql en el ZIP");
}
$db = \Config\Database::connect();
$sql = file_get_contents($sqlFiles[0]);
$db->query('SET FOREIGN_KEY_CHECKS=0;');
$db->query($sql);
$db->query('SET FOREIGN_KEY_CHECKS=1;');
$sqlFile = $sqlFiles[0];
$dbConfig = config('Database')->default;
$host = $dbConfig['hostname'];
$username = $dbConfig['username'];
$password = $dbConfig['password'];
$database = $dbConfig['database'];
$cmd = "mysql -h {$host} -u{$username} -p'{$password}' {$database} < {$sqlFile}";
system($cmd, $retval);
if ($retval !== 0) {
throw new \RuntimeException("Error al restaurar la base de datos. Código: $retval");
}
array_map('unlink', glob($extractPath . '*'));
rmdir($extractPath);
return redirect()->to('/backups')->with('message', 'Backup restaurado correctamente.');
return redirect()->to(route_to('backupsList'))->with('message', 'Backup restaurado correctamente (vía sistema).');
} else {
throw new \RuntimeException("No se pudo abrir el archivo ZIP");
}
}
public function deleteLocal($id)
{
$backup = $this->backupModel->find($id);
if ($backup && $backup['path_local'] && file_exists($backup['path_local'])) {
unlink($backup['path_local']);
$this->backupModel->update($id, ['path_local' => null]);
return redirect()->to(route_to('backupsList'))->with('message', 'Backup local eliminado.');
}
return redirect()->to(route_to('backupsList'))->with('error', 'Archivo no encontrado.');
}
public function restoreRemote($filename)
{
@ -178,9 +436,9 @@ class Backups extends BaseController
}
// Parámetros SFTP
$sftpHost = 'sftp.hidrive.ionos.com';
$sftpUser = 'erp2019';
$sftpPass = 'Z2CjX7kd2h';
$sftpHost = getenv('HIDRIVE_HOST');
$sftpUser = getenv('HIDRIVE_USER');
$sftpPass = getenv('HIDRIVE_PASS');
$remotePath = $backup['path_remote'];
$localPath = WRITEPATH . 'backups/' . $filename;
@ -216,9 +474,9 @@ class Backups extends BaseController
private function sendToSFTP($localPath, $remoteFilename)
{
$sftpHost = 'sftp.hidrive.ionos.com';
$sftpUser = 'erp2019';
$sftpPass = 'Z2CjX7kd2h';
$sftpHost = getenv('HIDRIVE_HOST');
$sftpUser = getenv('HIDRIVE_USER');
$sftpPass = getenv('HIDRIVE_PASS');
$remotePath = '/users/erp2019/backups_erp/' . $remoteFilename;
$sftp = new SFTP($sftpHost);