├── .gitignore ├── LICENSE.md ├── README.md └── pelican ├── clientarea.tpl └── pelican.php /.gitignore: -------------------------------------------------------------------------------- 1 | # JetBrains 2 | .idea/ -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Stepan Fedotov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## WHMCS 2 | 3 | WHMCS Module for the [Pelican Panel](https://github.com/pelican-dev/panel/). 4 | 5 | ## Configuration support 6 | 7 | Please use the [Pelican Discord](https://discord.gg/pelican-panel) for configuration related support instead of GitHub issues. 8 | 9 | ## NOTE 10 | 11 | This module requires the panel to be on version 1.0.0 and above. 12 | 13 | ## Installation 14 | 15 | [Video Tutorial](https://www.youtube.com/watch?v=wURpRD9vfj4) (uses 0.7 version of the panel but nothing changed functionality wise) 16 | 17 | 1. Download/Git clone this repository. 18 | 2. Move the ``pelican/`` folder into ``/modules/servers/``. 19 | 3. Create API Credentials with these permissions: ![Image](https://i.imgur.com/ZM2NRxD.png) 20 | 4. In WHMCS 8+ navigate to System Settings → Servers. In WHMCS 7 or below navigate to Setup → Products/Services → Servers 21 | 5. Create new server, fill the name with anything you want, hostname as the url to the panel either as an IP or domain. For example: ``123.123.123.123`` or ``my.pelican.panel`` 22 | 6. Change Server Type to Pelican, leave username empty, fill the password field with your generated API Key. 23 | 7. Tick the "Secure" option if your panel is using SSL. 24 | 8. Confirm that everything works by clicking the Test Connection button -> Save Changes. 25 | 9. Go back to the Servers screen and press Create New Group, name it anything you want and choose the created server and press the Add button, Save Changes. 26 | 10. Navigate to Setup > Products/Services > Products/Services 27 | 11. Create your desired product (and product group if you haven't already) with the type of Other and product name of anything -> Continue. 28 | 12. Click the Module Settings tab, choose for Module Name Pelican and for the Server Group the group you created in step 8. 29 | 13. Fill all non-optional fields, and you are good to go! 30 | 31 | ## Credits 32 | 33 | [Dane](https://github.com/DaneEveritt) and [everyone else](https://github.com/pelican-dev/panel/graphs/contributors) involved in development of the Pelican Panel. 34 | [death-droid](https://github.com/death-droid) for the original WHMCS module. 35 | 36 | # FAQ 37 | 38 | ## Overwriting values through configurable options 39 | 40 | Overwriting values can be done through either Configurable Options or Custom Fields. 41 | 42 | Their name should be exactly what you want to overwrite. 43 | dedicated_ip => Will overwrite dedicated_ip if its ticked or not. 44 | Valid options: ``server_name, memory, swap, io, cpu, disk, egg_id, location_id, dedicated_ip, port_range, image, startup, databases, allocations, backups, oom_killer, username`` 45 | 46 | This also works for any name of environment variable: 47 | Player Slots => Will overwrite the environment variable named "Player Slots" to its value. 48 | 49 | Useful trick: You can use the | seperator to change the display name of the variable like this: 50 | dedicated_ip|Dedicated IP => Will be displayed as "Dedicated IP" but will work correctly. 51 | 52 | [Sample configuration for configurable memory](https://owo.whats-th.is/85JwhVX.png) 53 | 54 | ## Couldn't find any nodes satisfying the request 55 | 56 | This can be caused from any of the following: Wrong location, not enough disk space/CPU/RAM, or no allocations matching the provided criteria. 57 | 58 | ## The username/password field is empty, how does the user get credentials? 59 | 60 | The customer gets an email from the panel to setup their account (incl. password) if they didn't have an account before. Otherwise they should be able to use their existing credentials. 61 | 62 | ## The customer didn't receive any emails from the panel 63 | 64 | Double check that you've configured the panel's mail settings correctly, the Test button works in the admin area's mail settings, and that you've restarted pteroq afterwards confirming that everything works. 65 | 66 | ## My game requires multiple ports allocated 67 | 68 | Currently, this isn't possible with this module but is planned. 69 | 70 | ## The server gets assigned to the first/admin user of the panel instead of the user who ordered the service 71 | 72 | Please update your module (by redownloading it). 73 | 74 | ## The feature_limits.backups field must be present 75 | 76 | Please update your module (by redownloading it). 77 | 78 | ## How to enable module debug log 79 | 80 | 1. In WHMCS 7 or below navigate to Utilities > Logs > Module Log. For WHMCS 8.x navigate to System Logs > Module Log in the left sidebar. 81 | 2. Click the Enable Debug Logging button. 82 | 3. Do the action that failed again and you will have required logs to debug the issue. All 404 errors can be ignored. 83 | 4. Remember to Disable Debug Logging if you are using this in production, as it's not recommended to have it enabled. 84 | -------------------------------------------------------------------------------- /pelican/clientarea.tpl: -------------------------------------------------------------------------------- 1 |
2 |
3 | Go to Panel 4 |
5 |
-------------------------------------------------------------------------------- /pelican/pelican.php: -------------------------------------------------------------------------------- 1 | 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | **/ 26 | 27 | if(!defined("WHMCS")) { 28 | die("This file cannot be accessed directly"); 29 | } 30 | 31 | use Illuminate\Database\Capsule\Manager as Capsule; 32 | 33 | function pelican_GetHostname(array $params) { 34 | $hostname = $params['serverhostname']; 35 | if ($hostname === '') throw new Exception('Could not find the panel\'s hostname - did you configure server group for the product?'); 36 | 37 | // For whatever reason, WHMCS converts some characters of the hostname to their literal meanings (- => dash, etc) in some cases 38 | foreach([ 39 | 'DOT' => '.', 40 | 'DASH' => '-', 41 | ] as $from => $to) { 42 | $hostname = str_replace($from, $to, $hostname); 43 | } 44 | 45 | if(ip2long($hostname) !== false) $hostname = 'http://' . $hostname; 46 | else $hostname = ($params['serversecure'] ? 'https://' : 'http://') . $hostname; 47 | 48 | return rtrim($hostname, '/'); 49 | } 50 | 51 | function pelican_API(array $params, $endpoint, array $data = [], $method = "GET", $dontLog = false) { 52 | $url = pelican_GetHostname($params) . '/api/application/' . $endpoint; 53 | 54 | $curl = curl_init(); 55 | curl_setopt($curl, CURLOPT_URL, $url); 56 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 57 | curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method); 58 | curl_setopt($curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); 59 | curl_setopt($curl, CURLOPT_USERAGENT, "Pelican-WHMCS"); 60 | curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); 61 | curl_setopt($curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_301); 62 | curl_setopt($curl, CURLOPT_TIMEOUT, 5); 63 | 64 | $headers = [ 65 | "Authorization: Bearer " . $params['serverpassword'], 66 | "Accept: application/json", 67 | ]; 68 | 69 | if($method === 'POST' || $method === 'PATCH') { 70 | $jsonData = json_encode($data); 71 | curl_setopt($curl, CURLOPT_POSTFIELDS, $jsonData); 72 | array_push($headers, "Content-Type: application/json"); 73 | array_push($headers, "Content-Length: " . strlen($jsonData)); 74 | } 75 | 76 | curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); 77 | 78 | $response = curl_exec($curl); 79 | $responseData = json_decode($response, true); 80 | $responseData['status_code'] = curl_getinfo($curl, CURLINFO_HTTP_CODE); 81 | 82 | if($responseData['status_code'] === 0 && !$dontLog) logModuleCall("Pelican-WHMCS", "CURL ERROR", curl_error($curl), ""); 83 | 84 | curl_close($curl); 85 | 86 | if(!$dontLog) logModuleCall("Pelican-WHMCS", $method . " - " . $url, 87 | isset($data) ? json_encode($data) : "", 88 | print_r($responseData, true)); 89 | 90 | return $responseData; 91 | } 92 | 93 | function pelican_Error($func, $params, Exception $err) { 94 | logModuleCall("Pelican-WHMCS", $func, $params, $err->getMessage(), $err->getTraceAsString()); 95 | } 96 | 97 | function pelican_MetaData() { 98 | return [ 99 | "DisplayName" => "Pelican", 100 | "APIVersion" => "1.1", 101 | "RequiresServer" => true, 102 | ]; 103 | } 104 | 105 | function pelican_ConfigOptions() { 106 | return [ 107 | "cpu" => [ 108 | "FriendlyName" => "CPU Limit (%)", 109 | "Description" => "Amount of CPU to assign to the created server.", 110 | "Type" => "text", 111 | "Size" => 10, 112 | ], 113 | "disk" => [ 114 | "FriendlyName" => "Disk Space (MiB)", 115 | "Description" => "Amount of Disk Space to assign to the created server.", 116 | "Type" => "text", 117 | "Size" => 10, 118 | ], 119 | "memory" => [ 120 | "FriendlyName" => "Memory (MiB)", 121 | "Description" => "Amount of Memory to assign to the created server.", 122 | "Type" => "text", 123 | "Size" => 10, 124 | ], 125 | "swap" => [ 126 | "FriendlyName" => "Swap (MiB)", 127 | "Description" => "Amount of Swap to assign to the created server.", 128 | "Type" => "text", 129 | "Size" => 10, 130 | ], 131 | "location_id" => [ 132 | "FriendlyName" => "Location ID", 133 | "Description" => "ID of the Location to automatically deploy to. (deprecated)", 134 | "Type" => "text", 135 | "Size" => 10, 136 | ], 137 | "dedicated_ip" => [ 138 | "FriendlyName" => "Dedicated IP", 139 | "Description" => "Assign dedicated ip to the server (optional)", 140 | "Type" => "yesno", 141 | ], 142 | "egg_id" => [ 143 | "FriendlyName" => "Egg ID", 144 | "Description" => "ID of the Egg for the server to use.", 145 | "Type" => "text", 146 | "Size" => 10, 147 | ], 148 | "io" => [ 149 | "FriendlyName" => "Block IO Weight", 150 | "Description" => "Block IO Adjustment number (10-1000)", 151 | "Type" => "text", 152 | "Size" => 10, 153 | "Default" => "500", 154 | ], 155 | "port_range" => [ 156 | "FriendlyName" => "Port Range", 157 | "Description" => "Port ranges seperated by comma to assign to the server (Example: 25565-25570,25580-25590) (optional)", 158 | "Type" => "text", 159 | "Size" => 25, 160 | ], 161 | "startup" => [ 162 | "FriendlyName" => "Startup", 163 | "Description" => "Custom startup command to assign to the created server (optional)", 164 | "Type" => "text", 165 | "Size" => 25, 166 | ], 167 | "image" => [ 168 | "FriendlyName" => "Image", 169 | "Description" => "Custom Docker image to assign to the created server (optional)", 170 | "Type" => "text", 171 | "Size" => 25, 172 | ], 173 | "databases" => [ 174 | "FriendlyName" => "Databases", 175 | "Description" => "Client will be able to create this amount of databases for their server (optional)", 176 | "Type" => "text", 177 | "Size" => 10, 178 | ], 179 | "server_name" => [ 180 | "FriendlyName" => "Server Name", 181 | "Description" => "The name of the server as shown on the panel (optional)", 182 | "Type" => "text", 183 | "Size" => 25, 184 | ], 185 | "oom_killer" => [ 186 | "FriendlyName" => "Enable OOM Killer", 187 | "Description" => "Should the Out Of Memory Killer be enabled (optional)", 188 | "Type" => "yesno", 189 | ], 190 | "backups" => [ 191 | "FriendlyName" => "Backups", 192 | "Description" => "Client will be able to create this amount of backups for their server (optional)", 193 | "Type" => "text", 194 | "Size" => 10, 195 | ], 196 | "allocations" => [ 197 | "FriendlyName" => "Allocations", 198 | "Description" => "Client will be able to create this amount of allocations for their server (optional)", 199 | "Type" => "text", 200 | "Size" => 10, 201 | ], 202 | ]; 203 | } 204 | 205 | function pelican_TestConnection(array $params) { 206 | $solutions = [ 207 | 0 => "Check module debug log for more detailed error.", 208 | 401 => "Authorization header either missing or not provided.", 209 | 403 => "Double check the password (which should be the Application Key).", 210 | 404 => "Result not found.", 211 | 422 => "Validation error.", 212 | 500 => "Panel errored, check panel logs.", 213 | ]; 214 | 215 | $err = ""; 216 | try { 217 | $response = pelican_API($params, 'nodes'); 218 | 219 | if($response['status_code'] !== 200) { 220 | $status_code = $response['status_code']; 221 | $err = "Invalid status_code received: " . $status_code . ". Possible solutions: " 222 | . (isset($solutions[$status_code]) ? $solutions[$status_code] : "None."); 223 | } else { 224 | if($response['meta']['pagination']['count'] === 0) { 225 | $err = "Authentication successful, but no nodes are available."; 226 | } 227 | } 228 | } catch(Exception $e) { 229 | pelican_Error(__FUNCTION__, $params, $e); 230 | $err = $e->getMessage(); 231 | } 232 | 233 | return [ 234 | "success" => $err === "", 235 | "error" => $err, 236 | ]; 237 | } 238 | 239 | function random($length) { 240 | if (class_exists("\Illuminate\Support\Str")) { 241 | return \Illuminate\Support\Str::random($length); 242 | } else if (function_exists("str_random")) { 243 | return str_random($length); 244 | } else { 245 | throw new \Exception("Unable to find a valid function for generating random strings"); 246 | } 247 | } 248 | 249 | function pelican_GenerateUsername($length = 8) { 250 | $returnable = false; 251 | while (!$returnable) { 252 | $generated = random($length); 253 | if (preg_match('/[A-Z]+[a-z]+[0-9]+/', $generated)) { 254 | $returnable = true; 255 | } 256 | } 257 | return $generated; 258 | } 259 | 260 | function pelican_GetOption(array $params, $id, $default = NULL) { 261 | $options = pelican_ConfigOptions(); 262 | 263 | $friendlyName = $options[$id]['FriendlyName']; 264 | if(isset($params['configoptions'][$friendlyName]) && $params['configoptions'][$friendlyName] !== '') { 265 | return $params['configoptions'][$friendlyName]; 266 | } else if(isset($params['configoptions'][$id]) && $params['configoptions'][$id] !== '') { 267 | return $params['configoptions'][$id]; 268 | } else if(isset($params['customfields'][$friendlyName]) && $params['customfields'][$friendlyName] !== '') { 269 | return $params['customfields'][$friendlyName]; 270 | } else if(isset($params['customfields'][$id]) && $params['customfields'][$id] !== '') { 271 | return $params['customfields'][$id]; 272 | } 273 | 274 | $found = false; 275 | $i = 0; 276 | foreach(pelican_ConfigOptions() as $key => $value) { 277 | $i++; 278 | if($key === $id) { 279 | $found = true; 280 | break; 281 | } 282 | } 283 | 284 | if($found && isset($params['configoption' . $i]) && $params['configoption' . $i] !== '') { 285 | return $params['configoption' . $i]; 286 | } 287 | 288 | return $default; 289 | } 290 | 291 | function pelican_CreateAccount(array $params) { 292 | try { 293 | $serverId = pelican_GetServerID($params); 294 | if(isset($serverId)) throw new Exception('Failed to create server because it is already created.'); 295 | 296 | $userResult = pelican_API($params, 'users/external/' . $params['clientsdetails']['id']); 297 | if($userResult['status_code'] === 404) { 298 | $userResult = pelican_API($params, 'users?filter[email]=' . urlencode($params['clientsdetails']['email'])); 299 | if($userResult['meta']['pagination']['total'] === 0) { 300 | $userResult = pelican_API($params, 'users', [ 301 | 'username' => pelican_GetOption($params, 'username', pelican_GenerateUsername()), 302 | 'email' => $params['clientsdetails']['email'], 303 | 'first_name' => $params['clientsdetails']['firstname'], 304 | 'last_name' => $params['clientsdetails']['lastname'], 305 | 'external_id' => (string) $params['clientsdetails']['id'], 306 | ], 'POST'); 307 | } else { 308 | foreach($userResult['data'] as $key => $value) { 309 | if($value['attributes']['email'] === $params['clientsdetails']['email']) { 310 | $userResult = array_merge($userResult, $value); 311 | break; 312 | } 313 | } 314 | $userResult = array_merge($userResult, $userResult['data'][0]); 315 | } 316 | } 317 | 318 | if($userResult['status_code'] === 200 || $userResult['status_code'] === 201) { 319 | $userId = $userResult['attributes']['id']; 320 | } else { 321 | throw new Exception('Failed to create user, received error code: ' . $userResult['status_code'] . '. Enable module debug log for more info.'); 322 | } 323 | 324 | $eggId = pelican_GetOption($params, 'egg_id'); 325 | 326 | $eggData = pelican_API($params, 'eggs/' . $eggId . '?include=variables'); 327 | if($eggData['status_code'] !== 200) throw new Exception('Failed to get egg data, received error code: ' . $eggData['status_code'] . '. Enable module debug log for more info.'); 328 | 329 | $environment = []; 330 | foreach($eggData['attributes']['relationships']['variables']['data'] as $key => $val) { 331 | $attr = $val['attributes']; 332 | $var = $attr['env_variable']; 333 | $default = $attr['default_value']; 334 | $friendlyName = pelican_GetOption($params, $attr['name']); 335 | $envName = pelican_GetOption($params, $attr['env_variable']); 336 | 337 | if(isset($friendlyName)) $environment[$var] = $friendlyName; 338 | elseif(isset($envName)) $environment[$var] = $envName; 339 | else $environment[$var] = $default; 340 | } 341 | 342 | $name = pelican_GetOption($params, 'server_name', pelican_GenerateUsername() . '_' . $params['serviceid']); 343 | $memory = pelican_GetOption($params, 'memory'); 344 | $swap = pelican_GetOption($params, 'swap'); 345 | $io = pelican_GetOption($params, 'io'); 346 | $cpu = pelican_GetOption($params, 'cpu'); 347 | $disk = pelican_GetOption($params, 'disk'); 348 | $location_id = pelican_GetOption($params, 'location_id'); 349 | $dedicated_ip = pelican_GetOption($params, 'dedicated_ip') ? true : false; 350 | $port_range = pelican_GetOption($params, 'port_range'); 351 | $port_range = isset($port_range) ? explode(',', $port_range) : []; 352 | $image = pelican_GetOption($params, 'image', $eggData['attributes']['docker_image']); 353 | $startup = pelican_GetOption($params, 'startup', $eggData['attributes']['startup']); 354 | $databases = pelican_GetOption($params, 'databases'); 355 | $allocations = pelican_GetOption($params, 'allocations'); 356 | $backups = pelican_GetOption($params, 'backups'); 357 | $oom_killer = pelican_GetOption($params, 'oom_killer') ? true : false; 358 | $serverData = [ 359 | 'name' => $name, 360 | 'user' => (int) $userId, 361 | 'egg' => (int) $eggId, 362 | 'docker_image' => $image, 363 | 'startup' => $startup, 364 | 'oom_killer' => $oom_killer, 365 | 'limits' => [ 366 | 'memory' => (int) $memory, 367 | 'swap' => (int) $swap, 368 | 'io' => (int) $io, 369 | 'cpu' => (int) $cpu, 370 | 'disk' => (int) $disk, 371 | ], 372 | 'feature_limits' => [ 373 | 'databases' => $databases ? (int) $databases : null, 374 | 'allocations' => (int) $allocations, 375 | 'backups' => (int) $backups, 376 | ], 377 | 'deploy' => [ 378 | 'locations' => [(int) $location_id], 379 | 'dedicated_ip' => $dedicated_ip, 380 | 'port_range' => $port_range, 381 | ], 382 | 'environment' => $environment, 383 | 'start_on_completion' => true, 384 | 'external_id' => (string) $params['serviceid'], 385 | ]; 386 | 387 | $server = pelican_API($params, 'servers?include=allocations', $serverData, 'POST'); 388 | 389 | if($server['status_code'] === 400) throw new Exception('Couldn\'t find any nodes satisfying the request.'); 390 | if($server['status_code'] !== 201) throw new Exception('Failed to create the server, received the error code: ' . $server['status_code'] . '. Enable module debug log for more info.'); 391 | 392 | unset($params['password']); 393 | 394 | // Get IP & Port and set on WHMCS "Dedicated IP" field 395 | $_IP = $server['attributes']['relationships']['allocations']['data'][0]['attributes']['ip']; 396 | $_Port = $server['attributes']['relationships']['allocations']['data'][0]['attributes']['port']; 397 | 398 | // Check if IP & Port field have value. Prevents ":" being added if API error 399 | if (isset($_IP) && isset($_Port)) { 400 | try { 401 | $query = Capsule::table('tblhosting')->where('id', $params['serviceid'])->where('userid', $params['userid'])->update(array('dedicatedip' => $_IP . ":" . $_Port)); 402 | } catch (Exception $e) { return $e->getMessage() . "
" . $e->getTraceAsString(); } 403 | } 404 | 405 | Capsule::table('tblhosting')->where('id', $params['serviceid'])->update([ 406 | 'username' => '', 407 | 'password' => '', 408 | ]); 409 | } catch(Exception $err) { 410 | return $err->getMessage(); 411 | } 412 | 413 | return 'success'; 414 | } 415 | 416 | // Function to allow backwards compatibility with death-droid's module 417 | function pelican_GetServerID(array $params, $raw = false) { 418 | $serverResult = pelican_API($params, 'servers/external/' . $params['serviceid'], [], 'GET', true); 419 | if($serverResult['status_code'] === 200) { 420 | if($raw) return $serverResult; 421 | else return $serverResult['attributes']['id']; 422 | } else if($serverResult['status_code'] === 500) { 423 | throw new Exception('Failed to get server, panel errored. Check panel logs for more info.'); 424 | } 425 | 426 | if(Capsule::schema()->hasTable('tbl_pelicanproduct')) { 427 | $oldData = Capsule::table('tbl_pelicanproduct') 428 | ->select('user_id', 'server_id') 429 | ->where('service_id', '=', $params['serviceid']) 430 | ->first(); 431 | 432 | if(isset($oldData) && isset($oldData->server_id)) { 433 | if($raw) { 434 | $serverResult = pelican_API($params, 'servers/' . $oldData->server_id); 435 | if($serverResult['status_code'] === 200) return $serverResult; 436 | else throw new Exception('Failed to get server, received the error code: ' . $serverResult['status_code'] . '. Enable module debug log for more info.'); 437 | } else { 438 | return $oldData->server_id; 439 | } 440 | } 441 | } 442 | } 443 | 444 | function pelican_SuspendAccount(array $params) { 445 | try { 446 | $serverId = pelican_GetServerID($params); 447 | if(!isset($serverId)) throw new Exception('Failed to suspend server because it doesn\'t exist.'); 448 | 449 | $suspendResult = pelican_API($params, 'servers/' . $serverId . '/suspend', [], 'POST'); 450 | if($suspendResult['status_code'] !== 204) throw new Exception('Failed to suspend the server, received error code: ' . $suspendResult['status_code'] . '. Enable module debug log for more info.'); 451 | } catch(Exception $err) { 452 | return $err->getMessage(); 453 | } 454 | 455 | return 'success'; 456 | } 457 | 458 | function pelican_UnsuspendAccount(array $params) { 459 | try { 460 | $serverId = pelican_GetServerID($params); 461 | if(!isset($serverId)) throw new Exception('Failed to unsuspend server because it doesn\'t exist.'); 462 | 463 | $suspendResult = pelican_API($params, 'servers/' . $serverId . '/unsuspend', [], 'POST'); 464 | if($suspendResult['status_code'] !== 204) throw new Exception('Failed to unsuspend the server, received error code: ' . $suspendResult['status_code'] . '. Enable module debug log for more info.'); 465 | } catch(Exception $err) { 466 | return $err->getMessage(); 467 | } 468 | 469 | return 'success'; 470 | } 471 | 472 | function pelican_TerminateAccount(array $params) { 473 | try { 474 | $serverId = pelican_GetServerID($params); 475 | if(!isset($serverId)) throw new Exception('Failed to terminate server because it doesn\'t exist.'); 476 | 477 | $deleteResult = pelican_API($params, 'servers/' . $serverId, [], 'DELETE'); 478 | if($deleteResult['status_code'] !== 204) throw new Exception('Failed to terminate the server, received error code: ' . $deleteResult['status_code'] . '. Enable module debug log for more info.'); 479 | } catch(Exception $err) { 480 | return $err->getMessage(); 481 | } 482 | 483 | // Remove the "Dedicated IP" Field on Termination 484 | try { 485 | $query = Capsule::table('tblhosting')->where('id', $params['serviceid'])->where('userid', $params['userid'])->update(array('dedicatedip' => "")); 486 | } catch (Exception $e) { return $e->getMessage() . "
" . $e->getTraceAsString(); } 487 | 488 | return 'success'; 489 | } 490 | 491 | function pelican_ChangePassword(array $params) { 492 | try { 493 | if($params['password'] === '') throw new Exception('The password cannot be empty.'); 494 | 495 | $serverData = pelican_GetServerID($params, true); 496 | if(!isset($serverData)) throw new Exception('Failed to change password because linked server doesn\'t exist.'); 497 | 498 | $userId = $serverData['attributes']['user']; 499 | $userResult = pelican_API($params, 'users/' . $userId); 500 | if($userResult['status_code'] !== 200) throw new Exception('Failed to retrieve user, received error code: ' . $userResult['status_code'] . '.'); 501 | 502 | $updateResult = pelican_API($params, 'users/' . $serverData['attributes']['user'], [ 503 | 'username' => $userResult['attributes']['username'], 504 | 'email' => $userResult['attributes']['email'], 505 | 'first_name' => $userResult['attributes']['first_name'], 506 | 'last_name' => $userResult['attributes']['last_name'], 507 | 508 | 'password' => $params['password'], 509 | ], 'PATCH'); 510 | if($updateResult['status_code'] !== 200) throw new Exception('Failed to change password, received error code: ' . $updateResult['status_code'] . '.'); 511 | 512 | unset($params['password']); 513 | Capsule::table('tblhosting')->where('id', $params['serviceid'])->update([ 514 | 'username' => '', 515 | 'password' => '', 516 | ]); 517 | } catch(Exception $err) { 518 | return $err->getMessage(); 519 | } 520 | 521 | return 'success'; 522 | } 523 | 524 | function pelican_ChangePackage(array $params) { 525 | try { 526 | $serverData = pelican_GetServerID($params, true); 527 | if($serverData['status_code'] === 404 || !isset($serverData['attributes']['id'])) throw new Exception('Failed to change package of server because it doesn\'t exist.'); 528 | $serverId = $serverData['attributes']['id']; 529 | 530 | $memory = pelican_GetOption($params, 'memory'); 531 | $swap = pelican_GetOption($params, 'swap'); 532 | $io = pelican_GetOption($params, 'io'); 533 | $cpu = pelican_GetOption($params, 'cpu'); 534 | $disk = pelican_GetOption($params, 'disk'); 535 | $databases = pelican_GetOption($params, 'databases'); 536 | $allocations = pelican_GetOption($params, 'allocations'); 537 | $backups = pelican_GetOption($params, 'backups'); 538 | $oom_killer = pelican_GetOption($params, 'oom_killer') ? true : false; 539 | $updateData = [ 540 | 'allocation' => $serverData['attributes']['allocation'], 541 | 'memory' => (int) $memory, 542 | 'swap' => (int) $swap, 543 | 'io' => (int) $io, 544 | 'cpu' => (int) $cpu, 545 | 'disk' => (int) $disk, 546 | 'oom_killer' => $oom_killer, 547 | 'feature_limits' => [ 548 | 'databases' => (int) $databases, 549 | 'allocations' => (int) $allocations, 550 | 'backups' => (int) $backups, 551 | ], 552 | ]; 553 | 554 | $updateResult = pelican_API($params, 'servers/' . $serverId . '/build', $updateData, 'PATCH'); 555 | if($updateResult['status_code'] !== 200) throw new Exception('Failed to update build of the server, received error code: ' . $updateResult['status_code'] . '. Enable module debug log for more info.'); 556 | 557 | $eggId = pelican_GetOption($params, 'egg_id'); 558 | $eggData = pelican_API($params, 'eggs/' . $eggId . '?include=variables'); 559 | if($eggData['status_code'] !== 200) throw new Exception('Failed to get egg data, received error code: ' . $eggData['status_code'] . '. Enable module debug log for more info.'); 560 | 561 | $environment = []; 562 | foreach($eggData['attributes']['relationships']['variables']['data'] as $key => $val) { 563 | $attr = $val['attributes']; 564 | $var = $attr['env_variable']; 565 | $friendlyName = pelican_GetOption($params, $attr['name']); 566 | $envName = pelican_GetOption($params, $attr['env_variable']); 567 | 568 | if(isset($friendlyName)) $environment[$var] = $friendlyName; 569 | elseif(isset($envName)) $environment[$var] = $envName; 570 | elseif(isset($serverData['attributes']['container']['environment'][$var])) $environment[$var] = $serverData['attributes']['container']['environment'][$var]; 571 | elseif(isset($attr['default_value'])) $environment[$var] = $attr['default_value']; 572 | } 573 | 574 | $image = pelican_GetOption($params, 'image', $serverData['attributes']['container']['image']); 575 | $startup = pelican_GetOption($params, 'startup', $serverData['attributes']['container']['startup_command']); 576 | $updateData = [ 577 | 'environment' => $environment, 578 | 'startup' => $startup, 579 | 'egg' => (int) $eggId, 580 | 'image' => $image, 581 | 'skip_scripts' => false, 582 | ]; 583 | 584 | $updateResult = pelican_API($params, 'servers/' . $serverId . '/startup', $updateData, 'PATCH'); 585 | if($updateResult['status_code'] !== 200) throw new Exception('Failed to update startup of the server, received error code: ' . $updateResult['status_code'] . '. Enable module debug log for more info.'); 586 | } catch(Exception $err) { 587 | return $err->getMessage(); 588 | } 589 | 590 | return 'success'; 591 | } 592 | 593 | function pelican_LoginLink(array $params) { 594 | if($params['moduletype'] !== 'pelican') return; 595 | 596 | try { 597 | $serverId = pelican_GetServerID($params); 598 | if(!isset($serverId)) return; 599 | 600 | $hostname = pelican_GetHostname($params); 601 | echo '[Go to Service]'; 602 | echo '

[Report A Bug]

'; 603 | # echo '

[Report A Bug]

'; 604 | } catch(Exception $err) { 605 | // Ignore 606 | } 607 | } 608 | 609 | function pelican_ClientArea(array $params) { 610 | if($params['moduletype'] !== 'pelican') return; 611 | 612 | try { 613 | $hostname = pelican_GetHostname($params); 614 | $serverData = pelican_GetServerID($params, true); 615 | if($serverData['status_code'] === 404 || !isset($serverData['attributes']['id'])) return [ 616 | 'templatefile' => 'clientarea', 617 | 'vars' => [ 618 | 'serviceurl' => $hostname, 619 | ], 620 | ]; 621 | 622 | return [ 623 | 'templatefile' => 'clientarea', 624 | 'vars' => [ 625 | 'serviceurl' => $hostname . '/server/' . $serverData['attributes']['identifier'], 626 | ], 627 | ]; 628 | } catch (Exception $err) { 629 | // Ignore 630 | } 631 | } 632 | --------------------------------------------------------------------------------