From 29f3defc73da32fe557b857dd723ba34293a0dac Mon Sep 17 00:00:00 2001 From: MartinOscar <40749467+rmartinoscar@users.noreply.github.com> Date: Tue, 18 Mar 2025 23:07:21 +0100 Subject: [PATCH] Catch `DaemonFileRepository` & show Alert (#1129) * Catch `DaemonFileRepository` Co-authored-by: notCharles * Pint --------- Co-authored-by: notCharles --- .../FileResource/Pages/ListFiles.php | 97 ++++++++++++------- app/Models/File.php | 15 ++- 2 files changed, 71 insertions(+), 41 deletions(-) diff --git a/app/Filament/Server/Resources/FileResource/Pages/ListFiles.php b/app/Filament/Server/Resources/FileResource/Pages/ListFiles.php index a2183bec3..49ce60224 100644 --- a/app/Filament/Server/Resources/FileResource/Pages/ListFiles.php +++ b/app/Filament/Server/Resources/FileResource/Pages/ListFiles.php @@ -12,6 +12,7 @@ use App\Models\Server; use App\Repositories\Daemon\DaemonFileRepository; use App\Filament\Components\Tables\Columns\BytesColumn; use App\Filament\Components\Tables\Columns\DateTimeColumn; +use App\Livewire\AlertBanner; use Filament\Actions\Action as HeaderAction; use Filament\Facades\Filament; use Filament\Forms\Components\CheckboxList; @@ -36,6 +37,7 @@ use Filament\Tables\Actions\EditAction; use Filament\Tables\Columns\TextColumn; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Collection; +use Illuminate\Http\Client\ConnectionException; use Illuminate\Http\UploadedFile; use Illuminate\Routing\Route; use Illuminate\Support\Facades\Route as RouteFacade; @@ -50,10 +52,19 @@ class ListFiles extends ListRecords private DaemonFileRepository $fileRepository; + private bool $isDisabled = false; + public function mount(?string $path = null): void { parent::mount(); $this->path = $path ?? '/'; + + try { + $this->getDaemonFileRepository()->getDirectory('/'); + } catch (ConnectionException) { + $this->isDisabled = true; + $this->getFailureNotification(); + } } public function getBreadcrumbs(): array @@ -113,18 +124,21 @@ class ListFiles extends ListRecords ->actions([ Action::make('view') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_READ, $server)) + ->disabled($this->isDisabled) ->label('Open') ->icon('tabler-eye') ->visible(fn (File $file) => $file->is_directory) ->url(fn (File $file) => self::getUrl(['path' => join_paths($this->path, $file->name)])), EditAction::make('edit') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_READ_CONTENT, $server)) + ->disabled($this->isDisabled) ->icon('tabler-edit') ->visible(fn (File $file) => $file->canEdit()) ->url(fn (File $file) => EditFiles::getUrl(['path' => join_paths($this->path, $file->name)])), ActionGroup::make([ Action::make('rename') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server)) + ->disabled($this->isDisabled) ->label('Rename') ->icon('tabler-forms') ->form([ @@ -136,8 +150,7 @@ class ListFiles extends ListRecords ->action(function ($data, File $file) { $files = [['to' => $data['name'], 'from' => $file->name]]; - $this->getDaemonFileRepository() - ->renameFiles($this->path, $files); + $this->getDaemonFileRepository()->renameFiles($this->path, $files); Activity::event('server:file.rename') ->property('directory', $this->path) @@ -154,12 +167,12 @@ class ListFiles extends ListRecords }), Action::make('copy') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server)) + ->disabled($this->isDisabled) ->label('Copy') ->icon('tabler-copy') ->visible(fn (File $file) => $file->is_file) ->action(function (File $file) { - $this->getDaemonFileRepository() - ->copyFile(join_paths($this->path, $file->name)); + $this->getDaemonFileRepository()->copyFile(join_paths($this->path, $file->name)); Activity::event('server:file.copy') ->property('file', join_paths($this->path, $file->name)) @@ -174,12 +187,14 @@ class ListFiles extends ListRecords }), Action::make('download') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_READ_CONTENT, $server)) + ->disabled($this->isDisabled) ->label('Download') ->icon('tabler-download') ->visible(fn (File $file) => $file->is_file) ->url(fn (File $file) => DownloadFiles::getUrl(['path' => join_paths($this->path, $file->name)]), true), Action::make('move') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server)) + ->disabled($this->isDisabled) ->label('Move') ->icon('tabler-replace') ->form([ @@ -195,8 +210,7 @@ class ListFiles extends ListRecords $location = rtrim($data['location'], '/'); $files = [['to' => join_paths($location, $file->name), 'from' => $file->name]]; - $this->getDaemonFileRepository() - ->renameFiles($this->path, $files); + $this->getDaemonFileRepository()->renameFiles($this->path, $files); $oldLocation = join_paths($this->path, $file->name); $newLocation = resolve_path(join_paths($this->path, $location, $file->name)); @@ -216,6 +230,7 @@ class ListFiles extends ListRecords }), Action::make('permissions') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server)) + ->disabled($this->isDisabled) ->label('Permissions') ->icon('tabler-license') ->form([ @@ -263,8 +278,7 @@ class ListFiles extends ListRecords $mode = $owner . $group . $public; - $this->getDaemonFileRepository() - ->chmodFiles($this->path, [['file' => $file->name, 'mode' => $mode]]); + $this->getDaemonFileRepository()->chmodFiles($this->path, [['file' => $file->name, 'mode' => $mode]]); Notification::make() ->title('Permissions changed to ' . $mode) @@ -273,11 +287,11 @@ class ListFiles extends ListRecords }), Action::make('archive') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_ARCHIVE, $server)) + ->disabled($this->isDisabled) ->label('Archive') ->icon('tabler-archive') ->action(function (File $file) { - $this->getDaemonFileRepository() - ->compressFiles($this->path, [$file->name]); + $this->getDaemonFileRepository()->compressFiles($this->path, [$file->name]); Activity::event('server:file.compress') ->property('directory', $this->path) @@ -293,12 +307,12 @@ class ListFiles extends ListRecords }), Action::make('unarchive') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_ARCHIVE, $server)) + ->disabled($this->isDisabled) ->label('Unarchive') ->icon('tabler-archive') ->visible(fn (File $file) => $file->isArchive()) ->action(function (File $file) { - $this->getDaemonFileRepository() - ->decompressFile($this->path, $file->name); + $this->getDaemonFileRepository()->decompressFile($this->path, $file->name); Activity::event('server:file.decompress') ->property('directory', $this->path) @@ -315,14 +329,14 @@ class ListFiles extends ListRecords ]), DeleteAction::make() ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_DELETE, $server)) + ->disabled($this->isDisabled) ->label('') ->icon('tabler-trash') ->requiresConfirmation() ->modalDescription(fn (File $file) => $file->name) ->modalHeading('Delete file?') ->action(function (File $file) { - $this->getDaemonFileRepository() - ->deleteFiles($this->path, [$file->name]); + $this->getDaemonFileRepository()->deleteFiles($this->path, [$file->name]); Activity::event('server:file.delete') ->property('directory', $this->path) @@ -334,6 +348,7 @@ class ListFiles extends ListRecords BulkActionGroup::make([ BulkAction::make('move') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_UPDATE, $server)) + ->disabled($this->isDisabled) ->form([ TextInput::make('location') ->label('Directory') @@ -362,11 +377,11 @@ class ListFiles extends ListRecords }), BulkAction::make('archive') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_ARCHIVE, $server)) + ->disabled($this->isDisabled) ->action(function (Collection $files) { $files = $files->map(fn ($file) => $file['name'])->toArray(); - $this->getDaemonFileRepository() - ->compressFiles($this->path, $files); + $this->getDaemonFileRepository()->compressFiles($this->path, $files); Activity::event('server:file.compress') ->property('directory', $this->path) @@ -382,10 +397,10 @@ class ListFiles extends ListRecords }), DeleteBulkAction::make() ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_DELETE, $server)) + ->disabled($this->isDisabled) ->action(function (Collection $files) { $files = $files->map(fn ($file) => $file['name'])->toArray(); - $this->getDaemonFileRepository() - ->deleteFiles($this->path, $files); + $this->getDaemonFileRepository()->deleteFiles($this->path, $files); Activity::event('server:file.delete') ->property('directory', $this->path) @@ -409,13 +424,13 @@ class ListFiles extends ListRecords return [ HeaderAction::make('new_file') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server)) + ->disabled($this->isDisabled) ->label('New File') ->color('gray') ->keyBindings('') ->modalSubmitActionLabel('Create') ->action(function ($data) { - $this->getDaemonFileRepository() - ->putContent(join_paths($this->path, $data['name']), $data['editor'] ?? ''); + $this->getDaemonFileRepository()->putContent(join_paths($this->path, $data['name']), $data['editor'] ?? ''); Activity::event('server:file.write') ->property('file', join_paths($this->path, $data['name'])) @@ -439,11 +454,11 @@ class ListFiles extends ListRecords ]), HeaderAction::make('new_folder') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server)) + ->disabled($this->isDisabled) ->label('New Folder') ->color('gray') ->action(function ($data) { - $this->getDaemonFileRepository() - ->createDirectory($data['name'], $this->path); + $this->getDaemonFileRepository()->createDirectory($data['name'], $this->path); Activity::event('server:file.create-directory') ->property(['directory' => $this->path, 'name' => $data['name']]) @@ -456,13 +471,13 @@ class ListFiles extends ListRecords ]), HeaderAction::make('upload') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_CREATE, $server)) + ->disabled($this->isDisabled) ->label('Upload') ->action(function ($data) { if (count($data['files']) > 0 && !isset($data['url'])) { /** @var UploadedFile $file */ foreach ($data['files'] as $file) { - $this->getDaemonFileRepository() - ->putContent(join_paths($this->path, $file->getClientOriginalName()), $file->getContent()); + $this->getDaemonFileRepository()->putContent(join_paths($this->path, $file->getClientOriginalName()), $file->getContent()); Activity::event('server:file.uploaded') ->property('directory', $this->path) @@ -470,8 +485,7 @@ class ListFiles extends ListRecords ->log(); } } elseif ($data['url'] !== null) { - $this->getDaemonFileRepository() - ->pull($data['url'], $this->path); + $this->getDaemonFileRepository()->pull($data['url'], $this->path); Activity::event('server:file.pull') ->property('url', $data['url']) @@ -507,6 +521,7 @@ class ListFiles extends ListRecords ]), HeaderAction::make('search') ->authorize(fn () => auth()->user()->can(Permission::ACTION_FILE_READ, $server)) + ->disabled($this->isDisabled) ->label('Global Search') ->modalSubmitActionLabel('Search') ->form([ @@ -522,17 +537,6 @@ class ListFiles extends ListRecords ]; } - public static function route(string $path): PageRegistration - { - return new PageRegistration( - page: static::class, - route: fn (Panel $panel): Route => RouteFacade::get($path, static::class) - ->middleware(static::getRouteMiddleware($panel)) - ->withoutMiddleware(static::getWithoutRouteMiddleware($panel)) - ->where('path', '.*'), - ); - } - /** * @return string[] */ @@ -558,4 +562,23 @@ class ListFiles extends ListRecords return $this->fileRepository; } + + public function getFailureNotification(): AlertBanner + { + return AlertBanner::make() + ->title('Could not connect to the node!') + ->danger() + ->send(); + } + + public static function route(string $path): PageRegistration + { + return new PageRegistration( + page: static::class, + route: fn (Panel $panel): Route => RouteFacade::get($path, static::class) + ->middleware(static::getRouteMiddleware($panel)) + ->withoutMiddleware(static::getWithoutRouteMiddleware($panel)) + ->where('path', '.*'), + ); + } } diff --git a/app/Models/File.php b/app/Models/File.php index ddd5a1a2b..80077f63e 100644 --- a/app/Models/File.php +++ b/app/Models/File.php @@ -8,6 +8,7 @@ use Carbon\Carbon; use Exception; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; +use Illuminate\Http\Client\ConnectionException; use Sushi\Sushi; /** @@ -150,10 +151,16 @@ class File extends Model try { $fileRepository = (new DaemonFileRepository())->setServer(self::$server); - if (!is_null(self::$searchTerm)) { - $contents = cache()->remember('file_search_' . self::$path . '_' . self::$searchTerm, now()->addMinute(), fn () => $fileRepository->search(self::$searchTerm, self::$path)); - } else { - $contents = $fileRepository->getDirectory(self::$path ?? '/'); + $contents = []; + + try { + if (!is_null(self::$searchTerm)) { + $contents = cache()->remember('file_search_' . self::$path . '_' . self::$searchTerm, now()->addMinute(), fn () => $fileRepository->search(self::$searchTerm, self::$path)); + } else { + $contents = $fileRepository->getDirectory(self::$path ?? '/'); + } + } catch (ConnectionException $exception) { + report($exception); } if (isset($contents['error'])) {