From 8406f4686c18c807955f7eb5d12148a5ae2123c3 Mon Sep 17 00:00:00 2001 From: Boy132 Date: Fri, 9 May 2025 08:44:18 +0200 Subject: [PATCH] Enable ipv6 on frontend (#1350) --- .../AllocationsRelationManager.php | 27 ++++++------------- .../ServerResource/Pages/CreateServer.php | 12 +++------ .../AllocationsRelationManager.php | 25 ++++++++++------- app/Models/Allocation.php | 2 +- app/Models/Node.php | 5 ++-- .../Allocations/AssignmentService.php | 7 +---- app/helpers.php | 14 ++++++++++ 7 files changed, 45 insertions(+), 47 deletions(-) diff --git a/app/Filament/Admin/Resources/NodeResource/RelationManagers/AllocationsRelationManager.php b/app/Filament/Admin/Resources/NodeResource/RelationManagers/AllocationsRelationManager.php index 73840530e..4c11002d0 100644 --- a/app/Filament/Admin/Resources/NodeResource/RelationManagers/AllocationsRelationManager.php +++ b/app/Filament/Admin/Resources/NodeResource/RelationManagers/AllocationsRelationManager.php @@ -12,8 +12,7 @@ use Filament\Forms\Components\TextInput; use Filament\Forms\Get; use Filament\Forms\Set; use Filament\Resources\RelationManagers\RelationManager; -use Filament\Tables; -use Filament\Tables\Actions\BulkActionGroup; +use Filament\Tables\Actions\Action; use Filament\Tables\Actions\DeleteBulkAction; use Filament\Tables\Columns\SelectColumn; use Filament\Tables\Columns\TextColumn; @@ -32,18 +31,12 @@ class AllocationsRelationManager extends RelationManager public function setTitle(): string { return trans('admin/server.allocations'); - } public function table(Table $table): Table { return $table - ->recordTitleAttribute('ip') - - // Non Primary Allocations - // ->checkIfRecordIsSelectableUsing(fn (Allocation $allocation) => $allocation->id !== $allocation->server?->allocation_id) - - // All assigned allocations + ->recordTitleAttribute('address') ->checkIfRecordIsSelectableUsing(fn (Allocation $allocation) => $allocation->server_id === null) ->paginationPageOptions(['10', '20', '50', '100', '200', '500']) ->searchable() @@ -72,14 +65,14 @@ class AllocationsRelationManager extends RelationManager ->label(trans('admin/node.table.ip')), ]) ->headerActions([ - Tables\Actions\Action::make('create new allocation') + Action::make('create new allocation') ->label(trans('admin/node.create_allocation')) ->form(fn () => [ Select::make('allocation_ip') ->options(collect($this->getOwnerRecord()->ipAddresses())->mapWithKeys(fn (string $ip) => [$ip => $ip])) ->label(trans('admin/node.ip_address')) ->inlineLabel() - ->ipv4() + ->ip() ->helperText(trans('admin/node.ip_help')) ->afterStateUpdated(fn (Set $set) => $set('allocation_ports', [])) ->live() @@ -96,19 +89,15 @@ class AllocationsRelationManager extends RelationManager ->inlineLabel() ->live() ->disabled(fn (Get $get) => empty($get('allocation_ip'))) - ->afterStateUpdated(fn ($state, Set $set, Get $get) => $set('allocation_ports', - CreateServer::retrieveValidPorts($this->getOwnerRecord(), $state, $get('allocation_ip'))) - ) + ->afterStateUpdated(fn ($state, Set $set, Get $get) => $set('allocation_ports', CreateServer::retrieveValidPorts($this->getOwnerRecord(), $state, $get('allocation_ip')))) ->splitKeys(['Tab', ' ', ',']) ->required(), ]) ->action(fn (array $data, AssignmentService $service) => $service->handle($this->getOwnerRecord(), $data)), ]) - ->bulkActions([ - BulkActionGroup::make([ - DeleteBulkAction::make() - ->authorize(fn () => auth()->user()->can('update node')), - ]), + ->groupedBulkActions([ + DeleteBulkAction::make() + ->authorize(fn () => auth()->user()->can('update node')), ]); } } diff --git a/app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php b/app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php index 1fa997377..9dc23262f 100644 --- a/app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php +++ b/app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php @@ -189,10 +189,7 @@ class CreateServer extends CreateRecord $set('allocation_additional', null); $set('allocation_additional.needstobeastringhere.extra_allocations', null); }) - ->getOptionLabelFromRecordUsing( - fn (Allocation $allocation) => "$allocation->ip:$allocation->port" . - ($allocation->ip_alias ? " ($allocation->ip_alias)" : '') - ) + ->getOptionLabelFromRecordUsing(fn (Allocation $allocation) => $allocation->address) ->placeholder(function (Get $get) { $node = Node::find($get('node_id')); @@ -218,7 +215,7 @@ class CreateServer extends CreateRecord ->label(trans('admin/server.ip_address'))->inlineLabel() ->helperText(trans('admin/server.ip_address_helper')) ->afterStateUpdated(fn (Set $set) => $set('allocation_ports', [])) - ->ipv4() + ->ip() ->live() ->required(), TextInput::make('allocation_alias') @@ -269,10 +266,7 @@ class CreateServer extends CreateRecord ->columnSpan(2) ->disabled(fn (Get $get) => $get('../../node_id') === null) ->searchable(['ip', 'port', 'ip_alias']) - ->getOptionLabelFromRecordUsing( - fn (Allocation $allocation) => "$allocation->ip:$allocation->port" . - ($allocation->ip_alias ? " ($allocation->ip_alias)" : '') - ) + ->getOptionLabelFromRecordUsing(fn (Allocation $allocation) => $allocation->address) ->placeholder(trans('admin/server.select_additional')) ->disableOptionsWhenSelectedInSiblingRepeaterItems() ->relationship( diff --git a/app/Filament/Admin/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php b/app/Filament/Admin/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php index 0ad4997a3..14ca71d1d 100644 --- a/app/Filament/Admin/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php +++ b/app/Filament/Admin/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php @@ -16,6 +16,7 @@ use Filament\Support\Exceptions\Halt; use Filament\Tables\Actions\Action; use Filament\Tables\Actions\AssociateAction; use Filament\Tables\Actions\CreateAction; +use Filament\Tables\Actions\DissociateAction; use Filament\Tables\Actions\DissociateBulkAction; use Filament\Tables\Columns\IconColumn; use Filament\Tables\Columns\TextColumn; @@ -34,15 +35,18 @@ class AllocationsRelationManager extends RelationManager { return $table ->selectCurrentPageOnly() - ->recordTitleAttribute('ip') - ->recordTitle(fn (Allocation $allocation) => "$allocation->ip:$allocation->port") + ->recordTitleAttribute('address') + ->recordTitle(fn (Allocation $allocation) => $allocation->address) ->checkIfRecordIsSelectableUsing(fn (Allocation $record) => $record->id !== $this->getOwnerRecord()->allocation_id) ->inverseRelationship('server') ->heading(trans('admin/server.allocations')) ->columns([ - TextColumn::make('ip')->label(trans('admin/server.ip_address')), - TextColumn::make('port')->label(trans('admin/server.port')), - TextInputColumn::make('ip_alias')->label(trans('admin/server.alias')), + TextColumn::make('ip') + ->label(trans('admin/server.ip_address')), + TextColumn::make('port') + ->label(trans('admin/server.port')), + TextInputColumn::make('ip_alias') + ->label(trans('admin/server.alias')), IconColumn::make('primary') ->icon(fn ($state) => match ($state) { true => 'tabler-star-filled', @@ -58,8 +62,11 @@ class AllocationsRelationManager extends RelationManager ]) ->actions([ Action::make('make-primary') + ->label(trans('admin/server.make_primary')) ->action(fn (Allocation $allocation) => $this->getOwnerRecord()->update(['allocation_id' => $allocation->id]) && $this->deselectAllTableRecords()) - ->label(fn (Allocation $allocation) => $allocation->id === $this->getOwnerRecord()->allocation_id ? '' : trans('admin/server.make_primary')), + ->hidden(fn (Allocation $allocation) => $allocation->id === $this->getOwnerRecord()->allocation_id), + DissociateAction::make() + ->hidden(fn (Allocation $allocation) => $allocation->id === $this->getOwnerRecord()->allocation_id), ]) ->headerActions([ CreateAction::make()->label(trans('admin/server.create_allocation')) @@ -69,7 +76,7 @@ class AllocationsRelationManager extends RelationManager ->options(collect($this->getOwnerRecord()->node->ipAddresses())->mapWithKeys(fn (string $ip) => [$ip => $ip])) ->label(trans('admin/server.ip_address')) ->inlineLabel() - ->ipv4() + ->ip() ->live() ->afterStateUpdated(fn (Set $set) => $set('allocation_ports', [])) ->required(), @@ -85,9 +92,7 @@ class AllocationsRelationManager extends RelationManager ->inlineLabel() ->live() ->disabled(fn (Get $get) => empty($get('allocation_ip'))) - ->afterStateUpdated(fn ($state, Set $set, Get $get) => $set('allocation_ports', - CreateServer::retrieveValidPorts($this->getOwnerRecord()->node, $state, $get('allocation_ip'))) - ) + ->afterStateUpdated(fn ($state, Set $set, Get $get) => $set('allocation_ports', CreateServer::retrieveValidPorts($this->getOwnerRecord()->node, $state, $get('allocation_ip')))) ->splitKeys(['Tab', ' ', ',']) ->required(), ]) diff --git a/app/Models/Allocation.php b/app/Models/Allocation.php index a63f57a50..39ae0ad60 100644 --- a/app/Models/Allocation.php +++ b/app/Models/Allocation.php @@ -117,7 +117,7 @@ class Allocation extends Model protected function address(): Attribute { return Attribute::make( - get: fn () => "$this->alias:$this->port", + get: fn () => (is_ipv6($this->alias) ? "[$this->alias]" : $this->alias) . ":$this->port", ); } diff --git a/app/Models/Node.php b/app/Models/Node.php index 98ff118c8..c905bd4e1 100644 --- a/app/Models/Node.php +++ b/app/Models/Node.php @@ -403,10 +403,11 @@ class Node extends Model implements Validatable } } - // Only IPV4 - $ips = $ips->filter(fn (string $ip) => filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false); + $ips = $ips->filter(fn (string $ip) => is_ip($ip)); + // TODO: remove later $ips->push('0.0.0.0'); + $ips->push('::'); return $ips->unique()->all(); }); diff --git a/app/Services/Allocations/AssignmentService.php b/app/Services/Allocations/AssignmentService.php index 7f52713e0..39c67c37e 100644 --- a/app/Services/Allocations/AssignmentService.php +++ b/app/Services/Allocations/AssignmentService.php @@ -51,12 +51,7 @@ class AssignmentService } try { - // TODO: how should we approach supporting IPv6 with this? - // gethostbyname only supports IPv4, but the alternative (dns_get_record) returns - // an array of records, which is not ideal for this use case, we need a SINGLE - // IP to use, not multiple. - $underlying = gethostbyname($data['allocation_ip']); - $parsed = Network::parse($underlying); + $parsed = Network::parse($data['allocation_ip']); } catch (\Exception $exception) { throw new DisplayException("Could not parse provided allocation IP address ({$data['allocation_ip']}): {$exception->getMessage()}", $exception); } diff --git a/app/helpers.php b/app/helpers.php index 0fe63c7a5..6098eff07 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -18,6 +18,20 @@ if (!function_exists('is_ip')) { } } +if (!function_exists('is_ipv4')) { + function is_ipv4(?string $address): bool + { + return $address !== null && filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false; + } +} + +if (!function_exists('is_ipv6')) { + function is_ipv6(?string $address): bool + { + return $address !== null && filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false; + } +} + if (!function_exists('convert_bytes_to_readable')) { function convert_bytes_to_readable(int $bytes, int $decimals = 2, ?int $base = null): string {