diff --git a/app/Http/Controllers/Admin/NodesController.php b/app/Http/Controllers/Admin/NodesController.php index 44e902037..402cd6659 100644 --- a/app/Http/Controllers/Admin/NodesController.php +++ b/app/Http/Controllers/Admin/NodesController.php @@ -72,8 +72,36 @@ class NodesController extends Controller 'servers' => Models\Server::select('servers.*', 'users.email as a_ownerEmail', 'services.name as a_serviceName') ->join('users', 'users.id', '=', 'servers.owner') ->join('services', 'services.id', '=', 'servers.service') - ->where('node', $id)->paginate(10) + ->where('node', $id)->paginate(10), + 'stats' => Models\Server::select(DB::raw('SUM(memory) as memory, SUM(disk) as disk'))->where('node', $node->id)->first(), + 'locations' => Models\Location::all(), ]); } + public function postView(Request $request, $id) + { + try { + $node = new NodeRepository; + $node->update($id, $request->except([ + '_token' + ])); + Alert::success('Successfully update this node\'s information. If you changed any daemon settings you will need to restart it now.')->flash(); + return redirect()->route('admin.nodes.view', [ + 'id' => $id, + 'tab' => 'tab_settings' + ]); + } catch (\Pterodactyl\Exceptions\DisplayValidationException $e) { + return redirect()->route('admin.nodes.view', $id)->withErrors(json_decode($e->getMessage()))->withInput(); + } catch (\Pterodactyl\Exceptions\DisplayException $e) { + Alert::danger($e->getMessage())->flash(); + } catch (\Exception $e) { + Log::error($e); + Alert::danger('An unhandled exception occured while attempting to edit this node. Please try again.')->flash(); + } + return redirect()->route('admin.nodes.view', [ + 'id' => $id, + 'tab' => 'tab_settings' + ])->withInput(); + } + } diff --git a/app/Http/Routes/AdminRoutes.php b/app/Http/Routes/AdminRoutes.php index c00da60b9..bf4131a1e 100644 --- a/app/Http/Routes/AdminRoutes.php +++ b/app/Http/Routes/AdminRoutes.php @@ -168,6 +168,10 @@ class AdminRoutes { 'uses' => 'Admin\NodesController@getView' ]); + $router->post('/view/{id}', [ + 'uses' => 'Admin\NodesController@postView' + ]); + }); } diff --git a/app/Repositories/NodeRepository.php b/app/Repositories/NodeRepository.php index c4316d910..91d9eaa6e 100644 --- a/app/Repositories/NodeRepository.php +++ b/app/Repositories/NodeRepository.php @@ -32,7 +32,7 @@ class NodeRepository { 'disk_overallocate' => 'required|numeric|min:-1', 'daemonBase' => 'required|regex:/^([\/][\d\w.\-\/]+)$/', 'daemonSFTP' => 'required|numeric|between:1,65535', - 'daemonListen' => 'required|numeric|between:1,65535' + 'daemonListen' => 'required|numeric|between:1,65535', ]); // Run validator, throw catchable and displayable exception if it fails. @@ -42,6 +42,9 @@ class NodeRepository { } // Verify the FQDN + if (filter_var($data['fqdn'], FILTER_VALIDATE_IP)) { + throw new DisplayException('The FQDN provided was an IP address. You must use a FQDN.'); + } if (!filter_var(gethostbyname($data['fqdn']), FILTER_VALIDATE_IP)) { throw new DisplayException('The FQDN provided does not resolve to a valid IP address.'); } @@ -63,4 +66,61 @@ class NodeRepository { } + public function update($id, array $data) + { + // Validate Fields + $validator = $validator = Validator::make($data, [ + 'name' => 'regex:/^([\w .-]{1,100})$/', + 'location' => 'numeric|min:1|exists:locations,id', + 'public' => 'numeric|between:0,1', + 'fqdn' => 'string|unique:nodes,fqdn,' . $id, + 'scheme' => 'regex:/^(http(s)?)$/', + 'memory' => 'numeric|min:1', + 'memory_overallocate' => 'numeric|min:-1', + 'disk' => 'numeric|min:1', + 'disk_overallocate' => 'numeric|min:-1', + 'daemonBase' => 'regex:/^([\/][\d\w.\-\/]+)$/', + 'daemonSFTP' => 'numeric|between:1,65535', + 'daemonListen' => 'numeric|between:1,65535', + 'reset_secret' => 'sometimes|accepted', + ]); + + // Run validator, throw catchable and displayable exception if it fails. + // Exception includes a JSON result of failed validation rules. + if ($validator->fails()) { + throw new DisplayValidationException($validator->errors()); + } + + // Verify the FQDN + if (isset($data['fqdn'])) { + if (filter_var($data['fqdn'], FILTER_VALIDATE_IP)) { + throw new DisplayException('The FQDN provided was an IP address. You must use a FQDN.'); + } + if (!filter_var(gethostbyname($data['fqdn']), FILTER_VALIDATE_IP)) { + throw new DisplayException('The FQDN provided does not resolve to a valid IP address.'); + } + } + + // Should we be nulling the overallocations? + if (isset($data['memory_overallocate'])) { + $data['memory_overallocate'] = ($data['memory_overallocate'] < 0) ? null : $data['memory_overallocate']; + } + + if (isset($data['disk_overallocate'])) { + $data['disk_overallocate'] = ($data['disk_overallocate'] < 0) ? null : $data['disk_overallocate']; + } + + // Set the Secret + if (isset($data['reset_secret'])) { + $uuid = new UuidService; + $data['daemonSecret'] = (string) $uuid->generate('nodes', 'daemonSecret'); + unset($data['reset_secret']); + } + + // Store the Data + $node = Models\Node::findOrFail($id); + return $node->update($data); + + } + } diff --git a/public/css/pterodactyl.css b/public/css/pterodactyl.css index b5d42870d..9a0cf1e8f 100755 --- a/public/css/pterodactyl.css +++ b/public/css/pterodactyl.css @@ -77,3 +77,5 @@ form .text-muted {margin: 0 0 -5.5px} .tab-pane.active .panel.panel-default {border-top:0 !important} .tabs_with_panel > li.active > a {background-color: #f5f5f5 !important} .tabs_with_panel > li > a {background: transparent;} +.label{border-radius: .25em;padding: .2em .6em .3em;} +kbd{border-radius: .25em} diff --git a/resources/views/admin/nodes/view.blade.php b/resources/views/admin/nodes/view.blade.php index dac4b8f6b..fd6357c73 100644 --- a/resources/views/admin/nodes/view.blade.php +++ b/resources/views/admin/nodes/view.blade.php @@ -4,6 +4,29 @@ Managing Node: {{ $node->name }} @endsection +@section('scripts') + @parent + + + + +@endsection + @section('content')
auto-installer
or auto-updater
to setup the daemon.
+ {
+ "web": {
+ "listen": {{ $node->daemonListen }},
+ "ssl": {
+ "enabled": {{ $node->sceheme === 'https' ? 'true' : 'false' }},
+ "certificate": "~/.ssl/ssl.cert",
+ "key": "~/.ssl/ssl.key"
+ }
+ },
+ "docker": {
+ "socket": "/var/run/docker.sock"
+ },
+ "sftp": {
+ "path": "{{ $node->daemonBase }}",
+ "port": {{ $node->daemonSFTP }},
+ "container": "container_id"
+ },
+ "logger": {
+ "path": "logs/",
+ "src": false,
+ "level": "info",
+ "period": "1d",
+ "count": 3
+ },
+ "remote": {
+ "download": "{{ url('/remote/download') }}"
+ },
+ "uploads": {
+ "maximumSize": 1000000
+ },
+ "keys": [
+ "{{ $node->daemonSecret }}"
+ ]
+}
+ 10%
and the CPU usage below displays 90%
that means the server is using 9%
of the total system CPU.
+ The data below is live output from the daemon. CPU usage is displayed relative to the assigned CPU allocation. For example, if a server is assigned 10%
and the CPU usage below displays 90%
that means the server is using 9%
of the total system CPU.