From a903ae313afff49e46fd8bb675dcd1f1a29141b6 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 13 Feb 2016 17:29:52 -0500 Subject: [PATCH] Add per-service-option startup & executable Also fixes display issue on front-end where users could see and edit hidden settings Fixes a bug in relation to #57 --- .../Controllers/Admin/ServersController.php | 20 ++- .../Controllers/Server/ServerController.php | 34 +++-- app/Repositories/ServerRepository.php | 139 ++++++++++-------- ...306_add_service_option_default_startup.php | 33 +++++ resources/views/admin/servers/new.blade.php | 8 +- 5 files changed, 150 insertions(+), 84 deletions(-) create mode 100644 database/migrations/2016_02_13_154306_add_service_option_default_startup.php diff --git a/app/Http/Controllers/Admin/ServersController.php b/app/Http/Controllers/Admin/ServersController.php index b4c2b507b..325d788a1 100644 --- a/app/Http/Controllers/Admin/ServersController.php +++ b/app/Http/Controllers/Admin/ServersController.php @@ -25,6 +25,7 @@ namespace Pterodactyl\Http\Controllers\Admin; use Alert; use Debugbar; +use DB; use Log; use Pterodactyl\Models; @@ -196,11 +197,7 @@ class ServersController extends Controller } $service = Models\Service::select('executable', 'startup')->where('id', $request->input('service'))->first(); - return response()->json([ - 'exec' => $service->executable, - 'startup' => $service->startup, - 'options' => Models\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()); } @@ -219,7 +216,18 @@ class ServersController extends Controller ], 500); } - return response()->json(Models\ServiceVariables::where('option_id', $request->input('option'))->get()); + $option = Models\ServiceOptions::select( + DB::raw('COALESCE(service_options.executable, services.executable) as executable'), + DB::raw('COALESCE(service_options.startup, services.startup) as startup') + )->leftJoin('services', 'services.id', '=', 'service_options.parent_service') + ->where('service_options.id', $request->input('option')) + ->first(); + + return response()->json([ + 'variables' => Models\ServiceVariables::where('option_id', $request->input('option'))->get(), + 'exec' => $option->executable, + 'startup' => $option->startup + ]); } diff --git a/app/Http/Controllers/Server/ServerController.php b/app/Http/Controllers/Server/ServerController.php index acf37635b..59903f8ee 100644 --- a/app/Http/Controllers/Server/ServerController.php +++ b/app/Http/Controllers/Server/ServerController.php @@ -24,6 +24,7 @@ namespace Pterodactyl\Http\Controllers\Server; use Auth; +use DB; use Debugbar; use Uuid; use Alert; @@ -194,12 +195,24 @@ class ServerController extends Controller public function getSettings(Request $request, $uuid) { $server = Models\Server::getByUUID($uuid); - $variables = Models\ServiceVariables::select('service_variables.*', 'server_variables.variable_value as a_serverValue') - ->join('server_variables', 'server_variables.variable_id', '=', 'service_variables.id') - ->where('service_variables.option_id', $server->option) - ->where('server_variables.server_id', $server->id) + // $variables = Models\ServiceVariables::select('service_variables.*', 'server_variables.variable_value as a_serverValue') + // ->join('server_variables', 'server_variables.variable_id', '=', 'service_variables.id') + // ->where('service_variables.option_id', $server->option) + // ->where('server_variables.server_id', $server->id) + // ->get(); + $variables = Models\ServiceVariables::select( + 'service_variables.*', + DB::raw('COALESCE(server_variables.variable_value, service_variables.default_value) as a_serverValue') + )->leftJoin('server_variables', 'server_variables.variable_id', '=', 'service_variables.id') + ->where('option_id', $server->option) ->get(); - $service = Models\Service::findOrFail($server->service); + + $service = Models\Service::select( + DB::raw('COALESCE(service_options.executable, services.executable) as executable'), + DB::raw('COALESCE(service_options.startup, services.startup) as startup') + )->leftJoin('service_options', 'service_options.parent_service', '=', 'services.id') + ->where('services.id', $server->service) + ->first(); $serverVariables = [ '{{SERVER_MEMORY}}' => $server->memory, @@ -216,7 +229,7 @@ class ServerController extends Controller return view('server.settings', [ 'server' => $server, 'node' => Models\Node::find($server->node), - 'variables' => $variables, + 'variables' => $variables->where('user_viewable', 1), 'service' => $service, 'processedStartup' => $processed, ]); @@ -258,12 +271,11 @@ class ServerController extends Controller } catch(\Exception $ex) { Log::error($ex); Alert::danger('An unhandled exception occured while attemping to update startup variables for this server. Please try again.')->flash(); - } finally { - return redirect()->route('server.settings', [ - 'uuid' => $uuid, - 'tab' => 'tab_startup' - ])->withInput(); } + return redirect()->route('server.settings', [ + 'uuid' => $uuid, + 'tab' => 'tab_startup' + ]); } } diff --git a/app/Repositories/ServerRepository.php b/app/Repositories/ServerRepository.php index 6bc873410..cb501e091 100644 --- a/app/Repositories/ServerRepository.php +++ b/app/Repositories/ServerRepository.php @@ -554,70 +554,83 @@ class ServerRepository } // Check those Variables - $variables = Models\ServiceVariables::select('service_variables.*', 'server_variables.variable_value as a_currentValue') - ->join('server_variables', 'server_variables.variable_id', '=', 'service_variables.id') - ->where('option_id', $server->option)->get(); - - $variableList = []; - if ($variables) { - foreach($variables as &$variable) { - // Move on if the new data wasn't even sent - if (!isset($data[$variable->env_variable])) { - $variableList = array_merge($variableList, [[ - 'id' => $variable->id, - 'env' => $variable->env_variable, - 'val' => $variable->a_currentValue - ]]); - continue; - } - - // Update Empty but skip validation - if (empty($data[$variable->env_variable])) { - $variableList = array_merge($variableList, [[ - 'id' => $variable->id, - 'env' => $variable->env_variable, - 'val' => null - ]]); - continue; - } - - // Is the variable required? - // @TODO: is this even logical to perform this check? - if (isset($data[$variable->env_variable]) && empty($data[$variable->env_variable])) { - if ($variable->required === 1) { - throw new DisplayException('A required service option variable field (' . $variable->env_variable . ') was included in this request but was left blank.'); - } - } - - // Check aganist Regex Pattern - if (!is_null($variable->regex) && !preg_match($variable->regex, $data[$variable->env_variable])) { - throw new DisplayException('Failed to validate service option variable field (' . $variable->env_variable . ') aganist regex (' . $variable->regex . ').'); - } - - $variableList = array_merge($variableList, [[ - 'id' => $variable->id, - 'env' => $variable->env_variable, - 'val' => $data[$variable->env_variable] - ]]); - } - } - - // Add Variables - $environmentVariables = []; - $environmentVariables = array_merge($environmentVariables, [ - 'STARTUP' => $server->startup - ]); - foreach($variableList as $item) { - $environmentVariables = array_merge($environmentVariables, [ - $item['env'] => $item['val'] - ]); - $var = Models\ServerVariables::where('server_id', $server->id)->where('variable_id', $item['id'])->update([ - 'variable_value' => $item['val'] - ]); - } + $variables = Models\ServiceVariables::select( + 'service_variables.*', + DB::raw('COALESCE(server_variables.variable_value, service_variables.default_value) as a_currentValue') + )->leftJoin('server_variables', 'server_variables.variable_id', '=', 'service_variables.id') + ->where('option_id', $server->option) + ->get(); try { + $variableList = []; + if ($variables) { + foreach($variables as &$variable) { + // Move on if the new data wasn't even sent + if (!isset($data[$variable->env_variable])) { + $variableList = array_merge($variableList, [[ + 'id' => $variable->id, + 'env' => $variable->env_variable, + 'val' => $variable->a_currentValue + ]]); + continue; + } + + // Update Empty but skip validation + if (empty($data[$variable->env_variable])) { + $variableList = array_merge($variableList, [[ + 'id' => $variable->id, + 'env' => $variable->env_variable, + 'val' => null + ]]); + continue; + } + + // Is the variable required? + // @TODO: is this even logical to perform this check? + if (isset($data[$variable->env_variable]) && empty($data[$variable->env_variable])) { + if ($variable->required === 1) { + throw new DisplayException('A required service option variable field (' . $variable->env_variable . ') was included in this request but was left blank.'); + } + } + + // Variable hidden and/or not user editable + if ($variable->user_viewable === 0 || $variable->user_editable === 0) { + throw new DisplayException('A service option variable field (' . $variable->env_variable . ') does not exist or you do not have permission to edit it.'); + } + + // Check aganist Regex Pattern + if (!is_null($variable->regex) && !preg_match($variable->regex, $data[$variable->env_variable])) { + throw new DisplayException('Failed to validate service option variable field (' . $variable->env_variable . ') aganist regex (' . $variable->regex . ').'); + } + + $variableList = array_merge($variableList, [[ + 'id' => $variable->id, + 'env' => $variable->env_variable, + 'val' => $data[$variable->env_variable] + ]]); + } + } + + // Add Variables + $environmentVariables = []; + $environmentVariables = array_merge($environmentVariables, [ + 'STARTUP' => $server->startup + ]); + foreach($variableList as $item) { + $environmentVariables = array_merge($environmentVariables, [ + $item['env'] => $item['val'] + ]); + + // Update model or make a new record if it doesn't exist. + $model = Models\ServerVariables::firstOrNew([ + 'variable_id' => $item['id'], + 'server_id' => $server->id + ]); + $model->variable_value = $item['val']; + $model->save(); + } + $node = Models\Node::getByID($server->node); $client = Models\Node::guzzleRequest($server->node); @@ -638,9 +651,9 @@ class ServerRepository } catch (\GuzzleHttp\Exception\TransferException $ex) { DB::rollBack(); throw new DisplayException('An error occured while attempting to update the server configuration: ' . $ex->getMessage()); - } catch (\Exception $e) { + } catch (\Exception $ex) { DB::rollBack(); - throw $e; + throw $ex; } } diff --git a/database/migrations/2016_02_13_154306_add_service_option_default_startup.php b/database/migrations/2016_02_13_154306_add_service_option_default_startup.php new file mode 100644 index 000000000..1f8ad36fc --- /dev/null +++ b/database/migrations/2016_02_13_154306_add_service_option_default_startup.php @@ -0,0 +1,33 @@ +text('executable')->after('docker_image')->nullable()->default(null); + $table->text('startup')->after('executable')->nullable()->default(null); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('service_options', function (Blueprint $table) { + $table->dropColumn('executable'); + $table->dropColumn('startup'); + }); + } +} diff --git a/resources/views/admin/servers/new.blade.php b/resources/views/admin/servers/new.blade.php index 4d9bb9238..b89eb4845 100644 --- a/resources/views/admin/servers/new.blade.php +++ b/resources/views/admin/servers/new.blade.php @@ -362,9 +362,7 @@ $(document).ready(function () { service: $('#getService').val() } }).done(function (data) { - $('#startupExec').html(data.exec); - $('input[name="startup"]').val(data.startup); - $.each(data.options, function (i, option) { + $.each(data, function (i, option) { $('#getOption').append(''); }); $('#getOption').parent().parent().removeClass('hidden'); @@ -395,7 +393,9 @@ $(document).ready(function () { option: $('#getOption').val() } }).done(function (data) { - $.each(data, function (i, item) { + $('#startupExec').html(data.exec); + $('input[name="startup"]').val(data.startup); + $.each(data.variables, function (i, item) { var isRequired = (item.required === 1) ? 'Required ' : ''; var dataAppend = ' \
\