diff --git a/Events/DiscordWebhook/DiscordWebhook.php b/Events/DiscordWebhook/DiscordWebhook.php new file mode 100644 index 0000000..d89129f --- /dev/null +++ b/Events/DiscordWebhook/DiscordWebhook.php @@ -0,0 +1,50 @@ + 'webhook_url', + 'type' => 'text', + 'friendlyName' => 'Webhook URL', + 'required' => true, + ], + [ + 'name' => 'ping_type', + 'type' => 'dropdown', + 'friendlyName' => 'Ping Type', + 'description' => 'The type of user/role to ping', + 'required' => false, + 'options' => [ + [ + 'name' => 'None', + 'value' => 'none', + ], + [ + 'name' => 'User', + 'value' => 'user', + ], + [ + 'name' => 'Role', + 'value' => 'role', + ], + ], + ], + [ + 'name' => 'ping_id', + 'type' => 'text', + 'friendlyName' => 'Ping ID', + 'description' => 'The ID of the user/role to ping', + 'required' => false, + ], + ]; + } +} diff --git a/Events/DiscordWebhook/DiscordWebhookListeners.php b/Events/DiscordWebhook/DiscordWebhookListeners.php new file mode 100644 index 0000000..5651ee5 --- /dev/null +++ b/Events/DiscordWebhook/DiscordWebhookListeners.php @@ -0,0 +1,197 @@ + [ + [ + 'title' => $title, + 'description' => $message, + 'color' => hexdec($color), + 'fields' => $fields, + ], + ], + ]; + $url = ExtensionHelper::getConfig('DiscordWebhook', 'webhook_url'); + if (!$url) { + return; + } + if (ExtensionHelper::getConfig('DiscordWebhook', 'ping_type') == 'user') { + $data['content'] = '<@' . ExtensionHelper::getConfig('DiscordWebhook', 'ping_id') . '>'; + } else if (ExtensionHelper::getConfig('DiscordWebhook', 'ping_type') == 'role') { + $data['content'] = '<@&' . ExtensionHelper::getConfig('DiscordWebhook', 'ping_id') . '>'; + } + $curl = curl_init($url); + curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-type: application/json')); + curl_setopt($curl, CURLOPT_POST, 1); + curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data)); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); + curl_setopt($curl, CURLOPT_HEADER, 0); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_exec($curl); + curl_close($curl); + } + + /** + * Handle the Invoice created event. + */ + public function handleInvoiceCreated(InvoiceCreated $event): void + { + $invoice = $event->invoice; + $message = " "; + $fields = [ + [ + 'name' => 'Invoice ID', + 'value' => '[#' . $invoice->id . '](' . route('admin.invoices.show', $invoice->id) . ')', + 'inline' => true, + ], + [ + 'name' => 'Invoice total', + 'value' => config('settings::currency_sign') . $invoice->total(), + 'inline' => true, + ], + [ + 'name' => 'Invoice status', + 'value' => $invoice->status, + 'inline' => true, + ], + [ + 'name' => 'Invoice user', + 'value' => '[' . $invoice->user->name . '](' . route('admin.clients.edit', $invoice->user->id) . ')', + 'inline' => true, + ], + ]; + $this->sendWebhook('New invoice created', $message, $fields); + } + + /** + * Handle the Invoice paid event. + */ + public function handleInvoicePaid(InvoicePaid $event): void + { + $invoice = $event->invoice; + $message = "Invoice paid: {$invoice->id} - {$invoice->user->name} - " . config('settings::currency_sign') . "{$invoice->total()}"; + $this->sendWebhook('Invoice paid', $message); + } + + + public function newTicketMessage($event) + { + $ticket = $event->ticket; + $message = $event->message; + $dcmessage = "{$message->message}\n"; + $fields = [ + [ + 'name' => 'Ticket ID', + 'value' => '[#' . $ticket->id . '](' . route('admin.tickets.show', $ticket->id) . ')', + 'inline' => true, + ], + [ + 'name' => 'Ticket status', + 'value' => $ticket->status, + 'inline' => true, + ], + [ + 'name' => 'Ticket priority', + 'value' => $ticket->priority, + 'inline' => true, + ], + [ + 'name' => 'Ticket subject', + 'value' => $ticket->title, + 'inline' => true, + ], + [ + 'name' => 'Ticket user', + 'value' => '[' . $message->user->name . '](' . route('admin.clients.edit', $message->user->id) . ')', + 'inline' => true, + ], + ]; + $this->sendWebhook('New ticket message', $dcmessage, $fields); + } + + public function newUser($event) + { + $user = $event->user; + $message = " "; + $fields = [ + [ + 'name' => 'User ID', + 'value' => '[#' . $user->id . '](' . route('admin.clients.edit', $user->id) . ')', + 'inline' => true, + ], + [ + 'name' => 'User name', + 'value' => $user->name, + 'inline' => true, + ], + [ + 'name' => 'User email', + 'value' => $user->email, + 'inline' => true, + ], + ]; + $this->sendWebhook('New user', $message, $fields); + } + + public function newTicket($event) + { + $ticket = $event->ticket; + $message = " "; + $fields = [ + [ + 'name' => 'Ticket ID', + 'value' => '[#' . $ticket->id . '](' . route('admin.tickets.show', $ticket->id) . ')', + 'inline' => true, + ], + [ + 'name' => 'Ticket status', + 'value' => $ticket->status, + 'inline' => true, + ], + [ + 'name' => 'Ticket priority', + 'value' => $ticket->priority, + 'inline' => true, + ], + [ + 'name' => 'Ticket subject', + 'value' => $ticket->title, + 'inline' => true, + ], + [ + 'name' => 'Ticket user', + 'value' => '[' . $ticket->user->name . '](' . route('admin.clients.edit', $ticket->user->id) . ')', + 'inline' => true, + ], + ]; + $this->sendWebhook('New ticket', $message, $fields); + } + + /** + * Register the listeners for the subscriber. + * + * @return array + */ + public function subscribe(): array + { + return [ + InvoiceCreated::class => 'handleInvoiceCreated', + InvoicePaid::class => 'handleInvoicePaid', + TicketMessageCreated::class => 'newTicketMessage', + UserCreated::class => 'newUser', + TicketCreated::class => 'newTicket', + ]; + } +} diff --git a/Gateways/LitePay/LitePay.php b/Gateways/LitePay/LitePay.php new file mode 100644 index 0000000..5a79961 --- /dev/null +++ b/Gateways/LitePay/LitePay.php @@ -0,0 +1,70 @@ + 'LitePay', + 'version' => '1.0.0', + 'author' => 'Paymenter', + 'website' => 'https://paymenter.org', + ]; + } + + public function getConfig() + { + return [ + [ + 'name' => 'secret', + 'friendlyName' => 'Secret', + 'type' => 'text', + 'required' => true, + ], + [ + 'name' => 'merchant_id', + 'friendlyName' => 'VENDOR ID', + 'type' => 'text', + 'required' => true, + ], + ]; + } + + public function pay($total, $products, $invoiceId) + { + $litepay = new litepayclass('merchant'); + $req = $litepay->__call('pay', [[ + 'vendor' => ExtensionHelper::getConfig('LitePay', 'merchant_id'), + 'secret' => ExtensionHelper::getConfig('LitePay', 'secret'), + 'invoice' => $invoiceId, + 'price' => number_format($total, 2, '.', ''), + 'currency' => ExtensionHelper::getCurrency(), + 'callbackUrl' => url('/extensions/litepay/webhook') . '?invoiceId=' . $invoiceId . '&secret=' . ExtensionHelper::getConfig('LitePay', 'secret'), + 'returnUrl' => route('clients.invoice.show', $invoiceId), + ]]); + + return $req->url; + } + + public function webhook(Request $request) + { + $input = $request->all(); + $invoiceId = $input['invoiceId']; + $secret = $input['secret']; + if (!isset($invoiceId) || !isset($secret)) + return; + if ($secret !== ExtensionHelper::getConfig('LitePay', 'secret')) + return; + ExtensionHelper::paymentDone($invoiceId); + + // Return *ok* + return response('*ok*'); + } +} diff --git a/Gateways/LitePay/index.php b/Gateways/LitePay/index.php deleted file mode 100644 index a0bb6d0..0000000 --- a/Gateways/LitePay/index.php +++ /dev/null @@ -1,55 +0,0 @@ - 'secret', - 'friendlyName' => 'Secret', - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'merchant_id', - 'friendlyName' => 'VENDOR ID', - 'type' => 'text', - 'required' => true, - ], - ]; -} - -function LitePay_pay($total, $products, $invoiceId) -{ - include_once __DIR__ . '/litepay.php'; - - $litepay = new litepay('merchant'); - $req = $litepay->__call('pay', [[ - 'vendor' => ExtensionHelper::getConfig('LitePay', 'merchant_id'), - 'secret' => ExtensionHelper::getConfig('LitePay', 'secret'), - 'invoice' => $invoiceId, - 'price' => $total, - 'currency' => ExtensionHelper::getCurrency(), - 'callbackUrl' => url('/extensions/litepay/webhook') . '?invoiceId=' . $invoiceId . '&secret=' . ExtensionHelper::getConfig('LitePay', 'secret'), - 'returnUrl' => route('clients.invoice.show', $invoiceId), - ]]); - - return $req->url; -} - -function LitePay_webhook(Request $request) -{ - $input = $request->all(); - $invoiceId = $input['invoiceId']; - $secret = $input['secret']; - if (!isset($invoiceId) || !isset($secret)) - return; - if ($secret !== ExtensionHelper::getConfig('LitePay', 'secret')) - return; - ExtensionHelper::paymentDone($invoiceId); - - // Return *ok* - return response('*ok*'); -} diff --git a/Gateways/LitePay/litepay.php b/Gateways/LitePay/litepayclass.php similarity index 97% rename from Gateways/LitePay/litepay.php rename to Gateways/LitePay/litepayclass.php index 6c27f10..a352a88 100644 --- a/Gateways/LitePay/litepay.php +++ b/Gateways/LitePay/litepayclass.php @@ -1,10 +1,12 @@ 'Mollie', + 'version' => '1.0.0', + 'author' => 'Paymenter', + 'website' => 'https://paymenter.org', + ]; + } + + public function pay($total, $products, $orderId) + { + $url = 'https://api.mollie.com/v2/payments'; + $client_id = ExtensionHelper::getConfig('Mollie', 'api_key'); + $description = 'Products: '; + foreach ($products as $product) { + $description .= $product->name . ' x' . $product->quantity . ', '; + } + $response = Http::withHeaders([ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $client_id, + ])->post($url, [ + 'amount' => [ + 'currency' => ExtensionHelper::getCurrency(), + 'value' => number_format($total, 2, '.', ''), + ], + 'description' => $description, + 'redirectUrl' => route('clients.invoice.show', $orderId), + 'webhookUrl' => url('/extensions/mollie/webhook'), + 'metadata' => [ + 'order_id' => $orderId, + ], + ]); + + return $response->json()['_links']['checkout']['href']; + } + + public function webhook(Request $request) + { + $url = 'https://api.mollie.com/v2/payments/' . $request->id; + $client_id = ExtensionHelper::getConfig('Mollie', 'api_key'); + $response = Http::withHeaders([ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $client_id, + ])->get($url); + if ($response->json()['status'] == 'paid') { + $orderId = $response->json()['metadata']['order_id']; + ExtensionHelper::paymentDone($orderId, 'Mollie'); + } + + return $response->json(); + } + + public function getConfig() + { + return [ + [ + 'name' => 'api_key', + 'friendlyName' => 'API Key', + 'type' => 'text', + 'required' => true, + ], + ]; + } +} diff --git a/Gateways/Mollie/index.php b/Gateways/Mollie/index.php deleted file mode 100644 index 4d390ba..0000000 --- a/Gateways/Mollie/index.php +++ /dev/null @@ -1,59 +0,0 @@ -name . ' x' . $product->quantity . ', '; - } - $response = Http::withHeaders([ - 'Content-Type' => 'application/json', - 'Authorization' => 'Bearer ' . $client_id, - ])->post($url, [ - 'amount' => [ - 'currency' => ExtensionHelper::getCurrency(), - 'value' => number_format($total, 2, '.', ''), - ], - 'description' => $description, - 'redirectUrl' => route('clients.invoice.show', $orderId), - 'webhookUrl' => url('/extensions/mollie/webhook'), - 'metadata' => [ - 'order_id' => $orderId, - ], - ]); - - return $response->json()['_links']['checkout']['href']; -} - -function Mollie_webhook($request) -{ - $url = 'https://api.mollie.com/v2/payments/' . $request->id; - $client_id = ExtensionHelper::getConfig('Mollie', 'api_key'); - $response = Http::withHeaders([ - 'Content-Type' => 'application/json', - 'Authorization' => 'Bearer ' . $client_id, - ])->get($url); - if ($response->json()['status'] == 'paid') { - $orderId = $response->json()['metadata']['order_id']; - ExtensionHelper::paymentDone($orderId); - } - - return $response->json(); -} - -function Mollie_getConfig() -{ - return [ - [ - 'name' => 'api_key', - 'friendlyName' => 'API Key', - 'type' => 'text', - 'required' => true, - ], - ]; -} diff --git a/Gateways/Mollie/routes.php b/Gateways/Mollie/routes.php index 7dc3008..3557565 100644 --- a/Gateways/Mollie/routes.php +++ b/Gateways/Mollie/routes.php @@ -2,8 +2,5 @@ use Illuminate\Support\Facades\Route; -include_once __DIR__ . '/index.php'; -Route::post('/mollie/webhook', function () { - Mollie_webhook(request()); -}); +Route::post('/mollie/webhook', [App\Extensions\Gateways\Mollie\Mollie::class, 'webhook']); diff --git a/Gateways/PayPal/PayPal.php b/Gateways/PayPal/PayPal.php new file mode 100644 index 0000000..2b8aadc --- /dev/null +++ b/Gateways/PayPal/PayPal.php @@ -0,0 +1,128 @@ + 'PayPal', + 'version' => '1.0.0', + 'author' => 'Paymenter', + 'website' => 'https://paymenter.org', + ]; + } + + public function pay($total, $products, $orderId) + { + $client_id = ExtensionHelper::getConfig('PayPal', 'client_id'); + $client_secret = ExtensionHelper::getConfig('PayPal', 'client_secret'); + $live = ExtensionHelper::getConfig('PayPal', 'live'); + if ($live) { + $url = 'https://api-m.paypal.com'; + } else { + $url = 'https://api-m.sandbox.paypal.com'; + } + + $response = Http::withHeaders([ + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Authorization' => 'Basic ' . base64_encode($client_id . ':' . $client_secret), + ])->asForm()->post($url . '/v1/oauth2/token', [ + 'grant_type' => 'client_credentials', + ]); + + $token = $response->json()['access_token']; + $response = Http::withHeaders([ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $token, + 'PayPal-Request-Id' => $orderId, + ])->post($url . '/v2/checkout/orders', [ + 'intent' => 'CAPTURE', + 'purchase_units' => [ + [ + 'reference_id' => $orderId, + 'amount' => [ + 'currency_code' => ExtensionHelper::getCurrency(), + 'value' => round($total, 2) + ], + ], + ], + 'payment_source' => [ + 'paypal' => [ + 'experience_context' => [ + 'cancel_url' => route('clients.invoice.show', $orderId), + 'return_url' => route('clients.invoice.show', $orderId), + 'brand_name' => config('app.name', 'Paymenter'), + 'shipping_preference' => 'NO_SHIPPING', + 'user_action' => 'PAY_NOW', + 'payment_method_preference' => 'IMMEDIATE_PAYMENT_REQUIRED', + ] + ] + ], + ]); + + if ($response->failed()) { + ExtensionHelper::error('PayPal', $response->json()); + } + + return $response->json()['links'][1]['href']; + } + + public function webhook($request) + { + $body = $request->getContent(); + $sigString = $request->header('PAYPAL-TRANSMISSION-ID') . '|' . $request->header('PAYPAL-TRANSMISSION-TIME') . '|' . ExtensionHelper::getConfig('PayPal', 'webhookId') . '|' . crc32($body); + $pubKey = openssl_pkey_get_public(file_get_contents($request->header('PAYPAL-CERT-URL'))); + $details = openssl_pkey_get_details($pubKey); + $verifyResult = openssl_verify( + $sigString, + base64_decode( + $request->header('PAYPAL-TRANSMISSION-SIG') + ), + $details['key'], + 'sha256WithRSAEncryption' + ); + if ($verifyResult === 1) { + $data = json_decode($body, true); + if ($data['event_type'] == 'CHECKOUT.ORDER.APPROVED') { + $orderId = $data['resource']['purchase_units'][0]['reference_id']; + ExtensionHelper::paymentDone($orderId, 'PayPal', $data['resource']['id'] ?? null); + } + } + } + + public function getConfig() + { + return [ + [ + 'name' => 'client_id', + 'type' => 'text', + 'friendlyName' => 'Client ID', + 'required' => true, + ], + [ + 'name' => 'client_secret', + 'type' => 'text', + 'friendlyName' => 'Client Secret', + 'required' => true, + ], + [ + 'name' => 'live', + 'type' => 'boolean', + 'friendlyName' => 'Live mode', + 'required' => false, + ], + [ + 'name' => 'webhookId', + 'type' => 'text', + 'friendlyName' => 'Webhook ID', + 'required' => true, + ], + ]; + } +} diff --git a/Gateways/PayPal/index.php b/Gateways/PayPal/index.php deleted file mode 100644 index 24daba2..0000000 --- a/Gateways/PayPal/index.php +++ /dev/null @@ -1,105 +0,0 @@ - 'application/x-www-form-urlencoded', - 'Authorization' => 'Basic ' . base64_encode($client_id . ':' . $client_secret), - ])->asForm()->post($url . '/v1/oauth2/token', [ - 'grant_type' => 'client_credentials', - ]); - - $token = $response->json()['access_token']; - $response = Http::withHeaders([ - 'Content-Type' => 'application/json', - 'Authorization' => 'Bearer ' . $token, - ])->post($url . '/v2/checkout/orders', [ - 'intent' => 'CAPTURE', - 'purchase_units' => [ - [ - 'reference_id' => $orderId, - 'amount' => [ - 'currency_code' => ExtensionHelper::getCurrency(), - 'value' => round($total, 2) - ], - ], - ], - 'application_context' => [ - 'cancel_url' => route('clients.invoice.show', $orderId), - 'return_url' => route('clients.invoice.show', $orderId), - 'brand_name' => config('app.name', 'Paymenter'), - 'shipping_preference' => 'NO_SHIPPING', - ], - ]); - - if($response->failed()) { - ExtensionHelper::error('PayPal', $response->json()); - } - - return $response->json()['links'][1]['href']; -} - -function PayPal_webhook($request) -{ - $body = $request->getContent(); - $sigString = $request->header('PAYPAL-TRANSMISSION-ID') . '|' . $request->header('PAYPAL-TRANSMISSION-TIME') . '|' . ExtensionHelper::getConfig('PayPal', 'webhookId') . '|' . crc32($body); - $pubKey = openssl_pkey_get_public(file_get_contents($request->header('PAYPAL-CERT-URL'))); - $details = openssl_pkey_get_details($pubKey); - $verifyResult = openssl_verify( - $sigString, - base64_decode( - $request->header('PAYPAL-TRANSMISSION-SIG') - ), - $details['key'], - 'sha256WithRSAEncryption' - ); - if ($verifyResult === 1) { - $data = json_decode($body, true); - if ($data['event_type'] == 'CHECKOUT.ORDER.APPROVED') { - $orderId = $data['resource']['purchase_units'][0]['reference_id']; - ExtensionHelper::paymentDone($orderId); - } - } -} - -function PayPal_getConfig() -{ - return [ - [ - 'name' => 'client_id', - 'type' => 'text', - 'friendlyName' => 'Client ID', - 'required' => true, - ], - [ - 'name' => 'client_secret', - 'type' => 'text', - 'friendlyName' => 'Client Secret', - 'required' => true, - ], - [ - 'name' => 'live', - 'type' => 'boolean', - 'friendlyName' => 'Live mode', - 'required' => false, - ], - [ - 'name' => 'webhookId', - 'type' => 'text', - 'friendlyName' => 'Webhook ID', - 'required' => true, - ], - ]; -} diff --git a/Gateways/PayPal/routes.php b/Gateways/PayPal/routes.php index 6895c99..211a613 100644 --- a/Gateways/PayPal/routes.php +++ b/Gateways/PayPal/routes.php @@ -2,8 +2,5 @@ use Illuminate\Support\Facades\Route; -include_once __DIR__ . '/index.php'; -Route::post('/paypal/webhook', function () { - PayPal_webhook(request()); -}); +Route::post('/paypal/webhook', [App\Extensions\Gateways\PayPal\PayPal::class, 'webhook'])->name('paypal.webhook'); diff --git a/Gateways/PayPal_IPN/PayPal_IPN.php b/Gateways/PayPal_IPN/PayPal_IPN.php new file mode 100644 index 0000000..b85b074 --- /dev/null +++ b/Gateways/PayPal_IPN/PayPal_IPN.php @@ -0,0 +1,115 @@ + 'PayPal IPN', + 'version' => '1.0.0', + 'author' => 'Paymenter', + 'website' => 'https://paymenter.org', + ]; + } + + public function pay($total, $products, $orderId) + { + // Redirect to PayPal + $paypal_url = ExtensionHelper::getConfig('PayPal_IPN', 'live') ? 'https://www.paypal.com/cgi-bin/webscr' : 'https://www.sandbox.paypal.com/cgi-bin/webscr'; + $paypal_email = ExtensionHelper::getConfig('PayPal_IPN', 'paypal_email'); + $return_url = route('clients.invoice.show', $orderId); + $cancel_url = route('clients.invoice.show', $orderId); + $notify_url = route('paypal_ipn.webhook'); + $currency = ExtensionHelper::getCurrency(); + $item_name = ''; + foreach ($products as $product) { + $item_name .= $product['name'] . ', '; + } + $item_name = substr($item_name, 0, -2); + $item_number = $orderId; + // Force amount to be a .00 float + $amount = number_format($total, 2, '.', ''); + $custom = $orderId; + $querystring = '?cmd=_xclick&'; + $querystring .= 'business=' . urlencode($paypal_email) . '&'; + $querystring .= 'return=' . urlencode(stripslashes($return_url)) . '&'; + $querystring .= 'cancel_return=' . urlencode(stripslashes($cancel_url)) . '&'; + $querystring .= 'notify_url=' . urlencode($notify_url) . '&'; + $querystring .= 'item_name=' . urlencode($item_name) . '&'; + $querystring .= 'item_number=' . urlencode($item_number) . '&'; + $querystring .= 'amount=' . urlencode($amount) . '&'; + $querystring .= 'custom=' . urlencode($custom) . '&'; + $querystring .= 'currency_code=' . urlencode($currency); + + return $paypal_url . $querystring; + } + + public function getConfig() + { + return [ + 'paypal_id' => [ + 'type' => 'text', + 'name' => 'paypal_email', + 'friendlyName' => 'PayPal Email', + 'description' => 'Your PayPal email address', + 'required' => true, + ], + 'live' => [ + 'type' => 'boolean', + 'name' => 'live', + 'friendlyName' => 'Live', + 'description' => 'Check this box if you want to use the live PayPal API', + 'required' => false, + ], + ]; + } + + public function webhook() + { + $raw_post_data = file_get_contents('php://input'); + $raw_post_array = explode('&', $raw_post_data); + $myPost = []; + foreach ($raw_post_array as $keyval) { + $keyval = explode('=', $keyval); + if (count($keyval) == 2) { + $myPost[$keyval[0]] = urldecode($keyval[1]); + } + } + $req = 'cmd=_notify-validate'; + foreach ($myPost as $key => $value) { + $value = urlencode($value); + + $req .= "&$key=$value"; + } + + if (ExtensionHelper::getConfig('PayPal_IPN', 'live')) { + $ch = curl_init('https://ipnpb.paypal.com/cgi-bin/webscr'); + } else { + $ch = curl_init('https://ipnpb.sandbox.paypal.com/cgi-bin/webscr'); + } + curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $req); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); + curl_setopt($ch, CURLOPT_FORBID_REUSE, 1); + curl_setopt($ch, CURLOPT_HTTPHEADER, ['Connection: Close']); + if (!($res = curl_exec($ch))) { + error_log('Got ' . curl_error($ch) . ' when processing IPN data'); + curl_close($ch); + exit; + } + curl_close($ch); + if ($res == 'VERIFIED') { + error_log($myPost['custom']); + ExtensionHelper::paymentDone($myPost['custom']); + } + header('HTTP/1.1 200 OK'); + } +} diff --git a/Gateways/PayPal_IPN/index.php b/Gateways/PayPal_IPN/index.php deleted file mode 100644 index 8fd001c..0000000 --- a/Gateways/PayPal_IPN/index.php +++ /dev/null @@ -1,99 +0,0 @@ - [ - 'type' => 'text', - 'name' => 'paypal_email', - 'friendlyName' => 'PayPal Email', - 'description' => 'Your PayPal email address', - 'required' => true, - ], - 'live' => [ - 'type' => 'boolean', - 'name' => 'live', - 'friendlyName' => 'Live', - 'description' => 'Check this box if you want to use the live PayPal API', - 'required' => false, - ], - ]; -} - -function PayPal_IPN_webhook($request) -{ - $raw_post_data = file_get_contents('php://input'); - $raw_post_array = explode('&', $raw_post_data); - $myPost = []; - foreach ($raw_post_array as $keyval) { - $keyval = explode('=', $keyval); - if (count($keyval) == 2) { - $myPost[$keyval[0]] = urldecode($keyval[1]); - } - } - $req = 'cmd=_notify-validate'; - foreach ($myPost as $key => $value) { - $value = urlencode($value); - - $req .= "&$key=$value"; - } - - if (ExtensionHelper::getConfig('PayPal_IPN', 'live')) { - $ch = curl_init('https://ipnpb.paypal.com/cgi-bin/webscr'); - } else { - $ch = curl_init('https://ipnpb.sandbox.paypal.com/cgi-bin/webscr'); - } - curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_POSTFIELDS, $req); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); - curl_setopt($ch, CURLOPT_FORBID_REUSE, 1); - curl_setopt($ch, CURLOPT_HTTPHEADER, ['Connection: Close']); - if (!($res = curl_exec($ch))) { - error_log('Got ' . curl_error($ch) . ' when processing IPN data'); - curl_close($ch); - exit; - } - curl_close($ch); - if ($res == 'VERIFIED') { - error_log($myPost['custom']); - ExtensionHelper::paymentDone($myPost['custom']); - } - header('HTTP/1.1 200 OK'); -} diff --git a/Gateways/PayPal_IPN/routes.php b/Gateways/PayPal_IPN/routes.php index 98a70c0..51984f8 100644 --- a/Gateways/PayPal_IPN/routes.php +++ b/Gateways/PayPal_IPN/routes.php @@ -2,8 +2,4 @@ use Illuminate\Support\Facades\Route; -include_once __DIR__ . '/index.php'; - -Route::post('/paypal_ipn/webhook', function () { - PayPal_IPN_webhook(request()); -})->name('paypal_ipn.webhook'); +Route::post('/paypal_ipn/webhook', [\App\Extensions\Gateways\PayPal_IPN\PayPal_IPN::class, 'webhook'])->name('paypal_ipn.webhook'); diff --git a/Gateways/PayU/PayU.php b/Gateways/PayU/PayU.php new file mode 100644 index 0000000..566ea67 --- /dev/null +++ b/Gateways/PayU/PayU.php @@ -0,0 +1,143 @@ + 'PayU', + 'version' => '1.0.0', + 'author' => 'Paymenter', + 'website' => 'https://paymenter.org', + ]; + } + + public function getConfig() + { + return [ + [ + 'name' => 'merchant_key', + 'friendlyName' => 'Merchant Key', + 'type' => 'text', + 'required' => true, + ], + [ + 'name' => 'merchant_salt', + 'friendlyName' => 'Merchant Salt V1', + 'type' => 'text', + 'required' => true, + ], + [ + 'name' => 'test_mode', + 'friendlyName' => 'Test Mode', + 'type' => 'boolean', + 'required' => false, + ], + [ + 'name' => 'test_merchant_key', + 'friendlyName' => 'Test Merchant Key', + 'type' => 'text', + 'required' => false, + ], + [ + 'name' => 'test_merchant_salt', + 'friendlyName' => 'Test Merchant Salt V1', + 'type' => 'text', + 'required' => false, + ], + ]; + } + + public function pay($total, $products, $invoiceId) + { + $apiKey = ExtensionHelper::getConfig('PayU', 'test_mode') ? ExtensionHelper::getConfig('PayU', 'test_merchant_key') : ExtensionHelper::getConfig('PayU', 'merchant_key'); + $salt = ExtensionHelper::getConfig('PayU', 'test_mode') ? ExtensionHelper::getConfig('PayU', 'test_merchant_salt') : ExtensionHelper::getConfig('PayU', 'merchant_salt'); + $txnId = $invoiceId; + $amount = $total; + $productInfo = $products[0]->name; + $firstName = auth()->user()->name; + $email = auth()->user()->email; + $phone = auth()->user()->phone; + $surl = route('payu.success'); + $furl = route('payu.cancel'); + $hashString = "$apiKey|$txnId|$amount|$productInfo|$firstName|$email|||||||||||$salt"; + $hash = hash('sha512', $hashString); + + $data = array( + 'key' => $apiKey, + 'txnid' => $txnId, + 'amount' => $amount, + 'productinfo' => $productInfo, + 'firstname' => $firstName, + 'email' => $email, + 'phone' => $phone, + 'surl' => $surl, + 'furl' => $furl, + 'hash' => $hash, + ); + if (ExtensionHelper::getConfig('PayU', 'test_mode')) { + $url = "https://test.payu.in/_payment"; + } else { + $url = "https://secure.payu.in/_payment"; + } + + echo "
"; + foreach ($data as $key => $value) { + echo ""; + } + echo "
"; + echo ""; + exit; + } + + + + public function success(Request $request) + { + $posted = $request->all(); + $orderId = $posted['txnid']; + $apiKey = ExtensionHelper::getConfig('PayU', 'test_mode') ? ExtensionHelper::getConfig('PayU', 'test_merchant_key') : ExtensionHelper::getConfig('PayU', 'merchant_key'); + $salt = ExtensionHelper::getConfig('PayU', 'test_mode') ? ExtensionHelper::getConfig('PayU', 'test_merchant_salt') : ExtensionHelper::getConfig('PayU', 'merchant_salt'); + $hashString = "$apiKey|verify_payment|$orderId|$salt"; + $hash = hash('sha512', $hashString); + + $data = array( + 'key' => $apiKey, + 'command' => 'verify_payment', + 'var1' => $orderId, + 'hash' => $hash, + ); + + if (ExtensionHelper::getConfig('PayU', 'test_mode')) { + $url = "https://test.payu.in/merchant/postservice?form=2"; + } else { + $url = "https://info.payu.in/merchant/postservice?form=2"; + } + + $response = Http::withHeaders([ + 'Content-Type' => 'application/x-www-form-urlencoded', + ])->asForm()->post($url, $data); + + $response = $response->json(); + + if ($response['transaction_details'][$orderId]['status'] == 'success') { + ExtensionHelper::paymentDone($orderId); + return redirect()->route('clients.invoice.show', $orderId)->with('success', 'Payment Successful'); + } else { + return redirect()->route('clients.invoice.show', $orderId)->with('error', 'Payment Failed'); + } + } + + public function cancel(Request $request) + { + $posted = $request->all(); + return redirect()->route('clients.invoice.show', $posted['txnid'])->with('error', 'Payment Cancelled'); + } +} diff --git a/Gateways/PayU/index.php b/Gateways/PayU/index.php deleted file mode 100644 index 9adaa67..0000000 --- a/Gateways/PayU/index.php +++ /dev/null @@ -1,127 +0,0 @@ - 'merchant_key', - 'friendlyName' => 'Merchant Key', - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'merchant_salt', - 'friendlyName' => 'Merchant Salt V1', - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'test_mode', - 'friendlyName' => 'Test Mode', - 'type' => 'boolean', - 'required' => false, - ], - [ - 'name' => 'test_merchant_key', - 'friendlyName' => 'Test Merchant Key', - 'type' => 'text', - 'required' => false, - ], - [ - 'name' => 'test_merchant_salt', - 'friendlyName' => 'Test Merchant Salt V1', - 'type' => 'text', - 'required' => false, - ], - ]; -} - -function PayU_pay($total, $products, $invoiceId) -{ - $apiKey = ExtensionHelper::getConfig('PayU', 'test_mode') ? ExtensionHelper::getConfig('PayU', 'test_merchant_key') : ExtensionHelper::getConfig('PayU', 'merchant_key'); - $salt = ExtensionHelper::getConfig('PayU', 'test_mode') ? ExtensionHelper::getConfig('PayU', 'test_merchant_salt') : ExtensionHelper::getConfig('PayU', 'merchant_salt'); - $txnId = $invoiceId; - $amount = $total; - $productInfo = $products[0]->name; - $firstName = auth()->user()->name; - $email = auth()->user()->email; - $phone = auth()->user()->phone; - $surl = route('payu.success'); - $furl = route('payu.cancel'); - $hashString = "$apiKey|$txnId|$amount|$productInfo|$firstName|$email|||||||||||$salt"; - $hash = hash('sha512', $hashString); - - $data = array( - 'key' => $apiKey, - 'txnid' => $txnId, - 'amount' => $amount, - 'productinfo' => $productInfo, - 'firstname' => $firstName, - 'email' => $email, - 'phone' => $phone, - 'surl' => $surl, - 'furl' => $furl, - 'hash' => $hash, - ); - if (ExtensionHelper::getConfig('PayU', 'test_mode')) { - $url = "https://test.payu.in/_payment"; - } else { - $url = "https://secure.payu.in/_payment"; - } - - echo "
"; - foreach ($data as $key => $value) { - echo ""; - } - echo "
"; - echo ""; - exit; -} - - - -function PayU_success(Request $request) -{ - $posted = $request->all(); - $orderId = $posted['txnid']; - $apiKey = ExtensionHelper::getConfig('PayU', 'test_mode') ? ExtensionHelper::getConfig('PayU', 'test_merchant_key') : ExtensionHelper::getConfig('PayU', 'merchant_key'); - $salt = ExtensionHelper::getConfig('PayU', 'test_mode') ? ExtensionHelper::getConfig('PayU', 'test_merchant_salt') : ExtensionHelper::getConfig('PayU', 'merchant_salt'); - $hashString = "$apiKey|verify_payment|$orderId|$salt"; - $hash = hash('sha512', $hashString); - - $data = array( - 'key' => $apiKey, - 'command' => 'verify_payment', - 'var1' => $orderId, - 'hash' => $hash, - ); - - if (ExtensionHelper::getConfig('PayU', 'test_mode')) { - $url = "https://test.payu.in/merchant/postservice?form=2"; - } else { - $url = "https://info.payu.in/merchant/postservice?form=2"; - } - - $response = Http::withHeaders([ - 'Content-Type' => 'application/x-www-form-urlencoded', - ])->asForm()->post($url, $data); - - $response = $response->json(); - - if ($response['transaction_details'][$orderId]['status'] == 'success') { - ExtensionHelper::paymentDone($orderId); - return redirect()->route('clients.invoice.show', $orderId)->with('success', 'Payment Successful'); - } else { - return redirect()->route('clients.invoice.show', $orderId)->with('error', 'Payment Failed'); - } -} - -function PayU_cancel(Request $request) -{ - $posted = $request->all(); - return redirect()->route('clients.invoice.show', $posted['txnid'])->with('error', 'Payment Cancelled'); -} diff --git a/Gateways/PayU/routes.php b/Gateways/PayU/routes.php index 4f5ff8b..d330380 100644 --- a/Gateways/PayU/routes.php +++ b/Gateways/PayU/routes.php @@ -2,12 +2,6 @@ use Illuminate\Support\Facades\Route; -include_once __DIR__ . '/index.php'; +Route::post('/payu/cancel', [\App\Extensions\Gateways\PayU\PayU::class, 'cancel'])->name('payu.cancel'); -Route::post('/payu/cancel', function () { - return PayU_cancel(request()); -})->name('payu.cancel'); - -Route::post('/payu/success', function () { - return PayU_success(request()); -})->name('payu.success'); +Route::post('/payu/success', [\App\Extensions\Gateways\PayU\PayU::class, 'success'])->name('payu.success'); diff --git a/Gateways/Stripe/Stripe.php b/Gateways/Stripe/Stripe.php new file mode 100644 index 0000000..895663f --- /dev/null +++ b/Gateways/Stripe/Stripe.php @@ -0,0 +1,139 @@ + 'Stripe', + 'version' => '1.0.0', + 'author' => 'Paymenter', + 'website' => 'https://paymenter.org', + ]; + } + + public function getUrl($total, $products, $orderId) + { + $client = $this->stripeClient(); + // Create array with all the products + $items = []; + foreach ($products as $product) { + $items[] = [ + 'price_data' => [ + 'currency' => ExtensionHelper::getCurrency(), + 'product_data' => [ + 'name' => $product->name, + ], + 'unit_amount' => round($product->price / $product->quantity * 100, 0) + ], + 'quantity' => $product->quantity, + ]; + } + $order = $client->checkout->sessions->create([ + 'line_items' => $items, + 'mode' => 'payment', + 'success_url' => route('clients.invoice.show', $orderId), + 'cancel_url' => route('clients.invoice.show', $orderId), + 'customer_email' => auth()->user()->email, + 'metadata' => [ + 'user_id' => auth()->user()->id, + 'order_id' => $orderId, + ], + ]); + + return $order; + } + + public function webhook(Request $request) + { + $payload = $request->getContent(); + $sig_header = $request->header('stripe-signature'); + $endpoint_secret = ExtensionHelper::getConfig('Stripe', 'stripe_webhook_secret'); + $event = null; + + try { + $event = \Stripe\Webhook::constructEvent( + $payload, + $sig_header, + $endpoint_secret + ); + } catch (\UnexpectedValueException $e) { + // Invalid payload + http_response_code(400); + exit; + } catch (\Stripe\Exception\SignatureVerificationException $e) { + // Invalid signature + http_response_code(400); + exit; + } + if ($event->type == 'checkout.session.completed') { + $order = $event->data->object; + $order_id = $order->metadata->order_id; + ExtensionHelper::paymentDone($order_id, 'Stripe', $order->payment_intent); + } + } + + public function stripeClient() + { + if (!ExtensionHelper::getConfig('Stripe', 'stripe_test_mode')) { + $stripe = new StripeClient( + ExtensionHelper::getConfig('Stripe', 'stripe_secret_key') + ); + } else { + $stripe = new StripeClient( + ExtensionHelper::getConfig('Stripe', 'stripe_test_key') + ); + } + + return $stripe; + } + + public function pay($total, $products, $orderId) + { + $stripe = $this->stripeClient(); + $order = $this->getUrl($total, $products, $orderId); + + return $stripe->checkout->sessions->retrieve($order->id, [])->url; + } + + public function getConfig() + { + return [ + [ + 'name' => 'stripe_secret_key', + 'friendlyName' => 'Stripe Secret Key', + 'type' => 'text', + 'description' => 'Stripe secret key', + 'required' => true, + ], + [ + 'name' => 'stripe_webhook_secret', + 'friendlyName' => 'Stripe webhook secret', + 'type' => 'text', + 'description' => 'Stripe webhook secret', + 'required' => true, + ], + [ + 'name' => 'stripe_test_mode', + 'friendlyName' => 'Stripe test mode', + 'type' => 'boolean', + 'description' => 'Stripe test mode', + 'required' => false, + ], + [ + 'name' => 'stripe_test_key', + 'friendlyName' => 'Stripe test key', + 'type' => 'text', + 'description' => 'Stripe test key', + 'required' => false, + ], + ]; + } +} diff --git a/Gateways/Stripe/index.php b/Gateways/Stripe/index.php deleted file mode 100644 index c51012b..0000000 --- a/Gateways/Stripe/index.php +++ /dev/null @@ -1,122 +0,0 @@ - [ - 'currency' => ExtensionHelper::getCurrency(), - 'product_data' => [ - 'name' => $product->name, - ], - 'unit_amount' => $product->price * 100, - ], - 'quantity' => $product->quantity, - ]; - } - $order = $client->checkout->sessions->create([ - 'line_items' => $items, - 'mode' => 'payment', - 'success_url' => route('clients.invoice.show', $orderId), - 'cancel_url' => route('clients.invoice.show', $orderId), - 'customer_email' => auth()->user()->email, - 'metadata' => [ - 'user_id' => auth()->user()->id, - 'order_id' => $orderId, - ], - ]); - - return $order; -} - -function Stripe_webhook($request) -{ - $payload = $request->getContent(); - $sig_header = $request->header('stripe-signature'); - $endpoint_secret = ExtensionHelper::getConfig('Stripe', 'stripe_webhook_secret'); - $event = null; - - try { - $event = \Stripe\Webhook::constructEvent( - $payload, - $sig_header, - $endpoint_secret - ); - } catch (\UnexpectedValueException $e) { - // Invalid payload - http_response_code(400); - exit; - } catch (\Stripe\Exception\SignatureVerificationException $e) { - // Invalid signature - http_response_code(400); - exit; - } - if ($event->type == 'checkout.session.completed') { - $order = $event->data->object; - $order_id = $order->metadata->order_id; - ExtensionHelper::paymentDone($order_id); - } -} - -function stripeClient() -{ - if (!ExtensionHelper::getConfig('Stripe', 'stripe_test_mode')) { - $stripe = new StripeClient( - ExtensionHelper::getConfig('Stripe', 'stripe_secret_key') - ); - } else { - $stripe = new StripeClient( - ExtensionHelper::getConfig('Stripe', 'stripe_test_key') - ); - } - - return $stripe; -} - -function Stripe_pay($total, $products, $orderId) -{ - $stripe = stripeClient(); - $order = Stripe_getUrl($total, $products, $orderId); - - return $stripe->checkout->sessions->retrieve($order->id, [])->url; -} - -function Stripe_getConfig() -{ - return [ - [ - 'name' => 'stripe_secret_key', - 'friendlyName' => 'Stripe Secret Key', - 'type' => 'text', - 'description' => 'Stripe secret key', - 'required' => true, - ], - [ - 'name' => 'stripe_webhook_secret', - 'friendlyName' => 'Stripe webhook secret', - 'type' => 'text', - 'description' => 'Stripe webhook secret', - 'required' => true, - ], - [ - 'name' => 'stripe_test_mode', - 'friendlyName' => 'Stripe test mode', - 'type' => 'boolean', - 'description' => 'Stripe test mode', - 'required' => false, - ], - [ - 'name' => 'stripe_test_key', - 'friendlyName' => 'Stripe test key', - 'type' => 'text', - 'description' => 'Stripe test key', - 'required' => false, - ], - ]; -} diff --git a/Gateways/Stripe/routes.php b/Gateways/Stripe/routes.php index f741a51..04d1269 100644 --- a/Gateways/Stripe/routes.php +++ b/Gateways/Stripe/routes.php @@ -2,8 +2,4 @@ use Illuminate\Support\Facades\Route; -include_once __DIR__ . '/index.php'; - -Route::post('/stripe/webhook', function () { - Stripe_webhook(request()); -}); +Route::post('/stripe/webhook', [App\Extensions\Gateways\Stripe\Stripe::class, 'webhook']); diff --git a/Gateways/StripeSofort/StripeSofort.php b/Gateways/StripeSofort/StripeSofort.php new file mode 100644 index 0000000..5878577 --- /dev/null +++ b/Gateways/StripeSofort/StripeSofort.php @@ -0,0 +1,98 @@ +Client(); + $order = $client->paymentIntents->create([ + 'confirm' => true, + 'amount' => $total * 100, + 'currency' => ExtensionHelper::getCurrency(), + 'payment_method_types' => ['sofort'], + 'payment_method_data' => ['type' => 'sofort', 'sofort' => ['country' => ExtensionHelper::getConfig('StripeSofort', 'country')]], + 'return_url' => route('clients.invoice.show', $orderId), + 'metadata' => [ + 'user_id' => auth()->user()->id, + 'order_id' => $orderId, + ], + ]); + + return $order; + } + + public function webhook($request) + { + $payload = $request->getContent(); + $sig_header = $request->header('stripe-signature'); + $endpoint_secret = ExtensionHelper::getConfig('Stripe', 'stripe_webhook_secret'); + $event = null; + + try { + $event = \Stripe\Webhook::constructEvent( + $payload, + $sig_header, + $endpoint_secret + ); + } catch (\UnexpectedValueException $e) { + // Invalid payload + http_response_code(400); + exit; + } catch (\Stripe\Exception\SignatureVerificationException $e) { + // Invalid signature + http_response_code(400); + exit; + } + if ($event->type == 'checkout.session.completed') { + $order = $event->data->object; + $order_id = $order->metadata->order_id; + ExtensionHelper::paymentDone($order_id); + } + } + + private function Client() + { + if (!ExtensionHelper::getConfig('Stripe', 'stripe_test_mode')) { + $stripe = new StripeClient( + ExtensionHelper::getConfig('Stripe', 'stripe_secret_key') + ); + } else { + $stripe = new StripeClient( + ExtensionHelper::getConfig('Stripe', 'stripe_test_key') + ); + } + + return $stripe; + } + + public function pay($total, $products, $orderId) + { + $order = $this->getUrl($total, $products, $orderId); + if ($order->status == 'requires_action' && $order->next_action->type == 'redirect_to_url') { + $url = $order->next_action->redirect_to_url->url; + return $url; + } + dd($order); + } + + public function getConfig() + { + return [ + [ + 'name' => 'country', + 'friendlyName' => 'Country short code', + 'type' => 'text', + 'description' => 'The country code for sofort. For example: DE, NL', + 'required' => true, + ], + ]; + } +} diff --git a/Gateways/StripeSofort/index.php b/Gateways/StripeSofort/index.php deleted file mode 100644 index c7d7db9..0000000 --- a/Gateways/StripeSofort/index.php +++ /dev/null @@ -1,90 +0,0 @@ -paymentIntents->create([ - 'confirm' => true, - 'amount' => $total * 100, - 'currency' => ExtensionHelper::getCurrency(), - 'payment_method_types' => ['sofort'], - 'payment_method_data' => ['type' => 'sofort', 'sofort' => ['country' => ExtensionHelper::getConfig('StripeSofort', 'country')]], - 'return_url' => route('clients.invoice.show', $orderId), - 'metadata' => [ - 'user_id' => auth()->user()->id, - 'order_id' => $orderId, - ], - ]); - - return $order; -} - -function StripeSofort_webhook($request) -{ - $payload = $request->getContent(); - $sig_header = $request->header('stripe-signature'); - $endpoint_secret = ExtensionHelper::getConfig('Stripe', 'stripe_webhook_secret'); - $event = null; - - try { - $event = \Stripe\Webhook::constructEvent( - $payload, - $sig_header, - $endpoint_secret - ); - } catch (\UnexpectedValueException $e) { - // Invalid payload - http_response_code(400); - exit; - } catch (\Stripe\Exception\SignatureVerificationException $e) { - // Invalid signature - http_response_code(400); - exit; - } - if ($event->type == 'checkout.session.completed') { - $order = $event->data->object; - $order_id = $order->metadata->order_id; - ExtensionHelper::paymentDone($order_id); - } -} - -function StripeSofortClient() -{ - if (!ExtensionHelper::getConfig('Stripe', 'stripe_test_mode')) { - $stripe = new StripeClient( - ExtensionHelper::getConfig('Stripe', 'stripe_secret_key') - ); - } else { - $stripe = new StripeClient( - ExtensionHelper::getConfig('Stripe', 'stripe_test_key') - ); - } - - return $stripe; -} - -function StripeSofort_pay($total, $products, $orderId) -{ - $order = StripeSofort_getUrl($total, $products, $orderId); - if ($order->status == 'requires_action' && $order->next_action->type == 'redirect_to_url') { - $url = $order->next_action->redirect_to_url->url; - return $url; - } - dd($order); - } - -function StripeSofort_getConfig() -{ - return [ - [ - 'name' => 'country', - 'friendlyName' => 'Country short code', - 'type' => 'text', - 'description' => 'The country code for sofort. For example: DE, NL', - 'required' => true, - ], - ]; -} diff --git a/Gateways/StripeSofort/routes.php b/Gateways/StripeSofort/routes.php index 5ce5a96..75c2df6 100644 --- a/Gateways/StripeSofort/routes.php +++ b/Gateways/StripeSofort/routes.php @@ -2,8 +2,4 @@ use Illuminate\Support\Facades\Route; -include_once __DIR__ . '/index.php'; - -Route::post('/stripesofort/webhook', function () { - StripeSofort_webhook(request()); -}); +Route::post('/stripesofort/webhook', [\App\Extensions\Gateways\StripeSofort\StripeSofort::class, 'webhook'])->name('stripesofort.webhook'); diff --git a/Gateways/Xendit/Xendit.php b/Gateways/Xendit/Xendit.php new file mode 100644 index 0000000..a8da2ca --- /dev/null +++ b/Gateways/Xendit/Xendit.php @@ -0,0 +1,74 @@ + 'Xendit', + 'version' => '1.0.0', + 'author' => 'Paymenter', + 'website' => 'https://paymenter.org', + ]; + } + + public function pay($total, $products, $orderId) + { + $url = 'https://api.xendit.co/v2/invoices'; + $apiKey = ExtensionHelper::getConfig('Xendit', 'api_key'); + // Encode to base64 + $apiKey = base64_encode($apiKey . ':'); + $description = 'Products: '; + foreach ($products as $product) { + $description .= $product->name . ' x' . $product->quantity . ', '; + } + $response = Http::withHeaders([ + 'Content-Type' => 'application/json', + 'Authorization' => 'Basic ' . $apiKey, + ])->post($url, [ + 'amount' => number_format($total, 2, '.', ''), + 'description' => $description, + 'success_redirect_url' => route('clients.invoice.show', $orderId), + 'failure_redirect_url' => route('clients.invoice.show', $orderId), + 'external_id' => (string) $orderId, + ]); + + return $response->json()['invoice_url']; + } + + public function webhook(Request $request) + { + if ($request->header('x-callback-token') != ExtensionHelper::getConfig('Xendit', 'callback')) { + return response()->json(['message' => 'Invalid callback token'], 403); + } + $json = $request->getContent(); + $json = json_decode($json, true); + ExtensionHelper::paymentDone($json['external_id'], 'Xendit'); + return response()->json(['message' => 'Webhook received'], 200); + } + + public function getConfig() + { + return [ + [ + 'name' => 'api_key', + 'friendlyName' => 'API Key', + 'type' => 'text', + 'required' => true, + ], + [ + 'name' => 'callback', + 'friendlyName' => 'Callback verification token', + 'type' => 'text', + 'required' => true, + ], + ]; + } +} diff --git a/Gateways/Xendit/index.php b/Gateways/Xendit/index.php deleted file mode 100644 index 5c19b52..0000000 --- a/Gateways/Xendit/index.php +++ /dev/null @@ -1,57 +0,0 @@ -name . ' x' . $product->quantity . ', '; - } - $response = Http::withHeaders([ - 'Content-Type' => 'application/json', - 'Authorization' => 'Basic ' . $apiKey, - ])->post($url, [ - 'amount' => number_format($total, 2, '.', ''), - 'description' => $description, - 'success_redirect_url' => route('clients.invoice.show', $orderId), - 'failure_redirect_url' => route('clients.invoice.show', $orderId), - 'external_id' => (string) $orderId, - ]); - - return $response->json()['invoice_url']; -} - -function Xendit_webhook($request) -{ - if ($request->header('x-callback-token') != ExtensionHelper::getConfig('Xendit', 'callback')) { - return response()->json(['message' => 'Invalid callback token'], 403); - } - $json = $request->getContent(); - $json = json_decode($json, true); - ExtensionHelper::paymentDone($json['external_id']); - response()->json(['message' => 'Webhook received'], 200); -} - -function Xendit_getConfig() -{ - return [ - [ - 'name' => 'api_key', - 'friendlyName' => 'API Key', - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'callback', - 'friendlyName' => 'Callback verification token', - 'type' => 'text', - 'required' => true, - ], - ]; -} diff --git a/Gateways/Xendit/routes.php b/Gateways/Xendit/routes.php index 000c381..5c0014e 100644 --- a/Gateways/Xendit/routes.php +++ b/Gateways/Xendit/routes.php @@ -2,8 +2,5 @@ use Illuminate\Support\Facades\Route; -include_once __DIR__ . '/index.php'; -Route::post('/xendit/webhook', function () { - Xendit_webhook(request()); -})->name('xendit.webhook'); +Route::post('/xendit/webhook', [App\Extensions\Gateways\Xendit\Xendit::class, 'webhook'])->name('xendit.webhook'); diff --git a/Servers/Convoy/Convoy.php b/Servers/Convoy/Convoy.php new file mode 100644 index 0000000..2c15041 --- /dev/null +++ b/Servers/Convoy/Convoy.php @@ -0,0 +1,274 @@ +hostname = ExtensionHelper::getConfig('Convoy', 'hostname'); + $this->apikey = ExtensionHelper::getConfig('Convoy', 'api_key'); + } + + public function getMetadata() + { + return [ + 'display_name' => 'Convoy', + 'version' => '1.3.0', + 'author' => 'Paymenter', + 'website' => 'https://paymenter.org', + ]; + } + + public function getConfig() + { + return [ + [ + 'name' => 'hostname', + 'type' => 'text', + 'friendlyName' => 'Hostname', + 'required' => true, + 'validation' => 'url:http,https' + ], + [ + 'name' => 'api_key', + 'type' => 'text', + 'friendlyName' => 'API Key', + 'required' => true + ] + ]; + } + + public function getProductConfig($options) + { + return [ + [ + 'name' => 'cpu', + 'type' => 'text', + 'friendlyName' => 'CPU Cores', + 'required' => true + ], + [ + 'name' => 'ram', + 'type' => 'text', + 'friendlyName' => 'RAM (MiB)', + 'required' => true + ], + [ + 'name' => 'disk', + 'type' => 'text', + 'friendlyName' => 'Disk (MiB)', + 'required' => true + ], + [ + 'name' => 'bandwidth', + 'type' => 'text', + 'friendlyName' => 'Bandwidth (MiB)', + 'required' => false + ], + [ + 'name' => 'snapshot', + 'type' => 'text', + 'friendlyName' => 'Amount of snapshots', + 'required' => true + ], + [ + 'name' => 'backups', + 'type' => 'text', + 'friendlyName' => 'Amount of backups', + 'required' => true + ], + [ + 'name' => 'node', + 'type' => 'dropdown', + 'friendlyName' => 'Nodes', + 'required' => true, + 'options' => $this->getNodes() + ], + [ + 'name' => 'auto_assign_ip', + 'type' => 'boolean', + 'friendlyName' => 'Auto assign IP', + 'required' => false + ], + ]; + } + + public function getUserConfig(Product $product) + { + $node = $product->settings()->where('name', 'node')->first(); + return [ + [ + 'name' => 'hostname', + 'type' => 'text', + 'friendlyName' => 'Hostname', + 'required' => true, + 'validation' => 'min:4|regex:/^[a-zA-Z0-9.-]+$/' + ], + [ + 'name' => 'os', + 'type' => 'dropdown', + 'friendlyName' => 'Operating System', + 'required' => true, + 'options' => $this->getOS($node->value) + ], + [ + 'name' => 'password', + 'type' => 'password', + 'friendlyName' => 'Password', + 'required' => true, + 'validation' => 'min:8|regex:/[a-z]/|regex:/[A-Z]/|regex:/[0-9]/|regex:/[@$!%*#?&]/' + ], + ]; + } + + public function createServer($user, $params, $order, $orderProduct, $configurableOptions) + { + $node = $configurableOptions['node'] ?? $params['node']; + $os = $params['config']['os']; + $hostname = $params['config']['hostname']; + $password = $params['config']['password']; + $cpu = $configurableOptions['cpu'] ?? $params['cpu']; + $ram = $configurableOptions['ram'] ?? $params['ram']; + $disk = $configurableOptions['disk'] ?? $params['disk']; + $bandwidth = $configurableOptions['bandwidth'] ?? $params['bandwidth']; + $snapshot = $configurableOptions['snapshot'] ?? $params['snapshot']; + $backups = $configurableOptions['backups'] ?? $params['backups']; + if ($params['auto_assign_ip']) { + $ip = $this->request('get', 'nodes/' . $node . '/addresses?filter[server_id]'); + + $ip = [$ip['data'][0]['id']]; + } else { + $ip = []; + } + + $data = [ + 'node_id' => (int) $node, + 'user_id' => $this->getUser($user, $params['config']['password']), + 'name' => $hostname . ' ' . $user->name, + 'hostname' => $hostname, + 'vmid' => null, + 'limits' => [ + 'cpu' => (int) $cpu, + 'memory' => $ram * 1024 * 1024, + 'disk' => $disk * 1024 * 1024, + 'snapshots' => (int) $snapshot, + 'bandwidth' => (int) $bandwidth == 0 ? null : (int) $bandwidth * 1024 * 1024, + 'backups' => (int) $backups == 0 ? null : (int) $backups, + 'address_ids' => $ip, + ], + 'account_password' => $password, + 'template_uuid' => $os, + 'should_create_server' => true, + 'start_on_completion' => false, + ]; + + + $server = $this->request('post', 'servers', $data); + + if(!isset($server['data'])){ + ExtensionHelper::error('Convoy', $server['message'] ?? 'Something went wrong'); + } + + ExtensionHelper::setOrderProductConfig('server_uuid', $server['data']['uuid'], $orderProduct->id); + + return $server['data']['id']; + } + + public function suspendServer($user, $params, $order, $orderProduct, $configurableOptions) + { + $this->request('post', 'servers/' . $params['config']['server_uuid'] . '/settings/suspend'); + } + + public function unsuspendServer($user, $params, $order, $orderProduct, $configurableOptions) + { + $this->request('post', 'servers/' . $params['config']['server_uuid'] . '/settings/unsuspend'); + } + + public function terminateServer($user, $params, $order, $orderProduct, $configurableOptions) + { + $this->request('delete', 'servers/' . $params['config']['server_uuid']); + } + + private function getUser($user, $password) + { + $userr = $this->request('get', 'users', [ + 'filter[email]' => $user->email + ]); + if (count($userr['data']) == 0) { + $userr = $this->request('post', 'users', [ + 'email' => $user->email, + 'password' => $password, + 'password_confirmation' => $password, + 'name' => $user->name, + 'root_admin' => false + ]); + + return $userr['data']['id']; + } + return $userr['data'][0]['id']; + } + + private function getOS($node) + { + $os = $this->request('get', 'nodes/' . $node . '/template-groups'); + $options = []; + foreach ($os['data'] as $os) { + foreach ($os['templates'] as $template) { + foreach ($template as $template1) { + $options[] = [ + 'value' => $template1['uuid'], + 'name' => $template1['name'] + ]; + } + } + } + return $options; + } + + private function getNodes() + { + $nodes = $this->request('get', 'nodes'); + $options = []; + foreach ($nodes['data'] as $node) { + $options[] = [ + 'value' => $node['id'], + 'name' => $node['name'] + ]; + } + return $options; + } + + private function request($method, $url, $data = []) + { + if (!empty($data)) { + $response = Http::withHeaders([ + 'Authorization' => 'Bearer ' . $this->apikey, + ])->acceptJson()->$method($this->hostname . '/api/application/' . $url, $data); + } else { + $response = Http::withHeaders([ + 'Authorization' => 'Bearer ' . $this->apikey, + ])->acceptJson()->$method($this->hostname . '/api/application/' . $url); + } + + return $response->json(); + } + + public function getLink($user, $params, $order, $orderProduct): bool|string + { + $server = $this->request('post', 'users/' . $this->getUser($user, $params['config']['password']) . '/generate-sso-token'); + return $this->hostname . '/authenticate?token=' . $server['data']['token']; + } +} \ No newline at end of file diff --git a/Servers/CyberPanel/CyberPanel.php b/Servers/CyberPanel/CyberPanel.php new file mode 100644 index 0000000..46c8848 --- /dev/null +++ b/Servers/CyberPanel/CyberPanel.php @@ -0,0 +1,138 @@ + 'CyberPanel', + 'version' => '1.0.0', + 'author' => 'Paymenter', + 'website' => 'https://paymenter.org', + ]; + } + + public function getConfig() + { + return [ + [ + 'name' => 'host', + 'friendlyName' => 'Url to CyberPanel server (with port)', + 'type' => 'text', + 'required' => true, + ], + [ + 'name' => 'username', + 'friendlyName' => 'Username', + 'type' => 'text', + 'required' => true, + ], + [ + 'name' => 'password', + 'friendlyName' => 'Password', + 'type' => 'text', + 'required' => true, + ] + ]; + } + + public function getProductConfig($options) + { + return [ + [ + 'name' => 'packageName', + 'friendlyName' => 'Package Name', + 'type' => 'text', + 'required' => true, + 'description' => 'Package Name for the CyberPanel server', + ], + ]; + } + + public function getUserConfig(Product $product) + { + return [ + [ + 'name' => 'domain', + 'friendlyName' => 'Domain', + 'type' => 'text', + 'required' => true, + 'description' => 'Domain for the webhost', + ], + [ + 'name' => 'username', + 'friendlyName' => 'Username', + 'type' => 'text', + 'required' => true, + 'description' => 'Username to login to the website', + ], + [ + 'name' => 'password', + 'friendlyName' => 'Password', + 'type' => 'text', + 'required' => true, + 'description' => 'Password to login to the website', + ] + ]; + } + + public function createServer($user, $params, $order, $product, $configurableOptions) + { + $response = Http::post(ExtensionHelper::getConfig('CyberPanel', 'host') . '/api/createWebsite', [ + 'adminUser' => ExtensionHelper::getConfig('CyberPanel', 'username'), + 'adminPass' => ExtensionHelper::getConfig('CyberPanel', 'password'), + 'domainName' => $params['config']['domain'], + 'packageName' => $params['packageName'], + 'ownerEmail' => $user->email, + 'websiteOwner' => $params['config']['username'], + 'ownerPassword' => $params['config']['password'], + ]); + if (!$response->successful()) { + ExtensionHelper::error('CyberPanel', 'Failed to create server: ' . $response->body()); + } + } + + public function suspendServer($user, $params, $order, $product, $configurableOptions) + { + $response = Http::post(ExtensionHelper::getConfig('CyberPanel', 'host') . '/api/suspendWebsite', [ + 'adminUser' => ExtensionHelper::getConfig('CyberPanel', 'username'), + 'adminPass' => ExtensionHelper::getConfig('CyberPanel', 'password'), + 'domainName' => $params['config']['domain'], + 'state' => 'Suspend', + ]); + if (!$response->successful()) { + ExtensionHelper::error('CyberPanel', 'Failed to suspend server: ' . $response->body()); + } + } + + public function unsuspendServer($user, $params, $order, $product, $configurableOptions) + { + $response = Http::post(ExtensionHelper::getConfig('CyberPanel', 'host') . '/api/suspendWebsite', [ + 'adminUser' => ExtensionHelper::getConfig('CyberPanel', 'username'), + 'adminPass' => ExtensionHelper::getConfig('CyberPanel', 'password'), + 'domainName' => $params['config']['domain'], + 'state' => 'Active', + ]); + if (!$response->successful()) { + ExtensionHelper::error('CyberPanel', 'Failed to unsuspend server: ' . $response->body()); + } + } + + public function terminateServer($user, $params, $order, $product, $configurableOptions) + { + $response = Http::post(ExtensionHelper::getConfig('CyberPanel', 'host') . '/api/deleteWebsite', [ + 'adminUser' => ExtensionHelper::getConfig('CyberPanel', 'username'), + 'adminPass' => ExtensionHelper::getConfig('CyberPanel', 'password'), + 'domainName' => $params['config']['domain'], + ]); + if (!$response->successful()) { + ExtensionHelper::error('CyberPanel', 'Failed to terminate server: ' . $response->body()); + } + } +} diff --git a/Servers/CyberPanel/index.php b/Servers/CyberPanel/index.php deleted file mode 100644 index 189d478..0000000 --- a/Servers/CyberPanel/index.php +++ /dev/null @@ -1,122 +0,0 @@ - 'host', - 'friendlyName' => 'Url to CyberPanel server (with port)', - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'username', - 'friendlyName' => 'Username', - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'password', - 'friendlyName' => 'Password', - 'type' => 'text', - 'required' => true, - ] - ]; -} - -function CyberPanel_getProductConfig() -{ - return [ - [ - 'name' => 'packageName', - 'friendlyName' => 'Package Name', - 'type' => 'text', - 'required' => true, - 'description' => 'Package Name for the CyberPanel server', - ], - ]; -} - -function CyberPanel_getUserConfig() -{ - return [ - [ - 'name' => 'domain', - 'friendlyName' => 'Domain', - 'type' => 'text', - 'required' => true, - 'description' => 'Domain for the webhost', - ], - [ - 'name' => 'username', - 'friendlyName' => 'Username', - 'type' => 'text', - 'required' => true, - 'description' => 'Username to login to the website', - ], - [ - 'name' => 'password', - 'friendlyName' => 'Password', - 'type' => 'text', - 'required' => true, - 'description' => 'Password to login to the website', - ] - ]; -} - -function CyberPanel_createServer($user, $params, $order, $product) -{ - $response = Http::post(ExtensionHelper::getConfig('CyberPanel', 'host') . '/api/createWebsite', [ - 'adminUser' => ExtensionHelper::getConfig('CyberPanel', 'username'), - 'adminPass' => ExtensionHelper::getConfig('CyberPanel', 'password'), - 'domainName' => $params['config']['domain'], - 'packageName' => $params['packageName'], - 'ownerEmail' => $user->email, - 'websiteOwner' => $params['config']['username'], - 'ownerPassword' => $params['config']['password'], - ]); - if(!$response->successful()){ - ExtensionHelper::error('CyberPanel', 'Failed to create server: ' . $response->body()); - } -} - -function CyberPanel_suspendServer($user, $params, $order, $product) -{ - $response = Http::post(ExtensionHelper::getConfig('CyberPanel', 'host') . '/api/suspendWebsite', [ - 'adminUser' => ExtensionHelper::getConfig('CyberPanel', 'username'), - 'adminPass' => ExtensionHelper::getConfig('CyberPanel', 'password'), - 'domainName' => $params['config']['domain'], - 'state' => 'Suspend', - ]); - if(!$response->successful()){ - ExtensionHelper::error('CyberPanel', 'Failed to suspend server: ' . $response->body()); - } -} - -function CyberPanel_unsuspendServer($user, $params, $order, $product) -{ - $response = Http::post(ExtensionHelper::getConfig('CyberPanel', 'host') . '/api/suspendWebsite', [ - 'adminUser' => ExtensionHelper::getConfig('CyberPanel', 'username'), - 'adminPass' => ExtensionHelper::getConfig('CyberPanel', 'password'), - 'domainName' => $params['config']['domain'], - 'state' => 'Active', - ]); - if(!$response->successful()){ - ExtensionHelper::error('CyberPanel', 'Failed to unsuspend server: ' . $response->body()); - } -} - -function CyberPanel_terminateServer($user, $params, $order, $product) -{ - $response = Http::post(ExtensionHelper::getConfig('CyberPanel', 'host') . '/api/deleteWebsite', [ - 'adminUser' => ExtensionHelper::getConfig('CyberPanel', 'username'), - 'adminPass' => ExtensionHelper::getConfig('CyberPanel', 'password'), - 'domainName' => $params['config']['domain'], - ]); - if(!$response->successful()){ - ExtensionHelper::error('CyberPanel', 'Failed to terminate server: ' . $response->body()); - } -} diff --git a/Servers/DirectAdmin/httpsocket.php b/Servers/DirectAdmin/DAHTTPSocket.php similarity index 99% rename from Servers/DirectAdmin/httpsocket.php rename to Servers/DirectAdmin/DAHTTPSocket.php index 206bed5..fd3f477 100644 --- a/Servers/DirectAdmin/httpsocket.php +++ b/Servers/DirectAdmin/DAHTTPSocket.php @@ -1,4 +1,5 @@ 'DirectAdmin', + 'version' => '1.0.0', + 'author' => 'Paymenter', + 'website' => 'https://paymenter.org', + ]; + } + + public function createServer($user, $params, $order, $product, $configurableOptions) + { + $host = ExtensionHelper::getConfig('DirectAdmin', 'host'); + $sock = new DAHTTPSocket(); + if (ExtensionHelper::getConfig('DirectAdmin', 'ssl')) { + $sock->connect('ssl://' . $host, '2222'); + } else { + $sock->connect($host, '2222'); + } + $server_login = ExtensionHelper::getConfig('DirectAdmin', 'username'); + $server_pass = ExtensionHelper::getConfig('DirectAdmin', 'password'); + $sock->set_login($server_login, $server_pass); + // Generate random username with 8 characters + $password = substr(str_shuffle(str_repeat($x = 'abcdefghijklmnopqrstuvwxyz', ceil(10/ strlen($x)))), 1, 10); + $username = substr(str_shuffle(str_repeat($x = 'abcdefghijklmnopqrstuvwxyz', ceil(8 / strlen($x)))), 1, 8); + + if (isset($params['ip'])) { + $ip = $params['ip']; + } else { + $sock->query('/CMD_API_SHOW_RESELLER_IPS'); + $result = $sock->fetch_parsed_body(); + $ip = $result['list'][0]; + } + $response = $sock->query( + '/CMD_API_ACCOUNT_USER', + [ + 'action' => 'create', + 'add' => 'Submit', + 'username' => $username, + 'email' => $user->email, + 'passwd' => $password, + 'passwd2' => $password , + 'domain' => $params['config']['domain'], + 'package' => $params['package'], + 'ip' => $ip, + 'notify' => 'yes', + ] + ); + $result = $sock->fetch_parsed_body(); + if ($result['error'] != '0') { + ExtensionHelper::error('DirectAdmin', $result); + + return; + } else { + ExtensionHelper::setOrderProductConfig('username', $username, $product->id); + } + + return $response; + } + + public function suspendServer($user, $params, $order, $product, $configurableOptions) + { + $host = ExtensionHelper::getConfig('DirectAdmin', 'host'); + $sock = new DAHTTPSocket(); + if (ExtensionHelper::getConfig('DirectAdmin', 'ssl')) { + $sock->connect('ssl://' . $host, '2222'); + } else { + $sock->connect($host, '2222'); + } + $server_login = ExtensionHelper::getConfig('DirectAdmin', 'username'); + $server_pass = ExtensionHelper::getConfig('DirectAdmin', 'password'); + $sock->set_login($server_login, $server_pass); + $username = $params['config']['username']; + $response = $sock->query( + '/CMD_API_SELECT_USERS', + [ + 'location' => 'CMD_SELECT_USERS', + 'suspend' => 'suspend', + 'select0' => $username, + ] + ); + $result = $sock->fetch_parsed_body(); + if ($result['error'] != '0') { + ExtensionHelper::error('DirectAdmin', $result); + + return; + } + } + + public function unsuspendServer($user, $params, $order, $product, $configurableOptions) + { + $host = ExtensionHelper::getConfig('DirectAdmin', 'host'); + $sock = new DAHTTPSocket(); + if (ExtensionHelper::getConfig('DirectAdmin', 'ssl')) { + $sock->connect('ssl://' . $host, '2222'); + } else { + $sock->connect($host, '2222'); + } + $server_login = ExtensionHelper::getConfig('DirectAdmin', 'username'); + $server_pass = ExtensionHelper::getConfig('DirectAdmin', 'password'); + $sock->set_login($server_login, $server_pass); + $username = $params['config']['username']; + $response = $sock->query( + '/CMD_API_SELECT_USERS', + [ + 'location' => 'CMD_SELECT_USERS', + 'suspend' => 'unsuspend', + 'select0' => $username, + ] + ); + $result = $sock->fetch_parsed_body(); + if ($result['error'] != '0') { + ExtensionHelper::error('DirectAdmin', $result, true); + + return; + } + + return $response; + } + + public function terminateServer($user, $params, $order, $product, $configurableOptions) + { + $host = ExtensionHelper::getConfig('DirectAdmin', 'host'); + $sock = new DAHTTPSocket(); + if (ExtensionHelper::getConfig('DirectAdmin', 'ssl')) { + $sock->connect('ssl://' . $host, '2222'); + } else { + $sock->connect($host, '2222'); + } + $server_login = ExtensionHelper::getConfig('DirectAdmin', 'username'); + $server_pass = ExtensionHelper::getConfig('DirectAdmin', 'password'); + $sock->set_login($server_login, $server_pass); + $username = $params['config']['username']; + $response = $sock->query( + '/CMD_API_SELECT_USERS', + [ + 'confirmed' => 'Confirm', + 'delete' => 'yes', + 'select0' => $username, + ] + ); + $result = $sock->fetch_parsed_body(); + if ($result['error'] != '0') { + error_log(print_r($result, true)); + + return; + // TODO: Handle error + } + + return $response; + } + + public function getConfig() + { + return [ + [ + 'name' => 'host', + 'friendlyName' => 'Host', + 'type' => 'text', + 'required' => true, + 'description' => 'The IP address or domain name of the DirectAdmin server', + ], + [ + 'name' => 'username', + 'friendlyName' => 'Username', + 'type' => 'text', + 'required' => true, + 'description' => 'The username of the DirectAdmin server', + ], + [ + 'name' => 'password', + 'friendlyName' => 'Password', + 'type' => 'text', + 'required' => true, + 'description' => 'The password of the DirectAdmin server', + ], + [ + 'name' => 'ssl', + 'friendlyName' => 'SSL', + 'type' => 'boolean', + 'required' => true, + 'description' => 'Whether to use SSL to connect to the DirectAdmin server', + ], + ]; + } + + public function getUserConfig(Product $product) + { + return [ + [ + 'name' => 'domain', + 'type' => 'text', + 'friendlyName' => 'Domain', + 'required' => true, + ], + ]; + } + + public function getProductConfig($options) + { + // Get package options + $packages = []; + $host = ExtensionHelper::getConfig('DirectAdmin', 'host'); + $pass = ExtensionHelper::getConfig('DirectAdmin', 'password'); + $user = ExtensionHelper::getConfig('DirectAdmin', 'username'); + $ssl = ExtensionHelper::getConfig('DirectAdmin', 'ssl'); + $sock = new DAHTTPSocket(); + if ($ssl) { + $sock->connect('ssl://' . $host, '2222'); + } else { + $sock->connect($host, '2222'); + } + $sock->set_login($user, $pass); + $sock->query('/CMD_API_PACKAGES_USER'); + $result = $sock->fetch_parsed_body(); + if(!isset($result['list'])) { + throw new \Exception('No packages found or could not connect to DirectAdmin server'); + } + + foreach ($result['list'] as $package) { + $packages[] = [ + 'name' => $package, + 'value' => $package, + ]; + } + + $ips = []; + $sock->query('/CMD_API_SHOW_RESELLER_IPS'); + $result = $sock->fetch_parsed_body(); + foreach ($result['list'] as $ip) { + $ips[] = [ + 'name' => $ip, + 'value' => $ip, + ]; + } + + return [ + [ + 'name' => 'package', + 'type' => 'dropdown', + 'friendlyName' => 'Package', + 'required' => true, + 'options' => $packages, + ], + [ + 'name' => 'ip', + 'type' => 'dropdown', + 'friendlyName' => 'IP', + 'required' => true, + 'options' => $ips, + ], + ]; + } +} diff --git a/Servers/DirectAdmin/index.php b/Servers/DirectAdmin/index.php deleted file mode 100644 index ff590a6..0000000 --- a/Servers/DirectAdmin/index.php +++ /dev/null @@ -1,210 +0,0 @@ -connect('ssl://' . $host, '2222'); - } else { - $sock->connect($host, '2222'); - } - $server_login = ExtensionHelper::getConfig('DirectAdmin', 'username'); - $server_pass = ExtensionHelper::getConfig('DirectAdmin', 'password'); - $sock->set_login($server_login, $server_pass); - // Generate random username with 8 characters - $username = substr(str_shuffle(str_repeat($x = 'abcdefghijklmnopqrstuvwxyz', ceil(8 / strlen($x)))), 1, 8); - if (isset($params['ip'])) { - $ip = $params['ip']; - } else { - $sock->query('/CMD_API_SHOW_RESELLER_IPS'); - $result = $sock->fetch_parsed_body(); - $ip = $result['list'][0]; - } - $response = $sock->query( - '/CMD_API_ACCOUNT_USER', - [ - 'action' => 'create', - 'add' => 'Submit', - 'username' => $username, - 'email' => $user->email, - 'passwd' => 'Random', - 'passwd2' => 'Random', - 'domain' => $params['config']['domain'], - 'package' => 'test', - 'ip' => $ip, - 'notify' => 'yes', - ] - ); - $result = $sock->fetch_parsed_body(); - if ($result['error'] != '0') { - ExtensionHelper::error('DirectAdmin', $result); - - return; - } else { - ExtensionHelper::setOrderProductConfig('username', $username, $params['config_id']); - } - - return $response; -} - -function DirectAdmin_suspendServer($user, $params, $order) -{ - $host = ExtensionHelper::getConfig('DirectAdmin', 'host'); - $sock = new DAHTTPSocket(); - if (ExtensionHelper::getConfig('DirectAdmin', 'ssl')) { - $sock->connect('ssl://' . $host, '2222'); - } else { - $sock->connect($host, '2222'); - } - $server_login = ExtensionHelper::getConfig('DirectAdmin', 'username'); - $server_pass = ExtensionHelper::getConfig('DirectAdmin', 'password'); - $sock->set_login($server_login, $server_pass); - $username = $params['config']['username']; - $response = $sock->query( - '/CMD_API_SELECT_USERS', - [ - 'location' => 'CMD_SELECT_USERS', - 'suspend' => 'suspend', - 'select0' => $username, - ] - ); - $result = $sock->fetch_parsed_body(); - if ($result['error'] != '0') { - ExtensionHelper::error('DirectAdmin', $result); - - return; - } -} - -function DirectAdmin_unsuspendServer($user, $params, $order) -{ - $host = ExtensionHelper::getConfig('DirectAdmin', 'host'); - $sock = new DAHTTPSocket(); - if (ExtensionHelper::getConfig('DirectAdmin', 'ssl')) { - $sock->connect('ssl://' . $host, '2222'); - } else { - $sock->connect($host, '2222'); - } - $server_login = ExtensionHelper::getConfig('DirectAdmin', 'username'); - $server_pass = ExtensionHelper::getConfig('DirectAdmin', 'password'); - $sock->set_login($server_login, $server_pass); - $username = $params['config']['username']; - $response = $sock->query( - '/CMD_API_SELECT_USERS', - [ - 'location' => 'CMD_SELECT_USERS', - 'suspend' => 'unsuspend', - 'select0' => $username, - ] - ); - $result = $sock->fetch_parsed_body(); - if ($result['error'] != '0') { - ExtensionHelper::error('DirectAdmin', $result, true); - - return; - } - - return $response; -} - -function DirectAdmin_terminateServer($user, $params, $order) -{ - $host = ExtensionHelper::getConfig('DirectAdmin', 'host'); - $sock = new DAHTTPSocket(); - if (ExtensionHelper::getConfig('DirectAdmin', 'ssl')) { - $sock->connect('ssl://' . $host, '2222'); - } else { - $sock->connect($host, '2222'); - } - $server_login = ExtensionHelper::getConfig('DirectAdmin', 'username'); - $server_pass = ExtensionHelper::getConfig('DirectAdmin', 'password'); - $sock->set_login($server_login, $server_pass); - $username = $params['config']['username']; - $response = $sock->query( - '/CMD_API_SELECT_USERS', - [ - 'confirmed' => 'Confirm', - 'delete' => 'yes', - 'select0' => $username, - ] - ); - $result = $sock->fetch_parsed_body(); - if ($result['error'] != '0') { - error_log(print_r($result, true)); - - return; - // TODO: Handle error - } - - return $response; -} - -function DirectAdmin_getConfig() -{ - return [ - [ - 'name' => 'host', - 'friendlyName' => 'Host', - 'type' => 'text', - 'required' => true, - 'description' => 'The IP address or domain name of the DirectAdmin server', - ], - [ - 'name' => 'username', - 'friendlyName' => 'Username', - 'type' => 'text', - 'required' => true, - 'description' => 'The username of the DirectAdmin server', - ], - [ - 'name' => 'password', - 'friendlyName' => 'Password', - 'type' => 'text', - 'required' => true, - 'description' => 'The password of the DirectAdmin server', - ], - [ - 'name' => 'ssl', - 'friendlyName' => 'SSL', - 'type' => 'boolean', - 'required' => true, - 'description' => 'Whether to use SSL to connect to the DirectAdmin server', - ], - ]; -} - -function DirectAdmin_getUserConfig(Product $product) -{ - return [ - [ - 'name' => 'domain', - 'type' => 'text', - 'friendlyName' => 'Domain', - 'required' => true, - ], - ]; -} - -function DirectAdmin_getProductConfig() -{ - return [ - [ - 'name' => 'package', - 'type' => 'text', - 'friendlyName' => 'Package', - 'required' => true, - ], - [ - 'name' => 'ip', - 'type' => 'text', - 'friendlyName' => 'IP', - 'required' => false, - ], - ]; -} diff --git a/Servers/ISPConfig/ISPConfig.php b/Servers/ISPConfig/ISPConfig.php new file mode 100644 index 0000000..db53799 --- /dev/null +++ b/Servers/ISPConfig/ISPConfig.php @@ -0,0 +1,261 @@ + 'ISPConfig', + 'version' => '1.0.0', + 'author' => 'Paymenter', + 'website' => 'https://paymenter.org', + ]; + } + + public function getConfig() + { + return [ + [ + 'name' => 'host', + 'friendlyName' => 'ISPConfig panel url', + 'type' => 'text', + 'required' => true, + 'description' => 'Example: https://panel.example.com:8080/remote/json.php', + ], + [ + 'name' => 'username', + 'friendlyName' => 'Username', + 'type' => 'text', + 'required' => true, + ], + [ + 'name' => 'password', + 'friendlyName' => 'Password', + 'type' => 'text', + 'required' => true, + ] + ]; + } + + public function getProductConfig($options) + { + return [ + [ + 'name' => 'hd_quota', + 'friendlyName' => 'Disk space MB', + 'type' => 'text', + 'required' => true, + 'description' => 'Disk space in MB', + ], + [ + 'name' => 'traffic_quota', + 'friendlyName' => 'Traffic MB', + 'type' => 'text', + 'required' => true, + 'description' => 'Traffic in MB', + ], + [ + 'name' => 'pm_max_requests', + 'friendlyName' => 'Max requests', + 'type' => 'text', + 'required' => true, + 'description' => 'Max requests of the customer website', + ], + ]; + } + + public function createServer($user, $params, $order, $product, $configurableOptions) + { + $webService = new ISPConfigWS( + array( + 'host' => ExtensionHelper::getConfig('ISPConfig', 'host'), + 'user' => ExtensionHelper::getConfig('ISPConfig', 'username'), + 'pass' => ExtensionHelper::getConfig('ISPConfig', 'password'), + ), + ); + + $client_id = $this->getClient($user, $webService); + if (!$client_id) { + return false; + } + $result = $webService + ->with(array( + 'client_id' => $client_id, + 'domain' => $params['config']['domain'], + 'type' => 'vhost', + 'vhost_type' => 'name', + 'ip_address' => '*', + 'active' => 'y', + 'hd_quota' => -1, + 'traffic_quota' => -1, + 'client_group_id' => $client_id + 1, + 'server_id' => 1, + 'http_port' => 80, + 'https_port' => 443, + 'allow_override' => 'All', + 'php' => 'suphp', + 'pm_max_requests' => 500, + 'pm_process_idle_timeout' => 10, + 'added_date' => date('Y-m-d'), + )) + ->addWebDomain() + ->response(); + + $result = json_decode($result, true); + if (isset($result['error'])) { + return false; + } + if (empty($result)) { + return false; + } + if (isset($result['result'])) { + ExtensionHelper::setOrderProductConfig('domain_id', $result['result'], $product->id); + } + return true; + } + + public function getUserConfig(Product $product) + { + return [ + [ + 'name' => 'domain', + 'friendlyName' => 'Domain', + 'type' => 'text', + 'required' => true, + 'description' => 'Domain for the webhosting', + ], + ]; + } + + public function getClient($user, $webService) + { + $reseller_id = 1; + $result = $webService + ->with(array('customer_no' => $user->id)) + ->getClientByCustomerNo() + ->response(); + $result = json_decode($result, true); + $user->name = str_replace(' ', '', $user->name); + if (empty($result) || isset($result['error'])) { + $result = $webService + ->with(array( + 'reseller_id' => $reseller_id, + 'email' => $user->email, + 'username' => $user->name, + 'password' => $user->password, + 'ssh_chroot' => 'no', + 'contact_name' => $user->name, + 'web_php_options' => 'no', + 'customer_no' => $user->id, + )) + ->addClient() + ->response(); + $result = json_decode($result, true); + if (empty($result)) { + return false; + } else { + $result = $webService + ->with(array('customer_no' => $user->id)) + ->getClientByCustomerNo() + ->response(); + } + } + + if (isset($result['error'])) { + return false; + } + return $result['client_id']; + } + + public function suspendServer($user, $params, $order, $product, $configurableOptions) + { + // deactivate the domain + $webService = new ISPConfigWS( + array( + 'host' => ExtensionHelper::getConfig('ISPConfig', 'host'), + 'user' => ExtensionHelper::getConfig('ISPConfig', 'username'), + 'pass' => ExtensionHelper::getConfig('ISPConfig', 'password'), + ), + ); + + $result = $webService + ->with(array('customer_no' => $user->id)) + ->getClientByCustomerNo() + ->response(); + $result = json_decode($result, true); + if (isset($result['error'])) { + return false; + } + if (empty($result)) { + return false; + } + if (!isset($params['config']['domain_id'])) { + return false; + } + // Get website by domain + $result = $webService + ->with(array('domain_id' => $params['config']['domain_id'], 'client_id' => $result['client_id'], 'active' => 'n')) + ->updateWebDomain() + ->response(); + $result = json_decode($result, true); + } + + public function unsuspendServer($user, $params, $order, $product, $configurableOptions) + { + // deactivate the domain + $webService = new ISPConfigWS( + array( + 'host' => ExtensionHelper::getConfig('ISPConfig', 'host'), + 'user' => ExtensionHelper::getConfig('ISPConfig', 'username'), + 'pass' => ExtensionHelper::getConfig('ISPConfig', 'password'), + ), + ); + + $result = $this->getClient($user, $webService); + if (!isset($params['config']['domain_id'])) { + return false; + } + // Get website by domain + $result = $webService + ->with(array('domain_id' => $params['config']['domain_id'], 'client_id' => $result, 'active' => 'y')) + ->updateWebDomain() + ->response(); + $result = json_decode($result, true); + } + + public function terminateServer($user, $params, $order, $product, $configurableOptions) + { + + $webService = new ISPConfigWS( + array( + 'host' => ExtensionHelper::getConfig('ISPConfig', 'host'), + 'user' => ExtensionHelper::getConfig('ISPConfig', 'username'), + 'pass' => ExtensionHelper::getConfig('ISPConfig', 'password'), + ), + ); + + $result = $this->getClient($user, $webService); + if (empty($result)) { + return false; + } + if (!isset($params['config']['domain_id'])) { + return false; + } + // Get website by domain + $result = $webService + ->with(array('domain_id' => $params['config']['domain_id'], 'client_id' => $result['client_id'])) + ->deleteWebDomain() + ->response(); + $result = json_decode($result, true); + } +} diff --git a/Servers/ISPConfig/ISPConfigWS.php b/Servers/ISPConfig/ISPConfigWS.php index dd2d058..9bcb3a2 100644 --- a/Servers/ISPConfig/ISPConfigWS.php +++ b/Servers/ISPConfig/ISPConfigWS.php @@ -1,6 +1,5 @@ 'host', - 'friendlyName' => 'ISPConfig panel url', - 'type' => 'text', - 'required' => true, - 'description' => 'Example: https://panel.example.com:8080/remote/json.php', - ], - [ - 'name' => 'username', - 'friendlyName' => 'Username', - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'password', - 'friendlyName' => 'Password', - 'type' => 'text', - 'required' => true, - ] - ]; -} - -function ISPConfig_getProductConfig() -{ - return [ - [ - 'name' => 'hd_quota', - 'friendlyName' => 'Disk space MB', - 'type' => 'text', - 'required' => true, - 'description' => 'Disk space in MB', - ], - [ - 'name' => 'traffic_quota', - 'friendlyName' => 'Traffic MB', - 'type' => 'text', - 'required' => true, - 'description' => 'Traffic in MB', - ], - [ - 'name' => 'pm_max_requests', - 'friendlyName' => 'Max requests', - 'type' => 'text', - 'required' => true, - 'description' => 'Max requests of the customer website', - ], - ]; -} - -function ISPConfig_createServer($user, $params, $order, OrderProduct $product) -{ - $webService = new ISPConfigWS\ISPConfigWS( - array( - 'host' => ExtensionHelper::getConfig('ISPConfig', 'host'), - 'user' => ExtensionHelper::getConfig('ISPConfig', 'username'), - 'pass' => ExtensionHelper::getConfig('ISPConfig', 'password'), - ), - ); - - $client_id = ISPConfig_getClient($user, $webService); - if (!$client_id) { - return false; - } - $result = $webService - ->with(array( - 'client_id' => $client_id, - 'domain' => $params['config']['domain'], - 'type' => 'vhost', - 'vhost_type' => 'name', - 'ip_address' => '*', - 'active' => 'y', - 'hd_quota' => -1, - 'traffic_quota' => -1, - 'client_group_id' => $client_id + 1, - 'server_id' => 1, - 'http_port' => 80, - 'https_port' => 443, - 'allow_override' => 'All', - 'php' => 'suphp', - 'pm_max_requests' => 500, - 'pm_process_idle_timeout' => 10, - 'added_date' => date('Y-m-d'), - )) - ->addWebDomain() - ->response(); - - $result = json_decode($result, true); - if (isset($result['error'])) { - return false; - } - if (empty($result)) { - return false; - } - if (isset($result['result'])){ - ExtensionHelper::setOrderProductConfig('domain_id', $result['result'], $product->id); - } - return true; -} - -function ISPConfig_getUserConfig(Product $product) -{ - return [ - [ - 'name' => 'domain', - 'friendlyName' => 'Domain', - 'type' => 'text', - 'required' => true, - 'description' => 'Domain for the webhosting', - ], - ]; -} - -function ISPConfig_getClient($user, $webService) -{ - $reseller_id = 1; - $result = $webService - ->with(array('customer_no' => $user->id)) - ->getClientByCustomerNo() - ->response(); - $result = json_decode($result, true); - $user->name = str_replace(' ', '', $user->name); - if (empty($result) || isset($result['error'])) { - $result = $webService - ->with(array( - 'reseller_id' => $reseller_id, - 'email' => $user->email, - 'username' => $user->name, - 'password' => $user->password, - 'ssh_chroot' => 'no', - 'contact_name' => $user->name, - 'web_php_options' => 'no', - 'customer_no' => $user->id, - )) - ->addClient() - ->response(); - $result = json_decode($result, true); - if (empty($result)) { - return false; - } else { - $result = $webService - ->with(array('customer_no' => $user->id)) - ->getClientByCustomerNo() - ->response(); - } - } - - if (isset($result['error'])) { - return false; - } - return $result['client_id']; -} - -function ISPConfig_suspendServer(User $user, $params, Orders $order, OrderProducts $product) -{ - // deactivate the domain - $webService = new ISPConfigWS\ISPConfigWS( - array( - 'host' => ExtensionHelper::getConfig('ISPConfig', 'host'), - 'user' => ExtensionHelper::getConfig('ISPConfig', 'username'), - 'pass' => ExtensionHelper::getConfig('ISPConfig', 'password'), - ), - ); - - $result = $webService - ->with(array('customer_no' => $user->id)) - ->getClientByCustomerNo() - ->response(); - $result = json_decode($result, true); - if (isset($result['error'])) { - return false; - } - if (empty($result)) { - return false; - } - if (!isset($params['config']['domain_id'])){ - return false; - } - // Get website by domain - $result = $webService - ->with(array('domain_id' => $params['config']['domain_id'], 'client_id' => $result['client_id'], 'active' => 'n')) - ->updateWebDomain() - ->response(); - $result = json_decode($result, true); -} - -function ISPConfig_unsuspendServer(User $user, $params, Order $order, OrderProduct $product) -{ - // deactivate the domain - $webService = new ISPConfigWS\ISPConfigWS( - array( - 'host' => ExtensionHelper::getConfig('ISPConfig', 'host'), - 'user' => ExtensionHelper::getConfig('ISPConfig', 'username'), - 'pass' => ExtensionHelper::getConfig('ISPConfig', 'password'), - ), - ); - - $result = ISPConfig_getClient($user, $webService); - if (!isset($params['config']['domain_id'])){ - return false; - } - // Get website by domain - $result = $webService - ->with(array('domain_id' => $params['config']['domain_id'], 'client_id' => $result, 'active' => 'y')) - ->updateWebDomain() - ->response(); - $result = json_decode($result, true); -} - -function ISPConfig_terminateServer($user, $params, $order) { - - $webService = new ISPConfigWS\ISPConfigWS( - array( - 'host' => ExtensionHelper::getConfig('ISPConfig', 'host'), - 'user' => ExtensionHelper::getConfig('ISPConfig', 'username'), - 'pass' => ExtensionHelper::getConfig('ISPConfig', 'password'), - ), - ); - - $result = ISPConfig_getClient($user, $webService); - if (empty($result)) { - return false; - } - if (!isset($params['config']['domain_id'])){ - return false; - } - // Get website by domain - $result = $webService - ->with(array('domain_id' => $params['config']['domain_id'], 'client_id' => $result['client_id'])) - ->deleteWebDomain() - ->response(); - $result = json_decode($result, true); -} \ No newline at end of file diff --git a/Servers/Plesk/Plesk.php b/Servers/Plesk/Plesk.php new file mode 100644 index 0000000..49946b4 --- /dev/null +++ b/Servers/Plesk/Plesk.php @@ -0,0 +1,201 @@ + 'Plesk', + 'version' => '1.0.0', + 'author' => 'Paymenter', + 'website' => 'https://paymenter.org', + ]; + } + + private function getApiKey() + { + $username = ExtensionHelper::getConfig('Plesk', 'username'); + $password = ExtensionHelper::getConfig('Plesk', 'password'); + $host = ExtensionHelper::getConfig('Plesk', 'host'); + // Use without verify ssl + $response = Http::withBasicAuth($username, $password)->withoutVerifying()->post($host . '/api/v2/auth/keys'); + $response = json_decode($response->body(), true); + return $response['key']; + } + + public function getConfig() + { + return [ + [ + 'name' => 'host', + 'friendlyName' => 'Host', + 'type' => 'text', + 'required' => true, + 'description' => 'The IP address or domain name of the Plesk server (with http:// or https://)', + ], + [ + 'name' => 'username', + 'friendlyName' => 'Username', + 'type' => 'text', + 'required' => true, + 'description' => 'The username of the Plesk server', + ], + [ + 'name' => 'password', + 'friendlyName' => 'Password', + 'type' => 'text', + 'required' => true, + 'description' => 'The password of the Plesk server', + ] + ]; + } + + public function getProductConfig($options) + { + return [ + [ + 'name' => 'plan', + 'type' => 'text', + 'friendlyName' => 'Plan', + 'required' => true, + 'description' => 'The plan name of the wanted service plan', + ] + ]; + } + + public function getUserConfig() + { + return [ + [ + 'name' => 'domain', + 'type' => 'text', + 'friendlyName' => 'Domain', + 'required' => true, + ], + ]; + } + + public function createServer($user, $params, $order, $product, $configurableOptions) + { + $apiKey = $this->getApiKey(); + $host = ExtensionHelper::getConfig('Plesk', 'host'); + // Check if client already has a server + $clientCheck = Http::withHeaders([ + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + 'X-API-Key' => $apiKey + ])->withoutVerifying()->get($host . '/api/v2/clients/' . $user->id); + $clientCheck = json_decode($clientCheck->body(), true); + // Remove spaces and special characters from username + $username = preg_replace('/[^A-Za-z0-9\-]/', '', $user->name); + $uuid; + if (isset($clientCheck['code'])) { + $newClient = Http::withHeaders([ + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + 'X-API-Key' => $apiKey + ])->withoutVerifying()->post($host . '/api/v2/clients', [ + 'username' => $username, + 'email' => $user->email, + 'password' => $params['password'] ?? $user->password, + 'name' => $user->name, + 'login' => $username, + 'type' => 'customer', + 'external_id' => $user->id, + ]); + $newClient = json_decode($newClient->body(), true); + $uuid = $newClient['guid']; + } else { + $uuid = $clientCheck['guid']; + } + // Lowercase username + $username = strtolower($username); + $domain = Http::withHeaders([ + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + 'X-API-Key' => $apiKey + ])->withoutVerifying()->post($host . '/api/v2/domains', [ + 'name' => $params['config']['domain'], + 'external_id' => $order->id, + 'client' => $uuid, + 'hosting_type' => 'virtual', + 'hosting_settings' => [ + 'ftp_login' => $username, + 'ftp_password' => $params['password'] ?? $user->password, + ], + 'plan' => [ + 'name' => $params['plan'], + ] + ]); + $domain = json_decode($domain->body(), true); + return $domain; + } + + public function suspendServer($user, $params, $order, $product, $configurableOptions) + { + $apiKey = $this->getApiKey(); + $host = ExtensionHelper::getConfig('Plesk', 'host'); + $domain = Http::withHeaders([ + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + 'X-API-Key' => $apiKey + ])->withoutVerifying()->put($host . '/api/v2/domains/' . $this->getDomainID($params['config']['domain']) . '/status', [ + 'status' => 'suspended' + ]); + $domain = json_decode($domain->body(), true); + return $domain; + } + + private function getDomainID($domain) + { + $apiKey = $this->getApiKey(); + $host = ExtensionHelper::getConfig('Plesk', 'host'); + $domain = Http::withHeaders([ + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + 'X-API-Key' => $apiKey + ])->withoutVerifying()->get($host . '/api/v2/domains?name=' . $domain); + $domain = json_decode($domain->body(), true); + return $domain[0]['id']; + } + + public function unsuspendServer($user, $params, $order, $product, $configurableOptions) + { + $apiKey = $this->getApiKey(); + $host = ExtensionHelper::getConfig('Plesk', 'host'); + $domain = Http::withHeaders([ + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + 'X-API-Key' => $apiKey + ])->withoutVerifying()->put($host . '/api/v2/domains/' . $this->getDomainID($params['config']['domain']) . '/status', [ + 'status' => 'active' + ]); + $domain = json_decode($domain->body(), true); + return $domain; + } + + public function terminateServer($user, $params, $order, $product, $configurableOptions) + { + $apiKey = $this->getApiKey(); + $host = ExtensionHelper::getConfig('Plesk', 'host'); + $domain = Http::withHeaders([ + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + 'X-API-Key' => $apiKey + ])->withoutVerifying()->delete($host . '/api/v2/domains/' . $this->getDomainID($params['config']['domain'])); + $domain = json_decode($domain->body(), true); + return $domain; + } + + public function getLink($user, $params) + { + $host = ExtensionHelper::getConfig('Plesk', 'host'); + return $host . '/smb/web/overview/id/' . $this->getDomainID($params['config']['domain']) . '/type/domain'; + } +} diff --git a/Servers/Plesk/index.php b/Servers/Plesk/index.php deleted file mode 100644 index 23956f3..0000000 --- a/Servers/Plesk/index.php +++ /dev/null @@ -1,186 +0,0 @@ -withoutVerifying()->post($host . '/api/v2/auth/keys'); - $response = json_decode($response->body(), true); - return $response['key']; -} - -function Plesk_getConfig() -{ - return [ - [ - 'name' => 'host', - 'friendlyName' => 'Host', - 'type' => 'text', - 'required' => true, - 'description' => 'The IP address or domain name of the Plesk server (with http:// or https://)', - ], - [ - 'name' => 'username', - 'friendlyName' => 'Username', - 'type' => 'text', - 'required' => true, - 'description' => 'The username of the Plesk server', - ], - [ - 'name' => 'password', - 'friendlyName' => 'Password', - 'type' => 'text', - 'required' => true, - 'description' => 'The password of the Plesk server', - ] - ]; -} - -function Plesk_getProductConfig() -{ - - return [ - [ - 'name' => 'plan', - 'type' => 'text', - 'friendlyName' => 'Plan', - 'required' => true, - 'description' => 'The plan name of the wanted service plan', - ] - ]; -} - -function Plesk_getUserConfig() -{ - return [ - [ - 'name' => 'domain', - 'type' => 'text', - 'friendlyName' => 'Domain', - 'required' => true, - ], - ]; -} - -function Plesk_createServer($user, $params, $order) -{ - $apiKey = Plesk_getApiKey(); - $host = ExtensionHelper::getConfig('Plesk', 'host'); - // Check if client already has a server - $clientCheck = Http::withHeaders([ - 'Content-Type' => 'application/json', - 'Accept' => 'application/json', - 'X-API-Key' => $apiKey - ])->withoutVerifying()->get($host . '/api/v2/clients/' . $user->id); - $clientCheck = json_decode($clientCheck->body(), true); - // Remove spaces and special characters from username - $username = preg_replace('/[^A-Za-z0-9\-]/', '', $user->name); - $uuid; - if (isset($clientCheck['code'])) { - $newClient = Http::withHeaders([ - 'Content-Type' => 'application/json', - 'Accept' => 'application/json', - 'X-API-Key' => $apiKey - ])->withoutVerifying()->post($host . '/api/v2/clients', [ - 'username' => $username, - 'email' => $user->email, - 'password' => $params['password'] ?? $user->password, - 'name' => $user->name, - 'login' => $username, - 'type' => 'customer', - 'external_id' => $user->id, - ]); - $newClient = json_decode($newClient->body(), true); - $uuid = $newClient['guid']; - } else { - $uuid = $clientCheck['guid']; - } - // Lowercase username - $username = strtolower($username); - $domain = Http::withHeaders([ - 'Content-Type' => 'application/json', - 'Accept' => 'application/json', - 'X-API-Key' => $apiKey - ])->withoutVerifying()->post($host . '/api/v2/domains', [ - 'name' => $params['config']['domain'], - 'external_id' => $order->id, - 'client' => $uuid, - 'hosting_type' => 'virtual', - 'hosting_settings' => [ - 'ftp_login' => $username, - 'ftp_password' => $params['password'] ?? $user->password, - ], - 'plan' => [ - 'name' => $params['plan'], - ] - ]); - $domain = json_decode($domain->body(), true); - return $domain; -} - -function Plesk_suspendServer($user, $params) -{ - $apiKey = Plesk_getApiKey(); - $host = ExtensionHelper::getConfig('Plesk', 'host'); - $domain = Http::withHeaders([ - 'Content-Type' => 'application/json', - 'Accept' => 'application/json', - 'X-API-Key' => $apiKey - ])->withoutVerifying()->put($host . '/api/v2/domains/' . Plesk_getDomainID($params['config']['domain']) . '/status', [ - 'status' => 'suspended' - ]); - $domain = json_decode($domain->body(), true); - return $domain; -} - -function Plesk_getDomainID($domain) -{ - $apiKey = Plesk_getApiKey(); - $host = ExtensionHelper::getConfig('Plesk', 'host'); - $domain = Http::withHeaders([ - 'Content-Type' => 'application/json', - 'Accept' => 'application/json', - 'X-API-Key' => $apiKey - ])->withoutVerifying()->get($host . '/api/v2/domains?name=' . $domain); - $domain = json_decode($domain->body(), true); - return $domain[0]['id']; -} - -function Plesk_unsuspendServer($user, $params) -{ - $apiKey = Plesk_getApiKey(); - $host = ExtensionHelper::getConfig('Plesk', 'host'); - $domain = Http::withHeaders([ - 'Content-Type' => 'application/json', - 'Accept' => 'application/json', - 'X-API-Key' => $apiKey - ])->withoutVerifying()->put($host . '/api/v2/domains/' . Plesk_getDomainID($params['config']['domain']) . '/status', [ - 'status' => 'active' - ]); - $domain = json_decode($domain->body(), true); - return $domain; -} - -function Plesk_terminateServer($user, $params) -{ - $apiKey = Plesk_getApiKey(); - $host = ExtensionHelper::getConfig('Plesk', 'host'); - $domain = Http::withHeaders([ - 'Content-Type' => 'application/json', - 'Accept' => 'application/json', - 'X-API-Key' => $apiKey - ])->withoutVerifying()->delete($host . '/api/v2/domains/' . Plesk_getDomainID($params['config']['domain'])); - $domain = json_decode($domain->body(), true); - return $domain; -} - -function Plesk_getLink($user, $params){ - $host = ExtensionHelper::getConfig('Plesk', 'host'); - return $host . '/smb/web/overview/id/' . Plesk_getDomainID($params['config']['domain']) . '/type/domain'; -} diff --git a/Servers/Proxmox/Proxmox.php b/Servers/Proxmox/Proxmox.php new file mode 100644 index 0000000..cade90c --- /dev/null +++ b/Servers/Proxmox/Proxmox.php @@ -0,0 +1,906 @@ + 'Proxmox', + 'version' => '1.0.0', + 'author' => 'Paymenter', + 'website' => 'https://paymenter.org', + ]; + } + + public function getConfig() + { + return [ + [ + 'name' => 'host', + 'friendlyName' => 'Host', + 'type' => 'text', + 'required' => true, + 'description' => 'The IP address or domain name of the Proxmox server (with http:// or https://)', + ], + [ + 'name' => 'port', + 'friendlyName' => 'Port', + 'type' => 'text', + 'required' => true, + 'description' => 'The port of the Proxmox server', + ], + [ + 'name' => 'username', + 'friendlyName' => 'Username', + 'type' => 'text', + 'required' => true, + 'description' => 'The api username of the Proxmox server', + ], + [ + 'name' => 'password', + 'friendlyName' => 'API Token', + 'type' => 'text', + 'required' => true, + 'description' => 'The API Token of the Proxmox server', + ] + ]; + } + + public function getProductConfig($options) + { + $nodes = $this->getRequest('/nodes'); + if (!$nodes->json()) throw new Exception('Unable to get nodes'); + foreach ($nodes->json()['data'] as $node) { + $nodeList[] = [ + 'name' => $node['node'], + 'value' => $node['node'] + ]; + } + + $currentNode = isset($options['node']) ? $options['node'] : null; + $storageName = isset($options['storage']) ? $options['storage'] : null; + if ($currentNode == null) { + $currentNode = $nodeList[0]['value']; + } + $storage = $this->getRequest('/nodes/' . $currentNode . '/storage'); + $storageList = []; + if (!$storage->json()) throw new Exception('Unable to get storage'); + foreach ($storage->json()['data'] as $storage) { + $storageList[] = [ + 'name' => $storage['storage'], + 'value' => $storage['storage'] + ]; + } + + $resourcePool = $this->getRequest('/pools'); + $poolList = [ + [ + 'name' => 'None', + 'value' => '' + ] + ]; + + if (!$resourcePool->json()) throw new Exception('Unable to get resource pool'); + foreach ($resourcePool->json()['data'] as $pool) { + $poolList[] = [ + 'name' => $pool['poolid'], + 'value' => $pool['poolid'] + ]; + } + + // Only list contentVztmpl + $templateList = []; + $isoList = []; + foreach ($nodeList as $node) { + // Get all storage + $storage = $this->getRequest('/nodes/' . $node['value'] . '/storage'); + if (!$storage->json()) throw new Exception('Unable to get storage'); + foreach ($storage->json()['data'] as $storage) { + $storageName = $storage['storage']; + $template = $this->getRequest('/nodes/' . $node['value'] . '/storage/' . $storageName . '/content'); + if (!$template->json()) throw new Exception('Unable to get template'); + foreach ($template->json()['data'] as $template) { + if ($template['content'] == 'vztmpl') { + $templateList[] = [ + 'name' => $template['volid'], + 'value' => $template['volid'] + ]; + } else if ($template['content'] == 'iso') { + $isoList[] = [ + 'name' => $template['volid'], + 'value' => $template['volid'] + ]; + } + } + } + } + + + + $bridgeList = []; + $bridge = $this->getRequest('/nodes/' . $currentNode . '/network'); + if (!$bridge->json()) throw new Exception('Unable to get bridge'); + foreach ($bridge->json()['data'] as $bridge) { + if (!isset($bridge['active'])) continue; + if (!$bridge['active']) continue; + $bridgeList[] = [ + 'name' => $bridge['iface'], + 'value' => $bridge['iface'] + ]; + } + + $cpuList = [ + [ + 'name' => 'Default', + 'value' => '' + ] + ]; + $cpu = $this->getRequest('/nodes/' . $currentNode . '/capabilities/qemu/cpu'); + if (!$cpu->json()) throw new Exception('Unable to get cpu'); + foreach ($cpu->json()['data'] as $cpu) { + $cpuList[] = [ + 'name' => $cpu['name'] . ' (' . $cpu['vendor'] . ')', + 'value' => $cpu['name'] + ]; + } + + + + return [ + [ + 'type' => 'title', + 'friendlyName' => 'General', + 'description' => 'General options', + ], + [ + 'name' => 'node', + 'type' => 'dropdown', + 'friendlyName' => 'Node', + 'required' => true, + 'description' => 'The node name of the wanted node (submit to update the storage list)', + 'options' => $nodeList + ], + [ + 'name' => 'storage', + 'type' => 'dropdown', + 'friendlyName' => 'Storage', + 'description' => 'The storage name of the wanted storage', + 'options' => $storageList + ], + [ + 'name' => 'pool', + 'type' => 'dropdown', + 'friendlyName' => 'Resource Pool', + 'description' => 'Resource Pool places VMs in a group', + 'options' => $poolList + ], + [ + 'name' => 'type', + 'type' => 'dropdown', + 'friendlyName' => 'Type', + 'required' => true, + 'description' => 'The type of the wanted VM', + 'options' => [ + [ + 'name' => 'qemu', + 'value' => 'qemu' + ], + [ + 'name' => 'lxc', + 'value' => 'lxc' + ] + ] + ], + [ + 'name' => 'cores', + 'type' => 'text', + 'friendlyName' => 'Cores', + 'required' => true, + 'description' => 'The number of cores of the wanted VM', + ], + [ + 'name' => 'memory', + 'type' => 'text', + 'friendlyName' => 'Memory (MB)', + 'required' => true, + 'description' => 'The amount of memory of the wanted VM', + ], + [ + 'name' => 'disk', + 'type' => 'text', + 'friendlyName' => 'Disk (GB)', + 'required' => true, + 'description' => 'The amount of disk of the wanted VM', + ], + [ + 'name' => 'network_limit', + 'type' => 'text', + 'friendlyName' => 'Network Limit (MB)', + 'description' => 'The network limit of the wanted VM', + ], + + + [ + 'name' => 'lxc', + 'type' => 'title', + 'friendlyName' => 'LXC', + 'description' => 'All LXC options', + ], + [ + 'name' => 'template', + 'type' => 'dropdown', + 'friendlyName' => 'Template', + 'description' => 'The template name of the wanted VM', + 'options' => $templateList + ], + [ + 'name' => 'unprivileged', + 'type' => 'boolean', + 'friendlyName' => 'Unprivileged Container', + 'description' => 'Enable/disable unprivileged container', + ], + [ + 'name' => 'nesting', + 'type' => 'boolean', + 'friendlyName' => 'Nesting', + 'description' => 'Enable/disable nesting', + ], + [ + 'name' => 'ostypelxc', + 'type' => 'dropdown', + 'friendlyName' => 'OS Type', + 'description' => 'The OS type of the wanted VM', + 'options' => [ + [ + 'name' => 'debian', + 'value' => 'debian' + ], + [ + 'name' => 'devuan', + 'value' => 'devuan' + ], + [ + 'name' => 'ubuntu', + 'value' => 'ubuntu' + ], + [ + 'name' => 'centos', + 'value' => 'centos' + ], + [ + 'name' => 'fedora', + 'value' => 'fedora' + ], + [ + 'name' => 'opensuse', + 'value' => 'opensuse' + ], + [ + 'name' => 'archlinux', + 'value' => 'archlinux' + ], + [ + 'name' => 'alpine', + 'value' => 'alpine' + ], + [ + 'name' => 'gentoo', + 'value' => 'gentoo' + ], + [ + 'name' => 'nixos', + 'value' => 'nixos' + ], + [ + 'name' => 'unmanaged', + 'value' => 'unmanaged' + ] + ] + ], + [ + 'type' => 'text', + 'name' => 'swap', + 'friendlyName' => 'Swap (MB)', + 'description' => 'The amount of swap of the wanted VM', + ], + + [ + 'type' => 'title', + 'friendlyName' => 'QEMU', + 'description' => 'All QEMU options', + ], + [ + 'name' => 'nonetwork', + 'type' => 'boolean', + 'friendlyName' => 'No Network', + 'description' => 'Enable/disable network', + ], + [ + 'name' => 'bridge', + 'type' => 'dropdown', + 'friendlyName' => 'Bridge', + 'options' => $bridgeList + ], + [ + 'name' => 'model', + 'type' => 'dropdown', + 'friendlyName' => 'Model', + 'options' => [ + [ + 'name' => 'VirtIO', + 'value' => 'virtio' + ], + [ + 'name' => 'Intel E1000', + 'value' => 'e1000' + ], + [ + 'name' => 'Realtek RTL8139', + 'value' => 'rtl8139' + ], + [ + 'name' => 'VMWare VMXNET3', + 'value' => 'vmxnet3' + ] + ] + ], + [ + 'name' => 'vlantag', + 'type' => 'text', + 'friendlyName' => 'VLAN Tag', + 'description' => 'Optional VLAN tag', + ], + [ + 'name' => 'firewall', + 'type' => 'boolean', + 'friendlyName' => 'Firewall', + 'description' => 'Enable/disable firewall', + ], + [ + 'name' => 'os', + 'type' => 'dropdown', + 'friendlyName' => 'OS', + 'required' => true, + 'options' => [ + [ + 'name' => 'ISO', + 'value' => 'iso' + ], + [ + 'name' => 'Pysical CD/DVD drive', + 'value' => 'cdrom' + ], + [ + 'name' => 'None', + 'value' => 'none' + ] + ] + ], + [ + 'name' => 'iso', + 'type' => 'dropdown', + 'friendlyName' => 'ISO', + 'description' => 'The ISO name of the wanted VM', + 'options' => $isoList + ], + [ + 'name' => 'cloudinit', + 'type' => 'boolean', + 'friendlyName' => 'Cloudinit', + 'description' => 'Enable/disable cloudinit', + ], + [ + 'name' => 'storageType', + 'type' => 'dropdown', + 'friendlyName' => 'Bus/Device', + 'description' => 'The bus/device of the VM', + 'options' => + [ + [ + 'name' => 'IDE', + 'value' => 'ide' + ], + [ + 'name' => 'SATA', + 'value' => 'sata' + ], + [ + 'name' => 'SCSI', + 'value' => 'scsi' + ], + [ + 'name' => 'VirtIO block', + 'value' => 'virtio' + ] + ] + ], + [ + 'name' => 'storageFormat', + 'type' => 'dropdown', + 'friendlyName' => 'Storage Format', + 'description' => 'The storage format of the VM', + 'options' => [ + [ + 'name' => 'Raw', + 'value' => 'raw' + ], + [ + 'name' => 'Qcow2', + 'value' => 'qcow2' + ], + [ + 'name' => 'VMDK', + 'value' => 'vmdk' + ], + ] + ], + [ + 'name' => 'cache', + 'type' => 'dropdown', + 'friendlyName' => 'Cache', + 'description' => 'The cache of the VM', + 'options' => [ + [ + 'name' => 'Default (no cache)', + 'value' => 'default' + ], + [ + 'name' => 'Direct Sync', + 'value' => 'directsync' + ], + [ + 'name' => 'Write Through', + 'value' => 'writethrough' + ], + [ + 'name' => 'Write Back', + 'value' => 'write back' + ], + [ + 'name' => 'Write Back (unsafe)', + 'value' => 'unsafe' + ], + [ + 'name' => 'No Cache', + 'value' => 'none' + ], + ] + ], + [ + 'name' => 'ostype', + 'type' => 'dropdown', + 'friendlyName' => 'Guest OS type', + 'description' => 'The OS type of the VM', + 'options' => [ + [ + 'name' => 'other', + 'value' => 'other' + ], + [ + 'name' => 'Windows XP', + 'value' => 'wxp' + ], + [ + 'name' => 'Windows 2000', + 'value' => 'w2k' + ], + [ + 'name' => 'Windows 2003', + 'value' => 'w2k3' + ], + [ + 'name' => 'Windows 2008', + 'value' => 'w2k8' + ], + [ + 'name' => 'Windows Vista', + 'value' => 'wvista' + ], + [ + 'name' => 'Windows 7', + 'value' => 'win7' + ], + [ + 'name' => 'Windows 8', + 'value' => 'win8' + ], + [ + 'name' => 'Windows 10', + 'value' => 'win10' + ], + [ + 'name' => 'Windows 11', + 'value' => 'win11' + ], + [ + 'name' => 'Linux 2.4 Kernel', + 'value' => 'l24' + ], + [ + 'name' => 'Linux 6.x - 2.6 Kernel', + 'value' => 'l26' + ], + [ + 'name' => 'solaris', + 'value' => 'solaris' + ] + ] + ], + [ + 'name' => 'cputype', + 'type' => 'dropdown', + 'friendlyName' => 'CPU type', + 'description' => 'The CPU type of the VM', + 'options' => $cpuList + ], + [ + 'name' => 'vcpu', + 'type' => 'number', + 'friendlyName' => 'vCPU cores', + 'description' => 'The number of vCPU cores of the VM', + ], + [ + 'name' => 'sockets', + 'type' => 'number', + 'friendlyName' => 'Sockets', + 'description' => 'The number of sockets of the VM', + ], + + [ + 'type' => 'title', + 'friendlyName' => 'Clone options', + 'description' => 'Options for cloning a VM' + ], + [ + 'name' => 'clone', + 'type' => 'boolean', + 'friendlyName' => 'Clone', + 'description' => 'Enable/disable cloning', + ], + [ + 'name' => 'vmId', + 'type' => 'number', + 'friendlyName' => 'VM ID', + 'description' => 'The ID of the VM to clone', + ], + ]; + } + + private function getRequest($url) + { + $response = Http::withHeaders([ + 'Authorization' => 'PVEAPIToken=' . ExtensionHelper::getConfig('Proxmox', 'username') . '=' . ExtensionHelper::getConfig('Proxmox', 'password'), + 'Accept' => 'application/json', + 'Content-Type' => 'application/json' + ])->withoutVerifying()->get(ExtensionHelper::getConfig('Proxmox', 'host') . ':' . ExtensionHelper::getConfig('Proxmox', 'port') . '/api2/json' . $url); + + return $response; + } + + private function postRequest($url, $data = []) + { + $response = Http::withHeaders([ + 'Authorization' => 'PVEAPIToken=' . ExtensionHelper::getConfig('Proxmox', 'username') . '=' . ExtensionHelper::getConfig('Proxmox', 'password'), + 'Accept' => 'application/json', + 'Content-Type' => 'application/json' + ])->withoutVerifying()->post(ExtensionHelper::getConfig('Proxmox', 'host') . ':' . ExtensionHelper::getConfig('Proxmox', 'port') . '/api2/json' . $url, $data); + + return $response; + } + + private function deleteRequest($url) + { + $response = Http::withHeaders([ + 'Authorization' => 'PVEAPIToken=' . ExtensionHelper::getConfig('Proxmox', 'username') . '=' . ExtensionHelper::getConfig('Proxmox', 'password'), + 'Accept' => 'application/json', + 'Content-Type' => 'application/json' + ])->withoutVerifying()->delete(ExtensionHelper::getConfig('Proxmox', 'host') . ':' . ExtensionHelper::getConfig('Proxmox', 'port') . '/api2/json' . $url); + + return $response; + } + + private function putRequest($url, $data = []) + { + $response = Http::withHeaders([ + 'Authorization' => 'PVEAPIToken=' . ExtensionHelper::getConfig('Proxmox', 'username') . '=' . ExtensionHelper::getConfig('Proxmox', 'password'), + 'Accept' => 'application/json', + 'Content-Type' => 'application/json' + ])->withoutVerifying()->put(ExtensionHelper::getConfig('Proxmox', 'host') . ':' . ExtensionHelper::getConfig('Proxmox', 'port') . '/api2/json' . $url, $data); + + return $response; + } + + public function test() + { + $response = $this->getRequest('/nodes'); + if (!$response->json()) throw new Exception('Unable to get nodes'); + return true; + } + + public function getUserConfig(Product $product) + { + $currentConfig = $product->settings; + if ($currentConfig->where('name', 'type')->first()->value == 'lxc') { + return [ + [ + 'name' => 'hostname', + 'type' => 'text', + 'friendlyName' => 'Hostname', + 'description' => 'The hostname of the VM', + ], + [ + 'name' => 'password', + 'type' => 'password', + 'friendlyName' => 'Password', + 'description' => 'The password of the VM', + 'required' => true, + ], + ]; + } + + return [ + [ + 'name' => 'hostname', + 'type' => 'text', + 'friendlyName' => 'Hostname', + 'description' => 'The hostname of the VM', + ], + [ + 'name' => 'password', + 'type' => 'password', + 'friendlyName' => 'Password', + 'description' => 'The password of the VM', + 'required' => true, + ], + ]; + } + + public function createServer($user, $parmas, $order, $product, $configurableOptions) + { + $node = isset($configurableOptions['node']) ? $configurableOptions['node'] : $parmas['node']; + $storage = isset($configurableOptions['storage']) ? $configurableOptions['storage'] : $parmas['storage']; + $pool = isset($configurableOptions['pool']) ? $configurableOptions['pool'] : $parmas['pool']; + $cores = isset($configurableOptions['cores']) ? $configurableOptions['cores'] : $parmas['cores']; + $memory = isset($configurableOptions['memory']) ? $configurableOptions['memory'] : $parmas['memory']; + $disk = isset($configurableOptions['disk']) ? $configurableOptions['disk'] : $parmas['disk']; + $swap = isset($configurableOptions['swap']) ? $configurableOptions['swap'] : $parmas['swap']; + $network_limit = isset($configurableOptions['network_limit']) ? $configurableOptions['network_limit'] : ($parmas['network_limit'] ?? null); + $cpu = isset($configurableOptions['cpu']) ? $configurableOptions['cpu'] : ($parmas['cpu'] ?? null); + + $vmid = $this->getRequest('/cluster/nextid')->json()['data']; + + // Assign it to the orderProduct for further use + ExtensionHelper::setOrderProductConfig('vmid', $vmid, $product->id); + $postData = []; + + $currentConfig = $product->product->settings; + $postData = []; + $vmType = $currentConfig->where('name', 'type')->first()->value; + if ($currentConfig->where('name', 'clone')->first()->value == '1') { + $postData = [ + 'newid' => $vmid, + 'name' => $parmas['config']['hostname'], + 'target' => $node, + 'full' => 1, + ]; + isset($parmas['pool']) && $postData['pool'] = $parmas['pool']; + $response = $this->postRequest('/nodes/' . $node . '/' . $vmType . '/' . $parmas['vmId'] . '/clone', $postData); + if (!$response->json()) throw new Exception('Unable to clone server'); + + // Update hardware + $postData = [ + 'cores' => $cores, + 'memory' => $memory, + 'cipassword' => $parmas['config']['password'], + ]; + $response = $this->putRequest('/nodes/' . $node . '/' . $vmType . '/' . $vmid . '/config', $postData); + if (!$response->json()) throw new Exception('Unable to update hardware'); + + // Get disk + $disk = $this->getRequest('/nodes/' . $node . '/' . $vmType . '/' . $vmid . '/config')->json()['data']; + $disk = explode('order=', $disk['boot'])[1]; + $disk = explode(',', $disk)[0]; + $postData = [ + 'disk' => $disk, + 'size' => $parmas['disk'] . 'G', + ]; + $response = $this->putRequest('/nodes/' . $node . '/' . $vmType . '/' . $vmid . '/resize', $postData); + return true; + } else if ($vmType == 'lxc') { + $postData = [ + 'vmid' => $vmid, + 'node' => $node, + 'storage' => $storage, + 'cores' => $cores, + 'memory' => $memory, + 'onboot' => 1, + 'ostemplate' => $parmas['template'], + 'ostype' => $parmas['ostypelxc'], + 'description' => $parmas['config']['hostname'], + 'hostname' => $parmas['config']['hostname'], + 'password' => $parmas['config']['password'], + 'swap' => $swap ?? 512, + 'net0' => 'name=test' . ',bridge=' . $parmas['bridge'] . ',' . (isset($parmas['firewall']) ? 'firewall=1' : 'firewall=0') . (isset($network_limit) ? ',rate=' . $network_limit : ''), + ]; + isset($pool) ? $postData['pool'] = $pool : null; + $response = $this->postRequest('/nodes/' . $node . '/lxc', $postData); + } else { + $socket = isset($configurableOptions['sockets']) ? $configurableOptions['sockets'] : ($parmas['sockets'] ?? null); + $vcpu = isset($configurableOptions['vcpu']) ? $configurableOptions['vcpu'] : ($parmas['vcpu'] ?? null); + $postData = [ + 'vmid' => $vmid, + 'node' => $node, + 'storage' => $storage, + 'cores' => $cores, + 'memory' => $memory, + 'onboot' => 1, + 'sockets' => $socket ?? 1, + 'agent' => 1, + 'ostype' => $parmas['ostype'], + 'name' => $parmas['config']['hostname'], + 'description' => $parmas['config']['hostname'], + $parmas['storageType'] . '0' => $parmas['storage'] . ':' . $disk . ',format=' . $parmas['storageFormat'], + 'net0' => $parmas['model'] . ',bridge=' . $parmas['bridge'] . ',' . (isset($parmas['firewall']) ? 'firewall=1' : 'firewall=0'), + ]; + isset($pool) ? $postData['pool'] = $pool : null; + isset($parmas['cloudinit']) ? $postData[$parmas['storageType'] . '0'] = $parmas['storage'] . ':cloudinit' . ',format=' . $parmas['storageFormat'] : null; + isset($cpu) ? $postData['cpu'] = $cpu : null; + isset($vcpu) ? $postData['vcpus'] = $vcpu : null; + if (isset($parmas['os']) && $parmas['os'] == 'iso') { + $postData['ide2'] = $parmas['iso'] . ',media=cdrom'; + } + $response = $this->postRequest('/nodes/' . $node . '/qemu', $postData); + } + if (!$response->json()) throw new Exception('Unable to create server' . $response->body()); + return true; + } + + public function suspendServer($user, $parmas, $order, $product, $configurableOptions) + { + throw new Exception('Not implemented'); + } + + public function unsuspendServer($user, $parmas, $order, $product, $configurableOptions) + { + throw new Exception('Not implemented'); + } + + public function terminateServer($user, $parmas, $order, $product, $configurableOptions) + { + $vmType = $parmas['type']; + $vmid = $parmas['config']['vmid']; + // Stop the VM first + $response = $this->postRequest('/nodes/' . $parmas['node'] . '/' . $vmType . '/' . $vmid . '/status/stop'); + // Delete the VM + $postData = [ + 'purge' => 1, + 'destroy-unreferenced-disks' => 1, + ]; + $response = $this->deleteRequest('/nodes/' . $parmas['node'] . '/' . $vmType . '/' . $vmid, $postData); + if (!$response->json()) throw new Exception('Unable to terminate server'); + return true; + } + + + public function getCustomPages($user, $parmas, $order, $product, $configurableOptions) + { + $vmType = $parmas['type']; + $vmid = $parmas['config']['vmid']; + $status = $this->getRequest('/nodes/' . $parmas['node'] . '/' . $vmType . '/' . $vmid . '/status/current'); + if (!$status->json()) throw new Exception('Unable to get server status'); + $status = $status->json()['data']; + + $stats = $this->getRequest('/nodes/' . $parmas['node'] . '/' . $vmType . '/' . $vmid . '/rrddata?timeframe=hour'); + if (!$stats->json()) throw new Exception('Unable to get server stats'); + $stats = $stats->json()['data']; + + // $vnc; + // if ($vmType == 'lxc') $vnc = $this->postRequest('/nodes/' . $parmas['node'] . '/' . $vmType . '/' . $vmid . '/vncproxy', ['websocket' => 1]); + // else $vnc = $this->postRequest('/nodes/' . $parmas['node'] . '/' . $vmType . '/' . $vmid . '/vncproxy', ['websocket' => 1, 'generate-password' => 1]); + // if (!$vnc->json()) throw new Exception('Unable to get server vnc'); + // $vnc = $vnc->json()['data']; + // $websocket = ExtensionHelper::getConfig('Proxmox', 'host') . ':' . ExtensionHelper::getConfig('Proxmox', 'port') . '/?console=kvm&novnc=1&node=' . $parmas['node'] . '&resize=1&vmid=' . $vmid . '&path=api2/json/nodes/' . $parmas['node'] . '/' . $vmType . '/' . $vmid . '/vncwebsocket/port/' . $vnc['port'] . '/"vncticket"/' . $vnc['ticket']; + + $users = $this->getRequest('/nodes/' . $parmas['node'] . '/' . $vmType . '/' . $vmid . '/agent/get-users'); + if (!$users->json()) throw new Exception('Unable to get server users'); + $users = $users->json()['data']; + + $config = $this->getRequest('/nodes/' . $parmas['node'] . '/' . $vmType . '/' . $vmid . '/config'); + if (!$config->json()) throw new Exception('Unable to get server config'); + $config = $config->json()['data']; + + + // Make url for iframe + + + return [ + 'name' => 'Proxmox', + 'template' => 'proxmox::control', + 'data' => [ + 'status' => $status, + 'node' => $parmas['node'], + 'vmid' => $vmid, + 'stats' => $stats, + // 'vnc' => $vnc, + // 'websocket' => $websocket, + 'users' => $users, + 'config' => (object) $config, + ], + 'pages' => [ + [ + 'template' => 'proxmox::stats', + 'name' => 'Statistics', + 'url' => 'stats', + ], + // [ + // 'template' => 'proxmox::vnc', + // 'name' => 'VNC', + // 'url' => 'vnc', + // ], + [ + 'template' => 'proxmox::settings', + 'name' => 'Settings', + 'url' => 'settings', + ] + ] + ]; + } + + public function status(Request $request, OrderProduct $product) + { + if (!ExtensionHelper::hasAccess($product, $request->user())) throw new Exception('You do not have access to this server'); + $request->validate([ + 'status' => ['required', 'string', 'in:stop,start,reboot,shutdown'], + ]); + $data = ExtensionHelper::getParameters($product); + $params = $data->config; + $vmid = $params['config']['vmid']; + $vmType = $params['type']; + $postData = [ + 'node' => $params['node'], + 'vmid' => $vmid, + ]; + // Change status + $status = $this->postRequest('/nodes/' . $params['node'] . '/' . $vmType . '/' . $vmid . '/status/' . $request->status, $postData); + if (!$status->json()) throw new Exception('Unable to ' . $request->status . ' server'); + + // Return json response + return response()->json([ + 'status' => 'success', + 'message' => 'Server has been ' . $request->status . 'ed successfully' + ]); + } + + public function configure(Request $request, OrderProduct $product) + { + if (!ExtensionHelper::hasAccess($product, $request->user())) throw new Exception('You do not have access to this server'); + $request->validate([ + 'hostname' => ['required', 'string', 'max:255'], + ]); + $data = ExtensionHelper::getParameters($product); + + $params = $data->config; + $vmid = $params['config']['vmid']; + $vmType = $params['type']; + + $postData = [ + 'hostname' => $request->hostname, + ]; + $config = $this->putRequest('/nodes/' . $params['node'] . '/' . $vmType . '/' . $vmid . '/config', $postData); + + if (!$config->json()) throw new Exception('Unable to configure server'); + return redirect()->back()->with('success', 'Server has been configured successfully'); + } +} diff --git a/Servers/Proxmox/index.php b/Servers/Proxmox/index.php deleted file mode 100644 index 17565e1..0000000 --- a/Servers/Proxmox/index.php +++ /dev/null @@ -1,868 +0,0 @@ - 'host', - 'friendlyName' => 'Host', - 'type' => 'text', - 'required' => true, - 'description' => 'The IP address or domain name of the Proxmox server (with http:// or https://)', - ], - [ - 'name' => 'port', - 'friendlyName' => 'Port', - 'type' => 'text', - 'required' => true, - 'description' => 'The port of the Proxmox server', - ], - [ - 'name' => 'username', - 'friendlyName' => 'Username', - 'type' => 'text', - 'required' => true, - 'description' => 'The api username of the Proxmox server', - ], - [ - 'name' => 'password', - 'friendlyName' => 'API Token', - 'type' => 'text', - 'required' => true, - 'description' => 'The API Token of the Proxmox server', - ] - ]; -} - -function Proxmox_getProductConfig($options) -{ - $nodes = Proxmox_getRequest('/nodes'); - if (!$nodes->json()) throw new Exception('Unable to get nodes'); - foreach ($nodes->json()['data'] as $node) { - $nodeList[] = [ - 'name' => $node['node'], - 'value' => $node['node'] - ]; - } - - $currentNode = isset($options['node']) ? $options['node'] : null; - $storageName = isset($options['storage']) ? $options['storage'] : null; - if ($currentNode == null) { - $currentNode = $nodeList[0]['value']; - } - $storage = Proxmox_getRequest('/nodes/' . $currentNode . '/storage'); - $storageList = []; - if (!$storage->json()) throw new Exception('Unable to get storage'); - foreach ($storage->json()['data'] as $storage) { - $storageList[] = [ - 'name' => $storage['storage'], - 'value' => $storage['storage'] - ]; - } - - $resourcePool = Proxmox_getRequest('/pools'); - $poolList = [ - [ - 'name' => 'None', - 'value' => '' - ] - ]; - - if (!$resourcePool->json()) throw new Exception('Unable to get resource pool'); - foreach ($resourcePool->json()['data'] as $pool) { - $poolList[] = [ - 'name' => $pool['poolid'], - 'value' => $pool['poolid'] - ]; - } - - // Only list contentVztmpl - $templateList = []; - $isoList = []; - foreach ($nodeList as $node) { - // Get all storage - $storage = Proxmox_getRequest('/nodes/' . $node['value'] . '/storage'); - if (!$storage->json()) throw new Exception('Unable to get storage'); - foreach ($storage->json()['data'] as $storage) { - $storageName = $storage['storage']; - $template = Proxmox_getRequest('/nodes/' . $node['value'] . '/storage/' . $storageName . '/content'); - if (!$template->json()) throw new Exception('Unable to get template'); - foreach ($template->json()['data'] as $template) { - if ($template['content'] == 'vztmpl') { - $templateList[] = [ - 'name' => $template['volid'], - 'value' => $template['volid'] - ]; - } else if ($template['content'] == 'iso') { - $isoList[] = [ - 'name' => $template['volid'], - 'value' => $template['volid'] - ]; - } - } - } - } - - - - $bridgeList = []; - $bridge = Proxmox_getRequest('/nodes/' . $currentNode . '/network'); - if (!$bridge->json()) throw new Exception('Unable to get bridge'); - foreach ($bridge->json()['data'] as $bridge) { - if (!isset($bridge['active'])) continue; - if (!$bridge['active']) continue; - $bridgeList[] = [ - 'name' => $bridge['iface'], - 'value' => $bridge['iface'] - ]; - } - - $cpuList = [ - [ - 'name' => 'Default', - 'value' => '' - ] - ]; - $cpu = Proxmox_getRequest('/nodes/' . $currentNode . '/capabilities/qemu/cpu'); - if (!$cpu->json()) throw new Exception('Unable to get cpu'); - foreach ($cpu->json()['data'] as $cpu) { - $cpuList[] = [ - 'name' => $cpu['name'] . ' (' . $cpu['vendor'] . ')', - 'value' => $cpu['name'] - ]; - } - - - - return [ - [ - 'type' => 'title', - 'friendlyName' => 'General', - 'description' => 'General options', - ], - [ - 'name' => 'node', - 'type' => 'dropdown', - 'friendlyName' => 'Node', - 'required' => true, - 'description' => 'The node name of the wanted node (submit to update the storage list)', - 'options' => $nodeList - ], - [ - 'name' => 'storage', - 'type' => 'dropdown', - 'friendlyName' => 'Storage', - 'description' => 'The storage name of the wanted storage', - 'options' => $storageList - ], - [ - 'name' => 'pool', - 'type' => 'dropdown', - 'friendlyName' => 'Resource Pool', - 'description' => 'Resource Pool places VMs in a group', - 'options' => $poolList - ], - [ - 'name' => 'type', - 'type' => 'dropdown', - 'friendlyName' => 'Type', - 'required' => true, - 'description' => 'The type of the wanted VM', - 'options' => [ - [ - 'name' => 'qemu', - 'value' => 'qemu' - ], - [ - 'name' => 'lxc', - 'value' => 'lxc' - ] - ] - ], - [ - 'name' => 'cores', - 'type' => 'text', - 'friendlyName' => 'Cores', - 'required' => true, - 'description' => 'The number of cores of the wanted VM', - ], - [ - 'name' => 'memory', - 'type' => 'text', - 'friendlyName' => 'Memory (MB)', - 'required' => true, - 'description' => 'The amount of memory of the wanted VM', - ], - [ - 'name' => 'disk', - 'type' => 'text', - 'friendlyName' => 'Disk (GB)', - 'required' => true, - 'description' => 'The amount of disk of the wanted VM', - ], - [ - 'name' => 'network_limit', - 'type' => 'text', - 'friendlyName' => 'Network Limit (MB)', - 'description' => 'The network limit of the wanted VM', - ], - - - [ - 'name' => 'lxc', - 'type' => 'title', - 'friendlyName' => 'LXC', - 'description' => 'All LXC options', - ], - [ - 'name' => 'template', - 'type' => 'dropdown', - 'friendlyName' => 'Template', - 'description' => 'The template name of the wanted VM', - 'options' => $templateList - ], - [ - 'name' => 'unprivileged', - 'type' => 'boolean', - 'friendlyName' => 'Unprivileged Container', - 'description' => 'Enable/disable unprivileged container', - ], - [ - 'name' => 'nesting', - 'type' => 'boolean', - 'friendlyName' => 'Nesting', - 'description' => 'Enable/disable nesting', - ], - [ - 'name' => 'ostypelxc', - 'type' => 'dropdown', - 'friendlyName' => 'OS Type', - 'description' => 'The OS type of the wanted VM', - 'options' => [ - [ - 'name' => 'debian', - 'value' => 'debian' - ], - [ - 'name' => 'devuan', - 'value' => 'devuan' - ], - [ - 'name' => 'ubuntu', - 'value' => 'ubuntu' - ], - [ - 'name' => 'centos', - 'value' => 'centos' - ], - [ - 'name' => 'fedora', - 'value' => 'fedora' - ], - [ - 'name' => 'opensuse', - 'value' => 'opensuse' - ], - [ - 'name' => 'archlinux', - 'value' => 'archlinux' - ], - [ - 'name' => 'alpine', - 'value' => 'alpine' - ], - [ - 'name' => 'gentoo', - 'value' => 'gentoo' - ], - [ - 'name' => 'nixos', - 'value' => 'nixos' - ], - [ - 'name' => 'unmanaged', - 'value' => 'unmanaged' - ] - ] - ], - [ - 'type' => 'text', - 'name' => 'swap', - 'friendlyName' => 'Swap (MB)', - 'description' => 'The amount of swap of the wanted VM', - ], - - [ - 'type' => 'title', - 'friendlyName' => 'QEMU', - 'description' => 'All QEMU options', - ], - [ - 'name' => 'nonetwork', - 'type' => 'boolean', - 'friendlyName' => 'No Network', - 'description' => 'Enable/disable network', - ], - [ - 'name' => 'bridge', - 'type' => 'dropdown', - 'friendlyName' => 'Bridge', - 'options' => $bridgeList - ], - [ - 'name' => 'model', - 'type' => 'dropdown', - 'friendlyName' => 'Model', - 'options' => [ - [ - 'name' => 'VirtIO', - 'value' => 'virtio' - ], - [ - 'name' => 'Intel E1000', - 'value' => 'e1000' - ], - [ - 'name' => 'Realtek RTL8139', - 'value' => 'rtl8139' - ], - [ - 'name' => 'VMWare VMXNET3', - 'value' => 'vmxnet3' - ] - ] - ], - [ - 'name' => 'vlantag', - 'type' => 'text', - 'friendlyName' => 'VLAN Tag', - 'description' => 'Optional VLAN tag', - ], - [ - 'name' => 'firewall', - 'type' => 'boolean', - 'friendlyName' => 'Firewall', - 'description' => 'Enable/disable firewall', - ], - [ - 'name' => 'os', - 'type' => 'dropdown', - 'friendlyName' => 'OS', - 'required' => true, - 'options' => [ - [ - 'name' => 'ISO', - 'value' => 'iso' - ], - [ - 'name' => 'Pysical CD/DVD drive', - 'value' => 'cdrom' - ], - [ - 'name' => 'None', - 'value' => 'none' - ] - ] - ], - [ - 'name' => 'iso', - 'type' => 'dropdown', - 'friendlyName' => 'ISO', - 'description' => 'The ISO name of the wanted VM', - 'options' => $isoList - ], - [ - 'name' => 'cloudinit', - 'type' => 'boolean', - 'friendlyName' => 'Cloudinit', - 'description' => 'Enable/disable cloudinit', - ], - [ - 'name' => 'storageType', - 'type' => 'dropdown', - 'friendlyName' => 'Bus/Device', - 'description' => 'The bus/device of the VM', - 'options' => - [ - [ - 'name' => 'IDE', - 'value' => 'ide' - ], - [ - 'name' => 'SATA', - 'value' => 'sata' - ], - [ - 'name' => 'SCSI', - 'value' => 'scsi' - ], - [ - 'name' => 'VirtIO block', - 'value' => 'virtio' - ] - ] - ], - [ - 'name' => 'storageFormat', - 'type' => 'dropdown', - 'friendlyName' => 'Storage Format', - 'description' => 'The storage format of the VM', - 'options' => [ - [ - 'name' => 'Raw', - 'value' => 'raw' - ], - [ - 'name' => 'Qcow2', - 'value' => 'qcow2' - ], - [ - 'name' => 'VMDK', - 'value' => 'vmdk' - ], - ] - ], - [ - 'name' => 'cache', - 'type' => 'dropdown', - 'friendlyName' => 'Cache', - 'description' => 'The cache of the VM', - 'options' => [ - [ - 'name' => 'Default (no cache)', - 'value' => 'default' - ], - [ - 'name' => 'Direct Sync', - 'value' => 'directsync' - ], - [ - 'name' => 'Write Through', - 'value' => 'writethrough' - ], - [ - 'name' => 'Write Back', - 'value' => 'write back' - ], - [ - 'name' => 'Write Back (unsafe)', - 'value' => 'unsafe' - ], - [ - 'name' => 'No Cache', - 'value' => 'none' - ], - ] - ], - [ - 'name' => 'ostype', - 'type' => 'dropdown', - 'friendlyName' => 'Guest OS type', - 'description' => 'The OS type of the VM', - 'options' => [ - [ - 'name' => 'other', - 'value' => 'other' - ], - [ - 'name' => 'Windows XP', - 'value' => 'wxp' - ], - [ - 'name' => 'Windows 2000', - 'value' => 'w2k' - ], - [ - 'name' => 'Windows 2003', - 'value' => 'w2k3' - ], - [ - 'name' => 'Windows 2008', - 'value' => 'w2k8' - ], - [ - 'name' => 'Windows Vista', - 'value' => 'wvista' - ], - [ - 'name' => 'Windows 7', - 'value' => 'win7' - ], - [ - 'name' => 'Windows 8', - 'value' => 'win8' - ], - [ - 'name' => 'Windows 10', - 'value' => 'win10' - ], - [ - 'name' => 'Windows 11', - 'value' => 'win11' - ], - [ - 'name' => 'Linux 2.4 Kernel', - 'value' => 'l24' - ], - [ - 'name' => 'Linux 6.x - 2.6 Kernel', - 'value' => 'l26' - ], - [ - 'name' => 'solaris', - 'value' => 'solaris' - ] - ] - ], - [ - 'name' => 'cputype', - 'type' => 'dropdown', - 'friendlyName' => 'CPU type', - 'description' => 'The CPU type of the VM', - 'options' => $cpuList - ], - [ - 'name' => 'vcpu', - 'type' => 'number', - 'friendlyName' => 'vCPU cores', - 'description' => 'The number of vCPU cores of the VM', - ], - [ - 'name' => 'sockets', - 'type' => 'number', - 'friendlyName' => 'Sockets', - 'description' => 'The number of sockets of the VM', - ], - - [ - 'type' => 'title', - 'friendlyName' => 'Clone options', - 'description' => 'Options for cloning a VM' - ], - [ - 'name' => 'clone', - 'type' => 'boolean', - 'friendlyName' => 'Clone', - 'description' => 'Enable/disable cloning', - ], - [ - 'name' => 'vmId', - 'type' => 'number', - 'friendlyName' => 'VM ID', - 'description' => 'The ID of the VM to clone', - ], - ]; -} - -function Proxmox_getRequest($url) -{ - $response = Http::withHeaders([ - 'Authorization' => 'PVEAPIToken=' . ExtensionHelper::getConfig('Proxmox', 'username') . '=' . ExtensionHelper::getConfig('Proxmox', 'password'), - 'Accept' => 'application/json', - 'Content-Type' => 'application/json' - ])->withoutVerifying()->get(ExtensionHelper::getConfig('Proxmox', 'host') . ':' . ExtensionHelper::getConfig('Proxmox', 'port') . '/api2/json' . $url); - - return $response; -} - -function Proxmox_postRequest($url, $data = []) -{ - $response = Http::withHeaders([ - 'Authorization' => 'PVEAPIToken=' . ExtensionHelper::getConfig('Proxmox', 'username') . '=' . ExtensionHelper::getConfig('Proxmox', 'password'), - 'Accept' => 'application/json', - 'Content-Type' => 'application/json' - ])->withoutVerifying()->post(ExtensionHelper::getConfig('Proxmox', 'host') . ':' . ExtensionHelper::getConfig('Proxmox', 'port') . '/api2/json' . $url, $data); - - return $response; -} - -function Proxmox_deleteRequest($url) -{ - $response = Http::withHeaders([ - 'Authorization' => 'PVEAPIToken=' . ExtensionHelper::getConfig('Proxmox', 'username') . '=' . ExtensionHelper::getConfig('Proxmox', 'password'), - 'Accept' => 'application/json', - 'Content-Type' => 'application/json' - ])->withoutVerifying()->delete(ExtensionHelper::getConfig('Proxmox', 'host') . ':' . ExtensionHelper::getConfig('Proxmox', 'port') . '/api2/json' . $url); - - return $response; -} - -function Proxmox_putRequest($url, $data = []) -{ - $response = Http::withHeaders([ - 'Authorization' => 'PVEAPIToken=' . ExtensionHelper::getConfig('Proxmox', 'username') . '=' . ExtensionHelper::getConfig('Proxmox', 'password'), - 'Accept' => 'application/json', - 'Content-Type' => 'application/json' - ])->withoutVerifying()->put(ExtensionHelper::getConfig('Proxmox', 'host') . ':' . ExtensionHelper::getConfig('Proxmox', 'port') . '/api2/json' . $url, $data); - - return $response; -} - -function Proxmox_test() -{ - $response = Proxmox_getRequest('/nodes'); - if (!$response->json()) throw new Exception('Unable to get nodes'); - return true; -} - -function Proxmox_getUserConfig(Product $product) -{ - $currentConfig = $product->settings; - if ($currentConfig->where('name', 'type')->first()->value == 'lxc') { - return [ - [ - 'name' => 'hostname', - 'type' => 'text', - 'friendlyName' => 'Hostname', - 'description' => 'The hostname of the VM', - ], - [ - 'name' => 'password', - 'type' => 'password', - 'friendlyName' => 'Password', - 'description' => 'The password of the VM', - 'required' => true, - ], - ]; - } - - return [ - [ - 'name' => 'hostname', - 'type' => 'text', - 'friendlyName' => 'Hostname', - 'description' => 'The hostname of the VM', - ], - [ - 'name' => 'password', - 'type' => 'password', - 'friendlyName' => 'Password', - 'description' => 'The password of the VM', - 'required' => true, - ], - ]; -} - -function Proxmox_createServer($user, $parmas, $order, $product, $configurableOptions) -{ - $node = isset($configurableOptions['node']) ? $configurableOptions['node'] : $parmas['node']; - $storage = isset($configurableOptions['storage']) ? $configurableOptions['storage'] : $parmas['storage']; - $pool = isset($configurableOptions['pool']) ? $configurableOptions['pool'] : $parmas['pool']; - $cores = isset($configurableOptions['cores']) ? $configurableOptions['cores'] : $parmas['cores']; - $memory = isset($configurableOptions['memory']) ? $configurableOptions['memory'] : $parmas['memory']; - $disk = isset($configurableOptions['disk']) ? $configurableOptions['disk'] : $parmas['disk']; - $swap = isset($configurableOptions['swap']) ? $configurableOptions['swap'] : $parmas['swap']; - $network_limit = isset($configurableOptions['network_limit']) ? $configurableOptions['network_limit'] : ($parmas['network_limit'] ?? null); - $cpu = isset($configurableOptions['cpu']) ? $configurableOptions['cpu'] : ($parmas['cpu'] ?? null); - - $vmid = Proxmox_getRequest('/cluster/nextid')->json()['data']; - - // Assign it to the orderProduct for further use - ExtensionHelper::setOrderProductConfig('vmid', $vmid, $order->id); - $postData = []; - - $currentConfig = $product->product->settings; - $postData = []; - $vmType = $currentConfig->where('name', 'type')->first()->value; - if ($currentConfig->where('name', 'clone')->first()->value == '1') { - $postData = [ - 'newid' => $vmid, - 'name' => $parmas['config']['hostname'], - 'target' => $node, - 'storage' => $storage, - 'full' => 1, - ]; - isset($parmas['pool']) && $postData['pool'] = $parmas['pool']; - $response = Proxmox_postRequest('/nodes/' . $node . '/' . $vmType . '/' . $parmas['vmId'] . '/clone', $postData); - } else if ($vmType == 'lxc') { - $postData = [ - 'vmid' => $vmid, - 'node' => $node, - 'storage' => $storage, - 'cores' => $cores, - 'memory' => $memory, - 'onboot' => 1, - 'ostemplate' => $parmas['template'], - 'ostype' => $parmas['ostypelxc'], - 'description' => $parmas['config']['hostname'], - 'hostname' => $parmas['config']['hostname'], - 'password' => $parmas['config']['password'], - 'swap' => $swap ?? 512, - 'net0' => 'name=test' . ',bridge=' . $parmas['bridge'] . ',' . (isset($parmas['firewall']) ? 'firewall=1' : 'firewall=0') . (isset($network_limit) ? ',rate=' . $network_limit : ''), - ]; - isset($pool) ? $postData['pool'] = $pool : null; - $response = Proxmox_postRequest('/nodes/' . $node . '/lxc', $postData); - } else { - $socket = isset($configurableOptions['sockets']) ? $configurableOptions['sockets'] : ($parmas['sockets'] ?? null); - $vcpu = isset($configurableOptions['vcpu']) ? $configurableOptions['vcpu'] : ($parmas['vcpu'] ?? null); - $postData = [ - 'vmid' => $vmid, - 'node' => $node, - 'storage' => $storage, - 'cores' => $cores, - 'memory' => $memory, - 'onboot' => 1, - 'sockets' => $socket ?? 1, - 'agent' => 1, - 'ostype' => $parmas['ostype'], - 'name' => $parmas['config']['hostname'], - 'description' => $parmas['config']['hostname'], - $parmas['storageType'] . '0' => $parmas['storage'] . ':' . $disk . ',format=' . $parmas['storageFormat'], - 'net0' => $parmas['model'] . ',bridge=' . $parmas['bridge'] . ',' . (isset($parmas['firewall']) ? 'firewall=1' : 'firewall=0'), - ]; - isset($pool) ? $postData['pool'] = $pool : null; - isset($parmas['cloudinit']) ? $postData[$parmas['storageType'] . '0'] = $parmas['storage'] . ':cloudinit' . ',format=' . $parmas['storageFormat'] : null; - isset($cpu) ? $postData['cpu'] = $cpu : null; - isset($vcpu) ? $postData['vcpus'] = $vcpu : null; - if (isset($parmas['os']) && $parmas['os'] == 'iso') { - $postData['ide2'] = $parmas['iso'] . ',media=cdrom'; - } - $response = Proxmox_postRequest('/nodes/' . $node . '/qemu', $postData); - } - if (!$response->json()) throw new Exception('Unable to create server' . $response->body()); - return true; -} - -function Proxmox_suspendServer($user, $parmas, $order, $product, $configurableOptions) -{ - throw new Exception('Not implemented'); -} - -function Proxmox_unsuspendServer($user, $parmas, $order, $product, $configurableOptions) -{ - throw new Exception('Not implemented'); -} - -function Proxmox_terminateServer($user, $parmas, $order, $product, $configurableOptions) -{ - $vmType = $parmas['type']; - $vmid = $parmas['config']['vmid']; - // Stop the VM first - $response = Proxmox_postRequest('/nodes/' . $parmas['node'] . '/' . $vmType . '/' . $vmid . '/status/stop'); - // Delete the VM - $postData = [ - 'purge' => 1, - 'destroy-unreferenced-disks' => 1, - ]; - $response = Proxmox_deleteRequest('/nodes/' . $parmas['node'] . '/' . $vmType . '/' . $vmid, $postData); - if (!$response->json()) throw new Exception('Unable to terminate server'); - return true; -} - - -function Proxmox_getCustomPages($user, $parmas, $order, $product, $configurableOptions) -{ - $vmType = $parmas['type']; - $vmid = $parmas['config']['vmid']; - $status = Proxmox_getRequest('/nodes/' . $parmas['node'] . '/' . $vmType . '/' . $vmid . '/status/current'); - if (!$status->json()) throw new Exception('Unable to get server status'); - $status = $status->json()['data']; - - $stats = Proxmox_getRequest('/nodes/' . $parmas['node'] . '/' . $vmType . '/' . $vmid . '/rrddata?timeframe=hour'); - if (!$stats->json()) throw new Exception('Unable to get server stats'); - $stats = $stats->json()['data']; - - // $vnc; - // if ($vmType == 'lxc') $vnc = Proxmox_postRequest('/nodes/' . $parmas['node'] . '/' . $vmType . '/' . $vmid . '/vncproxy', ['websocket' => 1]); - // else $vnc = Proxmox_postRequest('/nodes/' . $parmas['node'] . '/' . $vmType . '/' . $vmid . '/vncproxy', ['websocket' => 1, 'generate-password' => 1]); - // if (!$vnc->json()) throw new Exception('Unable to get server vnc'); - // $vnc = $vnc->json()['data']; - // $websocket = ExtensionHelper::getConfig('Proxmox', 'host') . ':' . ExtensionHelper::getConfig('Proxmox', 'port') . '/?console=kvm&novnc=1&node=' . $parmas['node'] . '&resize=1&vmid=' . $vmid . '&path=api2/json/nodes/' . $parmas['node'] . '/' . $vmType . '/' . $vmid . '/vncwebsocket/port/' . $vnc['port'] . '/"vncticket"/' . $vnc['ticket']; - - $users = Proxmox_getRequest('/nodes/' . $parmas['node'] . '/' . $vmType . '/' . $vmid . '/agent/get-users'); - if (!$users->json()) throw new Exception('Unable to get server users'); - $users = $users->json()['data']; - - $config = Proxmox_getRequest('/nodes/' . $parmas['node'] . '/' . $vmType . '/' . $vmid . '/config'); - if (!$config->json()) throw new Exception('Unable to get server config'); - $config = $config->json()['data']; - - - // Make url for iframe - - - return [ - 'name' => 'Proxmox', - 'template' => 'proxmox::control', - 'data' => [ - 'status' => $status, - 'node' => $parmas['node'], - 'vmid' => $vmid, - 'stats' => $stats, - // 'vnc' => $vnc, - // 'websocket' => $websocket, - 'users' => $users, - 'config' => (object) $config, - ], - 'pages' => [ - [ - 'template' => 'proxmox::stats', - 'name' => 'Statistics', - 'url' => 'stats', - ], - // [ - // 'template' => 'proxmox::vnc', - // 'name' => 'VNC', - // 'url' => 'vnc', - // ], - [ - 'template' => 'proxmox::settings', - 'name' => 'Settings', - 'url' => 'settings', - ] - ] - ]; -} - -function Proxmox_status(Request $request, OrderProduct $product) -{ - if (!ExtensionHelper::hasAccess($product, $request->user())) throw new Exception('You do not have access to this server'); - $request->validate([ - 'status' => ['required', 'string', 'in:stop,start,reboot,shutdown'], - ]); - $data = ExtensionHelper::getParameters($product); - $params = $data->config; - $vmid = $params['config']['vmid']; - $vmType = $params['type']; - $postData = [ - 'node' => $params['node'], - 'vmid' => $vmid, - ]; - // Change status - $status = Proxmox_postRequest('/nodes/' . $params['node'] . '/' . $vmType . '/' . $vmid . '/status/' . $request->status, $postData); - if (!$status->json()) throw new Exception('Unable to ' . $request->status . ' server'); - - // Return json response - return response()->json([ - 'status' => 'success', - 'message' => 'Server has been ' . $request->status . 'ed successfully' - ]); -} - -function Proxmox_configure(Request $request, OrderProduct $product) -{ - if (!ExtensionHelper::hasAccess($product, $request->user())) throw new Exception('You do not have access to this server'); - $request->validate([ - 'hostname' => ['required', 'string', 'max:255'], - ]); - $data = ExtensionHelper::getParameters($product); - - $params = $data->config; - $vmid = $params['config']['vmid']; - $vmType = $params['type']; - - $postData = [ - 'hostname' => $request->hostname, - ]; - $config = Proxmox_putRequest('/nodes/' . $params['node'] . '/' . $vmType . '/' . $vmid . '/config', $postData); - - if (!$config->json()) throw new Exception('Unable to configure server'); - return redirect()->back()->with('success', 'Server has been configured successfully'); -} diff --git a/Servers/Proxmox/routes.php b/Servers/Proxmox/routes.php index c5f7aac..db308ac 100644 --- a/Servers/Proxmox/routes.php +++ b/Servers/Proxmox/routes.php @@ -1,15 +1,7 @@ name('extensions.proxmox.status'); -Route::post('/proxmox/status/{id}', function (Request $request, OrderProduct $id) { - return Proxmox_status($request, $id); -})->name('extensions.proxmox.status'); - -Route::post('/proxmox/configure/{id}', function (Request $request, OrderProduct $id) { - return Proxmox_configure($request, $id); -})->name('extensions.proxmox.configure'); \ No newline at end of file +Route::post('/proxmox/configure/{product}', [App\Extensions\Servers\Proxmox\Proxmox::class, 'configure'])->name('extensions.proxmox.configure'); \ No newline at end of file diff --git a/Servers/Pterodactyl/Pterodactyl.php b/Servers/Pterodactyl/Pterodactyl.php new file mode 100644 index 0000000..1bb1e8d --- /dev/null +++ b/Servers/Pterodactyl/Pterodactyl.php @@ -0,0 +1,438 @@ + 'Pterodactyl', + 'version' => '1.0.0', + 'author' => 'Paymenter', + 'website' => 'https://paymenter.org', + ]; + } + + private function config($key): ?string + { + $config = ExtensionHelper::getConfig('Pterodactyl', $key); + if ($config) { + return $config; + } + + return null; + } + + public function getConfig(): array + { + return [ + [ + 'name' => 'host', + 'friendlyName' => 'Pterodactyl panel url', + 'type' => 'text', + 'required' => true, + ], + [ + 'name' => 'apiKey', + 'friendlyName' => 'API Key', + 'type' => 'text', + 'required' => true, + ], + ]; + } + + + private function postRequest($url, $data): \GuzzleHttp\Promise\PromiseInterface|\Illuminate\Http\Client\Response + { + return Http::withHeaders([ + 'Authorization' => 'Bearer ' . $this->config('apiKey'), + 'Accept' => 'Application/vnd.Pterodactyl.v1+json', + 'Content-Type' => 'application/json', + ])->post($url, $data); + } + + private function getRequest($url): \GuzzleHttp\Promise\PromiseInterface|\Illuminate\Http\Client\Response + { + return Http::withHeaders([ + 'Authorization' => 'Bearer ' . $this->config('apiKey'), + 'Accept' => 'Application/vnd.Pterodactyl.v1+json', + 'Content-Type' => 'application/json', + ])->get($url); + } + + public function deleteRequest($url): \GuzzleHttp\Promise\PromiseInterface|\Illuminate\Http\Client\Response + { + return Http::withHeaders([ + 'Authorization' => 'Bearer ' . $this->config('apiKey'), + 'Accept' => 'Application/vnd.Pterodactyl.v1+json', + 'Content-Type' => 'application/json', + ])->delete($url); + } + + public function getProductConfig($options): array + { + $nodes = $this->getRequest($this->config('host') . '/api/application/nodes'); + $nodeList = [ + [ + 'name' => 'None', + 'value' => '', + ], + ]; + foreach ($nodes->json()['data'] as $node) { + $nodeList[] = [ + 'name' => $node['attributes']['name'], + 'value' => $node['attributes']['id'], + ]; + } + + $location = $this->getRequest($this->config('host') . '/api/application/locations'); + $locationList = []; + foreach ($location->json()['data'] as $location) { + $locationList[] = [ + 'name' => $location['attributes']['short'], + 'value' => $location['attributes']['id'], + ]; + } + + $nests = $this->getRequest($this->config('host') . '/api/application/nests'); + $nestList = []; + foreach ($nests->json()['data'] as $nest) { + $nestList[] = [ + 'name' => $nest['attributes']['name'], + 'value' => $nest['attributes']['id'], + ]; + } + + + + return [ + [ + 'name' => 'node', + 'friendlyName' => 'Pterodactyl Node (leave empty for node assigned to location)', + 'type' => 'dropdown', + 'options' => $nodeList, + ], + [ + 'name' => 'location', + 'friendlyName' => 'Pterodactyl Location', + 'type' => 'dropdown', + 'options' => $locationList, + 'required' => true, + ], + [ + 'name' => 'egg', + 'friendlyName' => 'Pterodactyl Egg ID', + 'type' => 'text', + 'required' => true, + ], + [ + 'name' => 'nest', + 'friendlyName' => 'Pterodactyl Nest', + 'type' => 'dropdown', + 'options' => $nestList, + 'required' => true, + ], + [ + 'name' => 'memory', + 'friendlyName' => 'Pterodactyl Memory', + 'type' => 'text', + 'required' => true, + ], + [ + 'name' => 'swap', + 'friendlyName' => 'Pterodactyl Swap', + 'type' => 'text', + 'required' => true, + ], + [ + 'name' => 'disk', + 'friendlyName' => 'Pterodactyl Disk', + 'type' => 'text', + 'required' => true, + ], + [ + 'name' => 'io', + 'friendlyName' => 'Pterodactyl IO', + 'type' => 'text', + 'required' => true, + 'description' => 'IO is a number between 10 and 1000. 500 is the default value.', + ], + [ + 'name' => 'cpu', + 'friendlyName' => 'CPU limit', + 'type' => 'text', + 'required' => true, + ], + [ + 'name' => 'cpu_pinning', + 'friendlyName' => 'CPU pinning', + 'type' => 'text', + 'required' => false, + ], + [ + 'name' => 'databases', + 'friendlyName' => 'Pterodactyl Database', + 'type' => 'text', + 'required' => true, + ], + [ + 'name' => 'backups', + 'friendlyName' => 'Pterodactyl Backups', + 'type' => 'text', + 'required' => true, + ], + [ + 'name' => 'servername', + 'friendlyName' => 'Product Name (leave empty for auto generated name)', + 'type' => 'text', + 'required' => false, + 'description' => 'If you do not fill in this field the server name will be set to the PRODUCT NAME #ID eg. "Test Product #26"', + ], + [ + 'name' => 'allocation', + 'friendlyName' => 'Pterodactyl Allocation', + 'type' => 'text', + 'required' => true, + 'description' => 'How many ports the user can allocate. Must be at least one.', + ], + [ + 'name' => 'skip_scripts', + 'friendlyName' => 'Pterodactyl Skip Scripts', + 'type' => 'boolean', + 'description' => 'Decides if Pterodactyl will skip install scripts', + ], + ]; + } + + public function createServer($user, $params, $order, $orderProduct, $configurableOptions): bool + { + if ($this->serverExists($orderProduct->id)) { + ExtensionHelper::error('Pterodactyl', 'Server already exists for order ' . $orderProduct->id); + + return true; + } + + $url = $this->config('host') . '/api/application/servers'; + $nest_id = $configurableOptions['nest_id'] ?? $params['nest']; + $egg_id = $configurableOptions['egg'] ?? $params['egg']; + $eggData = $this->getRequest($this->config('host') . '/api/application/nests/' . $nest_id . '/eggs/' . $egg_id . '?include=variables')->json(); + + if (!isset($eggData['attributes'])) { + ExtensionHelper::error('Pterodactyl', 'No egg data found for ' . $params['egg']); + + return false; + } + + $environment = []; + + foreach ($eggData['attributes']['relationships']['variables']['data'] as $key => $val) { + $attr = $val['attributes']; + $var = $attr['env_variable']; + $default = $attr['default_value']; + // If the variable is configurable, get the value from the configurable options + if (isset($configurableOptions[$var])) { + $default = $configurableOptions[$var]; + } + $environment[$var] = $default; + } + + $cpu = $configurableOptions['cpu'] ?? $params['cpu']; + $cpu_pinning = $configurableOptions['cpu_pinning'] ?? $params['cpu_pinning'] ?? null; + $io = $configurableOptions['io'] ?? $params['io']; + $disk = $configurableOptions['disk'] ?? $params['disk']; + $swap = $configurableOptions['swap'] ?? $params['swap']; + $memory = $configurableOptions['memory'] ?? $params['memory']; + $allocations = $configurableOptions['allocation'] ?? $params['allocation']; + $location = $configurableOptions['location'] ?? $params['location']; + $databases = $configurableOptions['databases'] ?? $params['databases']; + $backups = $configurableOptions['backups'] ?? $params['backups']; + $startup = $configurableOptions['startup'] ?? $eggData['attributes']['startup']; + $node = $configurableOptions['node'] ?? $params['node']; + $servername = $configurableOptions['servername'] ?? $params['servername'] ?? false; + $servername = empty($servername) ? $orderProduct->product->name . ' #' . $orderProduct->id : $servername; + + if ($node) { + $allocation = $this->getRequest($this->config('host') . '/api/application/nodes/' . $params['node'] . '/allocations'); + $allocation = $allocation->json(); + foreach ($allocation['data'] as $key => $val) { + if (!$val['attributes']['assigned']) { + $allocation = $val['attributes']['id']; + break; + } + } + $json = [ + 'name' => $servername, + 'user' => (int) $this->getUser($user, $orderProduct), + 'egg' => (int) $egg_id, + 'docker_image' => $eggData['attributes']['docker_image'], + 'startup' => $startup, + 'limits' => [ + 'memory' => (int) $memory, + 'swap' => (int) $swap, + 'disk' => (int) $disk, + 'io' => (int) $io, + 'cpu' => (int) $cpu, + 'threads' => $cpu_pinning, + ], + 'feature_limits' => [ + 'databases' => $databases ? (int) $databases : null, + 'allocations' => (int) $allocations, + 'backups' => (int) $backups, + ], + 'allocation' => [ + 'default' => (int) $allocation, + ], + 'environment' => $environment, + 'external_id' => (string) $orderProduct->id, + ]; + } else { + $json = [ + 'name' => $servername, + 'user' => (int) $this->getUser($user, $orderProduct), + 'egg' => (int) $egg_id, + 'docker_image' => $eggData['attributes']['docker_image'], + 'startup' => $startup, + 'limits' => [ + 'memory' => (int) $memory, + 'swap' => (int) $swap, + 'disk' => (int) $disk, + 'io' => (int) $io, + 'cpu' => (int) $cpu, + 'threads' => $cpu_pinning, + ], + 'feature_limits' => [ + 'databases' => $databases ? (int) $databases : null, + 'allocations' => (int) $allocations, + 'backups' => (int) $backups, + ], + 'deploy' => [ + 'locations' => [(int) $location], + 'dedicated_ip' => false, + 'port_range' => [], + ], + 'environment' => $environment, + 'external_id' => (string) $orderProduct->id, + ]; + } + $response = $this->postRequest($url, $json); + + if (!$response->successful()) { + ExtensionHelper::error('Pterodactyl', 'Failed to create server for order ' . $orderProduct->id . ' with error ' . $response->body()); + + return false; + } + + return true; + } + + private function random_string($length = 10): string + { + $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $charactersLength = strlen($characters); + $randomString = ''; + for ($i = 0; $i < $length; ++$i) { + $randomString .= $characters[mt_rand(0, $charactersLength - 1)]; + } + + return $randomString; + } + + public function getUser($user, $product = null) + { + $url = $this->config('host') . '/api/application/users?filter%5Bemail%5D=' . $user->email; + $response = $this->getRequest($url); + $users = $response->json(); + if (count($users['data']) > 0) { + return $users['data'][0]['attributes']['id']; + } else { + $url = $this->config('host') . '/api/application/users'; + $sanitized = preg_replace('/[^a-zA-Z0-9]/', '', strtolower($user->name)); + if (empty($sanitized)) { + $sanitized = $this->random_string(8); // Ta funkcja musi być dostępna w twoim kodzie + } + $json = [ + 'username' => $sanitized.'_'.$this->random_string(3)??$this->random_string(8), + 'email' => $user->email, + 'first_name' => $user->name, + 'last_name' => $user->lastname??'User', + ]; + $response = $this->postRequest($url, $json); + if (!$response->successful()) { + ExtensionHelper::error('Pterodactyl', 'Failed to create user for order ' . $product->id . ' with error ' . $response->body()); + } + $user = $response->json(); + + return $user['attributes']['id']; + } + } + + private function serverExists($order) + { + $url = $this->config('host') . '/api/application/servers/external/' . $order; + $response = $this->getRequest($url); + $code = $response->status(); + if ($code == 200) { + return $response->json()['attributes']['id']; + } + + return false; + } + + public function suspendServer($user, $params, $order, $orderProduct, $configurableOptions): bool + { + $server = $this->serverExists($orderProduct->id); + if ($server) { + $url = $this->config('host') . '/api/application/servers/' . $server . '/suspend'; + $this->postRequest($url, []); + + return true; + } + + return false; + } + + public function unsuspendServer($user, $params, $order, $orderProduct, $configurableOptions): bool + { + $server = $this->serverExists($orderProduct->id); + if ($server) { + $url = $this->config('host') . '/api/application/servers/' . $server . '/unsuspend'; + $this->postRequest($url, []); + + return true; + } + + return false; + } + + public function terminateServer($user, $params, $order, $orderProduct, $configurableOptions): bool + { + $server = $this->serverExists($orderProduct->id); + if ($server) { + $url = $this->config('host') . '/api/application/servers/' . $server; + $this->deleteRequest($url); + + return true; + } + + return false; + } + + public function getLink($user, $params, $order, $orderProduct): bool|string + { + $server = $this->serverExists($orderProduct->id); + if ($server) { + $url = $this->config('host') . '/api/application/servers/' . $server; + $response = $this->getRequest($url); + $server = $response->json(); + + return $this->config('host') . '/server/' . $server['attributes']['identifier']; + } + + return false; + } +} diff --git a/Servers/Pterodactyl/index.php b/Servers/Pterodactyl/index.php deleted file mode 100644 index c947a57..0000000 --- a/Servers/Pterodactyl/index.php +++ /dev/null @@ -1,402 +0,0 @@ - 'host', - 'friendlyName' => 'Pterodactyl panel url', - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'apiKey', - 'friendlyName' => 'API Key', - 'type' => 'text', - 'required' => true, - ], - ]; -} - -function Pterodactyl_getProductConfig() -{ - $nodes = Pterodactyl_getRequest(pteroConfig('host') . '/api/application/nodes'); - $nodeList = [ - [ - 'name' => 'None', - 'value' => '', - ], - ]; - foreach ($nodes->json()['data'] as $node) { - $nodeList[] = [ - 'name' => $node['attributes']['name'], - 'value' => $node['attributes']['id'], - ]; - } - - $location = Pterodactyl_getRequest(pteroConfig('host') . '/api/application/locations'); - $locationList = []; - foreach ($location->json()['data'] as $location) { - $locationList[] = [ - 'name' => $location['attributes']['short'], - 'value' => $location['attributes']['id'], - ]; - } - - $nests = Pterodactyl_getRequest(pteroConfig('host') . '/api/application/nests'); - $nestList = []; - foreach ($nests->json()['data'] as $nest) { - $nestList[] = [ - 'name' => $nest['attributes']['name'], - 'value' => $nest['attributes']['id'], - ]; - } - - - - return [ - [ - 'name' => 'node', - 'friendlyName' => 'Pterodactyl Node (leave empty for node assigned to location)', - 'type' => 'dropdown', - 'options' => $nodeList, - ], - [ - 'name' => 'location', - 'friendlyName' => 'Pterodactyl Location', - 'type' => 'dropdown', - 'options' => $locationList, - 'required' => true, - ], - [ - 'name' => 'egg', - 'friendlyName' => 'Pterodactyl Egg ID', - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'nest', - 'friendlyName' => 'Pterodactyl Nest', - 'type' => 'dropdown', - 'options' => $nestList, - 'required' => true, - ], - [ - 'name' => 'memory', - 'friendlyName' => 'Pterodactyl Memory', - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'swap', - 'friendlyName' => 'Pterodactyl Swap', - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'disk', - 'friendlyName' => 'Pterodactyl Disk', - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'io', - 'friendlyName' => 'Pterodactyl IO', - 'type' => 'text', - 'required' => true, - 'description' => 'IO is a number between 10 and 1000. 500 is the default value.', - ], - [ - 'name' => 'cpu', - 'friendlyName' => 'CPU limit', - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'databases', - 'friendlyName' => 'Pterodactyl Database', - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'backups', - 'friendlyName' => 'Pterodactyl Backups', - 'type' => 'text', - 'required' => true, - ], - [ - 'name' => 'skip_scripts', - 'friendlyName' => 'Pterodactyl Skip Scripts', - 'type' => 'boolean', - 'description' => 'Decides if Pterodactyl will skip install scripts', - ], - [ - 'name' => 'allocation', - 'friendlyName' => 'Pterodactyl Allocation', - 'type' => 'text', - 'required' => true, - 'description' => 'How many ports the user can allocate. Must be at least one.', - ], - ]; -} - -function Pterodactyl_postRequest($url, $data) -{ - $response = Http::withHeaders([ - 'Authorization' => 'Bearer ' . pteroConfig('apiKey'), - 'Accept' => 'Application/vnd.Pterodactyl.v1+json', - 'Content-Type' => 'application/json', - ])->post($url, $data); - - return $response; -} - -function Pterodactyl_getRequest($url) -{ - $response = Http::withHeaders([ - 'Authorization' => 'Bearer ' . pteroConfig('apiKey'), - 'Accept' => 'Application/vnd.Pterodactyl.v1+json', - 'Content-Type' => 'application/json', - ])->get($url); - - return $response; -} - -function Pterodactyl_deleteRequest($url) -{ - $response = Http::withHeaders([ - 'Authorization' => 'Bearer ' . pteroConfig('apiKey'), - 'Accept' => 'Application/vnd.Pterodactyl.v1+json', - 'Content-Type' => 'application/json', - ])->delete($url); - - return $response; -} - -function Pterodactyl_createServer($user, $parmas, $order, $product, $configurableOptions) -{ - if (Pterodactyl_serverExists($product->id)) { - ExtensionHelper::error('Pterodactyl', 'Server already exists for order ' . $product->id); - - return; - } - $url = pteroConfig('host') . '/api/application/servers'; - $nest_id = isset($configurableOptions['nest_id']) ? $configurableOptions['nest_id'] : $parmas['nest']; - $egg_id = isset($configurableOptions['egg']) ? $configurableOptions['egg'] : $parmas['egg']; - $eggData = Pterodactyl_getRequest(pteroConfig('host') . '/api/application/nests/' . $nest_id . '/eggs/' . $egg_id . '?include=variables')->json(); - if (!isset($eggData['attributes'])) { - ExtensionHelper::error('Pterodactyl', 'No egg data found for ' . $parmas['egg']); - - return; - } - $environment = []; - foreach ($eggData['attributes']['relationships']['variables']['data'] as $key => $val) { - $attr = $val['attributes']; - $var = $attr['env_variable']; - $default = $attr['default_value']; - // If the variable is configurable, get the value from the configurable options - if (isset($configurableOptions[$var])) { - $default = $configurableOptions[$var]; - } - $environment[$var] = $default; - } - $cpu = isset($configurableOptions['cpu']) ? $configurableOptions['cpu'] : $parmas['cpu']; - $io = isset($configurableOptions['io']) ? $configurableOptions['io'] : $parmas['io']; - $disk = isset($configurableOptions['disk']) ? $configurableOptions['disk'] : $parmas['disk']; - $swap = isset($configurableOptions['swap']) ? $configurableOptions['swap'] : $parmas['swap']; - $memory = isset($configurableOptions['memory']) ? $configurableOptions['memory'] : $parmas['memory']; - $allocations = isset($configurableOptions['allocation']) ? $configurableOptions['allocation'] : $parmas['allocation']; - $location = isset($configurableOptions['location']) ? $configurableOptions['location'] : $parmas['location']; - $databases = isset($configurableOptions['databases']) ? $configurableOptions['databases'] : $parmas['databases']; - $backups = isset($configurableOptions['backups']) ? $configurableOptions['backups'] : $parmas['backups']; - $startup = isset($configurableOptions['startup']) ? $configurableOptions['startup'] : $eggData['attributes']['startup']; - $node = isset($configurableOptions['node']) ? $configurableOptions['node'] : $parmas['node']; - - if ($node) { - $allocation = Pterodactyl_getRequest(pteroConfig('host') . '/api/application/nodes/' . $parmas['node'] . '/allocations'); - $allocation = $allocation->json(); - foreach ($allocation['data'] as $key => $val) { - if ($val['attributes']['assigned'] == false) { - $allocation = $val['attributes']['id']; - break; - } - } - $json = [ - 'name' => Pterodactyl_random_string(8) . '-' . $product->id, - 'user' => (int) Pterodactyl_getUser($user), - 'egg' => (int) $egg_id, - 'docker_image' => $eggData['attributes']['docker_image'], - 'startup' => $startup, - 'limits' => [ - 'memory' => (int) $memory, - 'swap' => (int) $swap, - 'disk' => (int) $disk, - 'io' => (int) $io, - 'cpu' => (int) $cpu, - ], - 'feature_limits' => [ - 'databases' => $databases ? (int) $databases : null, - 'allocations' => $allocations, - 'backups' => $backups, - ], - 'allocation' => [ - 'default' => (int) $allocation, - ], - 'environment' => $environment, - 'external_id' => (string) $product->id, - ]; - } else { - $json = [ - 'name' => Pterodactyl_random_string(8) . '-' . $product->id, - 'user' => (int) Pterodactyl_getUser($user), - 'egg' => (int) $egg_id, - 'docker_image' => $eggData['attributes']['docker_image'], - 'startup' => $startup, - 'limits' => [ - 'memory' => (int) $memory, - 'swap' => (int) $swap, - 'disk' => (int) $disk, - 'io' => (int) $io, - 'cpu' => (int) $cpu, - ], - 'feature_limits' => [ - 'databases' => $databases ? (int) $databases : null, - 'allocations' => $allocations, - 'backups' => $backups, - ], - 'deploy' => [ - 'locations' => [(int) $location], - 'dedicated_ip' => false, - 'port_range' => [], - ], - 'environment' => $environment, - 'external_id' => (string) $product->id, - ]; - } - $response = Pterodactyl_postRequest($url, $json); - - if (!$response->successful()) { - ExtensionHelper::error('Pterodactyl', 'Failed to create server for order ' . $product->id . ' with error ' . $response->body()); - - return; - } - - return true; -} - -function Pterodactyl_random_string($length = 10) -{ - $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - $charactersLength = strlen($characters); - $randomString = ''; - for ($i = 0; $i < $length; ++$i) { - $randomString .= $characters[mt_rand(0, $charactersLength - 1)]; - } - - return $randomString; -} - -function Pterodactyl_getUser($user) -{ - $url = pteroConfig('host') . '/api/application/users?filter%5Bemail%5D=' . $user->email; - $response = Pterodactyl_getRequest($url); - $users = $response->json(); - if (count($users['data']) > 0) { - return $users['data'][0]['attributes']['id']; - } else { - $url = pteroConfig('host') . '/api/application/users'; - $json = [ - 'username' => Pterodactyl_random_string(8), - 'email' => $user->email, - 'first_name' => $user->name, - 'last_name' => 'User', - 'language' => 'en', - 'root_admin' => false, - ]; - $response = Pterodactyl_postRequest($url, $json); - if(!$response->successful()) { - ExtensionHelper::error('Pterodactyl', 'Failed to create user for order ' . $product->id . ' with error ' . $response->body()); - } - $user = $response->json(); - - return $user['attributes']['id']; - } -} - -function Pterodactyl_serverExists($order) -{ - $url = pteroConfig('host') . '/api/application/servers/external/' . $order; - $response = Pterodactyl_getRequest($url); - $code = $response->status(); - if ($code == 200) { - return $response->json()['attributes']['id']; - } - - return false; -} - -function Pterodactyl_suspendServer($user, $params, $order, $product) -{ - $server = Pterodactyl_serverExists($product->id); - if ($server) { - $url = pteroConfig('host') . '/api/application/servers/' . $server . '/suspend'; - Pterodactyl_postRequest($url, []); - - return true; - } - - return false; -} - -function Pterodactyl_unsuspendServer($user, $params, $order, $product) -{ - $server = Pterodactyl_serverExists($product->id); - if ($server) { - $url = pteroConfig('host') . '/api/application/servers/' . $server . '/unsuspend'; - Pterodactyl_postRequest($url, []); - - return true; - } - - return false; -} - -function Pterodactyl_terminateServer($user, $params,$order, $product) -{ - $server = Pterodactyl_serverExists($product->id); - if ($server) { - $url = pteroConfig('host') . '/api/application/servers/' . $server; - Pterodactyl_deleteRequest($url); - - return true; - } - - return false; -} - -function Pterodactyl_getLink($user, $params, $order, $product) -{ - $server = Pterodactyl_serverExists($product->id); - if ($server) { - $url = pteroConfig('host') . '/api/application/servers/' . $server; - $response = Pterodactyl_getRequest($url); - $server = $response->json(); - - return pteroConfig('host') . '/server/' . $server['attributes']['identifier']; - } - - return false; -} diff --git a/Servers/VirtFusion/VirtFusion.php b/Servers/VirtFusion/VirtFusion.php new file mode 100644 index 0000000..6484e37 --- /dev/null +++ b/Servers/VirtFusion/VirtFusion.php @@ -0,0 +1,287 @@ + 'VirtFusion', + 'version' => '1.0.0', + 'author' => 'Paymenter', + 'website' => 'https://paymenter.org', + ]; + } + + public function getConfig() + { + return [ + [ + 'name' => 'host', + 'type' => 'text', + 'friendlyName' => 'Hostname', + 'required' => true, + ], + [ + 'name' => 'apikey', + 'type' => 'text', + 'friendlyName' => 'API key', + 'required' => true, + ], + ]; + } + + public function getProductConfig($options) + { + $packages = $this->getRequest('/api/v1/packages'); + $package = []; + foreach ($packages->json()['data'] as $p) { + $package[] = [ + 'name' => $p['name'], + 'value' => $p['id'], + ]; + } + return [ + [ + 'name' => 'package', + 'type' => 'dropdown', + 'friendlyName' => 'Package', + 'required' => true, + 'options' => $package, + ], + [ + 'name' => 'hypervisor', + 'type' => 'text', + 'friendlyName' => 'Hypervisor Group ID', + 'required' => true, + ], + [ + 'name' => 'ips', + 'type' => 'text', + 'friendlyName' => 'Number IPs', + 'required' => true, + ], + ]; + } + + public function createServer($user, $params, $order, $product, $configurableOptions) + { + $package = $params['package']; + + $user = $this->getUser($user); + $response = $this->postRequest( + '/api/v1/servers', + [ + 'packageId' => $package, + 'userId' => $user, + 'hypervisorId' => $params['hypervisor'], + 'ipv4' => $params['ips'], + ] + ); + if (isset($response->json()['errors'])) { + // Array to string conversion + $error = implode(" ", $response->json()['errors']); + ExtensionHelper::error('VirtFusion', 'Failed to create server' . $error); + return; + } + ExtensionHelper::setOrderProductConfig('server_id', $response->json()['data']['id'], $product->id); + + return true; + } + + private function getRequest($url) + { + $apikey = ExtensionHelper::getConfig('VirtFusion', 'apikey'); + $host = ExtensionHelper::getConfig('VirtFusion', 'host'); + + $response = Http::withHeaders([ + 'Authorization' => 'Bearer ' . $apikey, + 'Accept' => 'Application/json', + 'Content-Type' => 'application/json', + ])->get( + $host . $url + ); + return $response; + } + + private function postRequest($url, $data) + { + $apikey = ExtensionHelper::getConfig('VirtFusion', 'apikey'); + $host = ExtensionHelper::getConfig('VirtFusion', 'host'); + + $response = Http::withHeaders([ + 'Authorization' => 'Bearer ' . $apikey, + 'Accept' => 'Application/json', + 'Content-Type' => 'application/json', + ])->post( + $host . $url, + $data + ); + return $response; + } + + private function getUser($user) + { + $response = $this->getRequest('/api/v1/users/' . $user->id . '/byExtRelation'); + + if (isset($response->json()['data'])) { + return $response->json()['data']['id']; + } else { + // Create user + $response = $this->postRequest( + '/api/v1/users', + [ + 'name' => $user->name, + 'email' => $user->email, + 'extRelationId' => $user->id, + ] + ); + + if ($response->successful()) { + return $response->json()['data']['id']; + } else { + ExtensionHelper::error('VirtFusion', 'Failed to create user ', (string) $response->json() . ' ' . $response->status()); + + return; + } + } + } + + public function suspendServer($user, $params, $order, $product, $configurableOptions) + { + $apikey = ExtensionHelper::getConfig('VirtFusion', 'apikey'); + $host = ExtensionHelper::getConfig('VirtFusion', 'host'); + if (!isset($params['config']['server_id'])) { + return; + } + $server = $params['config']['server_id']; + + $response = Http::withHeaders([ + 'Authorization' => 'Bearer ' . $apikey, + 'Accept' => 'Application/json', + 'Content-Type' => 'application/json', + ])->post( + $host . '/api/v1/servers/' . $server . '/suspend' + ); + if ($response->status() == 204) { + return true; + } else { + ExtensionHelper::error('VirtFusion', 'Failed to suspend server'); + + return; + } + + return true; + } + + public function unsuspendServer($user, $params, $order, $product, $configurableOptions) + { + $apikey = ExtensionHelper::getConfig('VirtFusion', 'apikey'); + $host = ExtensionHelper::getConfig('VirtFusion', 'host'); + if (!isset($params['config']['server_id'])) { + return; + } + $server = $params['config']['server_id']; + + $response = Http::withHeaders([ + 'Authorization' => 'Bearer ' . $apikey, + 'Accept' => 'Application/json', + 'Content-Type' => 'application/json', + ])->post( + $host . '/api/v1/servers/' . $server . '/unsuspend' + ); + if ($response->status() == 204) { + return true; + } else { + ExtensionHelper::error('VirtFusion', 'Failed to unsuspend server'); + + return; + } + + return true; + } + + public function terminateServer($user, $params, $order, $product, $configurableOptions) + { + $apikey = ExtensionHelper::getConfig('VirtFusion', 'apikey'); + $host = ExtensionHelper::getConfig('VirtFusion', 'host'); + if (!isset($params['config']['server_id'])) { + return; + } + $server = $params['config']['server_id']; + + $response = Http::withHeaders([ + 'Authorization' => 'Bearer ' . $apikey, + 'Accept' => 'Application/json', + 'Content-Type' => 'application/json', + ])->delete( + $host . '/api/v1/servers/' . $server . '?delay=5' + ); + if ($response->status() == 204) { + return true; + } else { + ExtensionHelper::error('VirtFusion', 'Failed to terminate server'); + + return; + } + + return true; + } + + public function getCustomPages($user, $params, $order, $product, $configurableOptions) + { + $apikey = ExtensionHelper::getConfig('VirtFusion', 'apikey'); + $host = ExtensionHelper::getConfig('VirtFusion', 'host'); + if (!isset($params['config']['server_id'])) { + return; + } + $server = $params['config']['server_id']; + + $response = Http::withHeaders([ + 'Authorization' => 'Bearer ' . $apikey, + 'Accept' => 'Application/json', + 'Content-Type' => 'application/json', + ])->get( + $host . '/api/v1/servers/' . $server + ); + + if ($response->status() !== 200) { + ExtensionHelper::error('VirtFusion', 'Failed to get custom pages'); + + return; + } + + return [ + 'name' => 'VirtFusion', + 'template' => 'virtfusion::control', + 'data' => [ + 'details' => (object) $response->json()['data'], + ], + ]; + } + + public function login(OrderProduct $id, Request $request) + { + + if (!ExtensionHelper::hasAccess($id, auth()->user())) { + return response()->json(['error' => 'You do not have access to this server'], 403); + } + $params = ExtensionHelper::getParameters($id)->config; + + $loginLink = $this->postRequest( + '/api/v1/users/' . auth()->user()->id . '/serverAuthenticationTokens/' . $params['config']['server_id'], + [] + ); + + $loginLink = $loginLink->json()['data']['authentication']['endpoint_complete']; + + return redirect(ExtensionHelper::getConfig('VirtFusion', 'host') . $loginLink); + } +} diff --git a/Servers/VirtFusion/index.php b/Servers/VirtFusion/index.php deleted file mode 100644 index ed37f16..0000000 --- a/Servers/VirtFusion/index.php +++ /dev/null @@ -1,239 +0,0 @@ - 'host', - 'type' => 'text', - 'friendlyName' => 'Hostname', - 'required' => true, - ], - [ - 'name' => 'apikey', - 'type' => 'text', - 'friendlyName' => 'API key', - 'required' => true, - ], - ]; -} - -function VirtFusion_getProductConfig() -{ - $packages = VirtFusion_getRequest('/api/v1/packages'); - $package = []; - foreach ($packages->json()['data'] as $p) { - $package[] = [ - 'name' => $p['name'], - 'value' => $p['id'], - ]; - } - return [ - [ - 'name' => 'package', - 'type' => 'dropdown', - 'friendlyName' => 'Package', - 'required' => true, - 'options' => $package, - ], - [ - 'name' => 'hypervisor', - 'type' => 'text', - 'friendlyName' => 'Hypervisor Group ID', - 'required' => true, - ], - [ - 'name' => 'ips', - 'type' => 'text', - 'friendlyName' => 'Number IPs', - 'required' => true, - ], - ]; -} - -function VirtFusion_createServer($user, $params, $order, $product, $configurableOptions) -{ - $package = $params['package']; - - $user = VirtFusion_getUser($user); - $response = VirtFusion_postRequest( - '/api/v1/servers', - [ - 'packageId' => $package, - 'userId' => $user, - 'hypervisorId' => $params['hypervisor'], - 'ipv4' => $params['ips'], - ] - ); - if (isset($response->json()['errors'])) { - // Array to string conversion - $error = implode(" ", $response->json()['errors']); - ExtensionHelper::error('VirtFusion', 'Failed to create server' . $error); - return; - } - ExtensionHelper::setOrderProductConfig('server_id', $response->json()['data']['id'], $product->id); - - return true; -} - -function VirtFusion_getRequest($url) -{ - $apikey = ExtensionHelper::getConfig('VirtFusion', 'apikey'); - $host = ExtensionHelper::getConfig('VirtFusion', 'host'); - - $response = Http::withHeaders([ - 'Authorization' => 'Bearer ' . $apikey, - 'Accept' => 'Application/json', - 'Content-Type' => 'application/json', - ])->get( - $host . $url - ); - return $response; -} - -function VirtFusion_postRequest($url, $data) -{ - $apikey = ExtensionHelper::getConfig('VirtFusion', 'apikey'); - $host = ExtensionHelper::getConfig('VirtFusion', 'host'); - - $response = Http::withHeaders([ - 'Authorization' => 'Bearer ' . $apikey, - 'Accept' => 'Application/json', - 'Content-Type' => 'application/json', - ])->post( - $host . $url, - $data - ); - return $response; -} - -function VirtFusion_getUser($user) -{ - $response = VirtFusion_getRequest('/api/v1/users/' . $user->id . '/byExtRelation'); - - if (isset($response->json()['data'])) { - return $response->json()['data']['id']; - } else { - // Create user - $response = VirtFusion_postRequest( - '/api/v1/users', - [ - 'name' => $user->name, - 'email' => $user->email, - 'extRelationId' => $user->id, - ] - ); - - if ($response->status() == 200) { - return $response->json()['data']['id']; - } else { - ExtensionHelper::error('VirtFusion', 'Failed to create user ', (string) $response->json() . ' ' . $response->status()); - - return; - } - } -} - -function VirtFusion_suspendServer($user, $params, $order) -{ - $apikey = ExtensionHelper::getConfig('VirtFusion', 'apikey'); - $host = ExtensionHelper::getConfig('VirtFusion', 'host'); - $server = $params['config']['server_id']; - - $response = Http::withHeaders([ - 'Authorization' => 'Bearer ' . $apikey, - 'Accept' => 'Application/json', - 'Content-Type' => 'application/json', - ])->post( - $host . '/api/v1/servers/' . $server . '/suspend' - ); - if ($response->status() == 204) { - return true; - } else { - ExtensionHelper::error('VirtFusion', 'Failed to suspend server'); - - return; - } - - return true; -} - -function VirtFusion_unsuspendServer($user, $params, $order) -{ - $apikey = ExtensionHelper::getConfig('VirtFusion', 'apikey'); - $host = ExtensionHelper::getConfig('VirtFusion', 'host'); - $server = $params['config']['server_id']; - - $response = Http::withHeaders([ - 'Authorization' => 'Bearer ' . $apikey, - 'Accept' => 'Application/json', - 'Content-Type' => 'application/json', - ])->post( - $host . '/api/v1/servers/' . $server . '/unsuspend' - ); - if ($response->status() == 204) { - return true; - } else { - ExtensionHelper::error('VirtFusion', 'Failed to unsuspend server'); - - return; - } - - return true; -} - -function VirtFusion_terminateServer($user, $params, $order) -{ - $apikey = ExtensionHelper::getConfig('VirtFusion', 'apikey'); - $host = ExtensionHelper::getConfig('VirtFusion', 'host'); - $server = $params['config']['server_id']; - - $response = Http::withHeaders([ - 'Authorization' => 'Bearer ' . $apikey, - 'Accept' => 'Application/json', - 'Content-Type' => 'application/json', - ])->delete( - $host . '/api/v1/servers/' . $server . '?delay=5' - ); - if ($response->status() == 204) { - return true; - } else { - ExtensionHelper::error('VirtFusion', 'Failed to terminate server'); - - return; - } - - return true; -} - -function VirtFusion_getCustomPages($user, $params, $order, $product, $configurableOptions) -{ - $apikey = ExtensionHelper::getConfig('VirtFusion', 'apikey'); - $host = ExtensionHelper::getConfig('VirtFusion', 'host'); - $server = $params['config']['server_id']; - - $response = Http::withHeaders([ - 'Authorization' => 'Bearer ' . $apikey, - 'Accept' => 'Application/json', - 'Content-Type' => 'application/json', - ])->get( - $host . '/api/v1/servers/' . $server - ); - - if ($response->status() !== 200) { - ExtensionHelper::error('VirtFusion', 'Failed to get custom pages'); - - return; - } - - return [ - 'name' => 'VirtFusion', - 'template' => 'virtfusion::control', - 'data' => [ - 'details' =>(object) $response->json()['data'], - ], - ]; -} diff --git a/Servers/VirtFusion/routes.php b/Servers/VirtFusion/routes.php new file mode 100644 index 0000000..93ba360 --- /dev/null +++ b/Servers/VirtFusion/routes.php @@ -0,0 +1,8 @@ +name('extensions.virtfusion.login'); \ No newline at end of file diff --git a/Servers/VirtFusion/views/control.blade.php b/Servers/VirtFusion/views/control.blade.php index 049e3d7..95d8072 100644 --- a/Servers/VirtFusion/views/control.blade.php +++ b/Servers/VirtFusion/views/control.blade.php @@ -7,7 +7,7 @@
CPU:
-
{{ $details->name ?? 'N/A'}}
+
@empty($details->name) N/A @else {{ $details->name }} @endempty
{{ $details->hostname ?? 'N/A'}}
{{ $details->settings['resources']['memory'] ?? 'N/A'}} MB
{{ $details->settings['resources']['cpuCores'] ?? 'N/A' }} @if($details->settings['resources']['cpuCores'] > 1)cores @else core @endif
@@ -23,8 +23,15 @@
{{ $details->network['interfaces'][0]['ipv4'][0]['address'] ?? 'N/A' }}
{{ $details->network['interfaces'][0]['ipv6'][0]['address'] ?? 'N/A' }}
-
{{ $details->settings['resources']['storage'] ?? 'N/A' }} MB
-
{{ $details->settings['resources']['traffic'] ?? 'N/A'}} MB
+
{{ $details->settings['resources']['storage'] ?? 'N/A' }} GB
+
{{ $details->settings['resources']['traffic'] ?? 'N/A'}} GB
+ + + +

Manage your server via our dedicated control panel. You will be automatically authenticated and the control panel will open in a new tab.

+ + Login to control panel + diff --git a/Servers/Virtualizor/Virtualizor.php b/Servers/Virtualizor/Virtualizor.php new file mode 100644 index 0000000..b7ec9ea --- /dev/null +++ b/Servers/Virtualizor/Virtualizor.php @@ -0,0 +1,268 @@ + 'Virtualizor', + 'version' => '1.0.0', + 'author' => 'Paymenter', + 'website' => 'https://paymenter.org', + ]; + } + + public function getConfig() + { + return [ + [ + 'name' => 'key', + 'type' => 'text', + 'friendlyName' => 'API Key', + 'required' => true, + ], + [ + 'name' => 'pass', + 'type' => 'text', + 'friendlyName' => 'API Password', + 'required' => true, + ], + [ + 'name' => 'ip', + 'type' => 'text', + 'friendlyName' => 'IP Address', + 'required' => true, + ], + [ + 'name' => 'port', + 'type' => 'text', + 'friendlyName' => 'Port', + 'required' => true, + ], + ]; + } + + public function getUserConfig(Product $product) + { + $key = ExtensionHelper::getConfig('virtualizor', 'key'); + $pass = ExtensionHelper::getConfig('virtualizor', 'pass'); + $ip = ExtensionHelper::getConfig('virtualizor', 'ip'); + $port = ExtensionHelper::getConfig('virtualizor', 'port'); + $admin = new Virtualizor_Admin_API(); + $admin->Virtualizor_Admin_API($ip, $key, $pass, $port); + // Get os list + $os = $admin->ostemplates(); + $allos = []; + if (!$os || !key($os['oslist']) || !key($os['oslist'][$product->settings()->get()->where('name', 'virt')->first()->value])) { + throw new \Exception('No OS found'); + } + foreach ($os['oslist'][$product->settings()->get()->where('name', 'virt')->first()->value] as $osid => $osname) { + foreach ($osname as $osid => $osname) { + $allos[] = [ + 'name' => $osname['name'], + 'value' => $osid, + ]; + } + } + + return [ + [ + 'name' => 'hostname', + 'type' => 'text', + 'validation' => 'regex:/^(?!:\/\/)(?=.{1,255}$)((.{1,63}\.){1,127}(?![0-9]*$)[a-z0-9-]+\.?)$/i', + 'friendlyName' => 'Hostname', + 'placeholder' => 'example.com', + 'required' => true, + ], + [ + 'name' => 'password', + 'type' => 'text', + 'validation' => 'max:6|regex:/^[a-zA-Z0-9]+$/i', + 'friendlyName' => 'Password (max 6 characters and only letters and numbers)', + 'required' => true, + ], + [ + 'name' => 'os', + 'type' => 'dropdown', + 'friendlyName' => 'Operating System', + 'required' => true, + 'options' => $allos, + ], + ]; + } + + public function getProductConfig($options) + { + $key = ExtensionHelper::getConfig('virtualizor', 'key'); + $pass = ExtensionHelper::getConfig('virtualizor', 'pass'); + $ip = ExtensionHelper::getConfig('virtualizor', 'ip'); + $port = ExtensionHelper::getConfig('virtualizor', 'port'); + $admin = new Virtualizor_Admin_API(); + $admin->Virtualizor_Admin_API($ip, $key, $pass, $port); + + // Get Plan list + $plans = $admin->plans(); + if (!$plans) { + throw new \Exception('No plans found'); + } + $allplans = []; + foreach ($plans['plans'] as $plan) { + $allplans[] = [ + 'name' => $plan['plan_name'], + 'value' => $plan['plan_name'], + ]; + } + + return [ + [ + 'name' => 'virt', + 'friendlyName' => 'Virtualizon Type', + 'type' => 'dropdown', + 'required' => true, + 'options' => [ + [ + 'name' => 'OpenVZ', + 'value' => 'openvz', + ], + [ + 'name' => 'Xen', + 'value' => 'xen', + ], + [ + 'name' => 'KVM', + 'value' => 'kvm', + ], + ], + ], + [ + 'name' => 'planname', + 'friendlyName' => 'Plan Name', + 'type' => 'dropdown', + 'required' => true, + 'options' => $allplans, + ], + ]; + } + + public function createServer($user, $params, $order, $product, $configurableOptions) + { + $config = $params['config']; + $key = ExtensionHelper::getConfig('virtualizor', 'key'); + $pass = ExtensionHelper::getConfig('virtualizor', 'pass'); + $ip = ExtensionHelper::getConfig('virtualizor', 'ip'); + $port = ExtensionHelper::getConfig('virtualizor', 'port'); + $admin = new Virtualizor_Admin_API(); + $admin->Virtualizor_Admin_API($ip, $key, $pass, $port); + // Get plan ID + $page = 1; + $reslen = 1; + $post = []; + $post['planname'] = $params['planname']; + $post['ptype'] = $params['virt']; + $plans = $admin->plans($page, $reslen, $post); + if (!$plans || !key($plans['plans'])) { + ExtensionHelper::error('Virtualizor', 'Plan not found'); + return; + } + $plan = $plans['plans'][key($plans['plans'])]; + // Create server + $post = []; + $post['virt'] = $params['virt']; + $post['user_email'] = $user->email; + $post['user_pass'] = $config['password']; + $post['fname'] = $user->name; + $post['lname'] = $user->name; + $post['osid'] = $config['os']; + $post['server_group'] = 0; + $post['hostname'] = $config['hostname']; + $post['rootpass'] = $config['password']; + $post['num_ips6'] = isset($configurableOptions['ips6']) ? $configurableOptions['ips6'] : $plan['ips6']; + $post['num_ips6_subnet'] = isset($configurableOptions['ips6_subnet']) ? $configurableOptions['ips6_subnet'] : $plan['ips6_subnet']; + $post['num_ips'] = isset($configurableOptions['ips']) ? $configurableOptions['ips'] : $plan['ips']; + $post['ram'] = isset($configurableOptions['ram']) ? $configurableOptions['ram'] : $plan['ram']; + $post['swapram'] = isset($configurableOptions['swap']) ? $configurableOptions['swap'] : $plan['swap']; + $post['bandwidth'] = isset($configurableOptions['bandwidth']) ? $configurableOptions['bandwidth'] : $plan['bandwidth']; + $post['network_speed'] = isset($configurableOptions['network_speed']) ? $configurableOptions['network_speed'] : $plan['network_speed']; + $post['cpu'] = isset($configurableOptions['cpu']) ? $configurableOptions['cpu'] : $plan['cpu']; + $post['cores'] = isset($configurableOptions['cores']) ? $configurableOptions['cores'] : $plan['cores']; + $post['cpu_percent'] = isset($configurableOptions['cpu_percent']) ? $configurableOptions['cpu_percent'] : $plan['cpu_percent']; + $post['vnc'] = isset($configurableOptions['vnc']) ? $configurableOptions['vnc'] : $plan['vnc']; + $post['kvm_cache'] = $plan['kvm_cache']; + $post['io_mode'] = $plan['io_mode']; + $post['vnc_keymap'] = $plan['vnc_keymap']; + $post['nic_type'] = $plan['nic_type']; + $post['osreinstall_limit'] = isset($configurableOptions['osreinstall_limit']) ? $configurableOptions['osreinstall_limit'] : $plan['osreinstall_limit']; + $post['space'] = isset($configurableOptions['space']) ? $configurableOptions['space'] : $plan['space']; + + $output = $admin->addvs_v2($post); + + if (isset($output['error']) && !empty($output['error'])) { + ExtensionHelper::error('Virtualizor', $output['error']); + return; + } + // Set server ID + $server = $output['vs_info']['vpsid']; + ExtensionHelper::setOrderProductConfig('external_id', $server, $product->id); + + return true; + } + + public function suspendServer($user, $params, $order, $product, $configurableOptions) + { + $key = ExtensionHelper::getConfig('virtualizor', 'key'); + $pass = ExtensionHelper::getConfig('virtualizor', 'pass'); + $ip = ExtensionHelper::getConfig('virtualizor', 'ip'); + $port = ExtensionHelper::getConfig('virtualizor', 'port'); + $admin = new Virtualizor_Admin_API(); + $admin->Virtualizor_Admin_API($ip, $key, $pass, $port); + if (!isset($params['config']['external_id'])) { + ExtensionHelper::error('Virtualizor', 'Server not found for order #' . $order->id . '.'); + return; + } + $admin->suspend($params['config']['external_id']); + + return true; + } + + public function unsuspendServer($user, $params, $order, $product, $configurableOptions) + { + $key = ExtensionHelper::getConfig('virtualizor', 'key'); + $pass = ExtensionHelper::getConfig('virtualizor', 'pass'); + $ip = ExtensionHelper::getConfig('virtualizor', 'ip'); + $port = ExtensionHelper::getConfig('virtualizor', 'port'); + $admin = new Virtualizor_Admin_API(); + $admin->Virtualizor_Admin_API($ip, $key, $pass, $port); + if (!isset($params['config']['external_id'])) { + ExtensionHelper::error('Virtualizor', 'Server not found for order #' . $order->id . '.'); + return; + } + $admin->unsuspend($params['config']['external_id']); + + return true; + } + + public function terminateServer($user, $params, $order, $product, $configurableOptions) + { + $key = ExtensionHelper::getConfig('virtualizor', 'key'); + $pass = ExtensionHelper::getConfig('virtualizor', 'pass'); + $ip = ExtensionHelper::getConfig('virtualizor', 'ip'); + $port = ExtensionHelper::getConfig('virtualizor', 'port'); + $admin = new Virtualizor_Admin_API(); + $admin->Virtualizor_Admin_API($ip, $key, $pass, $port); + if (!isset($params['config']['external_id'])) { + ExtensionHelper::error('Virtualizor', 'Server not found for order #' . $order->id . '.'); + return; + } + $admin->delete_vs($params['config']['external_id']); + + return true; + } +} diff --git a/Servers/Virtualizor/sdk.php b/Servers/Virtualizor/Virtualizor_Admin_API.php similarity index 99% rename from Servers/Virtualizor/sdk.php rename to Servers/Virtualizor/Virtualizor_Admin_API.php index 2c3e630..e9e3fe2 100644 --- a/Servers/Virtualizor/sdk.php +++ b/Servers/Virtualizor/Virtualizor_Admin_API.php @@ -1,4 +1,5 @@ 'key', - 'type' => 'text', - 'friendlyName' => 'API Key', - 'required' => true, - ], - [ - 'name' => 'pass', - 'type' => 'text', - 'friendlyName' => 'API Password', - 'required' => true, - ], - [ - 'name' => 'ip', - 'type' => 'text', - 'friendlyName' => 'IP Address', - 'required' => true, - ], - [ - 'name' => 'port', - 'type' => 'text', - 'friendlyName' => 'Port', - 'required' => true, - ], - ]; -} - -function Virtualizor_getUserConfig(Product $product) -{ - $key = ExtensionHelper::getConfig('virtualizor', 'key'); - $pass = ExtensionHelper::getConfig('virtualizor', 'pass'); - $ip = ExtensionHelper::getConfig('virtualizor', 'ip'); - $port = ExtensionHelper::getConfig('virtualizor', 'port'); - $admin = new Virtualizor_Admin_API(); - $admin->Virtualizor_Admin_API($ip, $key, $pass, $port); - // Get os list - $os = $admin->ostemplates(); - $allos = []; - foreach ($os['oslist'][$product->settings()->get()->where('name', 'virt')->first()->value] as $osid => $osname) { - foreach ($osname as $osid => $osname) { - $allos[] = [ - 'name' => $osname['name'], - 'value' => $osid, - ]; - } - } - - return [ - [ - 'name' => 'hostname', - 'type' => 'text', - 'friendlyName' => 'Hostname', - 'required' => true, - ], - [ - 'name' => 'password', - 'type' => 'text', - 'friendlyName' => 'Password', - 'required' => true, - ], - [ - 'name' => 'os', - 'type' => 'dropdown', - 'friendlyName' => 'Operating System', - 'required' => true, - 'options' => $allos, - ], - ]; -} - -function Virtualizor_getProductConfig() -{ - $key = ExtensionHelper::getConfig('virtualizor', 'key'); - $pass = ExtensionHelper::getConfig('virtualizor', 'pass'); - $ip = ExtensionHelper::getConfig('virtualizor', 'ip'); - $port = ExtensionHelper::getConfig('virtualizor', 'port'); - $admin = new Virtualizor_Admin_API(); - $admin->Virtualizor_Admin_API($ip, $key, $pass, $port); - - // Get Plan list - $plans = $admin->plans(); - $allplans = []; - foreach ($plans['plans'] as $plan) { - $allplans[] = [ - 'name' => $plan['plan_name'], - 'value' => $plan['plan_name'], - ]; - } - - return [ - [ - 'name' => 'virt', - 'friendlyName' => 'Virtualizon Type', - 'type' => 'dropdown', - 'required' => true, - 'options' => [ - [ - 'name' => 'OpenVZ', - 'value' => 'openvz', - ], - [ - 'name' => 'Xen', - 'value' => 'xen', - ], - [ - 'name' => 'KVM', - 'value' => 'kvm', - ], - ], - ], - [ - 'name' => 'planname', - 'friendlyName' => 'Plan Name', - 'type' => 'dropdown', - 'required' => true, - 'options' => $allplans, - ], - ]; -} - -function Virtualizor_createServer($user, $params, $order, $product, $configurableOptions) -{ - $config = $params['config']; - $key = ExtensionHelper::getConfig('virtualizor', 'key'); - $pass = ExtensionHelper::getConfig('virtualizor', 'pass'); - $ip = ExtensionHelper::getConfig('virtualizor', 'ip'); - $port = ExtensionHelper::getConfig('virtualizor', 'port'); - $admin = new Virtualizor_Admin_API(); - $admin->Virtualizor_Admin_API($ip, $key, $pass, $port); - // Get plan ID - $page = 1; - $reslen = 1; - $post = []; - $post['planname'] = $params['planname']; - $post['ptype'] = $params['virt']; - $plans = $admin->plans($page, $reslen, $post); - if (!key($plans['plans'])) { - ExtensionHelper::error('Virtualizor', 'Plan not found'); - return; - } - $plan = $plans['plans'][key($plans['plans'])]; - // Create server - $post = []; - $post['virt'] = $params['virt']; - $post['user_email'] = $user->email; - $post['user_pass'] = $config['password']; - $post['fname'] = $user->name; - $post['lname'] = $user->name; - $post['osid'] = 909; - $post['server_group'] = 0; - $post['hostname'] = $config['hostname']; - $post['rootpass'] = $config['password']; - $post['num_ips6'] = isset($configurableOptions['ips6']) ? $configurableOptions['ips6'] : $plan['ips6']; - $post['num_ips6_subnet'] = isset($configurableOptions['ips6_subnet']) ? $configurableOptions['ips6_subnet'] : $plan['ips6_subnet']; - $post['num_ips'] = isset($configurableOptions['ips']) ? $configurableOptions['ips'] : $plan['ips']; - $post['ram'] = isset($configurableOptions['ram']) ? $configurableOptions['ram'] : $plan['ram']; - $post['swapram'] = isset($configurableOptions['swap']) ? $configurableOptions['swap'] : $plan['swap']; - $post['bandwidth'] = isset($configurableOptions['bandwidth']) ? $configurableOptions['bandwidth'] : $plan['bandwidth']; - $post['network_speed'] = isset($configurableOptions['network_speed']) ? $configurableOptions['network_speed'] : $plan['network_speed']; - $post['cpu'] = isset($configurableOptions['cpu']) ? $configurableOptions['cpu'] : $plan['cpu']; - $post['cores'] = isset($configurableOptions['cores']) ? $configurableOptions['cores'] : $plan['cores']; - $post['cpu_percent'] = isset($configurableOptions['cpu_percent']) ? $configurableOptions['cpu_percent'] : $plan['cpu_percent']; - $post['vnc'] = isset($configurableOptions['vnc']) ? $configurableOptions['vnc'] : $plan['vnc']; - $post['vncpass'] = $config['password']; - $post['kvm_cache'] = $plan['kvm_cache']; - $post['io_mode'] = $plan['io_mode']; - $post['vnc_keymap'] = $plan['vnc_keymap']; - $post['nic_type'] = $plan['nic_type']; - $post['osreinstall_limit'] = isset($configurableOptions['osreinstall_limit']) ? $configurableOptions['osreinstall_limit'] : $plan['osreinstall_limit']; - $post['space'] = isset($configurableOptions['space']) ? $configurableOptions['space'] : $plan['space']; - - $output = $admin->addvs_v2($post); - - if (isset($output['error'])) { - ExtensionHelper::error('Virtualizor', $output['error']); - return; - } - // Set server ID - $server = $output['vs_info']['vpsid']; - ExtensionHelper::setOrderProductConfig('external_id', $server, $params['config_id']); - - return true; -} - -function Virtualizor_suspendServer($user, $params, $order) -{ - $key = ExtensionHelper::getConfig('virtualizor', 'key'); - $pass = ExtensionHelper::getConfig('virtualizor', 'pass'); - $ip = ExtensionHelper::getConfig('virtualizor', 'ip'); - $port = ExtensionHelper::getConfig('virtualizor', 'port'); - $admin = new Virtualizor_Admin_API(); - $admin->Virtualizor_Admin_API($ip, $key, $pass, $port); - if (!isset($params['config']['external_id'])) { - ExtensionHelper::error('Virtualizor', 'Server not found for order #' . $order->id . '.'); - return; - } - $admin->suspend($params['config']['external_id']); - - return true; -} - -function Virtualizor_unsuspendServer($user, $params, $order) -{ - $key = ExtensionHelper::getConfig('virtualizor', 'key'); - $pass = ExtensionHelper::getConfig('virtualizor', 'pass'); - $ip = ExtensionHelper::getConfig('virtualizor', 'ip'); - $port = ExtensionHelper::getConfig('virtualizor', 'port'); - $admin = new Virtualizor_Admin_API(); - $admin->Virtualizor_Admin_API($ip, $key, $pass, $port); - if (!isset($params['config']['external_id'])) { - ExtensionHelper::error('Virtualizor', 'Server not found for order #' . $order->id . '.'); - return; - } - $admin->unsuspend($params['config']['external_id']); - - return true; -} - -function Virtualizor_terminateServer($user, $params, $order) -{ - $key = ExtensionHelper::getConfig('virtualizor', 'key'); - $pass = ExtensionHelper::getConfig('virtualizor', 'pass'); - $ip = ExtensionHelper::getConfig('virtualizor', 'ip'); - $port = ExtensionHelper::getConfig('virtualizor', 'port'); - $admin = new Virtualizor_Admin_API(); - $admin->Virtualizor_Admin_API($ip, $key, $pass, $port); - if (!isset($params['config']['external_id'])) { - ExtensionHelper::error('Virtualizor', 'Server not found for order #' . $order->id . '.'); - return; - } - $admin->delete_vs($params['config']['external_id']); - - return true; -}