mirror of
https://github.com/itflow-org/itflow
synced 2026-03-22 21:45:37 +00:00
Use tmp directory to zip and backup itflow instance
This commit is contained in:
@@ -654,7 +654,7 @@ $sql_asset_retired = mysqli_query(
|
|||||||
|
|
||||||
?>
|
?>
|
||||||
<tr>
|
<tr>
|
||||||
<td><?php echo $log_created_at_time_ago; ?></td>
|
<td class="text-nowrap text-secondary"><?php echo $log_created_at_time_ago; ?></td>
|
||||||
<td><?php echo $log_description; ?></td>
|
<td><?php echo $log_description; ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
|||||||
@@ -90,22 +90,20 @@
|
|||||||
|
|
||||||
if (!empty($contact_name)) { ?>
|
if (!empty($contact_name)) { ?>
|
||||||
<div>
|
<div>
|
||||||
<i class="fa fa-fw fa-user text-secondary ml-1 mr-2"></i> <?php echo $contact_name; ?>
|
<i class="fa fa-fw fa-user text-secondary ml-1 mr-2"></i><?php echo $contact_name; ?>
|
||||||
</div>
|
</div>
|
||||||
<?php }
|
<?php }
|
||||||
|
|
||||||
if (!empty($contact_email)) { ?>
|
if (!empty($contact_email)) { ?>
|
||||||
<div class="mt-1">
|
<div class="mt-1">
|
||||||
<i class="fa fa-fw fa-envelope text-secondary ml-1 mr-2"></i>
|
<i class="fa fa-fw fa-envelope text-secondary ml-1 mr-2"></i><a href="mailto:<?php echo $contact_email; ?>"><?php echo $contact_email; ?></a>
|
||||||
<a href="mailto:<?php echo $contact_email; ?>"> <?php echo $contact_email; ?></a>
|
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($contact_phone)) { ?>
|
if (!empty($contact_phone)) { ?>
|
||||||
<div class="mt-1">
|
<div class="mt-1">
|
||||||
<i class="fa fa-fw fa-phone text-secondary ml-1 mr-2"></i>
|
<i class="fa fa-fw fa-phone text-secondary ml-1 mr-2"></i><a href="tel:<?php echo $contact_phone; ?>"><?php echo $contact_phone; ?></a>
|
||||||
<a href="tel:<?php echo $contact_phone; ?>"><?php echo $contact_phone; ?></a>
|
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
if (!empty($contact_extension)) {
|
if (!empty($contact_extension)) {
|
||||||
@@ -118,8 +116,7 @@
|
|||||||
|
|
||||||
if (!empty($contact_mobile)) { ?>
|
if (!empty($contact_mobile)) { ?>
|
||||||
<div class="mt-1">
|
<div class="mt-1">
|
||||||
<i class="fa fa-fw fa-mobile-alt text-secondary ml-1 mr-2"></i>
|
<i class="fa fa-fw fa-mobile-alt text-secondary ml-1 mr-2"></i><a href="tel:<?php echo $contact_mobile; ?>"><?php echo $contact_mobile; ?></a>
|
||||||
<a href="tel:<?php echo $contact_mobile; ?>"><?php echo $contact_mobile; ?></a>
|
|
||||||
</div>
|
</div>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
|
|
||||||
|
|||||||
@@ -8,64 +8,22 @@ defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
|||||||
|
|
||||||
require_once "includes/app_version.php";
|
require_once "includes/app_version.php";
|
||||||
|
|
||||||
if (isset($_GET['download_backup'])) {
|
if (!function_exists('zipFolder')) {
|
||||||
validateCSRFToken($_GET['csrf_token']);
|
|
||||||
global $mysqli, $database;
|
|
||||||
|
|
||||||
$timestamp = date('YmdHis');
|
|
||||||
$baseName = "itflow_$timestamp";
|
|
||||||
$sqlFile = "$baseName.sql";
|
|
||||||
$uploadsZip = "$baseName_uploads.zip";
|
|
||||||
$finalZip = "$baseName.zip";
|
|
||||||
$versionFile = "version.txt";
|
|
||||||
|
|
||||||
// === 1. Generate SQL Dump ===
|
|
||||||
$sqlContent = "-- UTF-8 + Foreign Key Safe Dump\n";
|
|
||||||
$sqlContent .= "SET NAMES 'utf8mb4';\n";
|
|
||||||
$sqlContent .= "SET foreign_key_checks = 0;\n\n";
|
|
||||||
|
|
||||||
$tables = [];
|
|
||||||
$res = $mysqli->query("SHOW TABLES");
|
|
||||||
while ($row = $res->fetch_row()) {
|
|
||||||
$tables[] = $row[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($tables as $table) {
|
|
||||||
$createRes = $mysqli->query("SHOW CREATE TABLE `$table`");
|
|
||||||
$createRow = $createRes->fetch_assoc();
|
|
||||||
$createSQL = array_values($createRow)[1];
|
|
||||||
|
|
||||||
$sqlContent .= "\n-- ----------------------------\n";
|
|
||||||
$sqlContent .= "-- Table structure for `$table`\n";
|
|
||||||
$sqlContent .= "-- ----------------------------\n";
|
|
||||||
$sqlContent .= "DROP TABLE IF EXISTS `$table`;\n";
|
|
||||||
$sqlContent .= $createSQL . ";\n\n";
|
|
||||||
|
|
||||||
$dataRes = $mysqli->query("SELECT * FROM `$table`");
|
|
||||||
if ($dataRes->num_rows > 0) {
|
|
||||||
$sqlContent .= "-- Dumping data for table `$table`\n";
|
|
||||||
while ($row = $dataRes->fetch_assoc()) {
|
|
||||||
$columns = array_map(fn($col) => '`' . $mysqli->real_escape_string($col) . '`', array_keys($row));
|
|
||||||
$values = array_map(function ($val) use ($mysqli) {
|
|
||||||
return is_null($val) ? "NULL" : "'" . $mysqli->real_escape_string($val) . "'";
|
|
||||||
}, array_values($row));
|
|
||||||
$sqlContent .= "INSERT INTO `$table` (" . implode(", ", $columns) . ") VALUES (" . implode(", ", $values) . ");\n";
|
|
||||||
}
|
|
||||||
$sqlContent .= "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$sqlContent .= "SET foreign_key_checks = 1;\n";
|
|
||||||
file_put_contents($sqlFile, $sqlContent);
|
|
||||||
|
|
||||||
// === 2. Create uploads.zip ===
|
|
||||||
function zipFolder($folderPath, $zipFilePath) {
|
function zipFolder($folderPath, $zipFilePath) {
|
||||||
$zip = new ZipArchive();
|
$zip = new ZipArchive();
|
||||||
if ($zip->open($zipFilePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
|
if ($zip->open($zipFilePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
|
||||||
die("Cannot open <$zipFilePath>");
|
error_log("Failed to open zip file: $zipFilePath");
|
||||||
|
http_response_code(500);
|
||||||
|
exit("Internal Server Error: Cannot open zip archive.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$folderPath = realpath($folderPath);
|
$folderPath = realpath($folderPath);
|
||||||
|
if (!$folderPath) {
|
||||||
|
error_log("Invalid folder path: $folderPath");
|
||||||
|
http_response_code(500);
|
||||||
|
exit("Internal Server Error: Invalid folder path.");
|
||||||
|
}
|
||||||
|
|
||||||
$files = new RecursiveIteratorIterator(
|
$files = new RecursiveIteratorIterator(
|
||||||
new RecursiveDirectoryIterator($folderPath),
|
new RecursiveDirectoryIterator($folderPath),
|
||||||
RecursiveIteratorIterator::LEAVES_ONLY
|
RecursiveIteratorIterator::LEAVES_ONLY
|
||||||
@@ -81,17 +39,82 @@ if (isset($_GET['download_backup'])) {
|
|||||||
|
|
||||||
$zip->close();
|
$zip->close();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_GET['download_backup'])) {
|
||||||
|
validateCSRFToken($_GET['csrf_token']);
|
||||||
|
global $mysqli;
|
||||||
|
|
||||||
|
$session_name = $_SESSION['username'] ?? 'unknown';
|
||||||
|
$timestamp = date('YmdHis');
|
||||||
|
$baseName = "itflow_$timestamp";
|
||||||
|
|
||||||
|
// Use temp files
|
||||||
|
$sqlFile = tempnam(sys_get_temp_dir(), $baseName . "_sql_");
|
||||||
|
$uploadsZip = tempnam(sys_get_temp_dir(), $baseName . "_uploads_");
|
||||||
|
$versionFile = tempnam(sys_get_temp_dir(), $baseName . "_version_");
|
||||||
|
$finalZip = tempnam(sys_get_temp_dir(), $baseName . "_backup_");
|
||||||
|
|
||||||
|
// === 1. Generate SQL Dump ===
|
||||||
|
$sqlContent = "-- UTF-8 + Foreign Key Safe Dump\n";
|
||||||
|
$sqlContent .= "SET NAMES 'utf8mb4';\n";
|
||||||
|
$sqlContent .= "SET foreign_key_checks = 0;\n\n";
|
||||||
|
|
||||||
|
$tables = [];
|
||||||
|
$res = $mysqli->query("SHOW TABLES");
|
||||||
|
if (!$res) {
|
||||||
|
error_log("MySQL Error: " . $mysqli->error);
|
||||||
|
exit("Error retrieving tables.");
|
||||||
|
}
|
||||||
|
|
||||||
|
while ($row = $res->fetch_row()) {
|
||||||
|
$tables[] = $row[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($tables as $table) {
|
||||||
|
$createRes = $mysqli->query("SHOW CREATE TABLE `$table`");
|
||||||
|
if (!$createRes) {
|
||||||
|
error_log("MySQL Error: " . $mysqli->error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$createRow = $createRes->fetch_assoc();
|
||||||
|
$createSQL = array_values($createRow)[1];
|
||||||
|
|
||||||
|
$sqlContent .= "\n-- ----------------------------\n";
|
||||||
|
$sqlContent .= "-- Table structure for `$table`\n";
|
||||||
|
$sqlContent .= "-- ----------------------------\n";
|
||||||
|
$sqlContent .= "DROP TABLE IF EXISTS `$table`;\n";
|
||||||
|
$sqlContent .= $createSQL . ";\n\n";
|
||||||
|
|
||||||
|
$dataRes = $mysqli->query("SELECT * FROM `$table`");
|
||||||
|
if ($dataRes && $dataRes->num_rows > 0) {
|
||||||
|
$sqlContent .= "-- Dumping data for table `$table`\n";
|
||||||
|
while ($row = $dataRes->fetch_assoc()) {
|
||||||
|
$columns = array_map(fn($col) => '`' . $mysqli->real_escape_string($col) . '`', array_keys($row));
|
||||||
|
$values = array_map(function ($val) use ($mysqli) {
|
||||||
|
return is_null($val) ? "NULL" : "'" . $mysqli->real_escape_string($val) . "'";
|
||||||
|
}, array_values($row));
|
||||||
|
$sqlContent .= "INSERT INTO `$table` (" . implode(", ", $columns) . ") VALUES (" . implode(", ", $values) . ");\n";
|
||||||
|
}
|
||||||
|
$sqlContent .= "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$sqlContent .= "SET foreign_key_checks = 1;\n";
|
||||||
|
file_put_contents($sqlFile, $sqlContent);
|
||||||
|
|
||||||
|
// === 2. Zip uploads folder ===
|
||||||
zipFolder("uploads", $uploadsZip);
|
zipFolder("uploads", $uploadsZip);
|
||||||
|
|
||||||
// === 3. Generate version.txt ===
|
// === 3. Create version.txt ===
|
||||||
$commitHash = trim(shell_exec('git log -1 --format=%H'));
|
$commitHash = trim(shell_exec('git log -1 --format=%H')) ?: 'N/A';
|
||||||
$gitBranch = trim(shell_exec('git rev-parse --abbrev-ref HEAD'));
|
$gitBranch = trim(shell_exec('git rev-parse --abbrev-ref HEAD')) ?: 'N/A';
|
||||||
|
|
||||||
$versionContent = "ITFlow Backup Metadata\n";
|
$versionContent = "ITFlow Backup Metadata\n";
|
||||||
$versionContent .= "-----------------------------\n";
|
$versionContent .= "-----------------------------\n";
|
||||||
$versionContent .= "Generated: " . date('Y-m-d H:i:s') . "\n";
|
$versionContent .= "Generated: " . date('Y-m-d H:i:s') . "\n";
|
||||||
$versionContent .= "Backup File: $baseName.zip\n";
|
$versionContent .= "Backup File: " . basename($finalZip) . "\n";
|
||||||
$versionContent .= "Generated By: $session_name\n";
|
$versionContent .= "Generated By: $session_name\n";
|
||||||
$versionContent .= "Host: " . gethostname() . "\n";
|
$versionContent .= "Host: " . gethostname() . "\n";
|
||||||
$versionContent .= "Git Branch: $gitBranch\n";
|
$versionContent .= "Git Branch: $gitBranch\n";
|
||||||
@@ -99,12 +122,17 @@ if (isset($_GET['download_backup'])) {
|
|||||||
$versionContent .= "ITFlow Version: " . (defined('APP_VERSION') ? APP_VERSION : 'Unknown') . "\n";
|
$versionContent .= "ITFlow Version: " . (defined('APP_VERSION') ? APP_VERSION : 'Unknown') . "\n";
|
||||||
$versionContent .= "Database Version: " . (defined('CURRENT_DATABASE_VERSION') ? CURRENT_DATABASE_VERSION : 'Unknown') . "\n";
|
$versionContent .= "Database Version: " . (defined('CURRENT_DATABASE_VERSION') ? CURRENT_DATABASE_VERSION : 'Unknown') . "\n";
|
||||||
|
|
||||||
file_put_contents($versionFile, $versionContent);
|
// Generate SHA256 checksum
|
||||||
|
$versionContent .= "Checksum (SHA256): \n";
|
||||||
|
|
||||||
// === 4. Combine into final .zip file ===
|
file_put_contents($versionFile, $versionContent); // Add metadata first
|
||||||
|
|
||||||
|
// === 4. Create final archive ===
|
||||||
$final = new ZipArchive();
|
$final = new ZipArchive();
|
||||||
if ($final->open($finalZip, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
|
if ($final->open($finalZip, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
|
||||||
die("Cannot create final backup zip.");
|
error_log("Failed to create final zip: $finalZip");
|
||||||
|
http_response_code(500);
|
||||||
|
exit("Internal Server Error: Unable to create backup archive.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$final->addFile($sqlFile, "db.sql");
|
$final->addFile($sqlFile, "db.sql");
|
||||||
@@ -112,18 +140,29 @@ if (isset($_GET['download_backup'])) {
|
|||||||
$final->addFile($versionFile, "version.txt");
|
$final->addFile($versionFile, "version.txt");
|
||||||
$final->close();
|
$final->close();
|
||||||
|
|
||||||
// Cleanup temp files before download
|
// Calculate checksum and re-write version.txt with it
|
||||||
|
$checksum = hash_file('sha256', $finalZip);
|
||||||
|
file_put_contents($versionFile, $versionContent . "$checksum\n");
|
||||||
|
|
||||||
|
// === 5. Serve final zip ===
|
||||||
|
header('Content-Type: application/zip');
|
||||||
|
header('Content-Disposition: attachment; filename="' . basename($finalZip) . '"');
|
||||||
|
header('Content-Length: ' . filesize($finalZip));
|
||||||
|
header('Pragma: public');
|
||||||
|
header('Expires: 0');
|
||||||
|
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||||
|
header('Content-Transfer-Encoding: binary');
|
||||||
|
|
||||||
|
flush();
|
||||||
|
$fp = fopen($finalZip, 'rb');
|
||||||
|
fpassthru($fp);
|
||||||
|
fclose($fp);
|
||||||
|
|
||||||
|
// === 6. Cleanup temp files ===
|
||||||
unlink($sqlFile);
|
unlink($sqlFile);
|
||||||
unlink($uploadsZip);
|
unlink($uploadsZip);
|
||||||
unlink($versionFile);
|
unlink($versionFile);
|
||||||
|
unlink($finalZip);
|
||||||
// === 5. Serve the zip for download ===
|
|
||||||
header('Content-Type: application/zip');
|
|
||||||
header('Content-Disposition: attachment; filename="' . $finalZip . '"');
|
|
||||||
header('Content-Length: ' . filesize($finalZip));
|
|
||||||
flush();
|
|
||||||
readfile($finalZip);
|
|
||||||
unlink($finalZip); // remove final zip after serving
|
|
||||||
|
|
||||||
logAction("System", "Backup Download", "$session_name downloaded full backup.");
|
logAction("System", "Backup Download", "$session_name downloaded full backup.");
|
||||||
$_SESSION['alert_message'] = "Full backup downloaded.";
|
$_SESSION['alert_message'] = "Full backup downloaded.";
|
||||||
|
|||||||
Reference in New Issue
Block a user