diff --git a/CHANGELOG.md b/CHANGELOG.md index 952c50709..291a6b32d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,27 @@ This file is a running track of new features and fixes to each version of the pa This project follows [Semantic Versioning](http://semver.org) guidelines. +## v0.5.2 (Bodacious Boreopterus) +### Fixed +* Time axis on server graphs is corrected to show the minutes rather than the current month. +* Node deletion now works correctly and deletes allocations as well. +* Fixes a bug that would leave orphaned databases on the system if there was an error during creation. +* Fixes an issue that could occur if a UUID contained `#e#` formatting within it when it comes to creating databases. +* Fixed node status display to account for updated daemon security changes. +* Fixes default language being selected as German (defaults to English now). +* Fixes bug preventing the deletion of database servers. + +### Changed +* Using `node:` when filtering servers now properly filters the servers by node name, rather than looking for the node ID. +* Using `owner:` when filtering servers now properly filters by the owner's email rather than ID. +* Added some quick help buttons to the admin index page for getting support or checking the documentation. +* Panel now displays `Pterodactyl Panel` as the company name if one is not set. + +### Added +* Added basic information about the daemon when viewing a node, including the host OS and version, CPU count, and the daemon version. +* Added version checking for the daemon and panel that alerts admins when daemons or the panel is out of date. +* Added multiplicator support to certain memory and disk fields that allow users to enter `10g` and have it converted to MB automatically. + ## v0.5.1 (Bodacious Boreopterus) ### Fixed * Fixes a bug that allowed a user to bypass 2FA authentication if using the correct username and password for an account. diff --git a/app/Console/Commands/ShowVersion.php b/app/Console/Commands/ShowVersion.php index 0dcce04a1..2033f9b0f 100644 --- a/app/Console/Commands/ShowVersion.php +++ b/app/Console/Commands/ShowVersion.php @@ -24,6 +24,7 @@ namespace Pterodactyl\Console\Commands; use Illuminate\Console\Command; +use Version; class ShowVersion extends Command { @@ -58,6 +59,6 @@ class ShowVersion extends Command */ public function handle() { - $this->info('You are running Pterodactyl Panel ' . config('app.version')); + $this->info('You are running Pterodactyl Panel v' . Version::getCurrentPanel() . ' (' . ((Version::isLatestPanel()) ? 'Up to Date' : 'Latest: ' . Version::getDaemon()) . ')'); } } diff --git a/app/Facades/Version.php b/app/Facades/Version.php new file mode 100644 index 000000000..840862600 --- /dev/null +++ b/app/Facades/Version.php @@ -0,0 +1,35 @@ + + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +namespace Pterodactyl\Facades; + +use Illuminate\Support\Facades\Facade; + +class Version extends Facade +{ + + protected static function getFacadeAccessor() + { + return '\Pterodactyl\Services\VersionService'; + } +} diff --git a/app/Http/Controllers/Admin/NodesController.php b/app/Http/Controllers/Admin/NodesController.php index 24ac37614..ef77da4e0 100644 --- a/app/Http/Controllers/Admin/NodesController.php +++ b/app/Http/Controllers/Admin/NodesController.php @@ -253,20 +253,22 @@ class NodesController extends Controller public function deleteNode(Request $request, $id) { - $node = Models\Node::findOrFail($id); - $servers = Models\Server::where('node', $id)->count(); - if ($servers > 0) { - Alert::danger('You cannot delete a node with servers currently attached to it.')->flash(); - return redirect()->route('admin.nodes.view', [ - 'id' => $id, - 'tab' => 'tab_delete' - ]); + try { + $repo = new NodeRepository; + $repo->delete($id); + Alert::success('Successfully deleted the requested node from the panel.')->flash(); + return redirect()->route('admin.nodes'); + } catch (DisplayException $e) { + Alert::danger($e->getMessage())->flash(); + } catch (\Exception $e) { + Log::error($e); + Alert::danger('An unhandled exception occured while attempting to delete this node. Please try again.')->flash(); } - $node->delete(); - Alert::success('Node successfully deleted.')->flash(); - return redirect()->route('admin.nodes'); - + return redirect()->route('admin.nodes.view', [ + 'id' => $id, + 'tab' => 'tab_delete' + ]); } } diff --git a/app/Http/Controllers/Admin/ServersController.php b/app/Http/Controllers/Admin/ServersController.php index 8f0b184c6..5ec7d9a2b 100644 --- a/app/Http/Controllers/Admin/ServersController.php +++ b/app/Http/Controllers/Admin/ServersController.php @@ -68,14 +68,23 @@ class ServersController extends Controller $match = str_replace('"', '', $match); if (strpos($match, ':')) { list($field, $term) = explode(':', $match); - $field = (strpos($field, '.')) ? $field : 'servers.' . $field; + if ($field === 'node') { + $field = 'nodes.name'; + } else if ($field === 'owner') { + $field = 'users.email'; + } else if (!strpos($field, '.')) { + $field = 'servers.' . $field; + } + $query->orWhere($field, 'LIKE', '%' . $term . '%'); } else { $query->where('servers.name', 'LIKE', '%' . $match . '%'); - $query->orWhere('servers.username', 'LIKE', '%' . $match . '%'); - $query->orWhere('users.email', 'LIKE', '%' . $match . '%'); - $query->orWhere('allocations.port', 'LIKE', '%' . $match . '%'); - $query->orWhere('allocations.ip', 'LIKE', '%' . $match . '%'); + $query->orWhere([ + ['servers.username', 'LIKE', '%' . $match . '%'], + ['users.email', 'LIKE', '%' . $match . '%'], + ['allocations.port', 'LIKE', '%' . $match . '%'], + ['allocations.ip', 'LIKE', '%' . $match . '%'], + ]); } } } diff --git a/app/Repositories/DatabaseRepository.php b/app/Repositories/DatabaseRepository.php index c1c9b4e37..1e9962a4e 100644 --- a/app/Repositories/DatabaseRepository.php +++ b/app/Repositories/DatabaseRepository.php @@ -56,13 +56,12 @@ class DatabaseRepository { } DB::beginTransaction(); - try { $db = new Models\Database; $db->fill([ 'server_id' => $server->id, 'db_server' => $options['db_server'], - 'database' => $server->uuidShort . '_' . $options['database'], + 'database' => "s{$server->id}_{$options['database']}", 'username' => $server->uuidShort . '_' . str_random(7), 'remote' => $options['remote'], 'password' => Crypt::encrypt(str_random(20)) @@ -90,16 +89,29 @@ class DatabaseRepository { $capsule->setAsGlobal(); - Capsule::statement('CREATE DATABASE ' . $db->database); - Capsule::statement('CREATE USER \'' . $db->username . '\'@\'' . $db->remote . '\' IDENTIFIED BY \'' . Crypt::decrypt($db->password) . '\''); - Capsule::statement('GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX ON ' . $db->database . '.* TO \'' . $db->username . '\'@\'' . $db->remote . '\''); - Capsule::statement('FLUSH PRIVILEGES'); - - DB::commit(); - return true; } catch (\Exception $ex) { - DB::rollback(); - throw $ex; + DB::rollBack(); + throw new DisplayException('There was an error while connecting to the Database Host Server. Please check the error logs.', $ex); + } + + try { + Capsule::statement('CREATE DATABASE `' . $db->database . '`'); + Capsule::statement('CREATE USER `' . $db->username . '`@`' . $db->remote . '` IDENTIFIED BY \'' . Crypt::decrypt($db->password) . '\''); + Capsule::statement('GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX ON `' . $db->database . '`.* TO `' . $db->username . '`@`' . $db->remote . '`'); + Capsule::statement('FLUSH PRIVILEGES'); + DB::commit(); + } catch (\Exception $ex) { + try { + Capsule::statement('DROP DATABASE `' . $db->database . '`'); + Capsule::statement('DROP USER `' . $db->username . '`@`' . $db->remote . '`'); + } catch (\Exception $exi) { + // ignore it, if it fails its probably + // because we failed to ever make the DB + // or the user on the system. + } finally { + DB::rollBack(); + throw $ex; + } } } @@ -138,7 +150,7 @@ class DatabaseRepository { $capsule->setAsGlobal(); Capsule::statement(sprintf( - 'SET PASSWORD FOR \'%s\'@\'%s\' = PASSWORD(\'%s\')', + 'SET PASSWORD FOR `%s`@`%s` = PASSWORD(\'%s\')', $db->username, $db->remote, $password @@ -182,8 +194,8 @@ class DatabaseRepository { $capsule->setAsGlobal(); - Capsule::statement('DROP USER \'' . $db->username . '\'@\'' . $db->remote . '\''); - Capsule::statement('DROP DATABASE ' . $db->database); + Capsule::statement('DROP USER `' . $db->username . '`@`' . $db->remote . '`'); + Capsule::statement('DROP DATABASE `' . $db->database . '`'); $db->delete(); @@ -219,6 +231,11 @@ class DatabaseRepository { */ public function add(array $data) { + + if (isset($data['host'])) { + $data['host'] = gethostbyname($data['host']); + } + $validator = Validator::make($data, [ 'name' => 'required|string|max:255', 'host' => 'required|ip|unique:database_servers,host', diff --git a/app/Repositories/NodeRepository.php b/app/Repositories/NodeRepository.php index a23cc7455..2d579f274 100644 --- a/app/Repositories/NodeRepository.php +++ b/app/Repositories/NodeRepository.php @@ -229,8 +229,30 @@ class NodeRepository { public function delete($id) { - // @TODO: add logic; - return true; + $node = Models\Node::findOrFail($id); + if (Models\Server::where('node', $id)->count() > 0) { + throw new DisplayException('You cannot delete a node with servers currently attached to it.'); + } + + DB::beginTransaction(); + + try { + // Unlink Database Servers + Models\DatabaseServer::where('linked_node', $node->id)->update([ + 'linked_node' => null, + ]); + + // Delete Allocations + Models\Allocation::where('node', $node->id)->delete(); + + // Delete Node + $node->delete(); + + DB::commit(); + } catch (\Exception $ex) { + DB::rollback(); + throw $ex; + } } } diff --git a/app/Services/VersionService.php b/app/Services/VersionService.php new file mode 100644 index 000000000..5afb97e46 --- /dev/null +++ b/app/Services/VersionService.php @@ -0,0 +1,93 @@ + + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +namespace Pterodactyl\Services; + +use Cache; +use GuzzleHttp\Client; + +class VersionService +{ + + protected static $versions; + + /** + * Constructor + */ + public function __construct() + { + self::$versions = Cache::remember('versions', env('VERSION_CACHE_TIME', 60), function () { + $client = new Client(); + + try { + $response = $client->request('GET', env('VERSION_CHECK_URL', 'https://cdn.pterodactyl.io/releases/latest.json')); + + if ($response->getStatusCode() === 200) { + return json_decode($response->getBody()); + } else { + throw new \Exception('Invalid response code.'); + } + } catch (\Exception $ex) { + // Failed request, just return errored version. + return (object) [ + 'panel' => 'error', + 'daemon' => 'error', + ]; + } + }); + } + + public static function getPanel() + { + return self::$versions->panel; + } + + public static function getDaemon() + { + return self::$versions->daemon; + } + + public function getCurrentPanel() + { + return config('app.version'); + } + + public static function isLatestPanel() + { + if (config('app.version') === 'canary') { + return true; + } + + return (version_compare(config('app.version'), self::$versions->panel) >= 0); + } + + public static function isLatestDaemon($daemon) + { + if ($daemon === '0.0.0-canary') { + return true; + } + + return (version_compare($daemon, self::$versions->daemon) >= 0); + } + +} diff --git a/config/app.php b/config/app.php index 619e51039..a3c3ff532 100644 --- a/config/app.php +++ b/config/app.php @@ -217,6 +217,7 @@ return [ 'URL' => Illuminate\Support\Facades\URL::class, 'Uuid' => Webpatser\Uuid\Uuid::class, 'Validator' => Illuminate\Support\Facades\Validator::class, + 'Version' => Pterodactyl\Facades\Version::class, 'View' => Illuminate\Support\Facades\View::class, ], diff --git a/public/js/admin.min.js b/public/js/admin.min.js index b6ed01f4c..54179cd27 100755 --- a/public/js/admin.min.js +++ b/public/js/admin.min.js @@ -46,4 +46,24 @@ $(document).ready(function () { centerModal($(this)); }); $(window).on('resize', centerModal); + + // Idea code for multiplicators submitted by @Taronyuu on Github + // https://github.com/Pterodactyl/Panel/issues/154#issuecomment-257116078 + $('input[data-multiplicator="true"]').on('change', function () { + var value = $(this).val(); + if (!/^\d+$/.test(value)) { + var multiplicator = value.replace(/[0-9]/g, '').toLowerCase(); + value = value.replace(/\D/g, ''); + + if (multiplicator === 't') { + value = value * (1024 * 1024); + } + + if (multiplicator === 'g') { + value = value * 1024; + } + } + + $(this).val(value); + }); }); diff --git a/resources/views/admin/databases/index.blade.php b/resources/views/admin/databases/index.blade.php index 00e176609..0549f1d60 100644 --- a/resources/views/admin/databases/index.blade.php +++ b/resources/views/admin/databases/index.blade.php @@ -93,7 +93,7 @@ {{ $db->username }} {{ $db->c_databases }} @if(is_null($db->a_linkedNode))unlinked@else{{ $db->a_linkedNode }}@endif - + @endforeach diff --git a/resources/views/admin/index.blade.php b/resources/views/admin/index.blade.php index 27e8d52e9..85ffa5708 100644 --- a/resources/views/admin/index.blade.php +++ b/resources/views/admin/index.blade.php @@ -24,13 +24,31 @@ @endsection @section('content') -
- -

Pterodactyl Admin Control Panel


-

Welcome to the most advanced, lightweight, and user-friendly open source game server control panel.

-

You are running version {{ config('app.version') }}.

+
+
+ +

Pterodactyl Admin Control Panel


+ @if (Version::isLatestPanel()) +
You are running Pterodactyl Panel version {{ Version::getCurrentPanel() }}. Your panel is up-to-date!
+ @else +
+ Your panel is not up-to-date! The latest version is {{ Version::getPanel() }} and you are currently running version {{ Version::getCurrentPanel() }}. +
+ @endif +
+
+ @endsection diff --git a/resources/views/admin/nodes/new.blade.php b/resources/views/admin/nodes/new.blade.php index 3278a006f..952850cb3 100644 --- a/resources/views/admin/nodes/new.blade.php +++ b/resources/views/admin/nodes/new.blade.php @@ -92,14 +92,14 @@
- + MB
- + %
diff --git a/resources/views/admin/nodes/view.blade.php b/resources/views/admin/nodes/view.blade.php index cf71bd691..a200bc74a 100644 --- a/resources/views/admin/nodes/view.blade.php +++ b/resources/views/admin/nodes/view.blade.php @@ -69,6 +69,18 @@
+ + + + + + + + + + + + @@ -171,7 +183,7 @@
- + MB
@@ -185,7 +197,7 @@
- + MB
@@ -777,6 +789,24 @@ $(document).ready(function () { element.parent().removeClass('has-error has-success'); } + (function getInformation() { + $.ajax({ + method: 'GET', + url: '{{ $node->scheme }}://{{ $node->fqdn }}:{{ $node->daemonListen }}', + timeout: 5000, + headers: { + 'X-Access-Token': '{{ $node->daemonSecret }}' + }, + }).done(function (data) { + $('[data-attr="info-version"]').html(data.version); + $('[data-attr="info-system"]').html(data.system.type + '(' + data.system.arch + ') ' + data.system.release + ''); + $('[data-attr="info-cpus"]').html(data.system.cpus); + }).fail(function (jqXHR) { + + }).always(function() { + setTimeout(getInformation, 10000); + }); + })(); }); @endsection diff --git a/resources/views/admin/servers/new.blade.php b/resources/views/admin/servers/new.blade.php index 8aa499f4f..8db93ffcd 100644 --- a/resources/views/admin/servers/new.blade.php +++ b/resources/views/admin/servers/new.blade.php @@ -118,14 +118,14 @@
- + MB
- + MB
@@ -150,7 +150,7 @@
- + MB
diff --git a/resources/views/admin/servers/view.blade.php b/resources/views/admin/servers/view.blade.php index d6178d872..a3ec84e1f 100644 --- a/resources/views/admin/servers/view.blade.php +++ b/resources/views/admin/servers/view.blade.php @@ -228,14 +228,14 @@
- + MB
- + MB

Setting this to 0 will disable swap space on this server.

@@ -373,7 +373,7 @@
-
{{ $server->uuidShort }}_
+
s{{ $server->id }}_
diff --git a/resources/views/admin/settings.blade.php b/resources/views/admin/settings.blade.php index d24f512b6..ac5964f92 100644 --- a/resources/views/admin/settings.blade.php +++ b/resources/views/admin/settings.blade.php @@ -44,7 +44,7 @@
Daemon Version (Latest: {{ Version::getPanel() }})
System Information
Total CPU Cores
Total Servers {{ count($servers) }}