schema([ TextInput::make('name') ->label('Name') ->columnSpanFull() ->required(), TextArea::make('ignored') ->columnSpanFull() ->label('Ignored Files & Directories'), Toggle::make('is_locked') ->label('Lock?') ->helperText('Prevents this backup from being deleted until explicitly unlocked.'), ]); } public function table(Table $table): Table { /** @var Server $server */ $server = Filament::getTenant(); return $table ->columns([ TextColumn::make('name') ->searchable(), BytesColumn::make('bytes') ->label('Size'), DateTimeColumn::make('created_at') ->label('Created') ->since() ->sortable(), IconColumn::make('is_successful') ->label('Successful') ->boolean(), IconColumn::make('is_locked') ->visibleFrom('md') ->label('Lock Status') ->icon(fn (Backup $backup) => !$backup->is_locked ? 'tabler-lock-open' : 'tabler-lock'), ]) ->actions([ ActionGroup::make([ Action::make('lock') ->icon(fn (Backup $backup) => !$backup->is_locked ? 'tabler-lock' : 'tabler-lock-open') ->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) ->label(fn (Backup $backup) => !$backup->is_locked ? 'Lock' : 'Unlock') ->action(fn (BackupController $backupController, Backup $backup, Request $request) => $backupController->toggleLock($request, $server, $backup)), Action::make('download') ->color('primary') ->icon('tabler-download') ->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_DOWNLOAD, $server)) ->url(fn (DownloadLinkService $downloadLinkService, Backup $backup, Request $request) => $downloadLinkService->handle($backup, $request->user()), true), Action::make('restore') ->color('success') ->icon('tabler-folder-up') ->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_RESTORE, $server)) ->form([ Placeholder::make('') ->helperText('Your server will be stopped. You will not be able to control the power state, access the file manager, or create additional backups until this process is completed.'), Checkbox::make('truncate') ->label('Delete all files before restoring backup?'), ]) ->action(function (Backup $backup, $data, DaemonBackupRepository $daemonRepository, DownloadLinkService $downloadLinkService) use ($server) { if (!is_null($server->status)) { return Notification::make() ->danger() ->title('Backup Restore Failed') ->body('This server is not currently in a state that allows for a backup to be restored.') ->send(); } if (!$backup->is_successful && is_null($backup->completed_at)) { //TODO Change to Notifications return Notification::make() ->danger() ->title('Backup Restore Failed') ->body('This backup cannot be restored at this time: not completed or failed.') ->send(); } $log = Activity::event('server:backup.restore') ->subject($backup) ->property(['name' => $backup->name, 'truncate' => $data['truncate']]); $log->transaction(function () use ($downloadLinkService, $daemonRepository, $backup, $server, $data) { // If the backup is for an S3 file we need to generate a unique Download link for // it that will allow daemon to actually access the file. if ($backup->disk === Backup::ADAPTER_AWS_S3) { $url = $downloadLinkService->handle($backup, auth()->user()); } // Update the status right away for the server so that we know not to allow certain // actions against it via the Panel API. $server->update(['status' => ServerState::RestoringBackup]); $daemonRepository->setServer($server)->restore($backup, $url ?? null, $data['truncate']); }); return Notification::make() ->title('Restoring Backup') ->send(); }), DeleteAction::make('delete') ->disabled(fn (Backup $backup): bool => $backup->is_locked) ->modalDescription(fn (Backup $backup) => 'Do you wish to delete, ' . $backup->name . '?') ->modalSubmitActionLabel('Delete Backup') ->action(fn (BackupController $backupController, Backup $backup, Request $request) => $backupController->delete($request, $server, $backup)), ]), ]); } protected function getHeaderActions(): array { /** @var Server $server */ $server = Filament::getTenant(); return [ Actions\CreateAction::make() ->authorize(fn () => auth()->user()->can(Permission::ACTION_BACKUP_CREATE, $server)) ->label(fn () => $server->backups()->count() >= $server->backup_limit ? 'Backup limit reached' : 'Create Backup') ->disabled(fn () => $server->backups()->count() >= $server->backup_limit) ->color(fn () => $server->backups()->count() >= $server->backup_limit ? 'danger' : 'primary') ->createAnother(false) ->action(function (InitiateBackupService $initiateBackupService, $data) use ($server) { $action = $initiateBackupService->setIgnoredFiles(explode(PHP_EOL, $data['ignored'] ?? '')); if (auth()->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) { $action->setIsLocked((bool) $data['is_locked']); } $backup = $action->handle($server, $data['name']); Activity::event('server:backup.start') ->subject($backup) ->property(['name' => $backup->name, 'locked' => (bool) $data['is_locked']]) ->log(); return Notification::make() ->title('Backup Created') ->body($backup->name . ' created.') ->success() ->send(); }), ]; } public function getBreadcrumbs(): array { return []; } }