commit 1bf67eef22c375aa9664ee90395250fc2d1dc2a2 Author: shanec Date: Sat Mar 30 22:13:41 2024 -0400 init diff --git a/README.md b/README.md new file mode 100644 index 0000000..da9415d --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# service-virtfusionservice +A WemX service integration with VirtFusion \ No newline at end of file diff --git a/Service.php b/Service.php new file mode 100644 index 0000000..87925b6 --- /dev/null +++ b/Service.php @@ -0,0 +1,439 @@ +order = $order; + } + + public function getDisplayName(): string + { + return settings('virtfusionserv::display_name', 'VirtFusion'); + } + + /** + * Returns the meta data about this Server/Service + * + * @return object + */ + public static function metaData(): object + { + return (object) + [ + 'display_name' => 'VirtfusionService', + 'author' => 'Shane C.', + 'version' => '1.0.0', + 'wemx_version' => ['dev', '>=1.8.0'], + ]; + } + + /** + * Define the default configuration values required to setup this service + * i.e host, api key, or other values. Use Laravel validation rules for + * + * Laravel validation rules: https://laravel.com/docs/10.x/validation + * + * @return array + */ + public static function setConfig(): array + { + + $doesNotEndWithSlash = function ($attribute, $value, $fail) { + if (preg_match('/\/$/', $value)) { + return $fail('Panel URL must not end with a slash "/". It should be like https://panel.example.com'); + } + }; + + return [ + [ + "key" => "virtfusionserv::host", + "name" => "VirtFusion Panel Host/URL", + "description" => "The Host/URL of the VirtFusion panel i.e https://panel.example.com", + "type" => "url", + "rules" => ["required", "active_url", $doesNotEndWithSlash] + ], + [ + "key" => "encrypted::virtfusionserv::apikey", + "name" => "VirtFusion API Key", + "description" => "API Key to manage VirtFusion panel.", + "type" => "password", + "rules" => ["required"], + ] + ]; + } + + /** + * Define the default package configuration values required when creatig + * new packages. i.e maximum ram usage, allowed databases and backups etc. + * + * Laravel validation rules: https://laravel.com/docs/10.x/validation + * + * @return array + */ + public static function setPackageConfig(Package $package): array + { + + $response = Service::api('get', '/packages'); + if ($response->failed()) { + if (isset($response['errors'])) { + throw new Exception("[VirtFusion] " . json_encode($response['errors'])); + } + + if (isset($response['message'])) { + throw new Exception("[VirtFusion] " . $response['message']); + } + } + + $collectPackages = collect($response['data']); + $packages = $collectPackages->mapWithKeys(function ($item) { + if (!$item['enabled']) { + return []; + } + + return [$item['id'] => $item['name']]; + }); + + return [ + [ + "col" => "col-12", + "key" => "package", + "name" => "Package", + "description" => "Select the package to use for this service", + "type" => "select", + "options" => $packages->toArray(), + "save_on_change" => true, + "rules" => ['required'], + ], + [ + "col" => "col-12", + "key" => "hypervisor_group_id", + "name" => "Hypervisor Group ID", + "description" => "Enter the Hypervisor Group ID to use for this service", + "type" => "number", + "save_on_change" => true, + "rules" => ['required', 'numeric'], + ], + [ + "key" => "allowed_ips", + "name" => "Number of Allowed IPv4 IPs", + "description" => "Enter the number of allowed IPv4 IPs for this service", + "type" => "number", + "rules" => ['required', 'numeric'], + ], + ]; + } + + /** + * Define the checkout config that is required at checkout and is fillable by + * the client. Its important to properly sanatize all inputted data with rules + * + * Laravel validation rules: https://laravel.com/docs/10.x/validation + * + * @return array + */ + public static function setCheckoutConfig(Package $package): array + { + return []; + } + + /** + * Define buttons shown at order management page + * + * @return array + */ + public static function setServiceButtons(Order $order): array + { + + try { + $response = Service::api('post', "/users/{$order->user->id}/serverAuthenticationTokens/{$order->data['id']}"); + if ($response->failed()) { + if (isset($response['errors'])) { + return []; + } + + if (isset($response['message'])) { + return []; + } + } + } catch (Exception $e) { + return []; + } + + $loginUrl = settings('virtfusionserv::host') . $response['data']['authentication']['endpoint_complete']; + + return [ + [ + "name" => "Panel Login", + "color" => "primary", + "href" => $loginUrl, + "target" => "_blank", + ] + ]; + } + + /** + * This function is responsible for creating an instance of the + * service. This can be anything such as a server, vps or any other instance. + * + * @return void + */ + public function create(array $data = []) + { + + $order = $this->order; + $user = $this->order->user; + $package = $this->order->package; + + if (!$order->hasExternalUser()) { + $response = Service::api('post', '/users', [ + "name" => $user->first_name . ' ' . $user->last_name, + "email" => $user->email, + "extRelationId" => $user->id, + "sendMail" => false, + ]); + + if ($response->failed() && $response->status() != 409) { + if (isset($response['errors'])) { + throw new Exception("[VirtFusion] " . json_encode($response['errors'])); + } + + if (isset($response['message'])) { + throw new Exception("[VirtFusion] " . $response['message']); + } + } + + if ($response->status() == 409) { + + $getUserRes = Service::api('get', "/users/{$user->id}/byExtRelation"); + + $virtFusionUser = $getUserRes['data']; + + $order->createExternalUser([ + 'external_id' => $virtFusionUser['id'], + 'username' => $user->email, + 'password' => Str::random(16), + 'data' => $virtFusionUser + ]); + + } else { + + $order->createExternalUser([ + 'external_id' => $response['data']['id'], // optional + 'username' => $user->email, + 'password' => $response['data']['password'], + 'data' => $response['data'], // Additional data about the user as an array (optional) + ]); + + + } + + $user->email([ + 'subject' => 'Panel Account Created', + 'content' => "Your account has been created on the vps panel. You can login using the following details:

Email: {$user->email}
Password: {$response['data']['password']}", + 'button' => [ + 'name' => 'VPS Panel', + 'url' => settings('virtfusionserv::host'), + ] + ]); + } + + // create the server + $createSrvRes = Service::api('post', '/servers', [ + "packageId" => $package->data('package'), + "userId" => $order->getExternalUser()->external_id, + "hypervisorId" => $package->data('hypervisor_group_id'), + "ipv4" => $package->data('allowed_ips', 1), + ]); + + if ($createSrvRes->failed()) { + if (isset($response['errors'])) { + throw new Exception("[VirtFusion] " . json_encode($response['errors'])); + } + + if (isset($response['message'])) { + throw new Exception("[VirtFusion] " . $response['message']); + } + } + + $order->external_id = $createSrvRes['data']['id']; + $order->data = $createSrvRes['data']; + $order->save(); + + return $createSrvRes; + + } + + /** + * This function is responsible for upgrading or downgrading + * an instance of this service. This method is optional + * If your service doesn't support upgrading, remove this method. + * + * Optional + * @return void + */ + public function upgrade(Package $oldPackage, Package $newPackage) + { + $order = $this->order; + $response = Service::api('put', "/servers/{$order->data['id']}/package/{$newPackage->data('package')}"); + if ($response->failed()) { + if (isset($response['errors'])) { + throw new Exception("[VirtFusion] " . json_encode($response['errors'])); + } + + if (isset($response['message'])) { + throw new Exception("[VirtFusion] " . $response['message']); + } + } + } + + /** + * This function is responsible for suspending an instance of the + * service. This method is called when a order is expired or + * suspended by an admin + * + * @return void + */ + public function suspend(array $data = []) + { + $order = $this->order; + $response = Service::api('post', "/servers/{$order->data['id']}/suspend"); + if ($response->failed()) { + if (isset($response['errors'])) { + throw new Exception("[VirtFusion] " . json_encode($response['errors'])); + } + + if (isset($response['message'])) { + throw new Exception("[VirtFusion] " . $response['message']); + } + } + } + + /** + * This function is responsible for unsuspending an instance of the + * service. This method is called when a order is activated or + * unsuspended by an admin + * + * @return void + */ + public function unsuspend(array $data = []) + { + $order = $this->order; + $response = Service::api('post', "/servers/{$order->data['id']}/unsuspend"); + if ($response->failed()) { + if (isset($response['errors'])) { + throw new Exception("[VirtFusion] " . json_encode($response['errors'])); + } + + if (isset($response['message'])) { + throw new Exception("[VirtFusion] " . $response['message']); + } + } + } + + /** + * This function is responsible for deleting an instance of the + * service. This can be anything such as a server, vps or any other instance. + * + * @return void + */ + public function terminate(array $data = []) + { + $order = $this->order; + Service::api('delete', "/servers/{$order->data['id']}?delay=5"); + } + + /** + * This function is responsible automatically logging in to the + * panel when the user clicks the login button in the client area. + * + * @return redirect + */ + public function loginToPanel(Order $order) + { + try { + $response = Service::api('post', "/users/{$order->user->id}/serverAuthenticationTokens/{$order->data['id']}"); + if ($response->failed()) { + if (isset($response['errors'])) { + throw new Exception("[VirtFusion] " . json_encode($response['errors'])); + } + + if (isset($response['message'])) { + throw new Exception("[VirtFusion] " . $response['message']); + } + } + return redirect(settings('virtfusionserv::host') . $response['data']['authentication']['endpoint_complete']); + } catch (Exception $e) { + return redirect()->back()->withError("Something went wrong, please try again later."); + } + } + + /** + * Test API connection + */ + public static function testConnection() + { + try { + // try to get list of packages through API request + $response = Service::api('get', '/connect'); + } catch (Exception $error) { + // if try-catch fails, return the error with details + return redirect()->back()->withError("Failed to connect to VirtFusion.

{$error->getMessage()}"); + } + + // if no errors are logged, return a success message + return redirect()->back()->withSuccess("Successfully connected with VirtFusion"); + } + + public static function api($method, $endpoint, $data = []) + { + // validate the method + if (!in_array($method, ['get', 'post', 'put', 'delete', 'patch', 'head'])) { + throw new Exception("[VirtFusion] Invalid method: {$method}"); + } + + // make the request + $url = settings('virtfusionserv::host') . '/api/v1' . $endpoint; + $response = Http::withHeaders([ + 'Authorization' => 'Bearer ' . settings('encrypted::virtfusionserv::apikey'), + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + ])->$method($url, $data); + + // dd($response, $response->json()); + + if ($response->failed()) { + // dd($response, $response->json()); + + if ($response->unauthorized() or $response->forbidden()) { + throw new Exception("[VirtFusion] This action is unauthorized! Confirm that API token has the right permissions"); + } + + if ($response->serverError()) { + throw new Exception("[VirtFusion] Internal Server Error: {$response->status()}"); + } + + } + + return $response; + } + +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..4205e22 --- /dev/null +++ b/composer.json @@ -0,0 +1,23 @@ +{ + "name": "wemx/service-virtfusionservice", + "type": "laravel-module", + "description": "A service integration with VirtFusion", + "authors": [ + { + "name": "Shane", + "email": "shane@scaffoe.com" + } + ], + "extra": { + "module-dir": "app/Services", + "laravel": { + "providers": [], + "aliases": {} + } + }, + "autoload": { + "psr-4": { + "App\\Services\\VirtfusionService\\": "" + } + } +} \ No newline at end of file diff --git a/module.json b/module.json new file mode 100644 index 0000000..8c13fe2 --- /dev/null +++ b/module.json @@ -0,0 +1,11 @@ +{ + "name": "VirtfusionService", + "alias": "virtfusionservice", + "description": "", + "keywords": [], + "priority": 0, + "providers": [ + "App\\Services\\VirtfusionService\\Providers\\VirtfusionServiceServiceProvider" + ], + "files": [] +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..30c1a80 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "private": true, + "scripts": { + "dev": "vite", + "build": "vite build" + }, + "devDependencies": { + "axios": "^0.21.4", + "dotenv": "^10.0.0", + "dotenv-expand": "^5.1.0", + "laravel-vite-plugin": "^0.6.0", + "lodash": "^4.17.21", + "postcss": "^8.3.7", + "vite": "^3.0.9" + } +}