From 8c475ed95f7ef799aed2df9c831635e18d917bf1 Mon Sep 17 00:00:00 2001 From: Lance Pioch Date: Thu, 12 Feb 2026 17:06:38 -0500 Subject: [PATCH] =?UTF-8?q?Add=20=E2=80=9Creachable=E2=80=9D=20column=20fo?= =?UTF-8?q?r=20Client=20->=20Wings=20connections=20for=20Nodes=20(#2200)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Admin/Resources/Nodes/Pages/ListNodes.php | 2 + .../Tables/Columns/NodeClientHealthColumn.php | 43 +++++++++ app/Livewire/NodeClientConnectivity.php | 94 +++++++++++++++++++ app/Models/Node.php | 2 + lang/en/admin/node.php | 1 + .../node-client-connectivity.blade.php | 67 +++++++++++++ 6 files changed, 209 insertions(+) create mode 100644 app/Filament/Components/Tables/Columns/NodeClientHealthColumn.php create mode 100644 app/Livewire/NodeClientConnectivity.php create mode 100644 resources/views/livewire/node-client-connectivity.blade.php diff --git a/app/Filament/Admin/Resources/Nodes/Pages/ListNodes.php b/app/Filament/Admin/Resources/Nodes/Pages/ListNodes.php index a413bc5d4..afaf12a77 100644 --- a/app/Filament/Admin/Resources/Nodes/Pages/ListNodes.php +++ b/app/Filament/Admin/Resources/Nodes/Pages/ListNodes.php @@ -4,6 +4,7 @@ namespace App\Filament\Admin\Resources\Nodes\Pages; use App\Enums\TablerIcon; use App\Filament\Admin\Resources\Nodes\NodeResource; +use App\Filament\Components\Tables\Columns\NodeClientHealthColumn; use App\Filament\Components\Tables\Columns\NodeHealthColumn; use App\Filament\Components\Tables\Filters\TagsFilter; use App\Models\Node; @@ -34,6 +35,7 @@ class ListNodes extends ListRecords ->searchable() ->hidden(), NodeHealthColumn::make('health'), + NodeClientHealthColumn::make('reachable'), TextColumn::make('name') ->label(trans('admin/node.table.name')) ->sortable() diff --git a/app/Filament/Components/Tables/Columns/NodeClientHealthColumn.php b/app/Filament/Components/Tables/Columns/NodeClientHealthColumn.php new file mode 100644 index 000000000..7ed823e3b --- /dev/null +++ b/app/Filament/Components/Tables/Columns/NodeClientHealthColumn.php @@ -0,0 +1,43 @@ +label(trans('admin/node.table.reachable')); + + $this->alignCenter(); + } + + public function toEmbeddedHtml(): string + { + $alignment = $this->getAlignment(); + + $attributes = $this->getExtraAttributeBag() + ->class([ + 'fi-ta-icon', + 'fi-inline' => $this->isInline(), + 'fi-ta-icon-has-line-breaks' => $this->isListWithLineBreaks(), + 'fi-wrapped' => $this->canWrap(), + ($alignment instanceof Alignment) ? "fi-align-{$alignment->value}" : (is_string($alignment) ? $alignment : ''), + ]) + ->toHtml(); + + return Blade::render(<<<'BLADE' +
> + @livewire('node-client-connectivity', ['node' => $record, 'lazy' => true]) +
+ BLADE, [ + 'attributes' => $attributes, + 'record' => $this->getRecord(), + ]); + } +} diff --git a/app/Livewire/NodeClientConnectivity.php b/app/Livewire/NodeClientConnectivity.php new file mode 100644 index 000000000..79de8a8ec --- /dev/null +++ b/app/Livewire/NodeClientConnectivity.php @@ -0,0 +1,94 @@ +getUserPermissionsService = $getUserPermissionsService; + $this->nodeJWTService = $nodeJWTService; + } + + public function render(): \Illuminate\Contracts\View\View + { + $httpUrl = $this->node->getConnectionAddress(); + + $wsUrl = null; + $wsToken = null; + + $server = $this->node->servers()->first(); + + if ($server) { + $user = Auth::user(); + + $permissions = $this->getUserPermissionsService->handle($server, $user); + + $wsToken = $this->nodeJWTService + ->setExpiresAt(now()->addMinute()->toImmutable()) + ->setUser($user) + ->setClaims([ + 'server_uuid' => $server->uuid, + 'permissions' => $permissions, + ]) + ->handle($this->node, $user->id . $server->uuid)->toString(); + + $wsUrl = str_replace(['https://', 'http://'], ['wss://', 'ws://'], $this->node->getConnectionAddress()); + $wsUrl .= sprintf('/api/servers/%s/ws', $server->uuid); + } + + return view('livewire.node-client-connectivity', [ + 'httpUrl' => $httpUrl, + 'wsUrl' => $wsUrl, + 'wsToken' => $wsToken, + 'loadingIcon' => $this->makeIcon(TablerIcon::WorldQuestion, 'warning', 'Checking...'), + 'offlineIcon' => $this->makeIcon(TablerIcon::WorldX, 'danger', 'Node is not reachable from your browser'), + 'onlineIcon' => $this->makeIcon(TablerIcon::WorldCheck, 'success', 'Node is reachable'), + 'warningIcon' => $this->makeIcon(TablerIcon::WorldExclamation, 'warning', 'Node is reachable, but WebSocket failed. Check reverse proxy config.'), + 'onlineNoWsIcon' => $this->makeIcon(TablerIcon::WorldCheck, 'success', 'Node is reachable (WebSocket not tested — no servers)'), + ]); + } + + private function makeIcon(TablerIcon $icon, string $color, string $tooltip): string + { + return generate_icon_html($icon, attributes: (new ComponentAttributeBag()) + ->merge([ + 'x-tooltip' => '{ + content: "' . $tooltip . '", + theme: $store.theme, + allowHTML: true, + placement: "bottom", + }', + 'style' => 'color: var(--dark-text, var(--text))', + ], escape: false) + ->color(IconComponent::class, $color), size: IconSize::Large) + ->toHtml(); + } + + public function placeholder(): string + { + return generate_icon_html(TablerIcon::WorldQuestion, attributes: (new ComponentAttributeBag()) + ->color(IconComponent::class, 'warning'), size: IconSize::Large) + ->toHtml(); + } +} diff --git a/app/Models/Node.php b/app/Models/Node.php index 19963b7a0..24e7cd984 100644 --- a/app/Models/Node.php +++ b/app/Models/Node.php @@ -255,6 +255,8 @@ class Node extends Model implements Validatable /** * Gets the servers associated with a node. + * + * @return HasMany */ public function servers(): HasMany { diff --git a/lang/en/admin/node.php b/lang/en/admin/node.php index 62d1311cd..918171a04 100644 --- a/lang/en/admin/node.php +++ b/lang/en/admin/node.php @@ -14,6 +14,7 @@ return [ ], 'table' => [ 'health' => 'Health', + 'reachable' => 'Reachable', 'name' => 'Name', 'address' => 'Address', 'public' => 'Public', diff --git a/resources/views/livewire/node-client-connectivity.blade.php b/resources/views/livewire/node-client-connectivity.blade.php new file mode 100644 index 000000000..ae52dfd0a --- /dev/null +++ b/resources/views/livewire/node-client-connectivity.blade.php @@ -0,0 +1,67 @@ +
+
+ {!! $loadingIcon !!} +
+
+ {!! $offlineIcon !!} +
+
+ {!! $onlineIcon !!} +
+
+ {!! $warningIcon !!} +
+
+ {!! $onlineNoWsIcon !!} +
+