From 57b2771c7e381da8d3ea2a6c7d6f2776692dacd2 Mon Sep 17 00:00:00 2001 From: Saeed Vaziry <61919774+saeedvaziry@users.noreply.github.com> Date: Sun, 17 Nov 2024 11:20:15 +0100 Subject: [PATCH] load DO regions and plans dynamically (#368) --- app/Models/ServerProvider.php | 9 +- app/ServerProviders/DigitalOcean.php | 118 ++++++++++++++++++--------- 2 files changed, 79 insertions(+), 48 deletions(-) diff --git a/app/Models/ServerProvider.php b/app/Models/ServerProvider.php index 66c4341b..585448df 100644 --- a/app/Models/ServerProvider.php +++ b/app/Models/ServerProvider.php @@ -105,13 +105,6 @@ public static function plans(?int $id, ?string $region): array return []; } - if (Cache::get('plans-'.$id.'-'.$region)) { - return Cache::get('plans-'.$id.'-'.$region); - } - - $plans = $profile->provider()->plans($region); - Cache::put('plans-'.$id.'-'.$region, $plans, 60); - - return $plans; + return $profile->provider()->plans($region); } } diff --git a/app/ServerProviders/DigitalOcean.php b/app/ServerProviders/DigitalOcean.php index 9358b0df..631fdf89 100644 --- a/app/ServerProviders/DigitalOcean.php +++ b/app/ServerProviders/DigitalOcean.php @@ -16,21 +16,10 @@ class DigitalOcean extends AbstractProvider public function createRules(array $input): array { - $rules = []; - // plans - $plans = []; - foreach (config('serverproviders.digitalocean.plans') as $plan) { - $plans[] = $plan['value']; - } - $rules['plan'] = 'required|in:'.implode(',', $plans); - // regions - $regions = []; - foreach (config('serverproviders.digitalocean.regions') as $region) { - $regions[] = $region['value']; - } - $rules['region'] = 'required|in:'.implode(',', $regions); - - return $rules; + return [ + 'plan' => 'required', + 'region' => 'required', + ]; } public function credentialValidationRules(array $input): array @@ -60,7 +49,12 @@ public function data(array $input): array */ public function connect(?array $credentials = null): bool { - $connect = Http::withToken($credentials['token'])->get($this->apiUrl.'/account'); + try { + $connect = Http::withToken($credentials['token'])->get($this->apiUrl.'/account'); + } catch (Exception) { + throw new CouldNotConnectToProvider('DigitalOcean'); + } + if (! $connect->ok()) { throw new CouldNotConnectToProvider('DigitalOcean'); } @@ -70,16 +64,46 @@ public function connect(?array $credentials = null): bool public function plans(?string $region): array { - return collect(config('serverproviders.digitalocean.plans')) - ->mapWithKeys(fn ($value) => [$value['value'] => $value['title']]) - ->toArray(); + try { + $plans = Http::withToken($this->serverProvider->credentials['token']) + ->get($this->apiUrl.'/sizes', ['per_page' => 200]) + ->json(); + + ds($region); + + return collect($plans['sizes'])->filter(function ($size) use ($region) { + return in_array($region, $size['regions']); + }) + ->mapWithKeys(function ($value) { + return [ + $value['slug'] => __('server_providers.plan', [ + 'name' => $value['description'], + 'cpu' => $value['vcpus'], + 'memory' => $value['memory'], + 'disk' => $value['disk'], + ]), + ]; + }) + ->toArray(); + } catch (Exception) { + return []; + } } public function regions(): array { - return collect(config('serverproviders.digitalocean.regions')) - ->mapWithKeys(fn ($value) => [$value['value'] => $value['title']]) - ->toArray(); + try { + $regions = Http::withToken($this->serverProvider->credentials['token']) + ->get($this->apiUrl.'/regions', ['per_page' => 200]) + ->json(); + + return collect($regions['regions']) + ->where('available', true) + ->mapWithKeys(fn ($value) => [$value['slug'] => $value['name']]) + ->toArray(); + } catch (Exception) { + return []; + } } /** @@ -89,26 +113,36 @@ public function create(): void { $this->generateKeyPair(); - $createSshKey = Http::withToken($this->server->serverProvider->credentials['token']) - ->post($this->apiUrl.'/account/keys', [ - 'public_key' => $this->server->sshKey()['public_key'], - 'name' => str($this->server->name)->slug().'-'.$this->server->id, - ]); + try { + $createSshKey = Http::withToken($this->server->serverProvider->credentials['token']) + ->post($this->apiUrl.'/account/keys', [ + 'public_key' => $this->server->sshKey()['public_key'], + 'name' => str($this->server->name)->slug().'-'.$this->server->id, + ]); + } catch (Exception) { + throw new ServerProviderError('DigitalOcean SSH Key'); + } + if ($createSshKey->status() != 201) { throw new ServerProviderError('DigitalOcean SSH Key'); } - $create = Http::withToken($this->server->serverProvider->credentials['token']) - ->post($this->apiUrl.'/droplets', [ - 'name' => str($this->server->name)->slug(), - 'region' => $this->server->provider_data['region'], - 'size' => $this->server->provider_data['plan'], - 'image' => config('serverproviders.digitalocean.images')[$this->server->os], - 'backups' => false, - 'ipv6' => false, - 'monitoring' => false, - 'ssh_keys' => [$createSshKey->json()['ssh_key']['id']], - ]); + try { + $create = Http::withToken($this->server->serverProvider->credentials['token']) + ->post($this->apiUrl.'/droplets', [ + 'name' => str($this->server->name)->slug(), + 'region' => $this->server->provider_data['region'], + 'size' => $this->server->provider_data['plan'], + 'image' => config('serverproviders.digitalocean.images')[$this->server->os], + 'backups' => false, + 'ipv6' => false, + 'monitoring' => false, + 'ssh_keys' => [$createSshKey->json()['ssh_key']['id']], + ]); + } catch (Exception) { + throw new ServerProviderError('DigitalOcean'); + } + if ($create->status() != 202) { $msg = __('Failed to create server on DigitalOcean'); Log::error('Failed to create server on DigitalOcean', $create->json()); @@ -122,8 +156,12 @@ public function create(): void public function isRunning(): bool { - $status = Http::withToken($this->server->serverProvider->credentials['token']) - ->get($this->apiUrl.'/droplets/'.$this->server->provider_data['droplet_id']); + try { + $status = Http::withToken($this->server->serverProvider->credentials['token']) + ->get($this->apiUrl.'/droplets/'.$this->server->provider_data['droplet_id']); + } catch (Exception) { + return false; + } if (! $status->ok()) { return false;