Allow users to upload DirectAdmin backups
This commit is contained in:
@@ -13,6 +13,7 @@ use Filament\Actions\Action;
|
|||||||
use Filament\Actions\Concerns\InteractsWithActions;
|
use Filament\Actions\Concerns\InteractsWithActions;
|
||||||
use Filament\Actions\Contracts\HasActions;
|
use Filament\Actions\Contracts\HasActions;
|
||||||
use Filament\Forms\Components\Checkbox;
|
use Filament\Forms\Components\Checkbox;
|
||||||
|
use Filament\Forms\Components\FileUpload;
|
||||||
use Filament\Forms\Components\Radio;
|
use Filament\Forms\Components\Radio;
|
||||||
use Filament\Forms\Components\Select;
|
use Filament\Forms\Components\Select;
|
||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
@@ -215,6 +216,113 @@ class DirectAdminMigration extends Page implements HasActions, HasForms
|
|||||||
->icon('heroicon-o-folder')
|
->icon('heroicon-o-folder')
|
||||||
->visible(fn (Get $get): bool => $get('importMethod') === 'backup_file')
|
->visible(fn (Get $get): bool => $get('importMethod') === 'backup_file')
|
||||||
->headerActions([
|
->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')
|
Action::make('refreshLocalBackups')
|
||||||
->label(__('Refresh'))
|
->label(__('Refresh'))
|
||||||
->icon('heroicon-o-arrow-path')
|
->icon('heroicon-o-arrow-path')
|
||||||
|
|||||||
Reference in New Issue
Block a user