commit da359a606ad1eb5d9c6ff2369f968e11fea8dcdd
Author: DeathbotGaming <61156296+DeathbotGaming@users.noreply.github.com>
Date: Sun Mar 31 10:03:34 2024 -0800
Added Contabo starter
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 @@
+