Files
jabali-panel/app/Http/Controllers/BackupDownloadController.php
2026-02-02 03:11:45 +02:00

121 lines
3.7 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Models\Backup;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
class BackupDownloadController extends Controller
{
public function download(Request $request): BinaryFileResponse
{
$encodedPath = (string) $request->query('path', '');
$decodedPath = base64_decode($encodedPath, true);
if ($decodedPath === false || $decodedPath === '') {
abort(404, 'Backup file not found');
}
$realPath = realpath($decodedPath);
if ($realPath === false || ! is_file($realPath)) {
abort(404, 'Backup file not found');
}
// Verify the user owns this backup (path should be in their home directory)
$user = Auth::guard('web')->user();
if (! $user) {
abort(403, 'Unauthorized');
}
$homeDirectory = $user->home_directory ?: "/home/{$user->username}";
$backupDirectory = rtrim($homeDirectory, '/').'/backups';
$backupRealPath = realpath($backupDirectory);
if ($backupRealPath === false) {
abort(404, 'Backup directory not found');
}
if ($realPath !== $backupRealPath && ! str_starts_with($realPath, $backupRealPath.DIRECTORY_SEPARATOR)) {
abort(403, 'Unauthorized access to this backup');
}
return response()->download($realPath);
}
public function adminDownload(Request $request): BinaryFileResponse|StreamedResponse
{
$backupId = $request->query('id');
if (empty($backupId)) {
abort(404, 'Backup ID required');
}
// Verify admin access
$user = Auth::guard('web')->user();
if (! $user || ! $user->is_admin) {
abort(403, 'Unauthorized');
}
$backup = Backup::find($backupId);
if (! $backup) {
abort(404, 'Backup not found');
}
$path = $backup->local_path;
if (empty($path)) {
abort(404, 'Backup file not found on disk');
}
$realPath = realpath($path);
if ($realPath === false) {
abort(404, 'Backup file not found on disk');
}
// Handle directory backups by creating a zip archive on-the-fly
if (is_dir($realPath)) {
$backupName = basename($realPath).'.zip';
return response()->streamDownload(function () use ($realPath) {
$zip = new \ZipArchive;
$tempFile = tempnam(sys_get_temp_dir(), 'backup_');
$zip->open($tempFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($realPath, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $file) {
if (! $file->isDir()) {
$filePath = $file->getRealPath();
$relativePath = substr($filePath, strlen($realPath) + 1);
$zip->addFile($filePath, $relativePath);
}
}
$zip->close();
readfile($tempFile);
unlink($tempFile);
}, $backupName, [
'Content-Type' => 'application/zip',
]);
}
// For single file backups (tar.gz)
if (! is_file($realPath)) {
abort(404, 'Backup file not found on disk');
}
return response()->download($realPath);
}
}