From fb21bf9282753567d2645c71153299de7c23ade2 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 25 Feb 2017 00:48:12 -0500 Subject: [PATCH] Begin implementation of server admin view Currently completed tabs: About, Details, Build Configuration --- .../Controllers/Admin/ServersController.php | 72 +--- app/Http/Routes/AdminRoutes.php | 17 +- app/Repositories/ServerRepository.php | 104 ++--- public/themes/pterodactyl/css/pterodactyl.css | 26 ++ .../pterodactyl/admin/servers/view.blade.php | 390 ++++++++++++++++++ .../pterodactyl/layouts/admin.blade.php | 2 +- 6 files changed, 464 insertions(+), 147 deletions(-) create mode 100644 resources/themes/pterodactyl/admin/servers/view.blade.php diff --git a/app/Http/Controllers/Admin/ServersController.php b/app/Http/Controllers/Admin/ServersController.php index 6372d042a..2f521804a 100644 --- a/app/Http/Controllers/Admin/ServersController.php +++ b/app/Http/Controllers/Admin/ServersController.php @@ -149,71 +149,13 @@ class ServersController extends Controller })->values(); } - /** - * Returns a JSON tree of all avaliable IPs and Ports on a given node. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Contracts\View\View - */ - public function postNewServerGetIps(Request $request) - { - return Models\Allocation::select('id', 'ip')->where('node_id', $request->input('node'))->whereNull('server_id')->get()->unique('ip')->values()->all(); - } - - /** - * Returns a JSON tree of all avaliable options for a given service. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Contracts\View\View - */ - public function postNewServerServiceOption(Request $request) - { - if (! $request->input('service')) { - return response()->json([ - 'error' => 'Missing service in request.', - ], 500); - } - - $service = Models\Service::select('executable', 'startup')->where('id', $request->input('service'))->first(); - - return response()->json(Models\ServiceOption::select('id', 'name', 'docker_image')->where('service_id', $request->input('service'))->orderBy('name', 'asc')->get()); - } - - /** - * Returns a JSON tree of all avaliable variables for a given service option. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Contracts\View\View - */ - public function postNewServerOptionDetails(Request $request) - { - if (! $request->input('option')) { - return response()->json([ - 'error' => 'Missing option in request.', - ], 500); - } - - $option = Models\ServiceOption::with('variables')->with(['packs' => function ($query) { - $query->where('selectable', true); - }])->findOrFail($request->input('option')); - - return response()->json([ - 'packs' => $option->packs, - 'variables' => $option->variables, - 'exec' => $option->display_executable, - 'startup' => $option->display_startup, - ]); - } - public function postUpdateServerDetails(Request $request, $id) { try { $server = new ServerRepository; - $server->updateDetails($id, [ - 'owner' => $request->input('owner'), - 'name' => $request->input('name'), - 'reset_token' => ($request->input('reset_token', false) === 'on') ? true : false, - ]); + $server->updateDetails($id, $request->intersect([ + 'owner_id', 'name', 'reset_token' + ])); Alert::success('Server details were successfully updated.')->flash(); } catch (DisplayValidationException $ex) { @@ -238,7 +180,7 @@ class ServersController extends Controller { try { $server = new ServerRepository; - $server->updateContainer($id, ['image' => $request->input('docker_image')]); + $server->updateContainer($id, $request->intersect('docker_image')); Alert::success('Successfully updated this server\'s docker image.')->flash(); } catch (DisplayValidationException $ex) { return redirect()->route('admin.servers.view', [ @@ -283,9 +225,9 @@ class ServersController extends Controller { try { $server = new ServerRepository; - $server->changeBuild($id, $request->only([ - 'default', 'add_additional', - 'remove_additional', 'memory', + $server->changeBuild($id, $request->intersect([ + 'allocation_id', 'add_allocations', + 'remove_allocations', 'memory', 'swap', 'io', 'cpu', ])); Alert::success('Server details were successfully updated.')->flash(); diff --git a/app/Http/Routes/AdminRoutes.php b/app/Http/Routes/AdminRoutes.php index 3422088ac..20c528478 100644 --- a/app/Http/Routes/AdminRoutes.php +++ b/app/Http/Routes/AdminRoutes.php @@ -140,22 +140,6 @@ class AdminRoutes 'uses' => 'Admin\ServersController@postNewServerGetNodes', ]); - $router->post('/new/get-ips', [ - 'as' => 'admin.servers.new.get-ips', - 'uses' => 'Admin\ServersController@postNewServerGetIps', - ]); - - $router->post('/new/service-options', [ - 'as' => 'admin.servers.new.service-options', - 'uses' => 'Admin\ServersController@postNewServerServiceOption', - ]); - - $router->post('/new/option-details', [ - 'as' => 'admin.servers.new.option-details', - 'uses' => 'Admin\ServersController@postNewServerOptionDetails', - ]); - // End Assorted Page Helpers - // View Specific Server $router->get('/view/{id}', [ 'as' => 'admin.servers.view', @@ -170,6 +154,7 @@ class AdminRoutes // Change Server Details $router->post('/view/{id}/details', [ + 'as' => 'admin.servers.view.details', 'uses' => 'Admin\ServersController@postUpdateServerDetails', ]); diff --git a/app/Repositories/ServerRepository.php b/app/Repositories/ServerRepository.php index 9a2b4eac3..756979908 100644 --- a/app/Repositories/ServerRepository.php +++ b/app/Repositories/ServerRepository.php @@ -352,8 +352,9 @@ class ServerRepository // Validate Fields $validator = Validator::make($data, [ - 'owner' => 'email|exists:users,email', - 'name' => 'regex:([\w .-]{1,200})', + 'owner_id' => 'sometimes|required|numeric|exists:users,id', + 'name' => 'sometimes|required|regex:([\w .-]{1,200})', + 'reset_token' => 'sometimes|required|accepted' ]); // Run validator, throw catchable and displayable exception if it fails. @@ -368,16 +369,15 @@ class ServerRepository $server = Models\Server::with('user')->findOrFail($id); // Update daemon secret if it was passed. - if ((isset($data['reset_token']) && $data['reset_token'] === true) || (isset($data['owner']) && $data['owner'] !== $server->user->email)) { + if (isset($data['reset_token']) || (isset($data['owner_id']) && $data['owner_id'] !== $server->user->id)) { $oldDaemonKey = $server->daemonSecret; $server->daemonSecret = $uuid->generate('servers', 'daemonSecret'); $resetDaemonKey = true; } // Update Server Owner if it was passed. - if (isset($data['owner']) && $data['owner'] !== $server->user->email) { - $newOwner = Models\User::select('id')->where('email', $data['owner'])->first(); - $server->owner_id = $newOwner->id; + if (isset($data['owner_id']) && $data['owner_id'] !== $server->user->id) { + $server->owner_id = $data['owner_id']; } // Update Server Name if it was passed. @@ -431,7 +431,7 @@ class ServerRepository public function updateContainer($id, array $data) { $validator = Validator::make($data, [ - 'image' => 'required|string', + 'docker_image' => 'required|string', ]); // Run validator, throw catchable and displayable exception if it fails. @@ -444,7 +444,7 @@ class ServerRepository try { $server = Models\Server::findOrFail($id); - $server->image = $data['image']; + $server->image = $data['docker_image']; $server->save(); $server->node->guzzleClient([ @@ -479,17 +479,14 @@ class ServerRepository public function changeBuild($id, array $data) { $validator = Validator::make($data, [ - 'default' => [ - 'string', - 'regex:/^(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5])):(\d{1,5})$/', - ], - 'add_additional' => 'nullable|array', - 'remove_additional' => 'nullable|array', - 'memory' => 'integer|min:0', - 'swap' => 'integer|min:-1', - 'io' => 'integer|min:10|max:1000', - 'cpu' => 'integer|min:0', - 'disk' => 'integer|min:0', + 'allocation_id' => 'sometimes|required|exists:allocations,id', + 'add_allocations' => 'sometimes|required|array', + 'remove_allocations' => 'sometimes|required|array', + 'memory' => 'sometimes|required|integer|min:0', + 'swap' => 'sometimes|required|integer|min:-1', + 'io' => 'sometimes|required|integer|min:10|max:1000', + 'cpu' => 'sometimes|required|integer|min:0', + 'disk' => 'sometimes|required|integer|min:0', ]); // Run validator, throw catchable and displayable exception if it fails. @@ -503,43 +500,33 @@ class ServerRepository try { $server = Models\Server::with('allocation', 'allocations')->findOrFail($id); $newBuild = []; + $newAllocations = []; - if (isset($data['default'])) { - list($ip, $port) = explode(':', $data['default']); - if ($ip !== $server->allocation->ip || (int) $port !== $server->allocation->port) { - $selection = $server->allocations->where('ip', $ip)->where('port', $port)->first(); + if (isset($data['allocation_id'])) { + if ((int) $data['allocation_id'] !== $server->allocation_id) { + $selection = $server->allocations->where('id', $data['allocation_id'])->first(); if (! $selection) { - throw new DisplayException('The requested default connection (' . $ip . ':' . $port . ') is not allocated to this server.'); + throw new DisplayException('The requested default connection is not allocated to this server.'); } $server->allocation_id = $selection->id; - $newBuild['default'] = [ - 'ip' => $ip, - 'port' => (int) $port, - ]; + $newBuild['default'] = ['ip' => $selection->ip, 'port' => $selection->port]; - // Re-Run to keep updated for rest of function $server->load('allocation'); } } $newPorts = false; // Remove Assignments - if (isset($data['remove_additional'])) { - foreach ($data['remove_additional'] as $id => $combo) { - list($ip, $port) = explode(':', $combo); - // Invalid, not worth killing the whole thing, we'll just skip over it. - if (! filter_var($ip, FILTER_VALIDATE_IP) || ! preg_match('/^(\d{1,5})$/', $port)) { - break; - } - + if (isset($data['remove_allocations'])) { + foreach ($data['remove_allocations'] as $allocation) { // Can't remove the assigned IP/Port combo - if ($ip === $server->allocation->ip && (int) $port === (int) $server->allocation->port) { - break; + if ((int) $allocation === $server->allocation_id) { + continue; } $newPorts = true; - $server->allocations->where('ip', $ip)->where('port', $port)->update([ + Models\Allocation::where('id', $allocation)->where('server_id', $server->id)->update([ 'server_id' => null, ]); } @@ -548,21 +535,15 @@ class ServerRepository } // Add Assignments - if (isset($data['add_additional'])) { - foreach ($data['add_additional'] as $id => $combo) { - list($ip, $port) = explode(':', $combo); - // Invalid, not worth killing the whole thing, we'll just skip over it. - if (! filter_var($ip, FILTER_VALIDATE_IP) || ! preg_match('/^(\d{1,5})$/', $port)) { - break; - } - - // Don't allow double port assignments - if ($server->allocations->where('port', $port)->count() !== 0) { - break; + if (isset($data['add_allocations'])) { + foreach ($data['add_allocations'] as $allocation) { + $model = Models\Allocation::where('id', $allocation)->whereNull('server_id')->first(); + if (! $model) { + continue; } $newPorts = true; - Models\Allocation::where('ip', $ip)->where('port', $port)->whereNull('server_id')->update([ + $model->update([ 'server_id' => $server->id, ]); } @@ -570,18 +551,10 @@ class ServerRepository $server->load('allocations'); } - // Loop All Assignments - $additionalAssignments = []; - foreach ($server->allocations as &$assignment) { - if (array_key_exists((string) $assignment->ip, $additionalAssignments)) { - array_push($additionalAssignments[(string) $assignment->ip], (int) $assignment->port); - } else { - $additionalAssignments[(string) $assignment->ip] = [(int) $assignment->port]; - } - } - - if ($newPorts === true) { - $newBuild['ports|overwrite'] = $additionalAssignments; + if ($newPorts) { + $newBuild['ports|overwrite'] = $server->allocations->groupBy('ip')->map(function ($item) { + return $item->pluck('port'); + })->toArray(); } // @TODO: verify that server can be set to this much memory without @@ -617,6 +590,7 @@ class ServerRepository // This won't be committed unless the HTTP request succeedes anyways $server->save(); + dd($newBuild); if (! empty($newBuild)) { $server->node->guzzleClient([ 'X-Access-Server' => $server->uuid, @@ -630,7 +604,7 @@ class ServerRepository DB::commit(); - return true; + return $server; } catch (TransferException $ex) { DB::rollBack(); throw new DisplayException('An error occured while attempting to update the configuration.', $ex); diff --git a/public/themes/pterodactyl/css/pterodactyl.css b/public/themes/pterodactyl/css/pterodactyl.css index 4a3fe3bf2..497c49324 100644 --- a/public/themes/pterodactyl/css/pterodactyl.css +++ b/public/themes/pterodactyl/css/pterodactyl.css @@ -198,3 +198,29 @@ span[aria-labelledby="select2-pUserId-container"] { color: #777 !important; background: transparent !important; } + +.tab-pane .box-footer { + margin: 0 -10px -10px; +} + +.select2-container{ width: 100% !important; } + +.nav-tabs-custom > .nav-tabs > li:hover { + border-top-color:#3c8dbc; +} + +.nav-tabs-custom > .nav-tabs > li.active.tab-danger, .nav-tabs-custom > .nav-tabs > li.tab-danger:hover { + border-top-color: #c23321; +} + +.nav-tabs-custom > .nav-tabs > li.active.tab-success, .nav-tabs-custom > .nav-tabs > li.tab-success:hover { + border-top-color: #00733e; +} + +.nav-tabs-custom > .nav-tabs > li.active.tab-info, .nav-tabs-custom > .nav-tabs > li.tab-info:hover { + border-top-color: #0097bc; +} + +.nav-tabs-custom > .nav-tabs > li.active.tab-warning, .nav-tabs-custom > .nav-tabs > li.tab-warning:hover { + border-top-color: #c87f0a; +} diff --git a/resources/themes/pterodactyl/admin/servers/view.blade.php b/resources/themes/pterodactyl/admin/servers/view.blade.php new file mode 100644 index 000000000..85a74536c --- /dev/null +++ b/resources/themes/pterodactyl/admin/servers/view.blade.php @@ -0,0 +1,390 @@ +{{-- Copyright (c) 2015 - 2017 Dane Everitt --}} + +{{-- 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. --}} +@extends('layouts.admin') + +@section('title') + Manage Server: {{ $server->name }} +@endsection + +@section('content-header') +

{{ $server->name }}{{ $server->uuid }}

+ +@endsection + +@section('content') + @if($server->suspended && ! $server->trashed()) +
+ This server is suspended and has no user access. Processes cannot be started and files cannot be modified. All API access is disabled unless using a master token. +
+ @elseif($server->trashed()) +
+ This server is marked for deletion {{ Carbon::parse($server->deleted_at)->addMinutes(env('APP_DELETE_MINUTES', 10))->diffForHumans() }}. If you want to cancel this action simply click the button below. +

+
+ + + + {!! csrf_field() !!} +
+
+ @endif + @if(! $server->installed) +
+ This server is still running through the install process and is not avaliable for use just yet. This message will disappear once this process is completed. +
+ @elseif($server->installed === 2) +
+ This server failed to install properly. You should delete it and try to create it again or check the daemon logs. +
+ @endif +
+
+ +
+
+@endsection + +@section('footer-scripts') + @parent + +@endsection diff --git a/resources/themes/pterodactyl/layouts/admin.blade.php b/resources/themes/pterodactyl/layouts/admin.blade.php index b3cff34e3..568b60acc 100644 --- a/resources/themes/pterodactyl/layouts/admin.blade.php +++ b/resources/themes/pterodactyl/layouts/admin.blade.php @@ -67,7 +67,7 @@
  • -
  • +