From da359a606ad1eb5d9c6ff2369f968e11fea8dcdd Mon Sep 17 00:00:00 2001 From: DeathbotGaming <61156296+DeathbotGaming@users.noreply.github.com> Date: Sun, 31 Mar 2024 10:03:34 -0800 Subject: [PATCH] Added Contabo starter --- .gitignore | 2 + ContaboAPI.php | 170 ++++++++++++++++ Providers/.gitkeep | 0 Providers/ContaboServiceProvider.php | 172 ++++++++++++++++ Providers/RouteServiceProvider.php | 86 ++++++++ README.md | 2 + Service.php | 288 +++++++++++++++++++++++++++ composer.json | 25 +++ helpers.php | 8 + module.json | 11 + package.json | 16 ++ 11 files changed, 780 insertions(+) create mode 100644 .gitignore create mode 100644 ContaboAPI.php create mode 100644 Providers/.gitkeep create mode 100644 Providers/ContaboServiceProvider.php create mode 100644 Providers/RouteServiceProvider.php create mode 100644 README.md create mode 100644 Service.php create mode 100644 composer.json create mode 100644 helpers.php create mode 100644 module.json create mode 100644 package.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2d79ca1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ + +.vscode/sftp.json diff --git a/ContaboAPI.php b/ContaboAPI.php new file mode 100644 index 0000000..3194e37 --- /dev/null +++ b/ContaboAPI.php @@ -0,0 +1,170 @@ +post('https://auth.contabo.com/auth/realms/contabo/protocol/openid-connect/token', [ + 'client_id' => settings('contabo::client_id'), + 'client_secret' => settings('encrypted::contabo::client_secret'), + 'username' => settings('contabo::username'), + 'password' => settings('encrypted::contabo::user_password'), + 'grant_type' => 'password', + ]); + + if($authenticate->failed()) + { + if($authenticate->unauthorized() OR $authenticate->forbidden()) { + throw new \Exception("[Contabo] This action is unauthorized! Confirm that the config is setup correctly"); + } + + // dd($authenticate); + if($authenticate->serverError()) { + throw new \Exception("[Contabo] Internal Server Error: {$authenticate->status()}"); + } + + throw new \Exception("[Contabo] Failed to connect to the API! Confirm that the config is setup correctly"); + } + + if(!isset($authenticate['access_token'])) { + throw new \Exception("[Contabo] Access token was not returned from the Contabo API."); + } + + $accessToken = $authenticate['access_token']; + + $response = Http::withHeaders([ + 'Authorization' => 'Bearer ' . $accessToken, + 'x-request-id' => (string) Str::uuid(), + ])->$method("https://api.contabo.com/v1{$endpoint}", $data); + + if($response->failed()) + { + if($response->unauthorized() OR $response->forbidden()) { + throw new \Exception("[Contabo] This action is unauthorized! Confirm that the config is setup correctly"); + } + + // dd($response); + if($response->serverError()) { + throw new \Exception("[Contabo] Internal Server Error: {$response->status()}"); + } + + throw new \Exception("[Contabo] Failed to connect to the API! Confirm that the config is setup correctly"); + } + + return $response; + } + + /** + * Get the available images as a Laravel collection + */ + public function getImages() + { + return $this->api('get', '/compute/images', [ + 'size' => 100, + ])->collect(); + } + + /** + * List all available servers + */ + public function getServers() + { + return $this->api('get', '/compute/instances', [ + 'size' => 100, + ])->collect(); + } + + /** + * Create a new server + */ + public function createServer($data) + { + return $this->api('post', "/compute/instances", [ + 'displayName' => $data['display_name'], + 'imageId' => $data['image'], + 'productId' => $data['product'], + 'region' => $data['region'], + 'period' => $data['period'], + ])->collect(); + } + + /** + * Get a server from ID + */ + public function getServer($serverID) + { + return $this->api('get', "/compute/instances/{$serverID}")->collect(); + } + + /** + * Get a server servers logs from ID + */ + public function getServerLogs($serverID) + { + return $this->api('get', "/compute/instances/actions/audits", [ + 'instanceId' => $serverID, + ])->collect(); + } + + /** + * cancel a server from ID + */ + public function cancelServer($serverID): void + { + try { + $this->api('post', "/compute/instances/{$serverID}/cancel")->collect(); + } catch (\Exception $e) { + ErrorLog("contabo::cancel::server::{$serverID}", $e->getMessage(), 'CRITICAL'); + } + } + + /** + * start a server from ID + */ + public function startServer($serverID) + { + return $this->api('post', "/compute/instances/{$serverID}/actions/start")->collect(); + } + + /** + * stop a server from ID + */ + public function stopServer($serverID) + { + return $this->api('post', "/compute/instances/{$serverID}/actions/stop")->collect(); + } + + /** + * shutdown a server from ID + */ + public function shutdownServer($serverID) + { + return $this->api('post', "/compute/instances/{$serverID}/actions/shutdown")->collect(); + } + + /** + * enable rescue mode for a server from ID + */ + public function enableRescueMode($serverID) + { + return $this->api('post', "/compute/instances/{$serverID}/actions/rescue")->collect(); + } + + /** + * reset password for a server from ID + */ + public function resetPassword($serverID, $password) + { + return $this->api('post', "/compute/instances/{$serverID}/actions/resetPassword", [ + 'rootPassword' => $password, + ])->collect(); + } +} \ No newline at end of file diff --git a/Providers/.gitkeep b/Providers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Providers/ContaboServiceProvider.php b/Providers/ContaboServiceProvider.php new file mode 100644 index 0000000..6468d4d --- /dev/null +++ b/Providers/ContaboServiceProvider.php @@ -0,0 +1,172 @@ +config) { + $this->registerConfig(); + } + + if ($this->views) { + $this->registerViews(); + } + + if ($this->migrations) { + $this->loadMigrationsFrom(module_path($this->moduleName, 'Database/Migrations')); + } + + $this->registerTranslations(); + } + + /** + * Register the service provider. + * + * @return void + */ + public function register() + { + if ($this->routes) { + $this->app->register(RouteServiceProvider::class); + } + } + + /** + * Register config. + * + * @return void + */ + protected function registerConfig() + { + $this->publishes([ + module_path($this->moduleName, 'Config/config.php') => config_path($this->moduleNameLower . '.php'), + ], 'config'); + $this->mergeConfigFrom( + module_path($this->moduleName, 'Config/config.php'), $this->moduleNameLower + ); + } + + /** + * Register views. + * + * @return void + */ + protected function registerViews() + { + $viewPath = resource_path('views/modules/' . $this->moduleNameLower); + + $sourcePath = module_path($this->moduleName, 'Resources/views'); + + $this->publishes([ + $sourcePath => $viewPath + ], ['views', $this->moduleNameLower . '-module-views']); + + $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower); + } + + /** + * Register translations. + * + * @return void + */ + protected function registerTranslations() + { + $langPath = resource_path('lang/modules/' . $this->moduleNameLower); + + if (is_dir($langPath)) { + $this->loadTranslationsFrom($langPath, $this->moduleNameLower); + $this->loadJsonTranslationsFrom($langPath, $this->moduleNameLower); + } else { + $this->loadTranslationsFrom(module_path($this->moduleName, 'Resources/lang'), $this->moduleNameLower); + $this->loadJsonTranslationsFrom(module_path($this->moduleName, 'Resources/lang'), $this->moduleNameLower); + } + } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides() + { + return []; + } + + /** + * Retrieve paths where views are published. + * + * @return array + */ + private function getPublishableViewPaths(): array + { + $paths = []; + foreach (\Config::get('view.paths') as $path) { + if (is_dir($path . '/modules/' . $this->moduleNameLower)) { + $paths[] = $path . '/modules/' . $this->moduleNameLower; + } + } + return $paths; + } +} \ No newline at end of file diff --git a/Providers/RouteServiceProvider.php b/Providers/RouteServiceProvider.php new file mode 100644 index 0000000..f5e8bfa --- /dev/null +++ b/Providers/RouteServiceProvider.php @@ -0,0 +1,86 @@ +mapApiRoutes(); + + $this->mapWebRoutes(); + + $this->mapAdminRoutes(); + } + + /** + * Define the "web" routes for the application. + * + * These routes all receive session state, CSRF protection, etc. + * + * @return void + */ + protected function mapWebRoutes() + { + Route::middleware('web') + ->namespace($this->moduleNamespace) + ->group(module_path('contabo', '/Routes/web.php')); + } + + /** + * Define the "admin" routes for the application. + * + * These routes all receive admin level permission status + * + * @return void + */ + protected function mapAdminRoutes() + { + Route::middleware('web', 'admin', 'permission') + ->namespace($this->moduleNamespace) + ->prefix('admin/contabo') + ->group(module_path('contabo', '/Routes/admin.php')); + } + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + * + * @return void + */ + protected function mapApiRoutes() + { + Route::prefix('api') + ->middleware('api') + ->namespace($this->moduleNamespace) + ->group(module_path('contabo', '/Routes/api.php')); + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1d0e422 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# service-contabo +Resell virtual contabo servers diff --git a/Service.php b/Service.php new file mode 100644 index 0000000..338847a --- /dev/null +++ b/Service.php @@ -0,0 +1,288 @@ +order = $order; + } + + /** + * Returns the meta data about this Server/Service + * + * @return object + */ + public static function metaData(): object + { + return (object) + [ + 'display_name' => 'Contabo', + 'author' => 'WemX', + '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 + { + return [ + [ + "key" => "contabo::client_id", + "name" => "Client ID", + "description" => "ClientId of your contabo account", + "type" => "text", + "rules" => ['required'], // laravel validation rules + ], + [ + "key" => "encrypted::contabo::client_secret", + "name" => "Client Secret", + "description" => "Client Secret of your contabo account", + "type" => "password", + "rules" => ['required'], // laravel validation rules + ], + [ + "key" => "contabo::username", + "name" => "Contabo Username", + "description" => "Username of your contabo account", + "type" => "email", + "rules" => ['required', 'email'], // laravel validation rules + ], + [ + "key" => "encrypted::contabo::user_password", + "name" => "Contabo Account Password", + "description" => "Password of your contabo account", + "type" => "password", + "rules" => ['required'], // laravel validation rules + ], + ]; + } + + /** + * 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 + { + return [ + [ + "col" => "col-12", + "key" => "product", + "name" => "Product ID", + "description" => "ID of the contabo product", + "type" => "select", + "options" => [ + 'V1' => 'VPS S SSD (200 GB SSD)', + 'V35' => 'VPS S Storage (400 GB SSD)', + 'V12' => 'VPS S NVMe (50 GB NVMe)', + 'V2' => 'VPS M SSD (400 GB SSD)', + 'V36' => 'VPS M Storage (800 GB SSD)', + 'V13' => 'VPS M NVMe (100 GB NVMe)', + 'V3' => 'VPS L SSD (800 GB SSD)', + 'V37' => 'VPS L Storage (1600 GB SSD)', + 'V14' => 'VPS L NVMe (200 GB NVMe)', + 'V4' => 'VPS XL SSD (1600 GB SSD)', + 'V38' => 'VPS XL Storage (3200 GB SSD)', + 'V15' => 'VPS XL NVMe (400 GB NVMe)', + 'V42' => 'VPS XXXL SSD (2400 GB SSD)', + 'V44' => 'VPS XXXL Storage (4800 GB SSD)', + 'V43' => 'VPS XXXL NVMe (600 GB NVMe)', + 'V45' => 'VPS 1 SSD (400 GB SSD)', + 'V46' => 'VPS 1 NVMe (100 GB NVMe)', + 'V48' => 'VPS 2 SSD (400 GB SSD)', + 'V49' => 'VPS 2 NVMe (200 GB NVMe)', + 'V51' => 'VPS 3 SSD (1200 GB SSD)', + 'V52' => 'VPS 3 NVMe (300 GB NVMe)', + 'V54' => 'VPS 4 SSD (1600 GB SSD)', + 'V55' => 'VPS 4 NVMe (400 GB NVMe)', + 'V57' => 'VPS 5 SSD (2000 GB SSD)', + 'V58' => 'VPS 5 NVMe (500 GB NVMe)', + 'V60' => 'VPS 6 SSD (2400 GB SSD)', + 'V61' => 'VPS 6 NVMe (600 GB NVMe)', + 'V8' => 'VDS S (180 GB NVMe)', + 'V9' => 'VDS M (240 GB NVMe)', + 'V10' => 'VDS L (360 GB NVMe)', + 'V11' => 'VDS XL (480 GB NVMe)', + 'V16' => 'VDS XXL (720 GB NVMe)', + ], + "default_value" => "V1", + "rules" => ['required'], + ], + [ + "col" => "col-12", + "key" => "region[]", + "name" => "Allowed Regions", + "description" => "Allowed regions for this package at checkout by the user", + "type" => "select", + "options" => [ + 'EU' => 'Germany (Europe)', + 'UK' => 'United Kingdom (Europe)', + 'US-central' => 'United States (Central)', + 'US-east' => 'United States (East)', + 'US-west' => 'United States (West)', + 'SIN' => 'Singapore (Asia)', + 'AUS' => 'Australia (Oceania)', + 'JPN' => 'Japan (Asia)', + ], + "multiple" => true, + "default_value" => "EU", + "rules" => ['required'], + ], + ]; + } + + /** + * 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 + [ + // TO DO: Load the regions from the package config + [ + "key" => "region", + "name" => "Region", + "description" => "Select the region for your server", + "type" => "select", + "options" => [ + 'EU' => 'Germany (Europe)', + 'UK' => 'United Kingdom (Europe)', + 'US-central' => 'United States (Central)', + 'US-east' => 'United States (East)', + 'US-west' => 'United States (West)', + 'SIN' => 'Singapore (Asia)', + 'AUS' => 'Australia (Oceania)', + 'JPN' => 'Japan (Asia)', + ], + "default_value" => "EU", + "rules" => ['required'], + ], + + // To Do: load OS types / images from API + [ + "key" => "image", + "name" => "Image", + "description" => "Select the image for your server", + "type" => "select", + "options" => [ + '04e0f898-37b4-48bc-a794-1a57abe6aa31' => 'Ubuntu 20.04', + ], + "default_value" => "04e0f898-37b4-48bc-a794-1a57abe6aa31", + "rules" => ['required'], + ], + ]; + } + + /** + * Test API connection + */ + public static function testConnection() + { + try { + contabo()->getServers(); + } catch(\Exception $error) { + return redirect()->back()->withError("Failed to connect to Contabo.

{$error->getMessage()}"); + } + + return redirect()->back()->withSuccess("Successfully connected with Contabo API"); + } + + /** + * Define buttons shown at order management page + * + * @return array + */ + public static function setServiceButtons(Order $order): array + { + return []; + } + + /** + * 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 = []) + { + return []; + } + + /** + * 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) + { + return []; + } + + /** + * 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 = []) + { + return []; + } + + /** + * 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 = []) + { + return []; + } + + /** + * 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 = []) + { + return []; + } +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..a6f267d --- /dev/null +++ b/composer.json @@ -0,0 +1,25 @@ +{ + "name": "wemx/service-contabo", + "type": "laravel-module", + "description": "A contabo service for developers", + "authors": [ + { + "name": "WemX", + "email": "mubeen@wemx.net" + } + ], + "extra": { + "module-dir": "app/Services", + "laravel": { + "providers": [], + "aliases": { + + } + } + }, + "autoload": { + "psr-4": { + "App\\Services\\Contabo\\": "" + } + } +} diff --git a/helpers.php b/helpers.php new file mode 100644 index 0000000..c809f4a --- /dev/null +++ b/helpers.php @@ -0,0 +1,8 @@ +