Allow users to upload DirectAdmin backups

This commit is contained in:
2026-02-11 01:23:08 +02:00
parent 443b05a677
commit 1e66f43d4e

View File

@@ -13,6 +13,7 @@ use Filament\Actions\Action;
use Filament\Actions\Concerns\InteractsWithActions;
use Filament\Actions\Contracts\HasActions;
use Filament\Forms\Components\Checkbox;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Radio;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
@@ -215,6 +216,113 @@ class DirectAdminMigration extends Page implements HasActions, HasForms
->icon('heroicon-o-folder')
->visible(fn (Get $get): bool => $get('importMethod') === 'backup_file')
->headerActions([
Action::make('uploadBackup')
->label(__('Upload'))
->icon('heroicon-o-arrow-up-tray')
->color('gray')
->modalHeading(__('Upload Backup'))
->modalDescription(fn (): string => ($user = $this->getUser())
? __('Upload a DirectAdmin backup archive into /home/:user/backups', ['user' => $user->username])
: __('Upload a DirectAdmin backup archive into your backups folder'))
->modalSubmitActionLabel(__('Upload'))
->form([
FileUpload::make('backup')
->label(__('DirectAdmin Backup Archive'))
->storeFiles(false)
->required()
->acceptedFileTypes([
'application/octet-stream',
'application/x-tar',
'application/zstd',
'application/x-zstd',
'application/gzip',
'application/x-gzip',
])
->maxSize(512000) // 500MB in KB
->helperText(__('Supported formats: .tar.zst, .tar.gz, .tgz (max 500MB via upload)')),
])
->action(function (array $data): void {
try {
$user = $this->getUser();
if (! $user) {
throw new Exception(__('You must be logged in.'));
}
$file = $data['backup'] ?? null;
if (! $file) {
throw new Exception(__('Please select a backup file.'));
}
$filename = (string) $file->getClientOriginalName();
$filename = basename($filename);
if (! preg_match('/\\.(tar\\.zst|tar\\.gz|tgz)$/i', $filename)) {
throw new Exception(__('Backup must be a .tar.zst, .tar.gz or .tgz file.'));
}
$maxBytes = 500 * 1024 * 1024;
$fileSize = (int) ($file->getSize() ?? 0);
if ($fileSize > $maxBytes) {
throw new Exception(__('File too large for upload (max 500MB). Upload it via SSH/SFTP to /home/:user/backups.', [
'user' => $user->username,
]));
}
// Ensure backups folder exists
$this->getAgent()->fileMkdir($user->username, 'backups');
// Stage into the agent-allowed temp dir, then let the agent move it.
$tmpDir = '/tmp/jabali-uploads';
if (! is_dir($tmpDir)) {
mkdir($tmpDir, 0700, true);
chmod($tmpDir, 0700);
} else {
@chmod($tmpDir, 0700);
}
$safeName = preg_replace('/[^a-zA-Z0-9._-]/', '_', $filename);
$tmpPath = $tmpDir.'/'.uniqid('upload_', true).'_'.$safeName;
if (! @copy($file->getRealPath(), $tmpPath)) {
throw new Exception(__('Failed to stage upload.'));
}
@chmod($tmpPath, 0600);
$result = $this->getAgent()->send('file.upload_temp', [
'username' => $user->username,
'path' => 'backups',
'filename' => $safeName,
'temp_path' => $tmpPath,
]);
if (! ($result['success'] ?? false)) {
if (file_exists($tmpPath)) {
@unlink($tmpPath);
}
throw new Exception((string) ($result['error'] ?? __('Upload failed')));
}
$this->loadLocalBackups();
$uploadedPath = $result['path'] ?? null;
if (is_string($uploadedPath) && $uploadedPath !== '') {
$this->localBackupPath = $uploadedPath;
$this->selectLocalBackup();
}
Notification::make()
->title(__('Backup uploaded'))
->body(__('Uploaded :name', ['name' => $safeName]))
->success()
->send();
} catch (Exception $e) {
Notification::make()
->title(__('Upload failed'))
->body($e->getMessage())
->danger()
->send();
}
}),
Action::make('refreshLocalBackups')
->label(__('Refresh'))
->icon('heroicon-o-arrow-path')