diff --git a/app/Casts/EndpointCollection.php b/app/Casts/EndpointCollection.php new file mode 100644 index 000000000..ece0268ae --- /dev/null +++ b/app/Casts/EndpointCollection.php @@ -0,0 +1,41 @@ +map(function ($value) { + return new Endpoint($value); + }); + } + + public function set($model, $key, $value, $attributes) + { + if (!$value instanceof Collection) { + return new Collection(); + } + + return $value->map(fn ($endpoint) => (string) $endpoint); + } + }; + } +} diff --git a/app/Livewire/EndpointSynth.php b/app/Livewire/EndpointSynth.php new file mode 100644 index 000000000..91a5d1e38 --- /dev/null +++ b/app/Livewire/EndpointSynth.php @@ -0,0 +1,26 @@ +ip = $ip ?? self::INADDR_ANY; + $this->port = (int) $port; + + if (str_contains($port, ':')) { + [$this->ip, $this->port] = explode(':', $port); } - $ip = $address; - if (str_contains($address, ':')) { - [$ip, $port] = explode(':', $address); + throw_unless(filter_var($this->ip, FILTER_VALIDATE_IP) !== false, new InvalidArgumentException("$this->ip is an invalid IP address")); + throw_unless($this->port > self::PORT_FLOOR, "Port $this->port must be greater than " . self::PORT_FLOOR); + throw_unless($this->port < self::PORT_CEIL, "Port $this->port must be less than " . self::PORT_CEIL); + } - throw_unless(is_numeric($port), new InvalidArgumentException("Port ($port) must be a number")); - - $port = (int) $port; + public function __toString() + { + if ($this->ip === self::INADDR_ANY) { + return (string) $this->port; } - throw_unless(is_int($port), new InvalidArgumentException("Port ($port) must be an integer")); - throw_unless(filter_var($ip, FILTER_VALIDATE_IP) !== false, new InvalidArgumentException("$ip is an invalid IP address")); - - $this->ip = $ip; - $this->port = $port; + return "$this->ip:$this->port"; } } diff --git a/app/Models/Server.php b/app/Models/Server.php index f119eceb5..52fce7771 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -2,8 +2,10 @@ namespace App\Models; +use App\Casts\EndpointCollection; use App\Enums\ServerState; use App\Exceptions\Http\Connection\DaemonConnectionException; +use App\Models\Objects\Endpoint; use GuzzleHttp\Exception\GuzzleException; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Notifications\Notifiable; @@ -67,7 +69,6 @@ class Server extends Model 'database_limit' => 'present|nullable|integer|min:0', 'allocation_limit' => 'sometimes|nullable|integer|min:0', 'backup_limit' => 'present|nullable|integer|min:0', - 'ports' => 'array', ]; protected function casts(): array @@ -90,7 +91,7 @@ class Server extends Model 'deleted_at' => 'datetime', 'installed_at' => 'datetime', 'docker_labels' => 'array', - 'ports' => 'array', + 'ports' => EndpointCollection::class, ]; } @@ -309,9 +310,16 @@ class Server extends Model return cache()->get("servers.$this->uuid.container.status") ?? 'missing'; } - public function getPrimaryEndpoint() + public function getPrimaryEndpoint(): ?Endpoint { - dd($this->ports); - dd($this->variables); + $endpoint = $this->ports->first(); + + $portEggVariable = $this->variables->firstWhere('env_variable', 'SERVER_PORT'); + if ($portEggVariable) { + $portServerVariable = $this->serverVariables->firstWhere('variable_id', $portEggVariable->id); + $endpoint = new Endpoint($portServerVariable->variable_value); + } + + return $endpoint; } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index c0dcc1a64..76474321a 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -3,6 +3,7 @@ namespace App\Providers; use App\Extensions\Themes\Theme; +use App\Livewire\EndpointSynth; use App\Models; use App\Models\ApiKey; use App\Models\Node; @@ -21,6 +22,7 @@ use Illuminate\Support\Facades\View; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Str; use Laravel\Sanctum\Sanctum; +use Livewire\Livewire; class AppServiceProvider extends ServiceProvider { @@ -72,6 +74,8 @@ class AppServiceProvider extends ServiceProvider $this->bootAuth(); $this->bootBroadcast(); + Livewire::propertySynthesizer(EndpointSynth::class); + $bearerTokens = fn (OpenApi $openApi) => $openApi->secure(SecurityScheme::http('bearer')); Gate::define('viewApiDocs', fn () => true); Scramble::registerApi('application', ['api_path' => 'api/application', 'info' => ['version' => '1.0']]); diff --git a/app/Services/Servers/StartupCommandService.php b/app/Services/Servers/StartupCommandService.php index 3dd7cdc11..f75e613fc 100644 --- a/app/Services/Servers/StartupCommandService.php +++ b/app/Services/Servers/StartupCommandService.php @@ -2,6 +2,7 @@ namespace App\Services\Servers; +use App\Models\Objects\Endpoint; use App\Models\Server; class StartupCommandService @@ -13,9 +14,8 @@ class StartupCommandService { $endpoint = $server->getPrimaryEndpoint(); - $find = ['{{SERVER_MEMORY}}', '{{SERVER_IP}}', '{{SERVER_PORT}}']; - $replace = [$server->memory, $server->allocation->ip, $server->allocation->port]; + $replace = [$server->memory, $endpoint->ip ?? Endpoint::INADDR_ANY, $endpoint->port ?? '']; foreach ($server->variables as $variable) { $find[] = '{{' . $variable->env_variable . '}}';