From 264431a2718e60a856606e14b523ea8a017e5ccf Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Tue, 15 Dec 2015 15:08:41 -0500 Subject: [PATCH] Whats this? We can add new servers now?! --- app/Exceptions/Handler.php | 4 +- .../Controllers/Admin/ServersController.php | 53 ++++++----- app/Models/Server.php | 7 ++ app/Models/ServerVariables.php | 7 ++ app/Repositories/ServerRepository.php | 94 ++++++++++++++++++- app/Repositories/UserRepository.php | 4 +- app/Services/UuidService.php | 48 ++-------- .../2015_12_15_175100_remove_daemon_table.php | 36 +++++++ resources/lang/en/base.php | 2 + resources/views/admin/servers/new.blade.php | 44 +++++---- 10 files changed, 214 insertions(+), 85 deletions(-) create mode 100644 database/migrations/2015_12_15_175100_remove_daemon_table.php diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 546f9b8f7..62a8d5dc4 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -4,7 +4,9 @@ namespace Pterodactyl\Exceptions; use Exception; use DisplayException; -use Debugbar; +use DisplayValidationException; +use AccountNotFoundException; + use Illuminate\Database\Eloquent\ModelNotFoundException; use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; diff --git a/app/Http/Controllers/Admin/ServersController.php b/app/Http/Controllers/Admin/ServersController.php index c307f0f3d..299721abb 100644 --- a/app/Http/Controllers/Admin/ServersController.php +++ b/app/Http/Controllers/Admin/ServersController.php @@ -2,15 +2,14 @@ namespace Pterodactyl\Http\Controllers\Admin; +use Alert; use Debugbar; + +use Pterodactyl\Models; use Pterodactyl\Repositories\ServerRepository; -use Pterodactyl\Models\Server; -use Pterodactyl\Models\Node; -use Pterodactyl\Models\Location; -use Pterodactyl\Models\Allocation; -use Pterodactyl\Models\Service; -use Pterodactyl\Models\ServiceOptions; -use Pterodactyl\Models\ServiceVariables; + +use Pterodactly\Exceptions\DisplayException; +use Pterodactly\Exceptions\DisplayValidationException; use Pterodactyl\Http\Controllers\Controller; use Illuminate\Http\Request; @@ -33,7 +32,7 @@ class ServersController extends Controller public function getIndex(Request $request) { return view('admin.servers.index', [ - 'servers' => Server::select('servers.*', 'nodes.name as a_nodeName', 'users.email as a_ownerEmail') + 'servers' => Models\Server::select('servers.*', 'nodes.name as a_nodeName', 'users.email as a_ownerEmail') ->join('nodes', 'servers.node', '=', 'nodes.id') ->join('users', 'servers.owner', '=', 'users.id') ->paginate(20), @@ -43,8 +42,8 @@ class ServersController extends Controller public function getNew(Request $request) { return view('admin.servers.new', [ - 'locations' => Location::all(), - 'services' => Service::all() + 'locations' => Models\Location::all(), + 'services' => Models\Service::all() ]); } @@ -57,14 +56,26 @@ class ServersController extends Controller { try { - $server = new ServerRepository; - $resp = $server->create($request->all()); - echo $resp . '
'; - } catch (\Exception $e) { - Debugbar::addException($e); - } - return json_encode($request->all()); + $server = new ServerRepository; + $response = $server->create($request->all()); + + return redirect()->route('admin.servers.view', [ 'id' => $response ]); + + } catch (\Exception $e) { + + if ($e instanceof \Pterodactyl\Exceptions\DisplayValidationException) { + return redirect()->route('admin.servers.new')->withErrors(json_decode($e->getMessage()))->withInput(); + } else if ($e instanceof \Pterodactyl\Exceptions\DisplayException) { + Alert::danger($e->getMessage())->flash(); + } else { + Debugbar::addException($e); + Alert::danger('An unhandled exception occured while attemping to add this server. Please try again.')->flash(); + } + + return redirect()->route('admin.servers.new')->withInput(); + + } } @@ -83,7 +94,7 @@ class ServersController extends Controller ], 500); } - return response()->json(Node::select('id', 'name', 'public')->where('location', $request->input('location'))->get()); + return response()->json(Models\Node::select('id', 'name', 'public')->where('location', $request->input('location'))->get()); } @@ -102,7 +113,7 @@ class ServersController extends Controller ], 500); } - $ips = Allocation::where('node', $request->input('node'))->whereNull('assigned_to')->get(); + $ips = Models\Allocation::where('node', $request->input('node'))->whereNull('assigned_to')->get(); $listing = []; foreach($ips as &$ip) { @@ -131,7 +142,7 @@ class ServersController extends Controller ], 500); } - return response()->json(ServiceOptions::select('id', 'name', 'docker_image')->where('parent_service', $request->input('service'))->orderBy('name', 'asc')->get()); + return response()->json(Models\ServiceOptions::select('id', 'name', 'docker_image')->where('parent_service', $request->input('service'))->orderBy('name', 'asc')->get()); } @@ -150,7 +161,7 @@ class ServersController extends Controller ], 500); } - return response()->json(ServiceVariables::where('option_id', $request->input('option'))->get()); + return response()->json(Models\ServiceVariables::where('option_id', $request->input('option'))->get()); } diff --git a/app/Models/Server.php b/app/Models/Server.php index 2fa7bb735..838564010 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -24,6 +24,13 @@ class Server extends Model */ protected $hidden = ['daemonSecret']; + /** + * Fields that are not mass assignable. + * + * @var array + */ + protected $guarded = ['id', 'installed', 'created_at', 'updated_at']; + /** * @var array */ diff --git a/app/Models/ServerVariables.php b/app/Models/ServerVariables.php index 611d57931..23e0d1b11 100644 --- a/app/Models/ServerVariables.php +++ b/app/Models/ServerVariables.php @@ -14,4 +14,11 @@ class ServerVariables extends Model */ protected $table = 'server_variables'; + /** + * Fields that are not mass assignable. + * + * @var array + */ + protected $guarded = ['id', 'created_at', 'updated_at']; + } diff --git a/app/Repositories/ServerRepository.php b/app/Repositories/ServerRepository.php index d0a00f793..131a8ac5e 100644 --- a/app/Repositories/ServerRepository.php +++ b/app/Repositories/ServerRepository.php @@ -3,6 +3,7 @@ namespace Pterodactyl\Repositories; use DB; +use Debugbar; use Validator; use Pterodactyl\Models; @@ -64,7 +65,7 @@ class ServerRepository // 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(json_encode($validator->errors()->all())); + throw new DisplayValidationException($validator->errors()); } // Get the User ID; user exists since we passed the 'exists:users,email' part of the validation @@ -91,6 +92,7 @@ class ServerRepository // Check those Variables $variables = Models\ServiceVariables::where('option_id', $data['option'])->get(); + $variableList = []; if ($variables) { foreach($variables as $variable) { @@ -100,7 +102,11 @@ class ServerRepository throw new DisplayException('A required service option variable field (env_' . $variable->env_variable . ') was missing from the request.'); } - $data['env_' . $variable->env_variable] = $variable->default_value; + $variableList = array_merge($variableList, [[ + 'var_id' => $variable->id, + 'var_val' => $variable->default_value + ]]); + continue; } @@ -109,13 +115,91 @@ class ServerRepository throw new DisplayException('Failed to validate service option variable field (env_' . $variable->env_variable . ') aganist regex (' . $variable->regex . ').'); } - continue; + $variableList = array_merge($variableList, [[ + 'var_id' => $variable->id, + 'var_val' => $data['env_' . $variable->env_variable] + ]]); + continue; } } - return (new UuidService)->generateShort(); - //return $this->generateSFTPUsername($data['name']); + // Check Overallocation + if (is_numeric($node->memory_overallocate) || is_numeric($node->disk_overallocate)) { + + $totals = Models\Server::select(DB::raw('SUM(memory) as memory, SUM(disk) as disk'))->where('node', $node->id)->first(); + + // Check memory limits + if (is_numeric($node->memory_overallocate)) { + $newMemory = $totals->memory + $data['memory']; + $memoryLimit = ($node->memory * (1 + ($node->memory_overallocate / 100))); + if($newMemory > $memoryLimit) { + throw new DisplayException('The amount of memory allocated to this server would put the node over its allocation limits. This node is allowed ' . ($node->memory_overallocate + 100) . '% of its assigned ' . $node->memory . 'Mb of memory (' . $memoryLimit . 'Mb) of which ' . (($totals->memory / $node->memory) * 100) . '% (' . $totals->memory . 'Mb) is in use already. By allocating this server the node would be at ' . (($newMemory / $node->memory) * 100) . '% (' . $newMemory . 'Mb) usage.'); + } + } + + // Check Disk Limits + if (is_numeric($node->disk_overallocate)) { + $newDisk = $totals->disk + $data['disk']; + $diskLimit = ($node->disk * (1 + ($node->disk_overallocate / 100))); + if($newDisk > $diskLimit) { + throw new DisplayException('The amount of disk allocated to this server would put the node over its allocation limits. This node is allowed ' . ($node->disk_overallocate + 100) . '% of its assigned ' . $node->disk . 'Mb of disk (' . $diskLimit . 'Mb) of which ' . (($totals->disk / $node->disk) * 100) . '% (' . $totals->disk . 'Mb) is in use already. By allocating this server the node would be at ' . (($newDisk / $node->disk) * 100) . '% (' . $newDisk . 'Mb) usage.'); + } + } + + } + + DB::beginTransaction(); + + $uuid = new UuidService; + + // Add Server to the Database + $server = new Models\Server; + $server->fill([ + 'uuid' => $uuid->generate('servers', 'uuid'), + 'uuidShort' => $uuid->generateShort(), + 'node' => $data['node'], + 'name' => $data['name'], + 'active' => 1, + 'owner' => $user->id, + 'memory' => $data['memory'], + 'disk' => $data['disk'], + 'io' => $data['io'], + 'cpu' => $data['cpu'], + 'ip' => $data['ip'], + 'port' => $data['port'], + 'service' => $data['service'], + 'option' => $data['option'], + 'daemonSecret' => $uuid->generate('servers', 'daemonSecret'), + 'username' => $this->generateSFTPUsername($data['name']) + ]); + $server->save(); + + // Mark Allocation in Use + $allocation->assigned_to = $server->id; + $allocation->save(); + + // Add Variables + foreach($variableList as $item) { + Models\ServerVariables::create([ + 'server_id' => $server->id, + 'variable_id' => $item['var_id'], + 'variable_value' => $item['var_val'] + ]); + } + + try { + + // Add logic for communicating with Wings to make the server in here. + // We should add the server regardless of the Wings response, but + // handle the error and then allow the server to be re-deployed. + + DB::commit(); + return $server->id; + } catch (\Exception $e) { + DB::rollBack(); + throw $e; + } } diff --git a/app/Repositories/UserRepository.php b/app/Repositories/UserRepository.php index bd3d48ce9..cf60b7b9a 100644 --- a/app/Repositories/UserRepository.php +++ b/app/Repositories/UserRepository.php @@ -17,7 +17,7 @@ class UserRepository /** * Creates a user on the panel. Returns the created user's ID. - * + * * @param string $username * @param string $email * @param string $password An unhashed version of the user's password. @@ -29,7 +29,7 @@ class UserRepository $user = new User; $uuid = new UuidService; - $user->uuid = $uuid->table('users')->generate(); + $user->uuid = $uuid->generate('users', 'uuid'); $user->username = $username; $user->email = $email; diff --git a/app/Services/UuidService.php b/app/Services/UuidService.php index e9ecf7f76..9217cd084 100644 --- a/app/Services/UuidService.php +++ b/app/Services/UuidService.php @@ -8,16 +8,6 @@ use Uuid; class UuidService { - /** - * @var string - */ - protected $table = 'users'; - - /** - * @var string - */ - protected $field = 'uuid'; - /** * Constructor */ @@ -26,45 +16,23 @@ class UuidService // } - /** - * Set the table that we need to be checking in the database. - * - * @param string $table - * @return void - */ - public function table($table) - { - $this->table = $table; - return $this; - } - - /** - * Set the field in the given table that we want to check for a unique UUID. - * - * @param string $field - * @return void - */ - public function field($field) - { - $this->field = $field; - return $this; - } - /** * Generate a unique UUID validating against specified table and column. * Defaults to `users.uuid` * + * @param string $table + * @param string $field * @param integer $type The type of UUID to generate. * @return string */ - public function generate($type = 4) + public function generate($table = 'users', $field = 'uuid', $type = 4) { $return = false; do { - $uuid = LaravelUUID::generate($type); - if (!DB::table($this->table)->where($this->field, $uuid)->exists()) { + $uuid = Uuid::generate($type); + if (!DB::table($table)->where($field, $uuid)->exists()) { $return = $uuid; } @@ -81,13 +49,15 @@ class UuidService * @param string $field * @return string */ - public function generateShort($table = 'servers', $field = 'uuidShort') + public function generateShort($table = 'servers', $field = 'uuidShort', $attachedUuid = null) { $return = false; do { - $short = substr(Uuid::generate(4), 0, 8); + $short = (is_null($attachedUuid)) ? substr(Uuid::generate(4), 0, 8) : substr($attachedUuid, 0, 8); + $attachedUuid = null; + if (!DB::table($table)->where($field, $short)->exists()) { $return = $short; } diff --git a/database/migrations/2015_12_15_175100_remove_daemon_table.php b/database/migrations/2015_12_15_175100_remove_daemon_table.php new file mode 100644 index 000000000..b6fb8b20d --- /dev/null +++ b/database/migrations/2015_12_15_175100_remove_daemon_table.php @@ -0,0 +1,36 @@ +increments('id')->unsigned(); + $table->mediumInteger('server')->unsigned(); + $table->string('parameter'); + $table->text('value'); + $table->tinyInteger('editable')->unsigned()->default(0); + $table->tinyInteger('visible')->unsigned()->default(0); + $table->text('regex')->nullable(); + $table->timestamps(); + }); + } +} diff --git a/resources/lang/en/base.php b/resources/lang/en/base.php index d92c41fa6..852b6e16b 100644 --- a/resources/lang/en/base.php +++ b/resources/lang/en/base.php @@ -12,6 +12,8 @@ return [ | */ + 'validation_error' => 'An error occured while validating the data you submitted:', + 'failed' => 'These credentials do not match our records.', 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', 'view_as_admin' => 'You are viewing this server listing as an admin. As such, all servers installed on the system are displayed. Any servers that you are set as the owner of are marked with a blue dot to the left of their name.', diff --git a/resources/views/admin/servers/new.blade.php b/resources/views/admin/servers/new.blade.php index 2e48a838d..272f506e0 100644 --- a/resources/views/admin/servers/new.blade.php +++ b/resources/views/admin/servers/new.blade.php @@ -11,11 +11,21 @@
  • Servers
  • Create New Server
  • + @if (count($errors) > 0) +
    + {{ trans('strings.whoops') }}! {{ trans('base.validation_error') }}

    + +
    + @endif @foreach (Alert::getMessages() as $type => $messages) @foreach ($messages as $message) @endforeach @endforeach @@ -26,14 +36,14 @@
    - +

    Character limits: a-zA-Z0-9_- and [Space] (max 35 characters)

    - +
    @@ -46,7 +56,7 @@
    - +

    The node which this server will be deployed to.

    @@ -69,7 +79,7 @@

    Select the main IP that this server will be listening on. You can assign additional open IPs and ports below.

    @@ -89,28 +99,28 @@
    - + MB
    - + MB
    - + %
    - + I/O
    @@ -130,7 +140,7 @@
    - +

    Select the type of service that this server will be running.

    @@ -157,9 +167,9 @@
    - + - +

    If you would like to use a custom docker image for this server please enter it here. Most users can ignore this option.

    @@ -210,8 +220,8 @@ $(document).ready(function () { currentNode = null; // Hide Existing, and Reset contents - $('#getNode').html('').parent().parent().addClass('hidden'); - $('#getIP').html('').parent().parent().addClass('hidden'); + $('#getNode').html('').parent().parent().addClass('hidden'); + $('#getIP').html('').parent().parent().addClass('hidden'); $('#getPort').html('').parent().parent().addClass('hidden'); handleLoader('#load_settings', true); @@ -249,7 +259,7 @@ $(document).ready(function () { currentNode = $('#getNode').val(); // Hide Existing, and Reset contents - $('#getIP').html('').parent().parent().addClass('hidden'); + $('#getIP').html('').parent().parent().addClass('hidden'); $('#getPort').html('').parent().parent().addClass('hidden'); handleLoader('#load_settings', true);