'required', 'region' => 'required', ]; } public function credentialValidationRules(array $input): array { return [ 'token' => 'required', ]; } public function credentialData(array $input): array { return [ 'token' => $input['token'], ]; } public function data(array $input): array { return [ 'plan' => $input['plan'], 'region' => $input['region'], ]; } /** * @throws CouldNotConnectToProvider * @throws ConnectionException */ public function connect(array $credentials): bool { $connect = Http::withToken($credentials['token'])->get($this->apiUrl.'/servers'); if (! $connect->ok()) { throw new CouldNotConnectToProvider('Hetzner'); } return true; } public function plans(?string $region): array { try { /** @var array{server_types?: array}>} $plans */ $plans = Http::withToken($this->serverProvider->credentials['token']) ->get($this->apiUrl.'/server_types', ['per_page' => 50]) ->json(); /** @var array}> $serverTypes */ $serverTypes = $plans['server_types'] ?? []; return collect($serverTypes) ->filter(fn (array $type): bool => collect($type['prices'])->contains(fn (array $price): bool => $price['location'] === $region) ) ->mapWithKeys(fn (array $value): array => [ $value['name'] => __('server_providers.plan', [ 'name' => $value['name'], 'cpu' => $value['cores'], 'memory' => $value['memory'], 'disk' => $value['disk'], ]), ]) ->toArray(); } catch (Exception) { return []; } } public function regions(): array { try { $regions = Http::withToken($this->serverProvider->credentials['token']) ->get($this->apiUrl.'/locations', ['per_page' => 50]) ->json(); /** @var array $locations */ $locations = $regions['locations']; return collect($locations) ->mapWithKeys(fn (array $value): array => [$value['name'] => $value['city'].' - '.$value['country']]) ->toArray(); } catch (Exception) { return []; } } /** * @throws ServerProviderError * @throws ConnectionException */ public function create(): void { $this->generateKeyPair(); $sshKey = Http::withToken($this->server->serverProvider->credentials['token']) ->post($this->apiUrl.'/ssh_keys', [ 'name' => 'server-'.$this->server->id.'-key', 'public_key' => $this->server->sshKey()['public_key'], ]); if ($sshKey->status() != 201) { $this->providerError($sshKey); } $this->server->jsonUpdate('provider_data', 'ssh_key_id', $sshKey->json()['ssh_key']['id']); $create = Http::withToken($this->server->serverProvider->credentials['token']) ->post($this->apiUrl.'/servers', [ 'automount' => false, 'image' => config('serverproviders.hetzner.images')[$this->server->os], // 'root_password' => $this->server->authentication['root_pass'], 'ssh_keys' => [ $sshKey->json()['ssh_key']['id'], ], 'name' => str($this->server->name)->slug(), 'location' => $this->server->provider_data['region'], 'server_type' => $this->server->provider_data['plan'], ]); if ($create->status() != 201) { $this->providerError($create); } $this->server->jsonUpdate('provider_data', 'hetzner_id', $create->json()['server']['id'], false); $this->server->ip = $create->json()['server']['public_net']['ipv4']['ip']; $this->server->save(); } /** * @throws ConnectionException */ public function isRunning(): bool { $status = Http::withToken($this->server->serverProvider->credentials['token']) ->get($this->apiUrl.'/servers/'.$this->server->provider_data['hetzner_id']); if (! $status->ok()) { return false; } return $status->json()['server']['status'] == 'running'; } /** * @throws ConnectionException */ public function delete(): void { if (isset($this->server->provider_data['hetzner_id'])) { $delete = Http::withToken($this->server->serverProvider->credentials['token']) ->delete($this->apiUrl.'/servers/'.$this->server->provider_data['hetzner_id']); if (! $delete->ok()) { Notifier::send($this->server, new FailedToDeleteServerFromProvider($this->server)); } } // delete key if (isset($this->server->provider_data['ssh_key_id'])) { Http::withToken($this->server->serverProvider->credentials['token']) ->delete($this->apiUrl.'/ssh_keys/'.$this->server->provider_data['ssh_key_id']); } } /** * @throws ServerProviderError */ private function providerError(Response $response): void { throw new ServerProviderError($response->json('error')['message']); } }