diff --git a/app/Filament/Admin/Resources/NodeResource/RelationManagers/AllocationsRelationManager.php b/app/Filament/Admin/Resources/NodeResource/RelationManagers/AllocationsRelationManager.php
index ba7ee06b01..7273ddbe0a 100644
--- a/app/Filament/Admin/Resources/NodeResource/RelationManagers/AllocationsRelationManager.php
+++ b/app/Filament/Admin/Resources/NodeResource/RelationManagers/AllocationsRelationManager.php
@@ -2,6 +2,7 @@
namespace App\Filament\Admin\Resources\NodeResource\RelationManagers;
+use App\Filament\Admin\Resources\ServerResource\Pages\CreateServer;
use App\Models\Allocation;
use App\Models\Node;
use App\Services\Allocations\AssignmentService;
@@ -9,6 +10,7 @@
use Filament\Forms\Components\TagsInput;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Form;
+use Filament\Forms\Get;
use Filament\Forms\Set;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
@@ -80,6 +82,8 @@ public function table(Table $table): Table
->inlineLabel()
->ipv4()
->helperText("Usually your machine's public IP unless you are port forwarding.")
+ ->afterStateUpdated(fn (Set $set) => $set('allocation_ports', []))
+ ->live()
->required(),
TextInput::make('allocation_alias')
->label('Alias')
@@ -97,54 +101,10 @@ public function table(Table $table): Table
->label('Ports')
->inlineLabel()
->live()
- ->afterStateUpdated(function ($state, Set $set) {
- $ports = collect();
- $update = false;
- foreach ($state as $portEntry) {
- if (!str_contains($portEntry, '-')) {
- if (is_numeric($portEntry)) {
- $ports->push((int) $portEntry);
-
- continue;
- }
-
- // Do not add non numerical ports
- $update = true;
-
- continue;
- }
-
- $update = true;
- [$start, $end] = explode('-', $portEntry);
- if (!is_numeric($start) || !is_numeric($end)) {
- continue;
- }
-
- $start = max((int) $start, 0);
- $end = min((int) $end, 2 ** 16 - 1);
- foreach (range($start, $end) as $i) {
- $ports->push($i);
- }
- }
-
- $uniquePorts = $ports->unique()->values();
- if ($ports->count() > $uniquePorts->count()) {
- $update = true;
- $ports = $uniquePorts;
- }
-
- $sortedPorts = $ports->sort()->values();
- if ($sortedPorts->all() !== $ports->all()) {
- $update = true;
- $ports = $sortedPorts;
- }
-
- $ports = $ports->filter(fn ($port) => $port > 1024 && $port < 65535)->values();
-
- if ($update) {
- $set('allocation_ports', $ports->all());
- }
- })
+ ->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')))
+ )
->splitKeys(['Tab', ' ', ','])
->required(),
])
diff --git a/app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php b/app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php
index 745e45aa76..caca65cce0 100644
--- a/app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php
+++ b/app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php
@@ -198,86 +198,47 @@ public function form(Form $form): Form
->where('node_id', $get('node_id'))
->whereNull('server_id'),
)
- ->createOptionForm(fn (Get $get) => [
- Select::make('allocation_ip')
- ->options(collect(Node::find($get('node_id'))?->ipAddresses())->mapWithKeys(fn (string $ip) => [$ip => $ip]))
- ->label('IP Address')
- ->inlineLabel()
- ->ipv4()
- ->helperText("Usually your machine's public IP unless you are port forwarding.")
- ->required(),
- TextInput::make('allocation_alias')
- ->label('Alias')
- ->inlineLabel()
- ->default(null)
- ->datalist([
- $get('name'),
- Egg::find($get('egg_id'))?->name,
- ])
- ->helperText('Optional display name to help you remember what these are.')
- ->required(false),
- TagsInput::make('allocation_ports')
- ->placeholder('Examples: 27015, 27017-27019')
- ->helperText(new HtmlString('
- These are the ports that users can connect to this Server through.
-
- You would have to port forward these on your home network.
- '))
- ->label('Ports')
- ->inlineLabel()
- ->live()
- ->afterStateUpdated(function ($state, Set $set) {
- $ports = collect();
- $update = false;
- foreach ($state as $portEntry) {
- if (!str_contains($portEntry, '-')) {
- if (is_numeric($portEntry)) {
- $ports->push((int) $portEntry);
-
- continue;
- }
-
- // Do not add non-numerical ports
- $update = true;
-
- continue;
- }
-
- $update = true;
- [$start, $end] = explode('-', $portEntry);
- if (!is_numeric($start) || !is_numeric($end)) {
- continue;
- }
-
- $start = max((int) $start, 0);
- $end = min((int) $end, 2 ** 16 - 1);
- $range = $start <= $end ? range($start, $end) : range($end, $start);
- foreach ($range as $i) {
- if ($i > 1024 && $i <= 65535) {
- $ports->push($i);
- }
- }
- }
-
- $uniquePorts = $ports->unique()->values();
- if ($ports->count() > $uniquePorts->count()) {
- $update = true;
- $ports = $uniquePorts;
- }
-
- $sortedPorts = $ports->sort()->values();
- if ($sortedPorts->all() !== $ports->all()) {
- $update = true;
- $ports = $sortedPorts;
- }
-
- if ($update) {
- $set('allocation_ports', $ports->all());
- }
- })
- ->splitKeys(['Tab', ' ', ','])
- ->required(),
- ])
+ ->createOptionForm(function (Get $get) {
+ $getPage = $get;
+
+ return [
+ Select::make('allocation_ip')
+ ->options(collect(Node::find($get('node_id'))?->ipAddresses())->mapWithKeys(fn (string $ip) => [$ip => $ip]))
+ ->label('IP Address')
+ ->helperText("Usually your machine's public IP unless you are port forwarding.")
+ ->afterStateUpdated(fn (Set $set) => $set('allocation_ports', []))
+ ->inlineLabel()
+ ->ipv4()
+ ->live()
+ ->required(),
+ TextInput::make('allocation_alias')
+ ->label('Alias')
+ ->inlineLabel()
+ ->default(null)
+ ->datalist([
+ $get('name'),
+ Egg::find($get('egg_id'))?->name,
+ ])
+ ->helperText('Optional display name to help you remember what these are.')
+ ->required(false),
+ TagsInput::make('allocation_ports')
+ ->placeholder('Examples: 27015, 27017-27019')
+ ->helperText(new HtmlString('
+ These are the ports that users can connect to this Server through.
+
+ You would have to port forward these on your home network.
+ '))
+ ->label('Ports')
+ ->inlineLabel()
+ ->live()
+ ->disabled(fn (Get $get) => empty($get('allocation_ip')))
+ ->afterStateUpdated(fn ($state, Set $set, Get $get) => $set('allocation_ports',
+ CreateServer::retrieveValidPorts(Node::find($getPage('node_id')), $state, $get('allocation_ip')))
+ )
+ ->splitKeys(['Tab', ' ', ','])
+ ->required(),
+ ];
+ })
->createOptionUsing(function (array $data, Get $get, AssignmentService $assignmentService): int {
return collect(
$assignmentService->handle(Node::find($get('node_id')), $data)
@@ -922,4 +883,88 @@ private function getSelectOptionsFromRules(Get $get): array
->mapWithKeys(fn ($value) => [$value => $value])
->all();
}
+
+ public static function retrieveValidPorts(Node $node, array $portEntries, string $ip): array
+ {
+ $portRangeLimit = AssignmentService::PORT_RANGE_LIMIT;
+ $portFloor = AssignmentService::PORT_FLOOR;
+ $portCeil = AssignmentService::PORT_CEIL;
+
+ $ports = collect();
+
+ $existingPorts = $node
+ ->allocations()
+ ->where('ip', $ip)
+ ->pluck('port')
+ ->all();
+
+ foreach ($portEntries as $portEntry) {
+ $start = $end = $portEntry;
+ if (str_contains($portEntry, '-')) {
+ [$start, $end] = explode('-', $portEntry);
+ }
+
+ if (!is_numeric($start) || !is_numeric($end)) {
+ Notification::make()
+ ->title('Invalid Port Range')
+ ->danger()
+ ->body("Your port range are not valid integers: $portEntry")
+ ->send();
+
+ continue;
+ }
+
+ $start = (int) $start;
+ $end = (int) $end;
+ $range = $start <= $end ? range($start, $end) : range($end, $start);
+
+ if (count($range) > $portRangeLimit) {
+ Notification::make()
+ ->title('Too many ports at one time!')
+ ->danger()
+ ->body("The current limit is $portRangeLimit number of ports at one time.")
+ ->send();
+
+ continue;
+ }
+
+ foreach ($range as $i) {
+ // Invalid port number
+ if ($i <= $portFloor || $i > $portCeil) {
+ Notification::make()
+ ->title('Port not in valid range')
+ ->danger()
+ ->body("$i is not in the valid port range between $portFloor-$portCeil")
+ ->send();
+
+ continue;
+ }
+
+ // Already exists
+ if (in_array($i, $existingPorts)) {
+ Notification::make()
+ ->title('Port already in use')
+ ->danger()
+ ->body("$i is already with an allocation")
+ ->send();
+
+ continue;
+ }
+
+ $ports->push($i);
+ }
+ }
+
+ $uniquePorts = $ports->unique()->values();
+ if ($ports->count() > $uniquePorts->count()) {
+ $ports = $uniquePorts;
+ }
+
+ $sortedPorts = $ports->sort()->values();
+ if ($sortedPorts->all() !== $ports->all()) {
+ $ports = $sortedPorts;
+ }
+
+ return $ports->all();
+ }
}
diff --git a/app/Filament/Admin/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php b/app/Filament/Admin/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php
index f0fca624a5..43a2ed2ab0 100644
--- a/app/Filament/Admin/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php
+++ b/app/Filament/Admin/Resources/ServerResource/RelationManagers/AllocationsRelationManager.php
@@ -2,6 +2,7 @@
namespace App\Filament\Admin\Resources\ServerResource\RelationManagers;
+use App\Filament\Admin\Resources\ServerResource\Pages\CreateServer;
use App\Models\Allocation;
use App\Models\Server;
use App\Services\Allocations\AssignmentService;
@@ -9,6 +10,7 @@
use Filament\Forms\Components\TagsInput;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Form;
+use Filament\Forms\Get;
use Filament\Forms\Set;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
@@ -78,6 +80,7 @@ public function table(Table $table): Table
->inlineLabel()
->ipv4()
->helperText("Usually your machine's public IP unless you are port forwarding.")
+ ->afterStateUpdated(fn (Set $set) => $set('allocation_ports', []))
->required(),
TextInput::make('allocation_alias')
->label('Alias')
@@ -95,54 +98,9 @@ public function table(Table $table): Table
->label('Ports')
->inlineLabel()
->live()
- ->afterStateUpdated(function ($state, Set $set) {
- $ports = collect();
- $update = false;
- foreach ($state as $portEntry) {
- if (!str_contains($portEntry, '-')) {
- if (is_numeric($portEntry)) {
- $ports->push((int) $portEntry);
-
- continue;
- }
-
- // Do not add non numerical ports
- $update = true;
-
- continue;
- }
-
- $update = true;
- [$start, $end] = explode('-', $portEntry);
- if (!is_numeric($start) || !is_numeric($end)) {
- continue;
- }
-
- $start = max((int) $start, 0);
- $end = min((int) $end, 2 ** 16 - 1);
- foreach (range($start, $end) as $i) {
- $ports->push($i);
- }
- }
-
- $uniquePorts = $ports->unique()->values();
- if ($ports->count() > $uniquePorts->count()) {
- $update = true;
- $ports = $uniquePorts;
- }
-
- $sortedPorts = $ports->sort()->values();
- if ($sortedPorts->all() !== $ports->all()) {
- $update = true;
- $ports = $sortedPorts;
- }
-
- $ports = $ports->filter(fn ($port) => $port > 1024 && $port < 65535)->values();
-
- if ($update) {
- $set('allocation_ports', $ports->all());
- }
- })
+ ->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/Node.php b/app/Models/Node.php
index 011abb2527..72e2b26121 100644
--- a/app/Models/Node.php
+++ b/app/Models/Node.php
@@ -387,6 +387,8 @@ public function ipAddresses(): array
// pass
}
+ $ips->push('0.0.0.0');
+
// Only IPV4
$ips = $ips->filter(fn (string $ip) => filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false);