mirror of
https://github.com/pelican-dev/panel.git
synced 2025-05-20 05:14:46 +02:00
Fix 500 on duplicate ports (#861)
* Fix 500 on duplicate ports This should also address N+1 issues from the last PR * Combine into one method * Pint * Add missing type * Add 0.0.0.0 * Add notifications to help the user * Pint * Too verbose * Show notification here * Simplify code * Reset the ports if the ip changes * Don’t limit these anymore --------- Co-authored-by: Lance Pioch <git@lance.sh>
This commit is contained in:
parent
168d37b996
commit
7cc4358a04
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Filament\Admin\Resources\NodeResource\RelationManagers;
|
namespace App\Filament\Admin\Resources\NodeResource\RelationManagers;
|
||||||
|
|
||||||
|
use App\Filament\Admin\Resources\ServerResource\Pages\CreateServer;
|
||||||
use App\Models\Allocation;
|
use App\Models\Allocation;
|
||||||
use App\Models\Node;
|
use App\Models\Node;
|
||||||
use App\Services\Allocations\AssignmentService;
|
use App\Services\Allocations\AssignmentService;
|
||||||
@ -9,6 +10,7 @@ use Filament\Forms\Components\Select;
|
|||||||
use Filament\Forms\Components\TagsInput;
|
use Filament\Forms\Components\TagsInput;
|
||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
use Filament\Forms\Form;
|
use Filament\Forms\Form;
|
||||||
|
use Filament\Forms\Get;
|
||||||
use Filament\Forms\Set;
|
use Filament\Forms\Set;
|
||||||
use Filament\Resources\RelationManagers\RelationManager;
|
use Filament\Resources\RelationManagers\RelationManager;
|
||||||
use Filament\Tables;
|
use Filament\Tables;
|
||||||
@ -80,6 +82,8 @@ class AllocationsRelationManager extends RelationManager
|
|||||||
->inlineLabel()
|
->inlineLabel()
|
||||||
->ipv4()
|
->ipv4()
|
||||||
->helperText("Usually your machine's public IP unless you are port forwarding.")
|
->helperText("Usually your machine's public IP unless you are port forwarding.")
|
||||||
|
->afterStateUpdated(fn (Set $set) => $set('allocation_ports', []))
|
||||||
|
->live()
|
||||||
->required(),
|
->required(),
|
||||||
TextInput::make('allocation_alias')
|
TextInput::make('allocation_alias')
|
||||||
->label('Alias')
|
->label('Alias')
|
||||||
@ -97,54 +101,10 @@ class AllocationsRelationManager extends RelationManager
|
|||||||
->label('Ports')
|
->label('Ports')
|
||||||
->inlineLabel()
|
->inlineLabel()
|
||||||
->live()
|
->live()
|
||||||
->afterStateUpdated(function ($state, Set $set) {
|
->disabled(fn (Get $get) => empty($get('allocation_ip')))
|
||||||
$ports = collect();
|
->afterStateUpdated(fn ($state, Set $set, Get $get) => $set('allocation_ports',
|
||||||
$update = false;
|
CreateServer::retrieveValidPorts($this->getOwnerRecord(), $state, $get('allocation_ip')))
|
||||||
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());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
->splitKeys(['Tab', ' ', ','])
|
->splitKeys(['Tab', ' ', ','])
|
||||||
->required(),
|
->required(),
|
||||||
])
|
])
|
||||||
|
@ -198,86 +198,47 @@ class CreateServer extends CreateRecord
|
|||||||
->where('node_id', $get('node_id'))
|
->where('node_id', $get('node_id'))
|
||||||
->whereNull('server_id'),
|
->whereNull('server_id'),
|
||||||
)
|
)
|
||||||
->createOptionForm(fn (Get $get) => [
|
->createOptionForm(function (Get $get) {
|
||||||
Select::make('allocation_ip')
|
$getPage = $get;
|
||||||
->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.
|
|
||||||
<br />
|
|
||||||
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;
|
return [
|
||||||
}
|
Select::make('allocation_ip')
|
||||||
|
->options(collect(Node::find($get('node_id'))?->ipAddresses())->mapWithKeys(fn (string $ip) => [$ip => $ip]))
|
||||||
// Do not add non-numerical ports
|
->label('IP Address')
|
||||||
$update = true;
|
->helperText("Usually your machine's public IP unless you are port forwarding.")
|
||||||
|
->afterStateUpdated(fn (Set $set) => $set('allocation_ports', []))
|
||||||
continue;
|
->inlineLabel()
|
||||||
}
|
->ipv4()
|
||||||
|
->live()
|
||||||
$update = true;
|
->required(),
|
||||||
[$start, $end] = explode('-', $portEntry);
|
TextInput::make('allocation_alias')
|
||||||
if (!is_numeric($start) || !is_numeric($end)) {
|
->label('Alias')
|
||||||
continue;
|
->inlineLabel()
|
||||||
}
|
->default(null)
|
||||||
|
->datalist([
|
||||||
$start = max((int) $start, 0);
|
$get('name'),
|
||||||
$end = min((int) $end, 2 ** 16 - 1);
|
Egg::find($get('egg_id'))?->name,
|
||||||
$range = $start <= $end ? range($start, $end) : range($end, $start);
|
])
|
||||||
foreach ($range as $i) {
|
->helperText('Optional display name to help you remember what these are.')
|
||||||
if ($i > 1024 && $i <= 65535) {
|
->required(false),
|
||||||
$ports->push($i);
|
TagsInput::make('allocation_ports')
|
||||||
}
|
->placeholder('Examples: 27015, 27017-27019')
|
||||||
}
|
->helperText(new HtmlString('
|
||||||
}
|
These are the ports that users can connect to this Server through.
|
||||||
|
<br />
|
||||||
$uniquePorts = $ports->unique()->values();
|
You would have to port forward these on your home network.
|
||||||
if ($ports->count() > $uniquePorts->count()) {
|
'))
|
||||||
$update = true;
|
->label('Ports')
|
||||||
$ports = $uniquePorts;
|
->inlineLabel()
|
||||||
}
|
->live()
|
||||||
|
->disabled(fn (Get $get) => empty($get('allocation_ip')))
|
||||||
$sortedPorts = $ports->sort()->values();
|
->afterStateUpdated(fn ($state, Set $set, Get $get) => $set('allocation_ports',
|
||||||
if ($sortedPorts->all() !== $ports->all()) {
|
CreateServer::retrieveValidPorts(Node::find($getPage('node_id')), $state, $get('allocation_ip')))
|
||||||
$update = true;
|
)
|
||||||
$ports = $sortedPorts;
|
->splitKeys(['Tab', ' ', ','])
|
||||||
}
|
->required(),
|
||||||
|
];
|
||||||
if ($update) {
|
})
|
||||||
$set('allocation_ports', $ports->all());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
->splitKeys(['Tab', ' ', ','])
|
|
||||||
->required(),
|
|
||||||
])
|
|
||||||
->createOptionUsing(function (array $data, Get $get, AssignmentService $assignmentService): int {
|
->createOptionUsing(function (array $data, Get $get, AssignmentService $assignmentService): int {
|
||||||
return collect(
|
return collect(
|
||||||
$assignmentService->handle(Node::find($get('node_id')), $data)
|
$assignmentService->handle(Node::find($get('node_id')), $data)
|
||||||
@ -922,4 +883,88 @@ class CreateServer extends CreateRecord
|
|||||||
->mapWithKeys(fn ($value) => [$value => $value])
|
->mapWithKeys(fn ($value) => [$value => $value])
|
||||||
->all();
|
->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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Filament\Admin\Resources\ServerResource\RelationManagers;
|
namespace App\Filament\Admin\Resources\ServerResource\RelationManagers;
|
||||||
|
|
||||||
|
use App\Filament\Admin\Resources\ServerResource\Pages\CreateServer;
|
||||||
use App\Models\Allocation;
|
use App\Models\Allocation;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Services\Allocations\AssignmentService;
|
use App\Services\Allocations\AssignmentService;
|
||||||
@ -9,6 +10,7 @@ use Filament\Forms\Components\Select;
|
|||||||
use Filament\Forms\Components\TagsInput;
|
use Filament\Forms\Components\TagsInput;
|
||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
use Filament\Forms\Form;
|
use Filament\Forms\Form;
|
||||||
|
use Filament\Forms\Get;
|
||||||
use Filament\Forms\Set;
|
use Filament\Forms\Set;
|
||||||
use Filament\Resources\RelationManagers\RelationManager;
|
use Filament\Resources\RelationManagers\RelationManager;
|
||||||
use Filament\Tables;
|
use Filament\Tables;
|
||||||
@ -78,6 +80,7 @@ class AllocationsRelationManager extends RelationManager
|
|||||||
->inlineLabel()
|
->inlineLabel()
|
||||||
->ipv4()
|
->ipv4()
|
||||||
->helperText("Usually your machine's public IP unless you are port forwarding.")
|
->helperText("Usually your machine's public IP unless you are port forwarding.")
|
||||||
|
->afterStateUpdated(fn (Set $set) => $set('allocation_ports', []))
|
||||||
->required(),
|
->required(),
|
||||||
TextInput::make('allocation_alias')
|
TextInput::make('allocation_alias')
|
||||||
->label('Alias')
|
->label('Alias')
|
||||||
@ -95,54 +98,9 @@ class AllocationsRelationManager extends RelationManager
|
|||||||
->label('Ports')
|
->label('Ports')
|
||||||
->inlineLabel()
|
->inlineLabel()
|
||||||
->live()
|
->live()
|
||||||
->afterStateUpdated(function ($state, Set $set) {
|
->afterStateUpdated(fn ($state, Set $set, Get $get) => $set('allocation_ports',
|
||||||
$ports = collect();
|
CreateServer::retrieveValidPorts($this->getOwnerRecord()->node, $state, $get('allocation_ip')))
|
||||||
$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());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
->splitKeys(['Tab', ' ', ','])
|
->splitKeys(['Tab', ' ', ','])
|
||||||
->required(),
|
->required(),
|
||||||
])
|
])
|
||||||
|
@ -387,6 +387,8 @@ class Node extends Model
|
|||||||
// pass
|
// pass
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$ips->push('0.0.0.0');
|
||||||
|
|
||||||
// Only IPV4
|
// Only IPV4
|
||||||
$ips = $ips->filter(fn (string $ip) => filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false);
|
$ips = $ips->filter(fn (string $ip) => filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user