From 70db4610751c939c6fb576044332dd5ef88afb61 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 10 Mar 2017 18:25:12 -0500 Subject: [PATCH 01/22] Start push of service changes. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes the way service files are stored and allows for much easier updates in the future that won’t affect custom services. Also stores more configurations in the database to make life easier for everyone. --- .../Controllers/Admin/OptionController.php | 71 +++ .../Controllers/Admin/ServiceController.php | 477 ++++++++++-------- .../Controllers/Daemon/ServiceController.php | 62 ++- app/Http/Routes/AdminRoutes.php | 143 +++--- app/Repositories/OptionRepository.php | 80 +++ .../Pack.php => PackRepository.php} | 4 +- app/Repositories/ServiceRepository.php | 178 +++++++ app/Repositories/ServiceRepository/Option.php | 124 ----- .../ServiceRepository/Service.php | 144 ------ .../Variable.php => VariableRepository.php} | 4 +- config/pterodactyl.php | 18 + ...5_212803_DeleteServiceExecutableOption.php | 38 ++ ..._10_162934_AddNewServiceOptionsColumns.php | 52 ++ ...03_10_173607_MigrateToNewServiceSystem.php | 223 ++++++++ .../admin/services/index.blade.php | 67 +++ .../pterodactyl/admin/services/new.blade.php | 86 ++++ .../admin/services/options/view.blade.php | 162 ++++++ .../pterodactyl/admin/services/view.blade.php | 113 +++++ .../pterodactyl/layouts/admin.blade.php | 11 + 19 files changed, 1459 insertions(+), 598 deletions(-) create mode 100644 app/Http/Controllers/Admin/OptionController.php create mode 100644 app/Repositories/OptionRepository.php rename app/Repositories/{ServiceRepository/Pack.php => PackRepository.php} (99%) create mode 100644 app/Repositories/ServiceRepository.php delete mode 100644 app/Repositories/ServiceRepository/Option.php delete mode 100644 app/Repositories/ServiceRepository/Service.php rename app/Repositories/{ServiceRepository/Variable.php => VariableRepository.php} (98%) create mode 100644 config/pterodactyl.php create mode 100644 database/migrations/2017_03_05_212803_DeleteServiceExecutableOption.php create mode 100644 database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php create mode 100644 database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php create mode 100644 resources/themes/pterodactyl/admin/services/index.blade.php create mode 100644 resources/themes/pterodactyl/admin/services/new.blade.php create mode 100644 resources/themes/pterodactyl/admin/services/options/view.blade.php create mode 100644 resources/themes/pterodactyl/admin/services/view.blade.php diff --git a/app/Http/Controllers/Admin/OptionController.php b/app/Http/Controllers/Admin/OptionController.php new file mode 100644 index 000000000..3dc9b24ff --- /dev/null +++ b/app/Http/Controllers/Admin/OptionController.php @@ -0,0 +1,71 @@ +. + * + * 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\Http\Controllers\Admin; + +use Log; +use Alert; +use Storage; +use Pterodactyl\Models; +use Illuminate\Http\Request; +use Pterodactyl\Exceptions\DisplayException; +use Pterodactyl\Http\Controllers\Controller; +use Pterodactyl\Repositories\OptionRepository; +use Pterodactyl\Exceptions\DisplayValidationException; + +class OptionController extends Controller +{ + /** + * Display option overview page. + * + * @param Request $request + * @param int $id + * @return \Illuminate\View\View + */ + public function viewConfiguration(Request $request, $id) + { + return view('admin.services.options.view', ['option' => Models\ServiceOption::findOrFail($id)]); + } + + public function editConfiguration(Request $request, $id) + { + $repo = new OptionRepository; + + try { + $repo->update($id, $request->intersect([ + 'name', 'description', 'tag', 'docker_image', 'startup', + 'config_from', 'config_stop', 'config_logs', 'config_files', 'config_startup', + ])); + + Alert::success('Service option configuration has been successfully updated.')->flash(); + } catch (DisplayValidationException $ex) { + return redirect()->route('admin.services.option.view', $id)->withErrors(json_decode($ex->getMessage())); + } catch (\Exception $ex) { + Log::error($ex); + Alert::danger('An unhandled exception occurred while attempting to update this service option. This error has been logged.')->flash(); + } + + return redirect()->route('admin.services.option.view', $id); + } +} diff --git a/app/Http/Controllers/Admin/ServiceController.php b/app/Http/Controllers/Admin/ServiceController.php index e831c8683..81ca528b8 100644 --- a/app/Http/Controllers/Admin/ServiceController.php +++ b/app/Http/Controllers/Admin/ServiceController.php @@ -36,276 +36,311 @@ use Pterodactyl\Exceptions\DisplayValidationException; class ServiceController extends Controller { - public function __construct() - { - // - } - - public function getIndex(Request $request) + /** + * Display service overview page. + * + * @param Request $request + * @return \Illuminate\View\View + */ + public function index(Request $request) { return view('admin.services.index', [ - 'services' => Models\Service::withCount('servers')->get(), + 'services' => Models\Service::withCount('servers', 'options', 'packs')->get(), ]); } - public function getNew(Request $request) + /** + * Display create service page. + * + * @param Request $request + * @return \Illuminate\View\View + */ + public function new(Request $request) { return view('admin.services.new'); } - public function postNew(Request $request) + /** + * Return base view for a service. + * + * @param Request $request + * @param int $id + * @return \Illuminate\View\View + */ + public function view(Request $request, $id) { + return view('admin.services.view', [ + 'service' => Models\Service::with('options', 'options.servers')->findOrFail($id), + ]); + } + + /** + * Handle post action for new service. + * + * @param Request $request + * @return \Illuminate\Response\RedirectResponse + */ + public function create(Request $request) + { + $repo = new ServiceRepository; + try { - $repo = new ServiceRepository\Service; - $service = $repo->create($request->only([ - 'name', 'description', 'file', - 'executable', 'startup', + $service = $repo->create($request->intersect([ + 'name', 'description', 'folder', 'startup', ])); Alert::success('Successfully created new service!')->flash(); - return redirect()->route('admin.services.service', $service->id); + return redirect()->route('admin.services.view', $service->id); } catch (DisplayValidationException $ex) { return redirect()->route('admin.services.new')->withErrors(json_decode($ex->getMessage()))->withInput(); } catch (DisplayException $ex) { Alert::danger($ex->getMessage())->flash(); } catch (\Exception $ex) { Log::error($ex); - Alert::danger('An error occured while attempting to add a new service.')->flash(); + Alert::danger('An error occured while attempting to add a new service. This error has been logged.')->flash(); } return redirect()->route('admin.services.new')->withInput(); } - public function getService(Request $request, $service) + /** + * Delete a service from the system. + * + * @param Request $request + * @param int $id + * @return \Illuminate\Response\RedirectResponse + */ + public function delete(Request $request, $id) { - return view('admin.services.view', [ - 'service' => Models\Service::with('options', 'options.servers')->findOrFail($service), - ]); - } + $repo = new ServiceRepository; - public function postService(Request $request, $service) - { try { - $repo = new ServiceRepository\Service; - $repo->update($service, $request->only([ - 'name', 'description', 'file', - 'executable', 'startup', - ])); - Alert::success('Successfully updated this service.')->flash(); - } catch (DisplayValidationException $ex) { - return redirect()->route('admin.services.service', $service)->withErrors(json_decode($ex->getMessage()))->withInput(); - } catch (DisplayException $ex) { - Alert::danger($ex->getMessage())->flash(); - } catch (\Exception $ex) { - Log::error($ex); - Alert::danger('An error occurred while attempting to update this service.')->flash(); - } - - return redirect()->route('admin.services.service', $service)->withInput(); - } - - public function deleteService(Request $request, $service) - { - try { - $repo = new ServiceRepository\Service; - $repo->delete($service); - Alert::success('Successfully deleted that service.')->flash(); + $repo->delete($id); + Alert::success('Successfully deleted service.')->flash(); return redirect()->route('admin.services'); } catch (DisplayException $ex) { Alert::danger($ex->getMessage())->flash(); } catch (\Exception $ex) { Log::error($ex); - Alert::danger('An error was encountered while attempting to delete that service.')->flash(); + Alert::danger('An error was encountered while attempting to delete that service. This error has been logged')->flash(); } - return redirect()->route('admin.services.service', $service); + return redirect()->route('admin.services.view', $id); } - public function getOption(Request $request, $service, $option) + /** + * Edits configuration for a specific service. + * + * @param Request $request + * @param int $id + * @return \Illuminate\Response\RedirectResponse + */ + public function edit(Request $request, $id) { - $option = Models\ServiceOption::with('service', 'variables')->findOrFail($option); - $option->setRelation('servers', $option->servers()->with('user')->paginate(25)); + $repo = new ServiceRepository; - return view('admin.services.options.view', ['option' => $option]); - } - - public function postOption(Request $request, $service, $option) - { try { - $repo = new ServiceRepository\Option; - $repo->update($option, $request->only([ - 'name', 'description', 'tag', - 'executable', 'docker_image', 'startup', + $repo->update($id, $request->intersect([ + 'name', 'description', 'folder', 'startup', ])); - Alert::success('Option settings successfully updated.')->flash(); + Alert::success('Service has been updated successfully.')->flash(); } catch (DisplayValidationException $ex) { - return redirect()->route('admin.services.option', [$service, $option])->withErrors(json_decode($ex->getMessage()))->withInput(); - } catch (\Exception $ex) { - Log::error($ex); - Alert::danger('An error occured while attempting to modify this option.')->flash(); - } - - return redirect()->route('admin.services.option', [$service, $option])->withInput(); - } - - public function deleteOption(Request $request, $service, $option) - { - try { - $repo = new ServiceRepository\Option; - $repo->delete($option); - - Alert::success('Successfully deleted that option.')->flash(); - - return redirect()->route('admin.services.service', $service); + return redirect()->route('admin.services.view', $id)->withErrors(json_decode($ex->getMessage()))->withInput(); } catch (DisplayException $ex) { Alert::danger($ex->getMessage())->flash(); } catch (\Exception $ex) { Log::error($ex); - Alert::danger('An error was encountered while attempting to delete this option.')->flash(); + Alert::danger('An error occurred while attempting to update this service. This error has been logged.')->flash(); } - return redirect()->route('admin.services.option', [$service, $option]); + return redirect()->route('admin.services.view', $id); } - public function postOptionVariable(Request $request, $service, $option, $variable) - { - try { - $repo = new ServiceRepository\Variable; - - // Because of the way old() works on the display side we prefix all of the variables with thier ID - // We need to remove that prefix here since the repo doesn't want it. - $data = [ - 'user_viewable' => '0', - 'user_editable' => '0', - 'required' => '0', - ]; - foreach ($request->except(['_token']) as $id => $val) { - $data[str_replace($variable . '_', '', $id)] = $val; - } - $repo->update($variable, $data); - Alert::success('Successfully updated variable.')->flash(); - } catch (DisplayValidationException $ex) { - $data = []; - foreach (json_decode($ex->getMessage(), true) as $id => $val) { - $data[$variable . '_' . $id] = $val; - } - - return redirect()->route('admin.services.option', [$service, $option])->withErrors((object) $data)->withInput(); - } catch (DisplayException $ex) { - Alert::danger($ex->getMessage())->flash(); - } catch (\Exception $ex) { - Log::error($ex); - Alert::danger('An error occurred while attempting to update this service.')->flash(); - } - - return redirect()->route('admin.services.option', [$service, $option])->withInput(); - } - - public function getNewVariable(Request $request, $service, $option) - { - return view('admin.services.options.variable', [ - 'option' => Models\ServiceOption::with('service')->findOrFail($option), - ]); - } - - public function postNewVariable(Request $request, $service, $option) - { - try { - $repo = new ServiceRepository\Variable; - $repo->create($option, $request->only([ - 'name', 'description', 'env_variable', - 'default_value', 'user_viewable', - 'user_editable', 'required', 'regex', - ])); - Alert::success('Successfully added new variable to this option.')->flash(); - - return redirect()->route('admin.services.option', [$service, $option]); - } catch (DisplayValidationException $ex) { - return redirect()->route('admin.services.option.variable.new', [$service, $option])->withErrors(json_decode($ex->getMessage()))->withInput(); - } catch (DisplayException $ex) { - Alert::danger($ex->getMessage())->flash(); - } catch (\Exception $ex) { - Log::error($ex); - Alert::danger('An error occurred while attempting to add this variable.')->flash(); - } - - return redirect()->route('admin.services.option.variable.new', [$service, $option])->withInput(); - } - - public function newOption(Request $request, $service) - { - return view('admin.services.options.new', [ - 'service' => Models\Service::findOrFail($service), - ]); - } - - public function postNewOption(Request $request, $service) - { - try { - $repo = new ServiceRepository\Option; - $id = $repo->create($service, $request->except([ - '_token', - ])); - Alert::success('Successfully created new service option.')->flash(); - - return redirect()->route('admin.services.option', [$service, $id]); - } catch (DisplayValidationException $ex) { - return redirect()->route('admin.services.option.new', $service)->withErrors(json_decode($ex->getMessage()))->withInput(); - } catch (\Exception $ex) { - Log::error($ex); - Alert::danger('An error occured while attempting to add this service option.')->flash(); - } - - return redirect()->route('admin.services.option.new', $service)->withInput(); - } - - public function deleteVariable(Request $request, $service, $option, $variable) - { - try { - $repo = new ServiceRepository\Variable; - $repo->delete($variable); - Alert::success('Deleted variable.')->flash(); - } catch (DisplayException $ex) { - Alert::danger($ex->getMessage())->flash(); - } catch (\Exception $ex) { - Log::error($ex); - Alert::danger('An error occured while attempting to delete that variable.')->flash(); - } - - return redirect()->route('admin.services.option', [$service, $option]); - } - - public function getConfiguration(Request $request, $serviceId) - { - $service = Models\Service::findOrFail($serviceId); - - return view('admin.services.config', [ - 'service' => $service, - 'contents' => [ - 'json' => Storage::get('services/' . $service->file . '/main.json'), - 'index' => Storage::get('services/' . $service->file . '/index.js'), - ], - ]); - } - - public function postConfiguration(Request $request, $serviceId) - { - try { - $repo = new ServiceRepository\Service; - $repo->updateFile($serviceId, $request->only(['file', 'contents'])); - - return response('', 204); - } catch (DisplayException $ex) { - return response()->json([ - 'error' => $ex->getMessage(), - ], 503); - } catch (\Exception $ex) { - Log::error($ex); - - return response()->json([ - 'error' => 'An error occured while attempting to save the file.', - ], 503); - } - } + // public function getOption(Request $request, $service, $option) + // { + // $option = Models\ServiceOption::with('service', 'variables')->findOrFail($option); + // $option->setRelation('servers', $option->servers()->with('user')->paginate(25)); + // + // return view('admin.services.options.view', ['option' => $option]); + // } + // + // public function postOption(Request $request, $service, $option) + // { + // try { + // $repo = new ServiceRepository\Option; + // $repo->update($option, $request->only([ + // 'name', 'description', 'tag', + // 'executable', 'docker_image', 'startup', + // ])); + // Alert::success('Option settings successfully updated.')->flash(); + // } catch (DisplayValidationException $ex) { + // return redirect()->route('admin.services.option', [$service, $option])->withErrors(json_decode($ex->getMessage()))->withInput(); + // } catch (\Exception $ex) { + // Log::error($ex); + // Alert::danger('An error occured while attempting to modify this option.')->flash(); + // } + // + // return redirect()->route('admin.services.option', [$service, $option])->withInput(); + // } + // + // public function deleteOption(Request $request, $service, $option) + // { + // try { + // $repo = new ServiceRepository\Option; + // $repo->delete($option); + // + // Alert::success('Successfully deleted that option.')->flash(); + // + // return redirect()->route('admin.services.service', $service); + // } catch (DisplayException $ex) { + // Alert::danger($ex->getMessage())->flash(); + // } catch (\Exception $ex) { + // Log::error($ex); + // Alert::danger('An error was encountered while attempting to delete this option.')->flash(); + // } + // + // return redirect()->route('admin.services.option', [$service, $option]); + // } + // + // public function postOptionVariable(Request $request, $service, $option, $variable) + // { + // try { + // $repo = new ServiceRepository\Variable; + // + // // Because of the way old() works on the display side we prefix all of the variables with thier ID + // // We need to remove that prefix here since the repo doesn't want it. + // $data = [ + // 'user_viewable' => '0', + // 'user_editable' => '0', + // 'required' => '0', + // ]; + // foreach ($request->except(['_token']) as $id => $val) { + // $data[str_replace($variable . '_', '', $id)] = $val; + // } + // $repo->update($variable, $data); + // Alert::success('Successfully updated variable.')->flash(); + // } catch (DisplayValidationException $ex) { + // $data = []; + // foreach (json_decode($ex->getMessage(), true) as $id => $val) { + // $data[$variable . '_' . $id] = $val; + // } + // + // return redirect()->route('admin.services.option', [$service, $option])->withErrors((object) $data)->withInput(); + // } catch (DisplayException $ex) { + // Alert::danger($ex->getMessage())->flash(); + // } catch (\Exception $ex) { + // Log::error($ex); + // Alert::danger('An error occurred while attempting to update this service.')->flash(); + // } + // + // return redirect()->route('admin.services.option', [$service, $option])->withInput(); + // } + // + // public function getNewVariable(Request $request, $service, $option) + // { + // return view('admin.services.options.variable', [ + // 'option' => Models\ServiceOption::with('service')->findOrFail($option), + // ]); + // } + // + // public function postNewVariable(Request $request, $service, $option) + // { + // try { + // $repo = new ServiceRepository\Variable; + // $repo->create($option, $request->only([ + // 'name', 'description', 'env_variable', + // 'default_value', 'user_viewable', + // 'user_editable', 'required', 'regex', + // ])); + // Alert::success('Successfully added new variable to this option.')->flash(); + // + // return redirect()->route('admin.services.option', [$service, $option]); + // } catch (DisplayValidationException $ex) { + // return redirect()->route('admin.services.option.variable.new', [$service, $option])->withErrors(json_decode($ex->getMessage()))->withInput(); + // } catch (DisplayException $ex) { + // Alert::danger($ex->getMessage())->flash(); + // } catch (\Exception $ex) { + // Log::error($ex); + // Alert::danger('An error occurred while attempting to add this variable.')->flash(); + // } + // + // return redirect()->route('admin.services.option.variable.new', [$service, $option])->withInput(); + // } + // + // public function newOption(Request $request, $service) + // { + // return view('admin.services.options.new', [ + // 'service' => Models\Service::findOrFail($service), + // ]); + // } + // + // public function postNewOption(Request $request, $service) + // { + // try { + // $repo = new ServiceRepository\Option; + // $id = $repo->create($service, $request->except([ + // '_token', + // ])); + // Alert::success('Successfully created new service option.')->flash(); + // + // return redirect()->route('admin.services.option', [$service, $id]); + // } catch (DisplayValidationException $ex) { + // return redirect()->route('admin.services.option.new', $service)->withErrors(json_decode($ex->getMessage()))->withInput(); + // } catch (\Exception $ex) { + // Log::error($ex); + // Alert::danger('An error occured while attempting to add this service option.')->flash(); + // } + // + // return redirect()->route('admin.services.option.new', $service)->withInput(); + // } + // + // public function deleteVariable(Request $request, $service, $option, $variable) + // { + // try { + // $repo = new ServiceRepository\Variable; + // $repo->delete($variable); + // Alert::success('Deleted variable.')->flash(); + // } catch (DisplayException $ex) { + // Alert::danger($ex->getMessage())->flash(); + // } catch (\Exception $ex) { + // Log::error($ex); + // Alert::danger('An error occured while attempting to delete that variable.')->flash(); + // } + // + // return redirect()->route('admin.services.option', [$service, $option]); + // } + // + // public function getConfiguration(Request $request, $serviceId) + // { + // $service = Models\Service::findOrFail($serviceId); + // + // return view('admin.services.config', [ + // 'service' => $service, + // 'contents' => [ + // 'json' => Storage::get('services/' . $service->file . '/main.json'), + // 'index' => Storage::get('services/' . $service->file . '/index.js'), + // ], + // ]); + // } + // + // public function postConfiguration(Request $request, $serviceId) + // { + // try { + // $repo = new ServiceRepository\Service; + // $repo->updateFile($serviceId, $request->only(['file', 'contents'])); + // + // return response('', 204); + // } catch (DisplayException $ex) { + // return response()->json([ + // 'error' => $ex->getMessage(), + // ], 503); + // } catch (\Exception $ex) { + // Log::error($ex); + // + // return response()->json([ + // 'error' => 'An error occured while attempting to save the file.', + // ], 503); + // } + // } } diff --git a/app/Http/Controllers/Daemon/ServiceController.php b/app/Http/Controllers/Daemon/ServiceController.php index e362f0d06..fbbad9b4e 100644 --- a/app/Http/Controllers/Daemon/ServiceController.php +++ b/app/Http/Controllers/Daemon/ServiceController.php @@ -25,35 +25,28 @@ namespace Pterodactyl\Http\Controllers\Daemon; use Storage; -use Pterodactyl\Models; use Illuminate\Http\Request; +use Pterodactyl\Models\Service; +use Pterodactyl\Models\ServiceOption; use Pterodactyl\Http\Controllers\Controller; class ServiceController extends Controller { - /** - * Controller Constructor. - */ - public function __construct() - { - // - } - /** * Returns a listing of all services currently on the system, * as well as the associated files and the file hashes for * caching purposes. * * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response + * @return \Illuminate\Http\JsonResponse */ public function list(Request $request) { $response = []; - foreach (Models\Service::all() as &$service) { - $response[$service->file] = [ - 'main.json' => sha1_file(storage_path('app/services/' . $service->file . '/main.json')), - 'index.js' => sha1_file(storage_path('app/services/' . $service->file . '/index.js')), + foreach (Service::all() as $service) { + $response[$service->folder] = [ + 'main.json' => sha1($this->getConfiguration($service->id)->toJson()), + 'index.js' => sha1_file(storage_path('app/services/' . $service->folder . '/index.js')), ]; } @@ -64,16 +57,45 @@ class ServiceController extends Controller * Returns the contents of the requested file for the given service. * * @param \Illuminate\Http\Request $request - * @param string $service + * @param string $folder * @param string $file - * @return \Illuminate\Http\Response + * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\FileResponse */ - public function pull(Request $request, $service, $file) + public function pull(Request $request, $folder, $file) { - if (! Storage::exists('services/' . $service . '/' . $file)) { - return response()->json(['error' => 'No such file.'], 404); + $service = Service::where('folder', $folder)->firstOrFail(); + + if ($file === 'index.js') { + return response()->file(storage_path('app/services/' . $service->folder . '/index.js')); + } else if ($file === 'main.json') { + return response()->json($this->getConfiguration($service->id)); } - return response()->file(storage_path('app/services/' . $service . '/' . $file)); + return abort(404); + } + + /** + * Returns a `main.json` file based on the configuration + * of each service option. + * + * @param int $id + * @return \Illuminate\Support\Collection + */ + protected function getConfiguration($id) + { + $options = ServiceOption::where('service_id', $id)->get(); + + return $options->mapWithKeys(function ($item) use ($options) { + return [ + $item->tag => array_filter([ + 'symlink' => $options->where('id', $item->config_from)->pluck('tag')->pop(), + 'startup' => json_decode($item->config_startup), + 'stop' => $item->config_stop, + 'configs' => json_decode($item->config_files), + 'log' => json_decode($item->config_logs), + 'query' => 'none', + ]), + ]; + }); } } diff --git a/app/Http/Routes/AdminRoutes.php b/app/Http/Routes/AdminRoutes.php index c25cb91e3..edf8fdc91 100644 --- a/app/Http/Routes/AdminRoutes.php +++ b/app/Http/Routes/AdminRoutes.php @@ -386,128 +386,101 @@ class AdminRoutes ], function () use ($router) { $router->get('/', [ 'as' => 'admin.services', - 'uses' => 'Admin\ServiceController@getIndex', + 'uses' => 'Admin\ServiceController@index', ]); $router->get('/new', [ 'as' => 'admin.services.new', - 'uses' => 'Admin\ServiceController@getNew', + 'uses' => 'Admin\ServiceController@new', ]); $router->post('/new', [ - 'uses' => 'Admin\ServiceController@postNew', + 'uses' => 'Admin\ServiceController@create', ]); - $router->get('/service/{id}', [ - 'as' => 'admin.services.service', - 'uses' => 'Admin\ServiceController@getService', + $router->get('/view/{id}', [ + 'as' => 'admin.services.view', + 'uses' => 'Admin\ServiceController@view', ]); - $router->post('/service/{id}', [ - 'uses' => 'Admin\ServiceController@postService', + $router->post('/view/{id}', [ + 'uses' => 'Admin\ServiceController@edit', ]); - $router->delete('/service/{id}', [ - 'uses' => 'Admin\ServiceController@deleteService', + $router->delete('/view/{id}', [ + 'uses' => 'Admin\ServiceController@delete', ]); - $router->get('/service/{id}/configuration', [ - 'as' => 'admin.services.service.config', - 'uses' => 'Admin\ServiceController@getConfiguration', - ]); - - $router->post('/service/{id}/configuration', [ - 'uses' => 'Admin\ServiceController@postConfiguration', - ]); - - $router->get('/service/{service}/option/new', [ + // --------------------- + // Service Option Routes + // --------------------- + $router->get('/option/new', [ 'as' => 'admin.services.option.new', - 'uses' => 'Admin\ServiceController@newOption', + 'uses' => 'Admin\OptionController@new', ]); - $router->post('/service/{service}/option/new', [ - 'uses' => 'Admin\ServiceController@postNewOption', + $router->get('/option/{id}', [ + 'as' => 'admin.services.option.view', + 'uses' => 'Admin\OptionController@viewConfiguration', ]); - $router->get('/service/{service}/option/{option}', [ - 'as' => 'admin.services.option', - 'uses' => 'Admin\ServiceController@getOption', + $router->get('/option/{id}/variables', [ + 'as' => 'admin.services.option.view.variables', + 'uses' => 'Admin\OptionController@viewVariables', ]); - $router->post('/service/{service}/option/{option}', [ - 'uses' => 'Admin\ServiceController@postOption', + $router->post('/option/{id}', [ + 'uses' => 'Admin\OptionController@editConfiguration', ]); - $router->delete('/service/{service}/option/{id}', [ - 'uses' => 'Admin\ServiceController@deleteOption', - ]); - - $router->get('/service/{service}/option/{option}/variable/new', [ - 'as' => 'admin.services.option.variable.new', - 'uses' => 'Admin\ServiceController@getNewVariable', - ]); - - $router->post('/service/{service}/option/{option}/variable/new', [ - 'uses' => 'Admin\ServiceController@postNewVariable', - ]); - - $router->post('/service/{service}/option/{option}/variable/{variable}', [ - 'as' => 'admin.services.option.variable', - 'uses' => 'Admin\ServiceController@postOptionVariable', - ]); - - $router->get('/service/{service}/option/{option}/variable/{variable}/delete', [ - 'as' => 'admin.services.option.variable.delete', - 'uses' => 'Admin\ServiceController@deleteVariable', - ]); }); // Service Packs $router->group([ - 'prefix' => 'admin/services/packs', + 'prefix' => 'admin/packs', 'middleware' => [ 'auth', 'admin', 'csrf', ], ], function () use ($router) { - $router->get('/new/{option?}', [ - 'as' => 'admin.services.packs.new', - 'uses' => 'Admin\PackController@new', - ]); - $router->post('/new', [ - 'uses' => 'Admin\PackController@create', - ]); - $router->get('/upload/{option?}', [ - 'as' => 'admin.services.packs.uploadForm', - 'uses' => 'Admin\PackController@uploadForm', - ]); - $router->post('/upload', [ - 'uses' => 'Admin\PackController@postUpload', - ]); + // $router->get('/new/{option?}', [ + // 'as' => 'admin.packs.new', + // 'uses' => 'Admin\PackController@new', + // ]); + // $router->post('/new', [ + // 'uses' => 'Admin\PackController@create', + // ]); + // $router->get('/upload/{option?}', [ + // 'as' => 'admin.packs.uploadForm', + // 'uses' => 'Admin\PackController@uploadForm', + // ]); + // $router->post('/upload', [ + // 'uses' => 'Admin\PackController@postUpload', + // ]); $router->get('/', [ - 'as' => 'admin.services.packs', + 'as' => 'admin.packs', 'uses' => 'Admin\PackController@listAll', ]); - $router->get('/for/option/{option}', [ - 'as' => 'admin.services.packs.option', - 'uses' => 'Admin\PackController@listByOption', - ]); - $router->get('/for/service/{service}', [ - 'as' => 'admin.services.packs.service', - 'uses' => 'Admin\PackController@listByService', - ]); - $router->get('/edit/{pack}', [ - 'as' => 'admin.services.packs.edit', - 'uses' => 'Admin\PackController@edit', - ]); - $router->post('/edit/{pack}', [ - 'uses' => 'Admin\PackController@update', - ]); - $router->get('/edit/{pack}/export/{archive?}', [ - 'as' => 'admin.services.packs.export', - 'uses' => 'Admin\PackController@export', - ]); + // $router->get('/for/option/{option}', [ + // 'as' => 'admin.packs.option', + // 'uses' => 'Admin\PackController@listByOption', + // ]); + // $router->get('/for/service/{service}', [ + // 'as' => 'admin.packs.service', + // 'uses' => 'Admin\PackController@listByService', + // ]); + // $router->get('/edit/{pack}', [ + // 'as' => 'admin.packs.edit', + // 'uses' => 'Admin\PackController@edit', + // ]); + // $router->post('/edit/{pack}', [ + // 'uses' => 'Admin\PackController@update', + // ]); + // $router->get('/edit/{pack}/export/{archive?}', [ + // 'as' => 'admin.packs.export', + // 'uses' => 'Admin\PackController@export', + // ]); }); } } diff --git a/app/Repositories/OptionRepository.php b/app/Repositories/OptionRepository.php new file mode 100644 index 000000000..7ded09689 --- /dev/null +++ b/app/Repositories/OptionRepository.php @@ -0,0 +1,80 @@ +. + * + * 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\Repositories; + +use DB; +use Validator; +use Pterodactyl\Models\ServiceOption; +use Pterodactyl\Exceptions\DisplayException; +use Pterodactyl\Exceptions\DisplayValidationException; + +class OptionRepository +{ + /** + * Updates a service option in the database which can then be used + * on nodes. + * + * @param int $id + * @param array $data + * @return \Pterodactyl\Models\ServiceOption + */ + public function update($id, array $data) + { + $option = ServiceOption::findOrFail($id); + + $validator = Validator::make($data, [ + 'name' => 'sometimes|required|string|max:255', + 'description' => 'sometimes|required|string', + 'tag' => 'sometimes|required|string|max:255|unique:service_options,tag,' . $option->id, + 'docker_image' => 'sometimes|required|string|max:255', + 'startup' => 'sometimes|required|string', + 'config_from' => 'sometimes|required|numeric|exists:service_options,id', + ]); + + $validator->sometimes('config_startup', 'required_without:config_from|json', function ($input) use ($option) { + return ! (! $input->config_from && ! is_null($option->config_from)); + }); + + $validator->sometimes('config_stop', 'required_without:config_from|string|max:255', function ($input) use ($option) { + return ! (! $input->config_from && ! is_null($option->config_from)); + }); + + $validator->sometimes('config_logs', 'required_without:config_from|json', function ($input) use ($option) { + return ! (! $input->config_from && ! is_null($option->config_from)); + }); + + $validator->sometimes('config_files', 'required_without:config_from|json', function ($input) use ($option) { + return ! (! $input->config_from && ! is_null($option->config_from)); + }); + + if ($validator->fails()) { + throw new DisplayValidationException($validator->errors()); + } + + $option->fill($data)->save(); + + return $option; + } +} diff --git a/app/Repositories/ServiceRepository/Pack.php b/app/Repositories/PackRepository.php similarity index 99% rename from app/Repositories/ServiceRepository/Pack.php rename to app/Repositories/PackRepository.php index 2132c4954..842bfdac6 100644 --- a/app/Repositories/ServiceRepository/Pack.php +++ b/app/Repositories/PackRepository.php @@ -22,7 +22,7 @@ * SOFTWARE. */ -namespace Pterodactyl\Repositories\ServiceRepository; +namespace Pterodactyl\Repositories; use DB; use Uuid; @@ -33,7 +33,7 @@ use Pterodactyl\Services\UuidService; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Exceptions\DisplayValidationException; -class Pack +class PackRepository { public function __construct() { diff --git a/app/Repositories/ServiceRepository.php b/app/Repositories/ServiceRepository.php new file mode 100644 index 000000000..ed9dbf03d --- /dev/null +++ b/app/Repositories/ServiceRepository.php @@ -0,0 +1,178 @@ +. + * + * 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\Repositories; + +use DB; +use Uuid; +use Storage; +use Validator; +use Pterodactyl\Models\Service; +use Pterodactyl\Models\ServiceVariable; +use Pterodactyl\Exceptions\DisplayException; +use Pterodactyl\Exceptions\DisplayValidationException; + +class ServiceRepository +{ + /** + * Creates a new service on the system. + * + * @param array $data + * @return \Pterodactyl\Models\Service + */ + public function create(array $data) + { + $validator = Validator::make($data, [ + 'name' => 'required|string|min:1|max:255', + 'description' => 'required|nullable|string', + 'folder' => 'required|unique:services,folder|regex:/^[\w.-]{1,50}$/', + 'startup' => 'required|nullable|string', + ]); + + if ($validator->fails()) { + throw new DisplayValidationException($validator->errors()); + } + + $service = DB::transaction(function () use ($data) { + $service = Service::create([ + 'author' => config('pterodactyl.service.author'), + 'name' => $data['name'], + 'description' => (isset($data['description'])) ? $data['description'] : null, + 'folder' => $data['folder'], + 'startup' => (isset($data['startup'])) ? $data['startup'] : null, + ]); + + // It is possible for an event to return false or throw an exception + // which won't necessarily be detected by this transaction. + // + // This check ensures the model was actually saved. + if (! $service->exists) { + throw new \Exception('Service model was created however the response appears to be invalid. Did an event fire wrongly?'); + } + + Storage::copy('services/.templates/index.js', 'services/' . $service->folder . '/index.js'); + + return $service; + }); + + return $service; + } + + /** + * Updates a service. + * + * @param int $id + * @param array $data + * @return \Pterodactyl\Models\Service + */ + public function update($id, array $data) + { + $service = Service::findOrFail($id); + + $validator = Validator::make($data, [ + 'name' => 'sometimes|required|string|min:1|max:255', + 'description' => 'sometimes|required|nullable|string', + 'folder' => 'sometimes|required|regex:/^[\w.-]{1,50}$/', + 'startup' => 'sometimes|required|nullable|string', + ]); + + if ($validator->fails()) { + throw new DisplayValidationException($validator->errors()); + } + + return DB::transaction(function () use ($data, $service) { + $moveFiles = (isset($data['folder']) && $data['folder'] !== $service->folder); + $oldFolder = $service->folder; + + $service->fill($data); + $service->save(); + + if ($moveFiles) { + Storage::move(sprintf('services/%s/index.js', $oldFolder), sprintf('services/%s/index.js', $service->folder)); + } + + return $service; + }); + } + + /** + * Deletes a service and associated files and options. + * + * @param int $id + * @return void + */ + public function delete($id) + { + $service = Service::withCount('servers', 'options')->findOrFail($id); + + if ($service->servers_count > 0) { + throw new DisplayException('You cannot delete a service that has servers associated with it.'); + } + + DB::transaction(function () use ($service) { + ServiceVariable::whereIn('option_id', $service->options->pluck('id')->all())->delete(); + + $service->options->each(function ($item) { + $item->delete(); + }); + + $service->delete(); + Storage::deleteDirectory('services/' . $service->folder); + }); + } + + /** + * Updates a service file on the system. + * + * @param int $id + * @param array $data + * @return void + * + * @deprecated + */ + // public function updateFile($id, array $data) + // { + // $service = Service::findOrFail($id); + // + // $validator = Validator::make($data, [ + // 'file' => 'required|in:index', + // 'contents' => 'required|string', + // ]); + // + // if ($validator->fails()) { + // throw new DisplayValidationException($validator->errors()); + // } + // + // $filepath = 'services/' . $service->folder . '/' . $filename; + // $backup = 'services/.bak/' . str_random(12) . '.bak'; + // + // try { + // Storage::move($filepath, $backup); + // Storage::put($filepath, $data['contents']); + // } catch (\Exception $ex) { + // Storage::move($backup, $filepath); + // throw $ex; + // } + // } +} diff --git a/app/Repositories/ServiceRepository/Option.php b/app/Repositories/ServiceRepository/Option.php deleted file mode 100644 index e0b82d347..000000000 --- a/app/Repositories/ServiceRepository/Option.php +++ /dev/null @@ -1,124 +0,0 @@ -. - * - * 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\Repositories\ServiceRepository; - -use DB; -use Validator; -use Pterodactyl\Models; -use Pterodactyl\Exceptions\DisplayException; -use Pterodactyl\Exceptions\DisplayValidationException; - -class Option -{ - public function __construct() - { - // - } - - public function create($service, array $data) - { - $service = Models\Service::findOrFail($service); - - $validator = Validator::make($data, [ - 'name' => 'required|string|max:255', - 'description' => 'required|string|min:1', - 'tag' => 'required|string|max:255', - 'executable' => 'sometimes|string|max:255', - 'docker_image' => 'required|string|max:255', - 'startup' => 'sometimes|string', - ]); - - if ($validator->fails()) { - throw new DisplayValidationException($validator->errors()); - } - - if (isset($data['executable']) && empty($data['executable'])) { - $data['executable'] = null; - } - - if (isset($data['startup']) && empty($data['startup'])) { - $data['startup'] = null; - } - - $option = new Models\ServiceOption; - $option->service_id = $service->id; - $option->fill($data); - $option->save(); - - return $option->id; - } - - public function delete($id) - { - $option = Models\ServiceOption::findOrFail($id); - $servers = Models\Server::where('option', $option->id)->get(); - - if (count($servers) !== 0) { - throw new DisplayException('You cannot delete an option that has servers attached to it currently.'); - } - - DB::beginTransaction(); - - try { - Models\ServiceVariable::where('option_id', $option->id)->delete(); - $option->delete(); - - DB::commit(); - } catch (\Exception $ex) { - DB::rollBack(); - throw $ex; - } - } - - public function update($id, array $data) - { - $option = Models\ServiceOption::findOrFail($id); - - $validator = Validator::make($data, [ - 'name' => 'sometimes|required|string|max:255', - 'description' => 'sometimes|required|string|min:1', - 'tag' => 'sometimes|required|string|max:255', - 'executable' => 'sometimes|string|max:255', - 'docker_image' => 'sometimes|required|string|max:255', - 'startup' => 'sometimes|string', - ]); - - if ($validator->fails()) { - throw new DisplayValidationException($validator->errors()); - } - - if (isset($data['executable']) && empty($data['executable'])) { - $data['executable'] = null; - } - - if (isset($data['startup']) && empty($data['startup'])) { - $data['startup'] = null; - } - - $option->fill($data); - - return $option->save(); - } -} diff --git a/app/Repositories/ServiceRepository/Service.php b/app/Repositories/ServiceRepository/Service.php deleted file mode 100644 index 206479a0a..000000000 --- a/app/Repositories/ServiceRepository/Service.php +++ /dev/null @@ -1,144 +0,0 @@ -. - * - * 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\Repositories\ServiceRepository; - -use DB; -use Uuid; -use Storage; -use Validator; -use Pterodactyl\Models; -use Pterodactyl\Exceptions\DisplayException; -use Pterodactyl\Exceptions\DisplayValidationException; - -class Service -{ - public function __construct() - { - // - } - - public function create(array $data) - { - $validator = Validator::make($data, [ - 'name' => 'required|string|min:1|max:255', - 'description' => 'required|string', - 'file' => 'required|unique:services,file|regex:/^[\w.-]{1,50}$/', - 'executable' => 'max:255|regex:/^(.*)$/', - 'startup' => 'string', - ]); - - if ($validator->fails()) { - throw new DisplayValidationException($validator->errors()); - } - - DB::beginTransaction(); - try { - $service = new Models\Service; - $service->author = env('SERVICE_AUTHOR', (string) Uuid::generate(4)); - $service->fill($data); - $service->save(); - - Storage::put('services/' . $service->file . '/main.json', '{}'); - Storage::copy('services/.templates/index.js', 'services/' . $service->file . '/index.js'); - DB::commit(); - } catch (\Exception $ex) { - DB::rollBack(); - throw $ex; - } - - return $service; - } - - public function update($id, array $data) - { - $service = Models\Service::findOrFail($id); - - $validator = Validator::make($data, [ - 'name' => 'sometimes|required|string|min:1|max:255', - 'description' => 'sometimes|required|string', - 'file' => 'sometimes|required|regex:/^[\w.-]{1,50}$/', - 'executable' => 'sometimes|max:255|regex:/^(.*)$/', - 'startup' => 'sometimes|string', - ]); - - if ($validator->fails()) { - throw new DisplayValidationException($validator->errors()); - } - - $service->fill($data); - - return $service->save(); - } - - public function delete($id) - { - $service = Models\Service::findOrFail($id); - $servers = Models\Server::where('service', $service->id)->get(); - $options = Models\ServiceOption::select('id')->where('service_id', $service->id); - - if (count($servers) !== 0) { - throw new DisplayException('You cannot delete a service that has servers associated with it.'); - } - - DB::beginTransaction(); - try { - Models\ServiceVariable::whereIn('option_id', $options->get()->toArray())->delete(); - $options->delete(); - $service->delete(); - - Storage::deleteDirectory('services/' . $service->file); - DB::commit(); - } catch (\Exception $ex) { - DB::rollBack(); - throw $ex; - } - } - - public function updateFile($id, array $data) - { - $service = Models\Service::findOrFail($id); - - $validator = Validator::make($data, [ - 'file' => 'required|in:index,main', - 'contents' => 'required|string', - ]); - - if ($validator->fails()) { - throw new DisplayValidationException($validator->errors()); - } - - $filename = ($data['file'] === 'main') ? 'main.json' : 'index.js'; - $filepath = 'services/' . $service->file . '/' . $filename; - $backup = 'services/.bak/' . str_random(12) . '.bak'; - - try { - Storage::move($filepath, $backup); - Storage::put($filepath, $data['contents']); - } catch (\Exception $ex) { - Storage::move($backup, $filepath); - throw $ex; - } - } -} diff --git a/app/Repositories/ServiceRepository/Variable.php b/app/Repositories/VariableRepository.php similarity index 98% rename from app/Repositories/ServiceRepository/Variable.php rename to app/Repositories/VariableRepository.php index f9836ff0f..744cb8bc7 100644 --- a/app/Repositories/ServiceRepository/Variable.php +++ b/app/Repositories/VariableRepository.php @@ -22,7 +22,7 @@ * SOFTWARE. */ -namespace Pterodactyl\Repositories\ServiceRepository; +namespace Pterodactyl\Repositories; use DB; use Validator; @@ -30,7 +30,7 @@ use Pterodactyl\Models; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Exceptions\DisplayValidationException; -class Variable +class VariableRepository { public function __construct() { diff --git a/config/pterodactyl.php b/config/pterodactyl.php new file mode 100644 index 000000000..ec0e430cd --- /dev/null +++ b/config/pterodactyl.php @@ -0,0 +1,18 @@ + [ + 'author' => env('SERVICE_AUTHOR'), + ], + +]; diff --git a/database/migrations/2017_03_05_212803_DeleteServiceExecutableOption.php b/database/migrations/2017_03_05_212803_DeleteServiceExecutableOption.php new file mode 100644 index 000000000..374219539 --- /dev/null +++ b/database/migrations/2017_03_05_212803_DeleteServiceExecutableOption.php @@ -0,0 +1,38 @@ +dropColumn('executable'); + $table->renameColumn('file', 'folder'); + $table->text('description')->nullable()->change(); + $table->text('startup')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('services', function (Blueprint $table) { + $table->string('executable')->after('folder'); + $table->renameColumn('folder', 'file'); + $table->text('description')->nullable(false)->change(); + $table->text('startup')->nullable(false)->change(); + }); + } +} diff --git a/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php b/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php new file mode 100644 index 000000000..535c3b477 --- /dev/null +++ b/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php @@ -0,0 +1,52 @@ +dropColumn('executable'); + + $table->unsignedInteger('config_from')->nullable()->after('docker_image'); + $table->string('config_stop')->nullable()->after('docker_image'); + $table->text('config_logs')->nullable()->after('docker_image'); + $table->text('config_startup')->nullable()->after('docker_image'); + $table->text('config_files')->nullable()->after('docker_image'); + + $table->foreign('config_from')->references('id')->on('service_options'); + }); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + DB::transaction(function() { + Schema::table('service_options', function (Blueprint $table) { + $table->dropForeign('config_from'); + + $table->dropColumn('config_from'); + $table->dropColumn('config_stop'); + $table->dropColumn('config_logs'); + $table->dropColumn('config_startup'); + $table->dropColumn('config_files'); + + $table->string('executable')->after('docker_image')->nullable(); + }); + }); + } +} diff --git a/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php b/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php new file mode 100644 index 000000000..afa9f6ee1 --- /dev/null +++ b/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php @@ -0,0 +1,223 @@ +. + * + * 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. + */ +use Pterodactyl\Models\Service; +use Pterodactyl\Models\ServiceOption; +use Illuminate\Support\Facades\Schema; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Database\Migrations\Migration; + +class MigrateToNewServiceSystem extends Migration +{ + protected $services; + + /** + * Run the migrations. + * + * @return void + */ + public function up() + { + $this->services = Service::where('author', 'ptrdctyl-v040-11e6-8b77-86f30ca893d3')->get(); + + $this->minecraft(); + $this->srcds(); + $this->terraria(); + $this->voice(); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // Not doing reversals right now... + } + + public function minecraft() + { + $service = $this->services->where('folder', 'minecraft')->first(); + if (! $service) { + return; + } + + // Set New Default Startup + $service->startup = 'java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}'; + + DB::transaction(function () use ($service) { + $options = ServiceOption::where('service_id', $service->id)->get(); + $options->each(function ($item) use ($options) { + switch($item->tag) { + case 'vanilla': + $item->config_startup = '{"done": ")! For help, type ", "userInteraction": [ "Go to eula.txt for more info."]}'; + $item->config_files = '{"server.properties":{"parser": "properties", "find":{"server-ip": "0.0.0.0", "enable-query": "true", "server-port": "{{server.build.default.port}}", "query.port": "{{server.build.default.port}}"}}}'; + $item->config_logs = '{"custom": false, "location": "logs/latest.log"}'; + $item->config_stop = 'stop'; + break; + case 'spigot': + $item->startup = null; + $item->config_from = $options->where('tag', 'vanilla')->pluck('id')->pop(); + $item->config_files = '{"spigot.yml":{"parser": "yaml", "find":{"settings.restart-on-crash": "false"}}}'; + break; + case 'bungeecord': + $item->config_startup = '{"done": "Listening on ", "userInteraction": [ "Listening on /0.0.0.0:25577"]}'; + $item->config_files = '{"config.yml":{"parser": "yaml", "find":{"listeners[0].query_enabled": true, "listeners[0].query_port": "{{server.build.default.port}}", "listeners[0].host": "0.0.0.0:{{server.build.default.port}}", "servers.*.address":{"127.0.0.1": "{{config.docker.interface}}", "localhost": "{{config.docker.interface}}"}}}}'; + $item->config_logs = '{"custom": false, "location": "proxy.log.0"}'; + $item->config_stop = 'end'; + break; + case 'sponge': + $item->startup = null; + $item->config_from = $options->where('tag', 'vanilla')->pluck('id')->pop(); + $item->config_startup = '{"userInteraction": [ "You need to agree to the EULA"]}'; + break; + default: + break; + } + + $item->save(); + }); + + $service->save(); + }); + } + + public function srcds() + { + $service = $this->services->where('folder', 'srcds')->first(); + if (! $service) { + return; + } + + $service->startup = './srcds_run -game {{SRCDS_GAME}} -console -port {{SERVER_PORT}} +ip 0.0.0.0 -strictportbind -norestart'; + + DB::transaction(function () use ($service) { + $options = ServiceOption::where('service_id', $service->id)->get(); + $options->each(function ($item) use ($options) { + if ($item->tag === 'srcds' && $item->name === 'Insurgency') { + $item->tag = 'insurgency'; + } else if ($item->tag === 'srcds' && $item->name === 'Team Fortress 2') { + $item->tag = 'tf2'; + } else if ($item->tag === 'srcds' && $item->name === 'Custom Source Engine Game') { + $item->tag = 'source'; + } + + switch($item->tag) { + case 'source': + $item->config_startup = '{"done": "Assigned anonymous gameserver Steam ID", "userInteraction": []}'; + $item->config_files = '{}'; + $item->config_logs = '{"custom": true, "location": "logs/latest.log"}'; + $item->config_stop = 'quit'; + break; + case 'insurgency': + case 'tf2': + $item->startup = './srcds_run -game {{SRCDS_GAME}} -console -port {{SERVER_PORT}} +map {{SRCDS_MAP}} +ip 0.0.0.0 -strictportbind -norestart'; + $item->config_from = $options->where('name', 'Custom Source Engine Game')->pluck('id')->pop(); + break; + case 'ark': + $item->startup = './ShooterGame/Binaries/Linux/ShooterGameServer TheIsland?listen?ServerPassword={{ARK_PASSWORD}}?ServerAdminPassword={{ARK_ADMIN_PASSWORD}}?Port={{SERVER_PORT}}?MaxPlayers={{SERVER_MAX_PLAYERS}}'; + $item->config_from = $options->where('name', 'Custom Source Engine Game')->pluck('id')->pop(); + $item->config_startup = '{"done": "Setting breakpad minidump AppID"}'; + $item->config_stop = '^C'; + break; + default: + break; + } + + $item->save(); + }); + + $service->save(); + }); + } + + public function terraria() + { + $service = $this->services->where('folder', 'terraria')->first(); + if (! $service) { + return; + } + + $service->startup = 'mono TerrariaServer.exe -port {{SERVER_PORT}} -autocreate 2 -worldname World'; + + DB::transaction(function () use ($service) { + $options = ServiceOption::where('service_id', $service->id)->get(); + $options->each(function ($item) use ($options) { + switch($item->tag) { + case 'tshock': + $item->startup = null; + $item->config_startup = '{"done": "Type \'help\' for a list of commands", "userInteraction": []}'; + $item->config_files = '{"tshock/config.json":{"parser": "json", "find":{"ServerPort": "{{server.build.default.port}}", "MaxSlots": "{{server.build.env.MAX_SLOTS}}"}}}'; + $item->config_logs = '{"custom": false, "location": "ServerLog.txt"}'; + $item->config_stop = 'exit'; + break; + default: + break; + } + + $item->save(); + }); + + $service->save(); + }); + } + + public function voice() + { + $service = $this->services->where('folder', 'voice')->first(); + if (! $service) { + return; + } + + $service->startup = null; + + DB::transaction(function () use ($service) { + $options = ServiceOption::where('service_id', $service->id)->get(); + $options->each(function ($item) use ($options) { + switch($item->tag) { + case 'mumble': + $item->startup = './murmur.x86 -fg'; + $item->config_startup = '{"done": "Server listening on", "userInteraction": [ "Generating new server certificate"]}'; + $item->config_files = '{"murmur.ini":{"parser": "ini", "find":{"logfile": "murmur.log", "port": "{{server.build.default.port}}", "host": "0.0.0.0", "users": "{{server.build.env.MAX_USERS}}"}}}'; + $item->config_logs = '{"custom": true, "location": "logs/murmur.log"}'; + $item->config_stop = '^C'; + break; + case 'ts3': + $item->startup = './ts3server_minimal_runscript.sh default_voice_port={{SERVER_PORT}} query_port={{SERVER_PORT}}'; + $item->config_startup = '{"done": "listening on 0.0.0.0:", "userInteraction": []}'; + $item->config_files = '{"ts3server.ini":{"parser": "ini", "find":{"default_voice_port": "{{server.build.default.port}}", "voice_ip": "0.0.0.0", "query_port": "{{server.build.default.port}}", "query_ip": "0.0.0.0"}}}'; + $item->config_logs = '{"custom": true, "location": "logs/ts3.log"}'; + $item->config_stop = '^C'; + break; + default: + break; + } + + $item->save(); + }); + + $service->save(); + }); + } +} diff --git a/resources/themes/pterodactyl/admin/services/index.blade.php b/resources/themes/pterodactyl/admin/services/index.blade.php new file mode 100644 index 000000000..677af8b77 --- /dev/null +++ b/resources/themes/pterodactyl/admin/services/index.blade.php @@ -0,0 +1,67 @@ +{{-- 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') + Services +@endsection + +@section('content-header') +

ServicesAll services currently available on this system.

+ +@endsection + +@section('content') +
+
+
+
+

Configured Services

+
+
+ + + + + + + + + @foreach($services as $service) + + + + + + + + @endforeach +
NameDescriptionOptionsPacksServers
{{ $service->name }}{{ $service->description }}{{ $service->options_count }}{{ $service->packs_count }}{{ $service->servers_count }}
+
+ +
+
+
+@endsection diff --git a/resources/themes/pterodactyl/admin/services/new.blade.php b/resources/themes/pterodactyl/admin/services/new.blade.php new file mode 100644 index 000000000..5d215e66f --- /dev/null +++ b/resources/themes/pterodactyl/admin/services/new.blade.php @@ -0,0 +1,86 @@ +{{-- 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') + New Service +@endsection + +@section('content-header') +

New ServiceConfigure a new service to deploy to all nodes.

+ +@endsection + +@section('content') +
+
+
+
+
+

New Service

+
+
+
+ +
+ +

This should be a descriptive category name that emcompasses all of the options within the service.

+
+
+
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +

Services are downloaded by the daemon and stored in a folder using this name. The storage location is /srv/daemon/services/{NAME} by default.

+
+
+
+ +
+ +

The default start command to use when running options under this service. This command can be modified per-option and should include the executable to be called in the container.

+
+
+
+ +
+
+
+
+@endsection diff --git a/resources/themes/pterodactyl/admin/services/options/view.blade.php b/resources/themes/pterodactyl/admin/services/options/view.blade.php new file mode 100644 index 000000000..01965d4ee --- /dev/null +++ b/resources/themes/pterodactyl/admin/services/options/view.blade.php @@ -0,0 +1,162 @@ +{{-- 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') + Services → Option: {{ $option->name }} +@endsection + +@section('content-header') +

{{ $option->name }}{{ str_limit($option->description, 50) }}

+ +@endsection + +@section('content') +
+
+ +
+
+
+
+
+
+
+

Configuration

+
+
+
+
+
+ + +

A simple, human-readable name to use as an identifier for this service.

+
+
+ + +

A description of this service that will be displayed throughout the panel as needed.

+
+
+
+
+ + +

This should be a unique identifer for this service option that is not used for any other service options.

+
+
+ + +

The default docker image that should be used for new servers under this service option. This can be left blank to use the parent service's defined image, and can also be changed per-server.

+
+
+ + +

The default statup command that should be used for new servers under this service option. This can be left blank to use the parent service's startup, and can also be changed per-server.

+
+
+
+
+
+
+
+
+
+

Process Management

+
+
+
+
+
+

The following configuration options should not be edited unless you understand how this system works. If wrongly modified it is possible for the daemon to break.

+

All fields are required unless you select a seperate option from the 'Copy Settings From' dropdown, in which case fields may be left blank to use the values from that option.

+
+
+
+
+ + +

If you would like to default to settings from another option select the option from the menu above.

+
+
+ + +

The command that should be sent to server processes to stop them gracefully. If you need to send a SIGINT you should enter ^C here.

+
+
+ + +

This should be a JSON representation of where log files are stored, and wether or not the daemon should be creating custom logs.

+
+
+
+
+ + +

This should be a JSON representation of configuration files to modify and what parts should be changed.

+
+
+ + +

This should be a JSON representation of what values the daemon should be looking for when booting a server to determine completion.

+
+
+
+ +
+
+
+ +@endsection + +@section('footer-scripts') + @parent + +@endsection diff --git a/resources/themes/pterodactyl/admin/services/view.blade.php b/resources/themes/pterodactyl/admin/services/view.blade.php new file mode 100644 index 000000000..42d151539 --- /dev/null +++ b/resources/themes/pterodactyl/admin/services/view.blade.php @@ -0,0 +1,113 @@ +{{-- 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') + Services: {{ $service->name }} +@endsection + +@section('content-header') +

{{ $service->name }}{{ str_limit($service->description, 50) }}

+ +@endsection + +@section('content') +
+
+
+
+
+
+ +
+ +

This should be a descriptive category name that emcompasses all of the options within the service.

+
+
+
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +

Services are downloaded by the daemon and stored in a folder using this name. The storage location is /srv/daemon/services/{NAME} by default.

+
+
+
+ +
+ +

The default start command to use when running options under this service. This command can be modified per-option and should include the executable to be called in the container.

+
+
+
+ +
+
+
+
+
+
+
+
+

Configured Options

+
+
+ + + + + + + + @foreach($service->options as $option) + + + + + + + @endforeach +
NameDescriptionTagServers
{{ $option->name }}{!! $option->description !!}{{ $option->tag }}{{ $option->servers->count() }}
+
+ +
+
+
+@endsection diff --git a/resources/themes/pterodactyl/layouts/admin.blade.php b/resources/themes/pterodactyl/layouts/admin.blade.php index 96b8c0e98..a6c735c60 100644 --- a/resources/themes/pterodactyl/layouts/admin.blade.php +++ b/resources/themes/pterodactyl/layouts/admin.blade.php @@ -106,6 +106,17 @@ Users +
  • SERVICE MANAGEMENT
  • +
  • + + Services + +
  • +
  • + + Packs + +
  • From 871b7354649f40acba7205fc8315c9a3e6fb0f3f Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 11 Mar 2017 14:44:18 -0500 Subject: [PATCH 02/22] Ignore VSCode files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a30c114f5..9f02de4ca 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.DS_Store* .env .vagrant/* +.vscode/* composer.lock From bccbb309b2da37aa222db7b2308edd0d1a1f492f Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 11 Mar 2017 15:02:04 -0500 Subject: [PATCH 03/22] Fix broken descriptors --- app/Models/User.php | 2 +- app/Repositories/DatabaseRepository.php | 2 +- app/Services/DeploymentService.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Models/User.php b/app/Models/User.php index b660e304c..cb78ad4a8 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -66,7 +66,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac /** * A list of mass-assignable variables. * - * @var [type] + * @var array */ protected $fillable = ['username', 'email', 'name_first', 'name_last', 'password', 'language', 'use_totp', 'totp_secret', 'gravatar', 'root_admin']; diff --git a/app/Repositories/DatabaseRepository.php b/app/Repositories/DatabaseRepository.php index b0d85b60f..5397fc5c7 100644 --- a/app/Repositories/DatabaseRepository.php +++ b/app/Repositories/DatabaseRepository.php @@ -199,7 +199,7 @@ class DatabaseRepository * Deletes a database server from the system if it is empty. * * @param int $server The ID of the Database Server. - * @return + * @return bool */ public function delete($server) { diff --git a/app/Services/DeploymentService.php b/app/Services/DeploymentService.php index af1ede940..d4d28e9a9 100644 --- a/app/Services/DeploymentService.php +++ b/app/Services/DeploymentService.php @@ -79,7 +79,7 @@ class DeploymentService * @param int $memory * @param int $disk * @param int $location - * @return \Pterodactyl\Models\Node; + * @return \Pterodactyl\Models\Node * * @throws \Pterodactyl\Exceptions\DisplayException */ @@ -103,7 +103,7 @@ class DeploymentService /** * Returns a random allocation for a node. * @param int $node - * @return \Models\Pterodactyl\Allocation; + * @return \Models\Pterodactyl\Allocation */ public static function randomAllocation($node) { From d7682bb7c91ff73b8d42b54ae12bd40bd02d8442 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 12 Mar 2017 00:00:06 -0500 Subject: [PATCH 04/22] Complete new service, option, and variable management interface in Admin CP --- .../Controllers/Admin/OptionController.php | 126 +++- .../Controllers/Admin/ServiceController.php | 41 +- app/Http/Routes/AdminRoutes.php | 11 +- app/Models/Service.php | 2 +- app/Repositories/OptionRepository.php | 84 +++ app/Repositories/ServiceRepository.php | 21 +- app/Repositories/VariableRepository.php | 140 ++-- config/pterodactyl.php | 1 + ..._ChangeServiceVariablesValidationRules.php | 47 ++ .../admin/services/options/new.blade.php | 170 +++++ .../services/options/variables.blade.php | 114 +++ .../admin/services/options/view.blade.php | 14 +- .../pterodactyl/admin/services/view.blade.php | 12 + resources/views/admin/servers/view.blade.php | 687 ------------------ .../admin/services/options/variable.blade.php | 122 ---- storage/app/services/index.js | 31 + 16 files changed, 698 insertions(+), 925 deletions(-) create mode 100644 database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php create mode 100644 resources/themes/pterodactyl/admin/services/options/new.blade.php create mode 100644 resources/themes/pterodactyl/admin/services/options/variables.blade.php delete mode 100644 resources/views/admin/servers/view.blade.php delete mode 100644 resources/views/admin/services/options/variable.blade.php create mode 100644 storage/app/services/index.js diff --git a/app/Http/Controllers/Admin/OptionController.php b/app/Http/Controllers/Admin/OptionController.php index 3dc9b24ff..b60c0df7b 100644 --- a/app/Http/Controllers/Admin/OptionController.php +++ b/app/Http/Controllers/Admin/OptionController.php @@ -27,15 +27,63 @@ namespace Pterodactyl\Http\Controllers\Admin; use Log; use Alert; use Storage; -use Pterodactyl\Models; +use Javascript; use Illuminate\Http\Request; +use Pterodactyl\Models\Service; +use Pterodactyl\Models\ServiceOption; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Repositories\OptionRepository; +use Pterodactyl\Repositories\VariableRepository; use Pterodactyl\Exceptions\DisplayValidationException; class OptionController extends Controller { + /** + * Handles request to view page for adding new option. + * + * @param Request $request + * @return \Illuminate\View\View + */ + public function new(Request $request) + { + $services = Service::with('options')->get(); + Javascript::put(['services' => $services->keyBy('id')]); + + return view('admin.services.options.new', ['services' => $services]); + } + + /** + * Handles POST request to create a new option. + + * @param Request $request + * @return \Illuminate\Response\RedirectResponse + */ + public function create(Request $request) + { + $repo = new OptionRepository; + + try { + $option = $repo->create($request->intersect([ + 'service_id', 'name', 'description', 'tag', + 'docker_image', 'startup', 'config_from', 'config_startup', + 'config_logs', 'config_files', 'config_stop' + ])); + Alert::success('Successfully created new service option.')->flash(); + + return redirect()->route('admin.services.option.view', $option->id); + } catch (DisplayValidationException $ex) { + return redirect()->route('admin.services.option.new')->withErrors(json_decode($ex->getMessage()))->withInput(); + } catch (DisplayException $ex) { + Alert::danger($ex->getMessage())->flash(); + } catch (\Exception $ex) { + Log::error($ex); + Alert::danger('An unhandled exception occurred while attempting to create this service. This error has been logged.')->flash(); + } + + return redirect()->route('admin.services.option.new')->withInput(); + } + /** * Display option overview page. * @@ -45,27 +93,89 @@ class OptionController extends Controller */ public function viewConfiguration(Request $request, $id) { - return view('admin.services.options.view', ['option' => Models\ServiceOption::findOrFail($id)]); + return view('admin.services.options.view', ['option' => ServiceOption::findOrFail($id)]); } + /** + * Display variable overview page for a service option. + * + * @param Request $request + * @param int $id + * @return \Illuminate\View\View + */ + public function viewVariables(Request $request, $id) + { + return view('admin.services.options.variables', ['option' => ServiceOption::with('variables')->findOrFail($id)]); + } + + /** + * Handles POST when editing a configration for a service option. + * + * @param Request $request + * @return \Illuminate\Response\RedirectResponse + */ public function editConfiguration(Request $request, $id) { $repo = new OptionRepository; try { - $repo->update($id, $request->intersect([ - 'name', 'description', 'tag', 'docker_image', 'startup', - 'config_from', 'config_stop', 'config_logs', 'config_files', 'config_startup', - ])); + if ($request->input('action') !== 'delete') { + $repo->update($id, $request->intersect([ + 'name', 'description', 'tag', 'docker_image', 'startup', + 'config_from', 'config_stop', 'config_logs', 'config_files', 'config_startup', + ])); + Alert::success('Service option configuration has been successfully updated.')->flash(); + } else { + $option = ServiceOption::with('service')->where('id', $id)->first(); + $repo->delete($id); + Alert::success('Successfully deleted service option from the system.')->flash(); - Alert::success('Service option configuration has been successfully updated.')->flash(); + return redirect()->route('admin.services.view', $option->service_id); + } } catch (DisplayValidationException $ex) { return redirect()->route('admin.services.option.view', $id)->withErrors(json_decode($ex->getMessage())); + } catch (DisplayException $ex) { + Alert::danger($ex->getMessage())->flash(); } catch (\Exception $ex) { Log::error($ex); - Alert::danger('An unhandled exception occurred while attempting to update this service option. This error has been logged.')->flash(); + Alert::danger('An unhandled exception occurred while attempting to perform that action. This error has been logged.')->flash(); } return redirect()->route('admin.services.option.view', $id); } + + /** + * Handles POST when editing a configration for a service option. + * + * @param Request $request + * @param int $option + * @param int $variable + * @return \Illuminate\Response\RedirectResponse + */ + public function editVariable(Request $request, $option, $variable) + { + $repo = new VariableRepository; + + try { + if ($request->input('action') !== 'delete') { + $variable = $repo->update($variable, $request->only([ + 'name', 'description', 'env_variable', + 'default_value', 'options', 'rules', + ])); + Alert::success("The service variable '{$variable->name}' has been updated.")->flash(); + } else { + $repo->delete($variable); + Alert::success("That service variable has been deleted.")->flash(); + } + } catch (DisplayValidationException $ex) { + return redirect()->route('admin.services.option.variables', $option)->withErrors(json_decode($ex->getMessage())); + } catch (DisplayException $ex) { + Alert::danger($ex->getMessage())->flash(); + } catch (\Exception $ex) { + Log::error($ex); + Alert::danger('An unhandled exception was encountered while attempting to process that request. This error has been logged.')->flash(); + } + + return redirect()->route('admin.services.option.variables', $option); + } } diff --git a/app/Http/Controllers/Admin/ServiceController.php b/app/Http/Controllers/Admin/ServiceController.php index 81ca528b8..60450ab28 100644 --- a/app/Http/Controllers/Admin/ServiceController.php +++ b/app/Http/Controllers/Admin/ServiceController.php @@ -103,32 +103,6 @@ class ServiceController extends Controller return redirect()->route('admin.services.new')->withInput(); } - /** - * Delete a service from the system. - * - * @param Request $request - * @param int $id - * @return \Illuminate\Response\RedirectResponse - */ - public function delete(Request $request, $id) - { - $repo = new ServiceRepository; - - try { - $repo->delete($id); - Alert::success('Successfully deleted service.')->flash(); - - return redirect()->route('admin.services'); - } catch (DisplayException $ex) { - Alert::danger($ex->getMessage())->flash(); - } catch (\Exception $ex) { - Log::error($ex); - Alert::danger('An error was encountered while attempting to delete that service. This error has been logged')->flash(); - } - - return redirect()->route('admin.services.view', $id); - } - /** * Edits configuration for a specific service. * @@ -141,10 +115,17 @@ class ServiceController extends Controller $repo = new ServiceRepository; try { - $repo->update($id, $request->intersect([ - 'name', 'description', 'folder', 'startup', - ])); - Alert::success('Service has been updated successfully.')->flash(); + if ($request->input('action') !== 'delete') { + $repo->update($id, $request->intersect([ + 'name', 'description', 'folder', 'startup', + ])); + Alert::success('Service has been updated successfully.')->flash(); + } else { + $repo->delete($id); + Alert::success('Successfully deleted service from the system.')->flash(); + + return redirect()->route('admin.services'); + } } catch (DisplayValidationException $ex) { return redirect()->route('admin.services.view', $id)->withErrors(json_decode($ex->getMessage()))->withInput(); } catch (DisplayException $ex) { diff --git a/app/Http/Routes/AdminRoutes.php b/app/Http/Routes/AdminRoutes.php index edf8fdc91..5cec94b55 100644 --- a/app/Http/Routes/AdminRoutes.php +++ b/app/Http/Routes/AdminRoutes.php @@ -419,20 +419,25 @@ class AdminRoutes 'uses' => 'Admin\OptionController@new', ]); + $router->post('/option/new', 'Admin\OptionController@create'); + $router->get('/option/{id}', [ 'as' => 'admin.services.option.view', 'uses' => 'Admin\OptionController@viewConfiguration', ]); $router->get('/option/{id}/variables', [ - 'as' => 'admin.services.option.view.variables', + 'as' => 'admin.services.option.variables', 'uses' => 'Admin\OptionController@viewVariables', ]); - $router->post('/option/{id}', [ - 'uses' => 'Admin\OptionController@editConfiguration', + $router->post('/option/{id}/variables/{variable}', [ + 'as' => 'admin.services.option.variables.edit', + 'uses' => 'Admin\OptionController@editVariable', ]); + $router->post('/option/{id}', 'Admin\OptionController@editConfiguration'); + }); // Service Packs diff --git a/app/Models/Service.php b/app/Models/Service.php index 83cdb70b0..50679633e 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -41,7 +41,7 @@ class Service extends Model * @var array */ protected $fillable = [ - 'name', 'description', 'file', 'executable', 'startup', + 'name', 'description', 'folder', 'startup', ]; /** diff --git a/app/Repositories/OptionRepository.php b/app/Repositories/OptionRepository.php index 7ded09689..e33eb652c 100644 --- a/app/Repositories/OptionRepository.php +++ b/app/Repositories/OptionRepository.php @@ -28,10 +28,74 @@ use DB; use Validator; use Pterodactyl\Models\ServiceOption; use Pterodactyl\Exceptions\DisplayException; +use Pterodactyl\Repositories\VariableRepository; use Pterodactyl\Exceptions\DisplayValidationException; class OptionRepository { + /** + * Creates a new service option on the system. + * + * @param array $data + * @return \Pterodactyl\Models\ServiceOption + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\DisplayValidationException + */ + public function create(array $data) + { + $validator = Validator::make($data, [ + 'service_id' => 'required|numeric|exists:services,id', + 'name' => 'required|string|max:255', + 'description' => 'required|string', + 'tag' => 'required|string|max:255|unique:service_options,tag', + 'docker_image' => 'required|string|max:255', + 'startup' => 'required|string', + 'config_from' => 'sometimes|required|numeric|exists:service_options,id', + 'config_startup' => 'required_without:config_from|json', + 'config_stop' => 'required_without:config_from|string|max:255', + 'config_logs' => 'required_without:config_from|json', + 'config_files' => 'required_without:config_from|json', + ]); + + if ($validator->fails()) { + throw new DisplayValidationException($validator->errors()); + } + + if (isset($data['config_from'])) { + if (! ServiceOption::where('service_id', $data['service_id'])->where('id', $data['config_from'])->first()) { + throw new DisplayException('The `configuration from` directive must be a child of the assigned service.'); + } + } + + return ServiceOption::create($data); + } + + /** + * Deletes a service option from the system. + * + * @param int $id + * @return void + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ + public function delete($id) + { + $option = ServiceOption::with('variables')->withCount('servers')->findOrFail($id); + + if ($option->servers_count > 0) { + throw new DisplayException('You cannot delete a service option that has servers associated with it.'); + } + + DB::transaction(function () use ($option) { + foreach($option->variables as $variable) { + (new VariableRepository)->delete($variable->id); + } + + $option->delete(); + }); + } + /** * Updates a service option in the database which can then be used * on nodes. @@ -39,11 +103,25 @@ class OptionRepository * @param int $id * @param array $data * @return \Pterodactyl\Models\ServiceOption + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\DisplayValidationException */ public function update($id, array $data) { $option = ServiceOption::findOrFail($id); + // Due to code limitations (at least when I am writing this currently) + // we have to make an assumption that if config_from is not passed + // that we should be telling it that no config is wanted anymore. + // + // This really is only an issue if we open API access to this function, + // in which case users will always need to pass `config_from` in order + // to keep it assigned. + if (! isset($data['config_from']) && ! is_null($option->config_from)) { + $option->config_from = null; + } + $validator = Validator::make($data, [ 'name' => 'sometimes|required|string|max:255', 'description' => 'sometimes|required|string', @@ -73,6 +151,12 @@ class OptionRepository throw new DisplayValidationException($validator->errors()); } + if (isset($data['config_from'])) { + if (! ServiceOption::where('service_id', $option->service_id)->where('id', $data['config_from'])->first()) { + throw new DisplayException('The `configuration from` directive must be a child of the assigned service.'); + } + } + $option->fill($data)->save(); return $option; diff --git a/app/Repositories/ServiceRepository.php b/app/Repositories/ServiceRepository.php index ed9dbf03d..a82c80b3f 100644 --- a/app/Repositories/ServiceRepository.php +++ b/app/Repositories/ServiceRepository.php @@ -31,6 +31,7 @@ use Validator; use Pterodactyl\Models\Service; use Pterodactyl\Models\ServiceVariable; use Pterodactyl\Exceptions\DisplayException; +use Pterodactyl\Repositories\OptionRepository; use Pterodactyl\Exceptions\DisplayValidationException; class ServiceRepository @@ -55,13 +56,14 @@ class ServiceRepository } $service = DB::transaction(function () use ($data) { - $service = Service::create([ - 'author' => config('pterodactyl.service.author'), + $service = new Service; + $service->author = config('pterodactyl.service.author'); + $service->fill([ 'name' => $data['name'], 'description' => (isset($data['description'])) ? $data['description'] : null, 'folder' => $data['folder'], 'startup' => (isset($data['startup'])) ? $data['startup'] : null, - ]); + ])->save(); // It is possible for an event to return false or throw an exception // which won't necessarily be detected by this transaction. @@ -105,8 +107,7 @@ class ServiceRepository $moveFiles = (isset($data['folder']) && $data['folder'] !== $service->folder); $oldFolder = $service->folder; - $service->fill($data); - $service->save(); + $service->fill($data)->save(); if ($moveFiles) { Storage::move(sprintf('services/%s/index.js', $oldFolder), sprintf('services/%s/index.js', $service->folder)); @@ -124,18 +125,16 @@ class ServiceRepository */ public function delete($id) { - $service = Service::withCount('servers', 'options')->findOrFail($id); + $service = Service::withCount('servers')->with('options')->findOrFail($id); if ($service->servers_count > 0) { throw new DisplayException('You cannot delete a service that has servers associated with it.'); } DB::transaction(function () use ($service) { - ServiceVariable::whereIn('option_id', $service->options->pluck('id')->all())->delete(); - - $service->options->each(function ($item) { - $item->delete(); - }); + foreach($service->options as $option) { + (new OptionRepository)->delete($option->id); + } $service->delete(); Storage::deleteDirectory('services/' . $service->folder); diff --git a/app/Repositories/VariableRepository.php b/app/Repositories/VariableRepository.php index 744cb8bc7..b9421897b 100644 --- a/app/Repositories/VariableRepository.php +++ b/app/Repositories/VariableRepository.php @@ -26,7 +26,7 @@ namespace Pterodactyl\Repositories; use DB; use Validator; -use Pterodactyl\Models; +use Pterodactyl\Models\ServiceVariable; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Exceptions\DisplayValidationException; @@ -37,108 +37,126 @@ class VariableRepository // } - public function create($id, array $data) + public function create(array $data) { - $option = Models\ServiceOption::select('id')->findOrFail($id); - $validator = Validator::make($data, [ + 'option_id' => 'required|numeric|exists:service_options,id', 'name' => 'required|string|min:1|max:255', - 'description' => 'required|string', + 'description' => 'sometimes|nullable|string', 'env_variable' => 'required|regex:/^[\w]{1,255}$/', - 'default_value' => 'string|max:255', - 'user_viewable' => 'sometimes|required|nullable|boolean', - 'user_editable' => 'sometimes|required|nullable|boolean', - 'required' => 'sometimes|required|nullable|boolean', - 'regex' => 'required|string|min:1', + 'default_value' => 'string', + 'options' => 'sometimes|required|array', + 'rules' => 'bail|required|string|min:1', ]); + // Ensure the default value is allowed by the rules provided. + $rules = (isset($data['rules'])) ? $data['rules'] : $variable->rules; + $validator->sometimes('default_value', $rules, function ($input) { + return $input->default_value; + }); + if ($validator->fails()) { throw new DisplayValidationException($validator->errors()); } - if ($data['default_value'] !== '' && ! preg_match($data['regex'], $data['default_value'])) { - throw new DisplayException('The default value you entered cannot violate the regex requirements.'); + if (isset($data['env_variable'])) { + $search = ServiceVariable::where('env_variable', $data['env_variable']) + ->where('option_id', $variable->option_id) + ->where('id', '!=', $variable->id); + if ($search->first()) { + throw new DisplayException('The envionment variable name assigned to this variable must be unique for this service option.'); + } } - if (Models\ServiceVariable::where('env_variable', $data['env_variable'])->where('option_id', $option->id)->first()) { - throw new DisplayException('An environment variable with that name already exists for this option.'); + if (! isset($data['options']) || ! is_array($data['options'])) { + $data['options'] = []; } - $data['user_viewable'] = (isset($data['user_viewable']) && in_array((int) $data['user_viewable'], [0, 1])) ? $data['user_viewable'] : 0; - $data['user_editable'] = (isset($data['user_editable']) && in_array((int) $data['user_editable'], [0, 1])) ? $data['user_editable'] : 0; - $data['required'] = (isset($data['required']) && in_array((int) $data['required'], [0, 1])) ? $data['required'] : 0; - $data['option_id'] = $option->id; + $data['user_viewable'] = (in_array('user_viewable', $data['options'])); + $data['user_editable'] = (in_array('user_editable', $data['options'])); + $data['required'] = (in_array('required', $data['options'])); - $variable = Models\ServiceVariable::create($data); + // Remove field that isn't used. + unset($data['options']); - return $variable; + return ServiceVariable::create($data); } + /** + * Deletes a specified option variable as well as all server + * variables currently assigned. + * + * @param int $id + * @return void + */ public function delete($id) { - $variable = Models\ServiceVariable::with('serverVariable')->findOrFail($id); + $variable = ServiceVariable::with('serverVariable')->findOrFail($id); - DB::beginTransaction(); - try { - foreach ($variable->serverVariable as $svar) { - $svar->delete(); + DB::transaction(function () use ($variable) { + foreach ($variable->serverVariable as $v) { + $v->delete(); } - $variable->delete(); - DB::commit(); - } catch (\Exception $ex) { - DB::rollBack(); - throw $ex; - } + $variable->delete(); + }); } + /** + * Updates a given service variable. + * + * @param int $id + * @param array $data + * @return \Pterodactyl\Models\ServiceVariable + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\DisplayValidationException + */ public function update($id, array $data) { - $variable = Models\ServiceVariable::findOrFail($id); + $variable = ServiceVariable::findOrFail($id); $validator = Validator::make($data, [ 'name' => 'sometimes|required|string|min:1|max:255', - 'description' => 'sometimes|required|string', + 'description' => 'sometimes|nullable|string', 'env_variable' => 'sometimes|required|regex:/^[\w]{1,255}$/', - 'default_value' => 'sometimes|string|max:255', - 'user_viewable' => 'sometimes|required|nullable|boolean', - 'user_editable' => 'sometimes|required|nullable|boolean', - 'required' => 'sometimes|required|nullable|boolean', - 'regex' => 'sometimes|required|string|min:1', + 'default_value' => 'string', + 'options' => 'sometimes|required|array', + 'rules' => 'bail|sometimes|required|string|min:1', ]); + // Ensure the default value is allowed by the rules provided. + $rules = (isset($data['rules'])) ? $data['rules'] : $variable->rules; + $validator->sometimes('default_value', $rules, function ($input) { + return $input->default_value; + }); + if ($validator->fails()) { throw new DisplayValidationException($validator->errors()); } - $data['default_value'] = (isset($data['default_value'])) ? $data['default_value'] : $variable->default_value; - $data['regex'] = (isset($data['regex'])) ? $data['regex'] : $variable->regex; - - if ($data['default_value'] !== '' && ! preg_match($data['regex'], $data['default_value'])) { - throw new DisplayException('The default value you entered cannot violate the regex requirements.'); + if (isset($data['env_variable'])) { + $search = ServiceVariable::where('env_variable', $data['env_variable']) + ->where('option_id', $variable->option_id) + ->where('id', '!=', $variable->id); + if ($search->first()) { + throw new DisplayException('The envionment variable name assigned to this variable must be unique for this service option.'); + } } - if (Models\ServiceVariable::where('id', '!=', $variable->id)->where('env_variable', $data['env_variable'])->where('option_id', $variable->option_id)->first()) { - throw new DisplayException('An environment variable with that name already exists for this option.'); + if (! isset($data['options']) || ! is_array($data['options'])) { + $data['options'] = []; } - $data['user_viewable'] = (isset($data['user_viewable']) && in_array((int) $data['user_viewable'], [0, 1])) ? $data['user_viewable'] : $variable->user_viewable; - $data['user_editable'] = (isset($data['user_editable']) && in_array((int) $data['user_editable'], [0, 1])) ? $data['user_editable'] : $variable->user_editable; - $data['required'] = (isset($data['required']) && in_array((int) $data['required'], [0, 1])) ? $data['required'] : $variable->required; + $data['user_viewable'] = (in_array('user_viewable', $data['options'])); + $data['user_editable'] = (in_array('user_editable', $data['options'])); + $data['required'] = (in_array('required', $data['options'])); - // Not using $data because the function that passes into this function - // can't do $requst->only() due to the page setup. - $variable->fill([ - 'name' => $data['name'], - 'description' => $data['description'], - 'env_variable' => $data['env_variable'], - 'default_value' => $data['default_value'], - 'user_viewable' => $data['user_viewable'], - 'user_editable' => $data['user_editable'], - 'required' => $data['required'], - 'regex' => $data['regex'], - ]); + // Remove field that isn't used. + unset($data['options']); - return $variable->save(); + $variable->fill($data)->save(); + + return $variable; } } diff --git a/config/pterodactyl.php b/config/pterodactyl.php index ec0e430cd..30ea7ef97 100644 --- a/config/pterodactyl.php +++ b/config/pterodactyl.php @@ -12,6 +12,7 @@ return [ | standard Pterodactyl shipped services. */ 'service' => [ + 'core' => 'ptrdctyl-v040-11e6-8b77-86f30ca893d3', 'author' => env('SERVICE_AUTHOR'), ], diff --git a/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php b/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php new file mode 100644 index 000000000..1ade5b53e --- /dev/null +++ b/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php @@ -0,0 +1,47 @@ +renameColumn('regex', 'rules'); + }); + + DB::transaction(function () { + foreach(ServiceVariable::all() as $variable) { + $variable->rules = ($variable->required) ? 'required|regex:' . $variable->rules : 'regex:' . $variable->regex; + $variable->save(); + } + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('service_variables', function (Blueprint $table) { + $table->renameColumn('rules', 'regex'); + }); + + DB::transaction(function () { + foreach(ServiceVariable::all() as $variable) { + $variable->regex = str_replace(['required|regex:', 'regex:'], '', $variable->regex); + $variable->save(); + } + }); + } +} diff --git a/resources/themes/pterodactyl/admin/services/options/new.blade.php b/resources/themes/pterodactyl/admin/services/options/new.blade.php new file mode 100644 index 000000000..763e6096c --- /dev/null +++ b/resources/themes/pterodactyl/admin/services/options/new.blade.php @@ -0,0 +1,170 @@ +{{-- 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') + Services → New Option +@endsection + +@section('content-header') +

    New OptionCreate a new service option to assign to servers.

    + +@endsection + +@section('content') +
    +
    +
    +
    +
    +

    Configuration

    +
    +
    +
    +
    +
    + + +
    +
    + + +

    A simple, human-readable name to use as an identifier for this service.

    +
    +
    + + +

    A description of this service that will be displayed throughout the panel as needed.

    +
    +
    +
    +
    + + +

    This should be a unique identifer for this service option that is not used for any other service options.

    +
    +
    + + +

    The default docker image that should be used for new servers under this service option. This can be left blank to use the parent service's defined image, and can also be changed per-server.

    +
    +
    + + +

    The default statup command that should be used for new servers under this service option. This can be left blank to use the parent service's startup, and can also be changed per-server.

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Process Management

    +
    +
    +
    +
    +
    +

    All fields are required unless you select a seperate option from the 'Copy Settings From' dropdown, in which case fields may be left blank to use the values from that option.

    +
    +
    +
    +
    + + +

    If you would like to default to settings from another option select the option from the menu above.

    +
    +
    + + +

    The command that should be sent to server processes to stop them gracefully. If you need to send a SIGINT you should enter ^C here.

    +
    +
    + + +

    This should be a JSON representation of where log files are stored, and wether or not the daemon should be creating custom logs.

    +
    +
    +
    +
    + + +

    This should be a JSON representation of configuration files to modify and what parts should be changed.

    +
    +
    + + +

    This should be a JSON representation of what values the daemon should be looking for when booting a server to determine completion.

    +
    +
    +
    + +
    +
    +
    + +@endsection + +@section('footer-scripts') + @parent + {!! Theme::js('vendor/lodash/lodash.js') !!} + +@endsection diff --git a/resources/themes/pterodactyl/admin/services/options/variables.blade.php b/resources/themes/pterodactyl/admin/services/options/variables.blade.php new file mode 100644 index 000000000..c1870d3ee --- /dev/null +++ b/resources/themes/pterodactyl/admin/services/options/variables.blade.php @@ -0,0 +1,114 @@ +{{-- 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') + Service Options → {{ $option->name }} → Variables +@endsection + +@section('content-header') +

    {{ $option->name }}Managing variables for this service option.

    + +@endsection + +@section('content') +
    +
    + +
    +
    +
    + @foreach($option->variables as $variable) +
    +
    +
    +

    {{ $variable->name }}

    +
    +
    +
    +
    + + +
    +
    + + +
    +
    +
    + + +
    +
    + + +
    +
    +

    This variable can be accessed in the statup command by using {{ $variable->env_variable }}.

    +
    +
    +
    + + +
    +
    + + +

    These rules are defined using standard Laravel Framework validation rules.

    +
    +
    + +
    +
    +
    + @endforeach +
    +@endsection + +@section('footer-scripts') + @parent + +@endsection diff --git a/resources/themes/pterodactyl/admin/services/options/view.blade.php b/resources/themes/pterodactyl/admin/services/options/view.blade.php index 01965d4ee..6450fdef3 100644 --- a/resources/themes/pterodactyl/admin/services/options/view.blade.php +++ b/resources/themes/pterodactyl/admin/services/options/view.blade.php @@ -39,7 +39,7 @@
    @@ -131,12 +131,16 @@

    This should be a JSON representation of what values the daemon should be looking for when booting a server to determine completion.

    +
    @@ -147,6 +151,12 @@ @section('footer-scripts') @parent +@endsection diff --git a/resources/views/admin/servers/view.blade.php b/resources/views/admin/servers/view.blade.php deleted file mode 100644 index 3f65dbd11..000000000 --- a/resources/views/admin/servers/view.blade.php +++ /dev/null @@ -1,687 +0,0 @@ -{{-- 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') - Managing Server: {{ $server->name }} ({{ $server->uuidShort}}) -@endsection - -@section('content') -
    - - @if($server->suspended === 1 && !$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 === 0) -
    - 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 - -
    -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    UUID{{ $server->uuid }}
    Docker Container ID
    Docker User ID
    Owner{{ $server->user->email }}
    Location{{ $server->node->location->short }}
    Node{{ $server->node->name }}
    Service{{ $server->option->service->name }} :: {{ $server->option->name }}
    Name{{ $server->name }}
    Memory{{ $server->memory }}MB / {{ $server->swap }}MB
    OOM Killer{!! ($server->oom_disabled === 0) ? 'Enabled' : 'Disabled' !!}
    Disk Space{{ $server->disk }}MB
    Block IO Weight{{ $server->io }}
    CPU Limit{{ $server->cpu }}%
    Default Connection{{ $server->allocation->ip }}:{{ $server->allocation->port }}
    Connection Alias - @if($server->allocation->alias !== $server->allocation->ip) - {{ $server->allocation->alias }}:{{ $server->allocation->port }} - @else - No Alias Assigned - @endif -
    Installed{!! ($server->installed === 1) ? 'Yes' : 'No' !!}
    Suspended{!! ($server->suspended === 1) ? 'Suspended' : 'No' !!}
    -
    -
    -
    - @if($server->installed === 1) -
    -
    -
    -
    -
    -
    - -
    - -

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

    -
    -
    -
    - -
    - -

    You can change the owner of this server by changing this field to an email matching another use on this system. If you do this a new daemon security token will be generated automatically.

    -
    -
    -
    - -
    - -

    This token should not be shared with anyone as it has full control over this server.

    -
    -
    -
    -
    - Yes, Reset Daemon Token -

    Resetting this token will cause any requests using the old token to fail.

    -
    -
    -
    - {!! csrf_field() !!} - -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - -

    The docker image to use for this server. The default image for this service and option combination is {{ $server->docker_image }}.

    -
    -
    -
    -
    -
    - -
    - {!! csrf_field() !!} - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - After editing any of the options below you will need to restart the server for changes to take effect. If the server is currently off, you just need to start it and the container will be rebuilt with the new settings. -
    -
    -
    -
    -
    -
    - -
    - - MB -
    -
    -
    - -
    - - MB -
    -

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

    -
    -
    -
    -
    - -
    - - % -
    -

    Each physical core on the system is considered to be 100%. Setting this value to 0 will allow a server to use CPU time without restrictions.

    -
    -
    - -
    - -
    -

    Changing this value can have negative effects on all containers on the system. We strongly recommend leaving this value as 500.

    -
    -
    -
    -
    -
    -
    - Additional IPs and Ports can be assigned to this server for use by plugins or other software. The game port is what will show up for the user to use to connect to thier server, and what their configuration files will be forced to use for binding. -
    -
    -
    - - @foreach ($assigned as $assignment) -
    - - id === $server->allocation_id) checked="checked" @endif - name="default" value="{{ $assignment->ip }}:{{ $assignment->port }}"/> - - has_alias) - data-toggle="tooltip" data-placement="left" title="{{ $assignment->ip }}:{{ $assignment->port }}" - @endif - /> -
    - @endforeach -
    -
    -
    -
    - -
    - -
    -

    Please note that due to software limitations you cannot assign identical ports on different IPs to the same server. For example, you cannot assign both 192.168.0.5:25565 and 192.168.10.5:25565 to the same server.

    -
    -
    -
    -
    - -
    - -
    -

    Simply select which ports you would like to remove from the list above. If you want to assign a port on a different IP that is already in use you can select it above and delete it down here.

    -
    -
    -
    -
    -
    -
    - {!! csrf_field() !!} - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    Changing any of the values below will require a restart for them to take effect.
    - -
    - {{ $server->option->display_executable }} - -
    -

    The following data replacers are avaliable for the startup command: @{{SERVER_MEMORY}}, @{{SERVER_IP}}, and @{{SERVER_PORT}}. They will be replaced with the allocated memory, server ip, and server port respectively.

    -
    -
    -
    -
    -
    -
    - @foreach($server->option->variables as $variable) -
    - -
    - -
    -

    {!! $variable->description !!}
    Regex: {{ $variable->regex }}
    Access as: {{{{ $variable->env_variable }}}}

    -
    - @endforeach -
    -
    -
    -
    -
    -
    - {!! csrf_field() !!} - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Add New Database

    -
    -
    -
    - -
    -
    s{{ $server->id }}_
    - -
    -
    -
    - -
    - -
    -

    Which IP to allow connections from. Standard MySQL wildcard notation allowed (e.g. 192.168.%.%).

    -
    -
    -
    -
    - - -
    -
    - -
    - {!! csrf_field() !!} - -
    -
    -
    -
    -
    - @if(count($server->databases) > 0) -
    -
    - - - - - - - - - - - - @foreach($server->databases as $database) - - - - - - - - @endforeach - -
    DatabaseUser (Connections From)PasswordDB Server
    {{ $database->database }}{{ $database->username }} ({{ $database->remote }}){{ Crypt::decrypt($database->password) }} {{ $database->host->host }}:{{ $database->host->port }}
    -
    - @endif -
    -
    - @endif - @if($server->installed !== 2) -
    -
    -
    -
    -
    - -
    -

    This will take you to the server management page that users normally see and allow you to manage server files as well as check the console and data usage.

    -
    -
    -
    -
    -
    -
    -
    -
    - {!! csrf_field() !!} - -
    -
    -
    -

    This will toggle the install status for the server.

    -
    If you have just created this server it is ill advised to perform this action as the daemon will contact the panel when finished which could cause the install status to be wrongly set.
    -
    -
    -
    - @if($server->installed === 1) -
    -
    -
    -
    -
    - {!! csrf_field() !!} - -
    -
    -
    -

    This will trigger a rebuild of the server container when it next starts up. This is useful if you modified the server configuration file manually, or something just didn't work out correctly.

    -
    A rebuild will automatically occur whenever you edit build configuration settings for the server.
    -
    -
    -
    - @endif -
    -
    -
    - @if($server->suspended === 0) -
    -
    - {!! csrf_field() !!} - -
    -
    -
    -

    This will suspend the server, stop any running processes, and immediately block the user from being able to access their files or otherwise manage the server through the panel or API.

    -
    - @else -
    -
    - {!! csrf_field() !!} - -
    -
    -
    -

    This will unsuspend the server and restore normal user access.

    -
    - @endif -
    -
    -
    -
    - @endif -
    -
    -
    - @if($server->installed === 1) -
    -
    -
    -
    - {!! csrf_field() !!} - {!! method_field('DELETE') !!} - -
    -
    -
    -
    Deleting a server is an irreversible action. All data will be immediately removed relating to this server.
    -
    -
    -
    -
    - @endif -
    -
    -
    -
    - {!! csrf_field() !!} - {!! method_field('DELETE') !!} - -
    -
    -
    -
    This is the same as deleting a server, however, if an error is returned by the daemon it is ignored and the server is still removed from the panel.
    -
    -
    -
    -
    -
    -
    -
    - -@endsection diff --git a/resources/views/admin/services/options/variable.blade.php b/resources/views/admin/services/options/variable.blade.php deleted file mode 100644 index 5ed9540c3..000000000 --- a/resources/views/admin/services/options/variable.blade.php +++ /dev/null @@ -1,122 +0,0 @@ -{{-- 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') - New Variable for {{ $option->name }} -@endsection - -@section('content') -
    - -

    New Option Variable


    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    -
    - -
    - -

    Regex code to use when verifying the contents of the field.

    -
    -
    -
    -
    -
    - -
    - -

    Accessed in startup by using {{}} parameter.

    -
    -
    -
    - -
    - -

    The default value to use for this field.

    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - {!! csrf_field() !!} - -
    -
    -
    -
    -
    - -@endsection diff --git a/storage/app/services/index.js b/storage/app/services/index.js new file mode 100644 index 000000000..f6b6695d0 --- /dev/null +++ b/storage/app/services/index.js @@ -0,0 +1,31 @@ +'use strict'; + +/** + * Pterodactyl - Daemon + * 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. + */ +const rfr = require('rfr'); + +const Core = rfr('src/services/index.js'); + +class Service extends Core {} + +module.exports = Service; From d58529428974db27ff0f3eab55ae5174c0608b32 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 12 Mar 2017 00:03:04 -0500 Subject: [PATCH 05/22] Misc. file removal and migration cleanup --- ...03_10_173607_MigrateToNewServiceSystem.php | 2 +- storage/app/services/index.js | 31 -------- storage/app/services/minecraft/main.json | 74 ------------------- storage/app/services/srcds/main.json | 23 ------ storage/app/services/terraria/main.json | 23 ------ storage/app/services/voice/main.json | 50 ------------- 6 files changed, 1 insertion(+), 202 deletions(-) delete mode 100644 storage/app/services/index.js delete mode 100644 storage/app/services/minecraft/main.json delete mode 100644 storage/app/services/srcds/main.json delete mode 100644 storage/app/services/terraria/main.json delete mode 100644 storage/app/services/voice/main.json diff --git a/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php b/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php index afa9f6ee1..c22954150 100644 --- a/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php +++ b/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php @@ -125,7 +125,7 @@ class MigrateToNewServiceSystem extends Migration switch($item->tag) { case 'source': - $item->config_startup = '{"done": "Assigned anonymous gameserver Steam ID", "userInteraction": []}'; + $item->config_startup = '{"done": "Assigned anonymous gameserver", "userInteraction": []}'; $item->config_files = '{}'; $item->config_logs = '{"custom": true, "location": "logs/latest.log"}'; $item->config_stop = 'quit'; diff --git a/storage/app/services/index.js b/storage/app/services/index.js deleted file mode 100644 index f6b6695d0..000000000 --- a/storage/app/services/index.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -/** - * Pterodactyl - Daemon - * 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. - */ -const rfr = require('rfr'); - -const Core = rfr('src/services/index.js'); - -class Service extends Core {} - -module.exports = Service; diff --git a/storage/app/services/minecraft/main.json b/storage/app/services/minecraft/main.json deleted file mode 100644 index 48a490cb3..000000000 --- a/storage/app/services/minecraft/main.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "vanilla": { - "startup": { - "done": ")! For help, type ", - "userInteraction": [ - "Go to eula.txt for more info." - ] - }, - "stop": "stop", - "configs": { - "server.properties": { - "parser": "properties", - "find": { - "server-ip": "0.0.0.0", - "enable-query": "true", - "server-port": "{{ server.build.default.port }}", - "query.port": "{{ server.build.default.port }}" - } - } - }, - "log": { - "custom": false, - "location": "logs/latest.log" - }, - "query": "minecraftping" - }, - "spigot": { - "symlink": "vanilla", - "configs": { - "spigot.yml": { - "parser": "yaml", - "find": { - "settings.restart-on-crash": "false" - } - } - } - }, - "bungeecord": { - "startup": { - "done": "Listening on ", - "userInteraction": [ - "Listening on /0.0.0.0:25577" - ] - }, - "stop": "end", - "configs": { - "config.yml": { - "parser": "yaml", - "find": { - "listeners[0].query_enabled": true, - "listeners[0].query_port": "{{ server.build.default.port }}", - "listeners[0].host": "0.0.0.0:{{ server.build.default.port }}", - "servers.*.address": { - "127.0.0.1": "{{ config.docker.interface }}", - "localhost": "{{ config.docker.interface }}" - } - } - } - }, - "log": { - "custom": false, - "location": "proxy.log.0" - }, - "query": "minecraftping" - }, - "sponge": { - "symlink": "vanilla", - "startup": { - "userInteraction": [ - "You need to agree to the EULA" - ] - } - } -} diff --git a/storage/app/services/srcds/main.json b/storage/app/services/srcds/main.json deleted file mode 100644 index 989a67537..000000000 --- a/storage/app/services/srcds/main.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "srcds": { - "startup": { - "done": "Assigned anonymous gameserver Steam ID", - "userInteraction": [] - }, - "stop": "quit", - "configs": {}, - "log": { - "custom": true, - "location": "logs/latest.log" - }, - "query": "protocol-valve" - }, - "ark": { - "symlink": "srcds", - "startup": { - "done": "Setting breakpad minidump AppID" - }, - "stop": "^C", - "query": "none" - } -} diff --git a/storage/app/services/terraria/main.json b/storage/app/services/terraria/main.json deleted file mode 100644 index 78f1b5bc0..000000000 --- a/storage/app/services/terraria/main.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "tshock": { - "startup": { - "done": "Type 'help' for a list of commands", - "userInteraction": [] - }, - "stop": "exit", - "configs": { - "tshock/config.json": { - "parser": "json", - "find": { - "ServerPort": "{{ server.build.default.port }}", - "MaxSlots": "{{ server.build.env.MAX_SLOTS }}" - } - } - }, - "log": { - "custom": false, - "location": "ServerLog.txt" - }, - "query": "none" - } -} diff --git a/storage/app/services/voice/main.json b/storage/app/services/voice/main.json deleted file mode 100644 index bc7232f46..000000000 --- a/storage/app/services/voice/main.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "mumble": { - "startup": { - "done": "Server listening on", - "userInteraction": [ - "Generating new server certificate" - ] - }, - "stop": "^C", - "configs": { - "murmur.ini": { - "parser": "ini", - "find": { - "logfile": "murmur.log", - "port": "{{ server.build.default.port }}", - "host": "0.0.0.0", - "users": "{{ server.build.env.MAX_USERS }}" - } - } - }, - "log": { - "custom": true, - "location": "logs/murmur.log" - }, - "query": "mumbleping" - }, - "ts3": { - "startup": { - "done": "listening on 0.0.0.0:", - "userInteraction": [] - }, - "stop": "^C", - "configs": { - "ts3server.ini": { - "parser": "ini", - "find": { - "default_voice_port": "{{ server.build.default.port }}", - "voice_ip": "0.0.0.0", - "query_port": "{{ server.build.default.port }}", - "query_ip": "0.0.0.0" - } - } - }, - "log": { - "custom": true, - "location": "logs/ts3.log" - }, - "query": "none" - } -} From 419031e0cd469364f56f6f045c274f3d83ac0234 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 12 Mar 2017 15:59:17 -0400 Subject: [PATCH 06/22] Add function file editing, and move all service file items into database --- .../Controllers/Admin/ServiceController.php | 217 +++--------------- app/Http/Routes/AdminRoutes.php | 7 +- app/Models/Service.php | 45 +++- app/Repositories/ServiceRepository.php | 51 +--- ...150648_MoveFunctionsFromFileToDatabase.php | 120 ++++++++++ .../admin/services/functions.blade.php | 88 +++++++ .../pterodactyl/admin/services/view.blade.php | 12 +- storage/app/services/.templates/index.js | 31 --- storage/app/services/minecraft/index.js | 38 --- storage/app/services/srcds/index.js | 31 --- storage/app/services/terraria/index.js | 31 --- storage/app/services/voice/index.js | 31 --- 12 files changed, 298 insertions(+), 404 deletions(-) create mode 100644 database/migrations/2017_03_12_150648_MoveFunctionsFromFileToDatabase.php create mode 100644 resources/themes/pterodactyl/admin/services/functions.blade.php delete mode 100644 storage/app/services/.templates/index.js delete mode 100644 storage/app/services/minecraft/index.js delete mode 100644 storage/app/services/srcds/index.js delete mode 100644 storage/app/services/terraria/index.js delete mode 100644 storage/app/services/voice/index.js diff --git a/app/Http/Controllers/Admin/ServiceController.php b/app/Http/Controllers/Admin/ServiceController.php index 60450ab28..523e6bb14 100644 --- a/app/Http/Controllers/Admin/ServiceController.php +++ b/app/Http/Controllers/Admin/ServiceController.php @@ -26,7 +26,6 @@ namespace Pterodactyl\Http\Controllers\Admin; use Log; use Alert; -use Storage; use Pterodactyl\Models; use Illuminate\Http\Request; use Pterodactyl\Exceptions\DisplayException; @@ -74,6 +73,18 @@ class ServiceController extends Controller ]); } + /** + * Return function editing view for a service. + * + * @param Request $request + * @param int $id + * @return \Illuminate\View\View + */ + public function viewFunctions(Request $request, $id) + { + return view('admin.services.functions', ['service' => Models\Service::findOrFail($id)]); + } + /** * Handle post action for new service. * @@ -113,11 +124,12 @@ class ServiceController extends Controller public function edit(Request $request, $id) { $repo = new ServiceRepository; + $redirectTo = ($request->input('redirect_to')) ? 'admin.services.view.functions' : 'admin.services.view'; try { if ($request->input('action') !== 'delete') { $repo->update($id, $request->intersect([ - 'name', 'description', 'folder', 'startup', + 'name', 'description', 'folder', 'startup', 'index_file', ])); Alert::success('Service has been updated successfully.')->flash(); } else { @@ -127,7 +139,7 @@ class ServiceController extends Controller return redirect()->route('admin.services'); } } catch (DisplayValidationException $ex) { - return redirect()->route('admin.services.view', $id)->withErrors(json_decode($ex->getMessage()))->withInput(); + return redirect()->route($redirectTo, $id)->withErrors(json_decode($ex->getMessage()))->withInput(); } catch (DisplayException $ex) { Alert::danger($ex->getMessage())->flash(); } catch (\Exception $ex) { @@ -135,193 +147,18 @@ class ServiceController extends Controller Alert::danger('An error occurred while attempting to update this service. This error has been logged.')->flash(); } - return redirect()->route('admin.services.view', $id); + return redirect()->route($redirectTo, $id); } - // public function getOption(Request $request, $service, $option) - // { - // $option = Models\ServiceOption::with('service', 'variables')->findOrFail($option); - // $option->setRelation('servers', $option->servers()->with('user')->paginate(25)); - // - // return view('admin.services.options.view', ['option' => $option]); - // } - // - // public function postOption(Request $request, $service, $option) - // { - // try { - // $repo = new ServiceRepository\Option; - // $repo->update($option, $request->only([ - // 'name', 'description', 'tag', - // 'executable', 'docker_image', 'startup', - // ])); - // Alert::success('Option settings successfully updated.')->flash(); - // } catch (DisplayValidationException $ex) { - // return redirect()->route('admin.services.option', [$service, $option])->withErrors(json_decode($ex->getMessage()))->withInput(); - // } catch (\Exception $ex) { - // Log::error($ex); - // Alert::danger('An error occured while attempting to modify this option.')->flash(); - // } - // - // return redirect()->route('admin.services.option', [$service, $option])->withInput(); - // } - // - // public function deleteOption(Request $request, $service, $option) - // { - // try { - // $repo = new ServiceRepository\Option; - // $repo->delete($option); - // - // Alert::success('Successfully deleted that option.')->flash(); - // - // return redirect()->route('admin.services.service', $service); - // } catch (DisplayException $ex) { - // Alert::danger($ex->getMessage())->flash(); - // } catch (\Exception $ex) { - // Log::error($ex); - // Alert::danger('An error was encountered while attempting to delete this option.')->flash(); - // } - // - // return redirect()->route('admin.services.option', [$service, $option]); - // } - // - // public function postOptionVariable(Request $request, $service, $option, $variable) - // { - // try { - // $repo = new ServiceRepository\Variable; - // - // // Because of the way old() works on the display side we prefix all of the variables with thier ID - // // We need to remove that prefix here since the repo doesn't want it. - // $data = [ - // 'user_viewable' => '0', - // 'user_editable' => '0', - // 'required' => '0', - // ]; - // foreach ($request->except(['_token']) as $id => $val) { - // $data[str_replace($variable . '_', '', $id)] = $val; - // } - // $repo->update($variable, $data); - // Alert::success('Successfully updated variable.')->flash(); - // } catch (DisplayValidationException $ex) { - // $data = []; - // foreach (json_decode($ex->getMessage(), true) as $id => $val) { - // $data[$variable . '_' . $id] = $val; - // } - // - // return redirect()->route('admin.services.option', [$service, $option])->withErrors((object) $data)->withInput(); - // } catch (DisplayException $ex) { - // Alert::danger($ex->getMessage())->flash(); - // } catch (\Exception $ex) { - // Log::error($ex); - // Alert::danger('An error occurred while attempting to update this service.')->flash(); - // } - // - // return redirect()->route('admin.services.option', [$service, $option])->withInput(); - // } - // - // public function getNewVariable(Request $request, $service, $option) - // { - // return view('admin.services.options.variable', [ - // 'option' => Models\ServiceOption::with('service')->findOrFail($option), - // ]); - // } - // - // public function postNewVariable(Request $request, $service, $option) - // { - // try { - // $repo = new ServiceRepository\Variable; - // $repo->create($option, $request->only([ - // 'name', 'description', 'env_variable', - // 'default_value', 'user_viewable', - // 'user_editable', 'required', 'regex', - // ])); - // Alert::success('Successfully added new variable to this option.')->flash(); - // - // return redirect()->route('admin.services.option', [$service, $option]); - // } catch (DisplayValidationException $ex) { - // return redirect()->route('admin.services.option.variable.new', [$service, $option])->withErrors(json_decode($ex->getMessage()))->withInput(); - // } catch (DisplayException $ex) { - // Alert::danger($ex->getMessage())->flash(); - // } catch (\Exception $ex) { - // Log::error($ex); - // Alert::danger('An error occurred while attempting to add this variable.')->flash(); - // } - // - // return redirect()->route('admin.services.option.variable.new', [$service, $option])->withInput(); - // } - // - // public function newOption(Request $request, $service) - // { - // return view('admin.services.options.new', [ - // 'service' => Models\Service::findOrFail($service), - // ]); - // } - // - // public function postNewOption(Request $request, $service) - // { - // try { - // $repo = new ServiceRepository\Option; - // $id = $repo->create($service, $request->except([ - // '_token', - // ])); - // Alert::success('Successfully created new service option.')->flash(); - // - // return redirect()->route('admin.services.option', [$service, $id]); - // } catch (DisplayValidationException $ex) { - // return redirect()->route('admin.services.option.new', $service)->withErrors(json_decode($ex->getMessage()))->withInput(); - // } catch (\Exception $ex) { - // Log::error($ex); - // Alert::danger('An error occured while attempting to add this service option.')->flash(); - // } - // - // return redirect()->route('admin.services.option.new', $service)->withInput(); - // } - // - // public function deleteVariable(Request $request, $service, $option, $variable) - // { - // try { - // $repo = new ServiceRepository\Variable; - // $repo->delete($variable); - // Alert::success('Deleted variable.')->flash(); - // } catch (DisplayException $ex) { - // Alert::danger($ex->getMessage())->flash(); - // } catch (\Exception $ex) { - // Log::error($ex); - // Alert::danger('An error occured while attempting to delete that variable.')->flash(); - // } - // - // return redirect()->route('admin.services.option', [$service, $option]); - // } - // - // public function getConfiguration(Request $request, $serviceId) - // { - // $service = Models\Service::findOrFail($serviceId); - // - // return view('admin.services.config', [ - // 'service' => $service, - // 'contents' => [ - // 'json' => Storage::get('services/' . $service->file . '/main.json'), - // 'index' => Storage::get('services/' . $service->file . '/index.js'), - // ], - // ]); - // } - // - // public function postConfiguration(Request $request, $serviceId) - // { - // try { - // $repo = new ServiceRepository\Service; - // $repo->updateFile($serviceId, $request->only(['file', 'contents'])); - // - // return response('', 204); - // } catch (DisplayException $ex) { - // return response()->json([ - // 'error' => $ex->getMessage(), - // ], 503); - // } catch (\Exception $ex) { - // Log::error($ex); - // - // return response()->json([ - // 'error' => 'An error occured while attempting to save the file.', - // ], 503); - // } - // } + /** + * Edits function file for a service. + * + * @param Request $request + * @param int $id + * @return \Illuminate\Response\RedirectResponse + */ + public function editFunctions(Request $request, $id) + { + + } } diff --git a/app/Http/Routes/AdminRoutes.php b/app/Http/Routes/AdminRoutes.php index 5cec94b55..60a6cb8ed 100644 --- a/app/Http/Routes/AdminRoutes.php +++ b/app/Http/Routes/AdminRoutes.php @@ -403,8 +403,11 @@ class AdminRoutes 'uses' => 'Admin\ServiceController@view', ]); - $router->post('/view/{id}', [ - 'uses' => 'Admin\ServiceController@edit', + $router->post('/view/{id}', 'Admin\ServiceController@edit'); + + $router->get('/view/{id}/functions', [ + 'as' => 'admin.services.view.functions', + 'uses' => 'Admin\ServiceController@viewFunctions', ]); $router->delete('/view/{id}', [ diff --git a/app/Models/Service.php b/app/Models/Service.php index 50679633e..c521b8035 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -41,9 +41,52 @@ class Service extends Model * @var array */ protected $fillable = [ - 'name', 'description', 'folder', 'startup', + 'name', 'description', 'folder', 'startup', 'index_file', ]; + /** + * Returns the default contents of the index.js file for a service. + * + * @return string + */ + public function defaultIndexFile() + { + return << + * + * 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. + */ +const rfr = require('rfr'); +const _ = require('lodash'); + +const Core = rfr('src/services/index.js'); + +class Service extends Core {} + +module.exports = Service; +EOF; + } + /** * Gets all service options associated with this service. * diff --git a/app/Repositories/ServiceRepository.php b/app/Repositories/ServiceRepository.php index a82c80b3f..801a29deb 100644 --- a/app/Repositories/ServiceRepository.php +++ b/app/Repositories/ServiceRepository.php @@ -26,7 +26,6 @@ namespace Pterodactyl\Repositories; use DB; use Uuid; -use Storage; use Validator; use Pterodactyl\Models\Service; use Pterodactyl\Models\ServiceVariable; @@ -55,7 +54,7 @@ class ServiceRepository throw new DisplayValidationException($validator->errors()); } - $service = DB::transaction(function () use ($data) { + return DB::transaction(function () use ($data) { $service = new Service; $service->author = config('pterodactyl.service.author'); $service->fill([ @@ -63,6 +62,7 @@ class ServiceRepository 'description' => (isset($data['description'])) ? $data['description'] : null, 'folder' => $data['folder'], 'startup' => (isset($data['startup'])) ? $data['startup'] : null, + 'index_file' => $service->defaultIndexFile(), ])->save(); // It is possible for an event to return false or throw an exception @@ -73,12 +73,8 @@ class ServiceRepository throw new \Exception('Service model was created however the response appears to be invalid. Did an event fire wrongly?'); } - Storage::copy('services/.templates/index.js', 'services/' . $service->folder . '/index.js'); - return $service; }); - - return $service; } /** @@ -97,6 +93,7 @@ class ServiceRepository 'description' => 'sometimes|required|nullable|string', 'folder' => 'sometimes|required|regex:/^[\w.-]{1,50}$/', 'startup' => 'sometimes|required|nullable|string', + 'index_file' => 'sometimes|required|string', ]); if ($validator->fails()) { @@ -104,15 +101,8 @@ class ServiceRepository } return DB::transaction(function () use ($data, $service) { - $moveFiles = (isset($data['folder']) && $data['folder'] !== $service->folder); - $oldFolder = $service->folder; - $service->fill($data)->save(); - if ($moveFiles) { - Storage::move(sprintf('services/%s/index.js', $oldFolder), sprintf('services/%s/index.js', $service->folder)); - } - return $service; }); } @@ -137,41 +127,6 @@ class ServiceRepository } $service->delete(); - Storage::deleteDirectory('services/' . $service->folder); }); } - - /** - * Updates a service file on the system. - * - * @param int $id - * @param array $data - * @return void - * - * @deprecated - */ - // public function updateFile($id, array $data) - // { - // $service = Service::findOrFail($id); - // - // $validator = Validator::make($data, [ - // 'file' => 'required|in:index', - // 'contents' => 'required|string', - // ]); - // - // if ($validator->fails()) { - // throw new DisplayValidationException($validator->errors()); - // } - // - // $filepath = 'services/' . $service->folder . '/' . $filename; - // $backup = 'services/.bak/' . str_random(12) . '.bak'; - // - // try { - // Storage::move($filepath, $backup); - // Storage::put($filepath, $data['contents']); - // } catch (\Exception $ex) { - // Storage::move($backup, $filepath); - // throw $ex; - // } - // } } diff --git a/database/migrations/2017_03_12_150648_MoveFunctionsFromFileToDatabase.php b/database/migrations/2017_03_12_150648_MoveFunctionsFromFileToDatabase.php new file mode 100644 index 000000000..bb37afd9a --- /dev/null +++ b/database/migrations/2017_03_12_150648_MoveFunctionsFromFileToDatabase.php @@ -0,0 +1,120 @@ + + * + * 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. + */ +const rfr = require('rfr'); +const _ = require('lodash'); + +const Core = rfr('src/services/index.js'); + +class Service extends Core {} + +module.exports = Service; +EOF; + + private $default_mc = << + * + * 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. + */ +const rfr = require('rfr'); +const _ = require('lodash'); + +const Core = rfr('src/services/index.js'); + +class Service extends Core { + onConsole(data) { + // Hide the output spam from Bungeecord getting pinged. + if (_.endsWith(data, '<-> InitialHandler has connected')) return; + return super.onConsole(data); + } +} + +module.exports = Service; +EOF; + + /** + * Run the migrations. + * + * @return void + */ + public function up() + { + Schema::table('services', function (Blueprint $table) { + $table->text('index_file')->after('startup'); + }); + + DB::transaction(function () { + Service::where('author', 'ptrdctyl-v040-11e6-8b77-86f30ca893d3')->where('folder', '!=', 'minecraft')->update([ + 'index_file' => $this->default, + ]); + + Service::where('author', 'ptrdctyl-v040-11e6-8b77-86f30ca893d3')->where('folder', 'minecraft')->update([ + 'index_file' => $this->default_mc, + ]); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('services', function (Blueprint $table) { + $table->dropColumn('index_file'); + }); + } +} diff --git a/resources/themes/pterodactyl/admin/services/functions.blade.php b/resources/themes/pterodactyl/admin/services/functions.blade.php new file mode 100644 index 000000000..634f38b72 --- /dev/null +++ b/resources/themes/pterodactyl/admin/services/functions.blade.php @@ -0,0 +1,88 @@ +{{-- 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') + Services → {{ $service->name }} → Functions +@endsection + +@section('content-header') +

    {{ $service->name }}Extend the default daemon functions using this service file.

    + +@endsection + +@section('content') +
    +
    + +
    +
    +
    +
    +
    +
    +

    Functions Control

    +
    +
    +
    +
    {{ $service->index_file }}
    + +
    + +
    +
    +
    +
    +@endsection + +@section('footer-scripts') + @parent + {!! Theme::js('js/vendor/ace/ace.js') !!} + {!! Theme::js('js/vendor/ace/ext-modelist.js') !!} + +@endsection diff --git a/resources/themes/pterodactyl/admin/services/view.blade.php b/resources/themes/pterodactyl/admin/services/view.blade.php index 49d6b727a..ab31abe0d 100644 --- a/resources/themes/pterodactyl/admin/services/view.blade.php +++ b/resources/themes/pterodactyl/admin/services/view.blade.php @@ -20,7 +20,7 @@ @extends('layouts.admin') @section('title') - Services: {{ $service->name }} + Services → {{ $service->name }} @endsection @section('content-header') @@ -33,6 +33,16 @@ @endsection @section('content') +
    +
    + +
    +
    diff --git a/storage/app/services/.templates/index.js b/storage/app/services/.templates/index.js deleted file mode 100644 index f6b6695d0..000000000 --- a/storage/app/services/.templates/index.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -/** - * Pterodactyl - Daemon - * 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. - */ -const rfr = require('rfr'); - -const Core = rfr('src/services/index.js'); - -class Service extends Core {} - -module.exports = Service; diff --git a/storage/app/services/minecraft/index.js b/storage/app/services/minecraft/index.js deleted file mode 100644 index b53364bec..000000000 --- a/storage/app/services/minecraft/index.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; - -/** - * Pterodactyl - Daemon - * 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. - */ -const rfr = require('rfr'); -const _ = require('lodash'); - -const Core = rfr('src/services/index.js'); - -class Service extends Core { - onConsole(data) { - // Hide the output spam from Bungeecord getting pinged. - if (_.endsWith(data, '<-> InitialHandler has connected')) return; - return super.onConsole(data); - } -} - -module.exports = Service; diff --git a/storage/app/services/srcds/index.js b/storage/app/services/srcds/index.js deleted file mode 100644 index f6b6695d0..000000000 --- a/storage/app/services/srcds/index.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -/** - * Pterodactyl - Daemon - * 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. - */ -const rfr = require('rfr'); - -const Core = rfr('src/services/index.js'); - -class Service extends Core {} - -module.exports = Service; diff --git a/storage/app/services/terraria/index.js b/storage/app/services/terraria/index.js deleted file mode 100644 index f6b6695d0..000000000 --- a/storage/app/services/terraria/index.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -/** - * Pterodactyl - Daemon - * 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. - */ -const rfr = require('rfr'); - -const Core = rfr('src/services/index.js'); - -class Service extends Core {} - -module.exports = Service; diff --git a/storage/app/services/voice/index.js b/storage/app/services/voice/index.js deleted file mode 100644 index f6b6695d0..000000000 --- a/storage/app/services/voice/index.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -/** - * Pterodactyl - Daemon - * 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. - */ -const rfr = require('rfr'); - -const Core = rfr('src/services/index.js'); - -class Service extends Core {} - -module.exports = Service; From b1b1f448e9d3fb7154f4c2ca6cd192f1647a33e8 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 12 Mar 2017 16:04:47 -0400 Subject: [PATCH 07/22] Send the correct information to daemon for new index.js setup --- app/Http/Controllers/Daemon/ServiceController.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/Daemon/ServiceController.php b/app/Http/Controllers/Daemon/ServiceController.php index fbbad9b4e..6638f30d6 100644 --- a/app/Http/Controllers/Daemon/ServiceController.php +++ b/app/Http/Controllers/Daemon/ServiceController.php @@ -24,7 +24,6 @@ namespace Pterodactyl\Http\Controllers\Daemon; -use Storage; use Illuminate\Http\Request; use Pterodactyl\Models\Service; use Pterodactyl\Models\ServiceOption; @@ -46,7 +45,7 @@ class ServiceController extends Controller foreach (Service::all() as $service) { $response[$service->folder] = [ 'main.json' => sha1($this->getConfiguration($service->id)->toJson()), - 'index.js' => sha1_file(storage_path('app/services/' . $service->folder . '/index.js')), + 'index.js' => sha1($service->index_file), ]; } @@ -66,7 +65,7 @@ class ServiceController extends Controller $service = Service::where('folder', $folder)->firstOrFail(); if ($file === 'index.js') { - return response()->file(storage_path('app/services/' . $service->folder . '/index.js')); + return response($service->index_file)->header('Content-Type', 'text/plain'); } else if ($file === 'main.json') { return response()->json($this->getConfiguration($service->id)); } From f5a4ec981d7badacffde737011e9830be852e7dc Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 12 Mar 2017 16:28:38 -0400 Subject: [PATCH 08/22] Add ability to create new service variable. --- .../Controllers/Admin/OptionController.php | 32 ++++++++++- app/Http/Routes/AdminRoutes.php | 7 +-- app/Repositories/VariableRepository.php | 24 +++++---- .../services/options/variables.blade.php | 54 +++++++++++++++++++ 4 files changed, 104 insertions(+), 13 deletions(-) diff --git a/app/Http/Controllers/Admin/OptionController.php b/app/Http/Controllers/Admin/OptionController.php index b60c0df7b..837e5a8aa 100644 --- a/app/Http/Controllers/Admin/OptionController.php +++ b/app/Http/Controllers/Admin/OptionController.php @@ -55,7 +55,7 @@ class OptionController extends Controller /** * Handles POST request to create a new option. - + * * @param Request $request * @return \Illuminate\Response\RedirectResponse */ @@ -84,6 +84,36 @@ class OptionController extends Controller return redirect()->route('admin.services.option.new')->withInput(); } + /** + * Handles POST request to create a new option variable. + * + * @param Request $request + * @param int $id The ID of the service option to assign this variable to. + * @return \Illuminate\Response\RedirectResponse + */ + public function createVariable(Request $request, $id) + { + $repo = new VariableRepository; + + try { + $variable = $repo->create($id, $request->only([ + 'name', 'description', 'env_variable', + 'default_value', 'options', 'rules', + ])); + + Alert::success('New variable successfully assigned to this service option.')->flash(); + } catch (DisplayValidationException $ex) { + return redirect()->route('admin.services.option.variables', $id)->withErrors(json_decode($ex->getMessage())); + } catch (DisplayException $ex) { + Alert::danger($ex->getMessage())->flash(); + } catch (\Exception $ex) { + Log::error($ex); + Alert::danger('An unhandled exception was encountered while attempting to process that request. This error has been logged.')->flash(); + } + + return redirect()->route('admin.services.option.variables', $id); + } + /** * Display option overview page. * diff --git a/app/Http/Routes/AdminRoutes.php b/app/Http/Routes/AdminRoutes.php index 60a6cb8ed..634568728 100644 --- a/app/Http/Routes/AdminRoutes.php +++ b/app/Http/Routes/AdminRoutes.php @@ -429,18 +429,19 @@ class AdminRoutes 'uses' => 'Admin\OptionController@viewConfiguration', ]); + $router->post('/option/{id}', 'Admin\OptionController@editConfiguration'); + $router->get('/option/{id}/variables', [ 'as' => 'admin.services.option.variables', 'uses' => 'Admin\OptionController@viewVariables', ]); + $router->post('/option/{id}/variables', 'Admin\OptionController@createVariable'); + $router->post('/option/{id}/variables/{variable}', [ 'as' => 'admin.services.option.variables.edit', 'uses' => 'Admin\OptionController@editVariable', ]); - - $router->post('/option/{id}', 'Admin\OptionController@editConfiguration'); - }); // Service Packs diff --git a/app/Repositories/VariableRepository.php b/app/Repositories/VariableRepository.php index b9421897b..91490b4b1 100644 --- a/app/Repositories/VariableRepository.php +++ b/app/Repositories/VariableRepository.php @@ -26,21 +26,28 @@ namespace Pterodactyl\Repositories; use DB; use Validator; +use Pterodactyl\Models\ServiceOption; use Pterodactyl\Models\ServiceVariable; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Exceptions\DisplayValidationException; class VariableRepository { - public function __construct() + /** + * Create a new service variable. + * + * @param int $option + * @param array $data + * @return \Pterodactyl\Models\ServiceVariable + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\DisplayValidationException + */ + public function create($option, array $data) { - // - } + $option = ServiceOption::select('id')->findOrFail($option); - public function create(array $data) - { $validator = Validator::make($data, [ - 'option_id' => 'required|numeric|exists:service_options,id', 'name' => 'required|string|min:1|max:255', 'description' => 'sometimes|nullable|string', 'env_variable' => 'required|regex:/^[\w]{1,255}$/', @@ -60,9 +67,7 @@ class VariableRepository } if (isset($data['env_variable'])) { - $search = ServiceVariable::where('env_variable', $data['env_variable']) - ->where('option_id', $variable->option_id) - ->where('id', '!=', $variable->id); + $search = ServiceVariable::where('env_variable', $data['env_variable'])->where('option_id', $option->id); if ($search->first()) { throw new DisplayException('The envionment variable name assigned to this variable must be unique for this service option.'); } @@ -72,6 +77,7 @@ class VariableRepository $data['options'] = []; } + $data['option_id'] = $option->id; $data['user_viewable'] = (in_array('user_viewable', $data['options'])); $data['user_editable'] = (in_array('user_editable', $data['options'])); $data['required'] = (in_array('required', $data['options'])); diff --git a/resources/themes/pterodactyl/admin/services/options/variables.blade.php b/resources/themes/pterodactyl/admin/services/options/variables.blade.php index c1870d3ee..c763e6cee 100644 --- a/resources/themes/pterodactyl/admin/services/options/variables.blade.php +++ b/resources/themes/pterodactyl/admin/services/options/variables.blade.php @@ -41,6 +41,7 @@
    @@ -99,6 +100,59 @@ @endforeach +
    @endsection @section('footer-scripts') From 37a723aa580819995ddc3eaefb0bb34bd41ced89 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 12 Mar 2017 16:29:04 -0400 Subject: [PATCH 09/22] Remove old views no longer in use. --- .../views/admin/services/config.blade.php | 180 -------------- .../views/admin/services/index.blade.php | 65 ----- resources/views/admin/services/new.blade.php | 95 -------- .../admin/services/options/new.blade.php | 98 -------- .../admin/services/options/view.blade.php | 211 ---------------- resources/views/admin/services/view.blade.php | 135 ----------- resources/views/errors/403.blade.php | 37 --- resources/views/errors/404.blade.php | 37 --- resources/views/layouts/admin.blade.php | 226 ------------------ 9 files changed, 1084 deletions(-) delete mode 100644 resources/views/admin/services/config.blade.php delete mode 100644 resources/views/admin/services/index.blade.php delete mode 100644 resources/views/admin/services/new.blade.php delete mode 100644 resources/views/admin/services/options/new.blade.php delete mode 100644 resources/views/admin/services/options/view.blade.php delete mode 100644 resources/views/admin/services/view.blade.php delete mode 100644 resources/views/errors/403.blade.php delete mode 100644 resources/views/errors/404.blade.php delete mode 100644 resources/views/layouts/admin.blade.php diff --git a/resources/views/admin/services/config.blade.php b/resources/views/admin/services/config.blade.php deleted file mode 100644 index 2aa814428..000000000 --- a/resources/views/admin/services/config.blade.php +++ /dev/null @@ -1,180 +0,0 @@ -{{-- 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 Service Configuration -@endsection - -@section('content') -
    - -

    Service Configuration


    - -
    -
    -
    -
    -
    -
    -
    -
    {{ $contents['json'] }}
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    {{ $contents['index'] }}
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -{!! Theme::js('js/vendor/ace/ace.js') !!} -{!! Theme::js('js/vendor/ace/ext-modelist.js') !!} - -@endsection diff --git a/resources/views/admin/services/index.blade.php b/resources/views/admin/services/index.blade.php deleted file mode 100644 index c97df4cf1..000000000 --- a/resources/views/admin/services/index.blade.php +++ /dev/null @@ -1,65 +0,0 @@ -{{-- 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 Services -@endsection - -@section('content') -
    - -

    Server Services


    - - - - - - - - - - - @foreach ($services as $service) - - - - - - - @endforeach - - - - - - - -
    Service TypeDescriptionServers
    {{ $service->name }}{!! $service->description !!}{{ $service->servers_count }}
    -
    - -@endsection diff --git a/resources/views/admin/services/new.blade.php b/resources/views/admin/services/new.blade.php deleted file mode 100644 index 1bb829e00..000000000 --- a/resources/views/admin/services/new.blade.php +++ /dev/null @@ -1,95 +0,0 @@ -{{-- 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') - New Service -@endsection - -@section('content') -
    - -

    Add New Service


    -
    -
    -
    - -
    - -

    This should be a descriptive category name that emcompasses all of the options within the service.

    -
    -
    -
    - -
    - -
    -
    -
    -
    -
    - -
    - /src/services/ - - /index.js -
    -

    This should be a unique alpha-numeric (a-z) name used to identify the service.

    -
    -
    - -
    - -
    -

    Changing this has no effect on operation of the daemon, it is simply used for display purposes on the panel. This can be changed per-option.

    -
    -
    -
    -
    - -
    - - -
    -

    This is the default startup that will be used for all servers created using this service. This can be changed per-option.

    -
    -
    -
    -
    -
    You will be able to add service options and variables once the service is created.
    - {!! csrf_field() !!} - -
    -
    -
    -
    - -@endsection diff --git a/resources/views/admin/services/options/new.blade.php b/resources/views/admin/services/options/new.blade.php deleted file mode 100644 index 5585fad0f..000000000 --- a/resources/views/admin/services/options/new.blade.php +++ /dev/null @@ -1,98 +0,0 @@ -{{-- 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') - New Service Option for {{ $service->name }} -@endsection - -@section('content') -
    - -

    Service Option Settings


    -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    -
    -
    - -
    - -
    -
    -
    - -
    - -

    Leave blank to use parent executable.

    -
    -
    -
    - -
    - -
    -
    -
    -
    -
    - -
    - -

    To use the default startup of the parent service simply leave this field blank.

    -
    -
    -
    -
    -
    -
    - {!! csrf_field() !!} - -
    -
    -
    -
    -
    - -@endsection diff --git a/resources/views/admin/services/options/view.blade.php b/resources/views/admin/services/options/view.blade.php deleted file mode 100644 index f40bc8154..000000000 --- a/resources/views/admin/services/options/view.blade.php +++ /dev/null @@ -1,211 +0,0 @@ -{{-- 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 Service Option {{ $option->name }} -@endsection - -@section('content') -
    - -
    Warning! This page contains advanced settings that the panel and daemon use to control servers. Modifying information on this page is not recommended unless you are absolutely sure of what you are doing.
    -

    Settings


    -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    -
    -
    - -
    - -
    -
    -
    - -
    - -

    Leave blank to use parent executable.

    -
    -
    -
    - -
    - -

    Changing the docker image will only effect servers created or modified after this point.

    -
    -
    -
    -
    -
    - -
    - -

    To use the default startup of the parent service simply leave this field blank.

    -
    -
    -
    -
    -
    -
    - {!! csrf_field() !!} - -
    -
    -
    -
    -

    Variables


    - @foreach($option->variables as $variable) -
    -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    -
    -
    - -
    - -

    Accessed in startup by using {{{{ $variable->env_variable }}}} prameter.

    -
    -
    -
    - -
    - -

    The default value to use for this field.

    -
    -
    -
    - -
    - -

    Regex code to use when verifying the contents of the field.

    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - {!! csrf_field() !!} - - -
    -
    -
    -
    - @endforeach -

    Servers


    - - - - - - - - - - @foreach ($option->servers as $server) - - - - - - @endforeach - -
    NameOwnerUpdated
    {{ $server->name }}{{ $server->user->email }}{{ $server->updated_at }}
    -
    - {!! $option->servers->render() !!} -
    -
    -
    -
    -
    - Deleting an option is an irreversible action. An option can only be deleted if no servers are associated with it. -
    - {!! csrf_field() !!} - {!! method_field('DELETE') !!} - -
    -
    -
    -
    - -@endsection diff --git a/resources/views/admin/services/view.blade.php b/resources/views/admin/services/view.blade.php deleted file mode 100644 index 6b660a5ac..000000000 --- a/resources/views/admin/services/view.blade.php +++ /dev/null @@ -1,135 +0,0 @@ -{{-- 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 Service -@endsection - -@section('content') -
    - -

    Service Options


    - - - - - - - - - - - @foreach($service->options as $option) - - - - - - - @endforeach - - - - - - - -
    Option NameDescriptionTagServers
    {{ $option->name }}{!! $option->description !!}{{ $option->tag }}{{ $option->servers->count() }}
    -
    -
    -
    -
    - -
    - -

    This should be a descriptive category name that emcompasses all of the options within the service.

    -
    -
    -
    - -
    - -
    -
    -
    -
    -
    - -
    - /src/services/ - - /index.js -
    -

    This should be the name of the folder on the daemon that contains all of the service logic. Changing this can have unintended effects on servers or causes errors to occur.

    -
    -
    - -
    - -
    -

    Changing this has no effect on operation of the daemon, it is simply used for display purposes on the panel. This can be changed per-option.

    -
    -
    -
    -
    - -
    - {{ $service->executable }} - -
    -

    This is the default startup that will be used for all servers created using this service. This can be changed per-option.

    -
    -
    -
    -
    - {!! csrf_field() !!} - - -
    -
    -
    -
    -
    -
    -
    -
    - Deleting a service is an irreversible action. A service can only be deleted if no servers are associated with it. -
    - {!! csrf_field() !!} - {!! method_field('DELETE') !!} - -
    -
    -
    -
    - -@endsection diff --git a/resources/views/errors/403.blade.php b/resources/views/errors/403.blade.php deleted file mode 100644 index bc83eb24a..000000000 --- a/resources/views/errors/403.blade.php +++ /dev/null @@ -1,37 +0,0 @@ -{{-- 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.master') - -@section('title', '403: Forbidden') - -@section('content') -
    -
    -
    -

    HTTP 403: Access Denied

    -
    -
    -

    You do not have permission to access that function. Please contact your server administrator to request permission.

    -
    -
    -

    -

    Take me back or go home.

    -
    -@endsection diff --git a/resources/views/errors/404.blade.php b/resources/views/errors/404.blade.php deleted file mode 100644 index bdaec2cd4..000000000 --- a/resources/views/errors/404.blade.php +++ /dev/null @@ -1,37 +0,0 @@ -{{-- 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.master') - -@section('title', '404: Not Found') - - -@section('right-nav') -@endsection - -@section('sidebar') -@endsection - -@section('content') -
    -

    404 - File Not Found

    -

    -

    Take me back or go home.

    -
    -@endsection diff --git a/resources/views/layouts/admin.blade.php b/resources/views/layouts/admin.blade.php deleted file mode 100644 index f800c1ac5..000000000 --- a/resources/views/layouts/admin.blade.php +++ /dev/null @@ -1,226 +0,0 @@ -{{-- 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. --}} - - - - @section('scripts') - - - - {!! Theme::css('css/vendor/bootstrap/bootstrap.css') !!} - {!! Theme::css('css/pterodactyl.css') !!} - {!! Theme::css('css/animate.css') !!} - {!! Theme::css('css/vendor/fontawesome/font-awesome.min.css') !!} - {!! Theme::css('css/vendor/sweetalert/sweetalert.min.css') !!} - {!! Theme::css('css/vendor/fuelux/fuelux.min.css') !!} - {!! Theme::js('js/vendor/jquery/jquery.min.js') !!} - {!! Theme::js('js/vendor/bootstrap/bootstrap.min.js') !!} - {!! Theme::js('js/vendor/sweetalert/sweetalert.min.js') !!} - {!! Theme::js('js/vendor/fuelux/fuelux.min.js') !!} - {!! Theme::js('js/admin.min.js') !!} - {!! Theme::js('js/bootstrap-notify.min.js') !!} - - @show - {{ Settings::get('company') }} - @yield('title') - - -
    - - -
    - -
    -
    -
    - @section('resp-errors') - @if (count($errors) > 0) -
    - - {{ trans('strings.whoops') }}! {{ trans('auth.errorencountered') }}

    -
      - @foreach ($errors->all() as $error) -
    • {{ $error }}
    • - @endforeach -
    -
    - @endif - @show - @section('resp-alerts') - @foreach (Alert::getMessages() as $type => $messages) - @foreach ($messages as $message) - - @endforeach - @endforeach - @show -
    -
    -
    - @yield('content') -
    -
    -
    - -
    - - - From 1897741381ea95301024489211cbc48061bdc6f0 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 12 Mar 2017 16:34:43 -0400 Subject: [PATCH 10/22] StyleCI cleanup --- app/Http/Controllers/Admin/OptionController.php | 5 ++--- app/Http/Controllers/Admin/ServiceController.php | 12 ------------ app/Http/Controllers/Daemon/ServiceController.php | 2 +- app/Models/Service.php | 2 +- app/Repositories/OptionRepository.php | 1 - app/Repositories/ServiceRepository.php | 5 +---- ...17_03_10_162934_AddNewServiceOptionsColumns.php | 4 ++-- ...2017_03_10_173607_MigrateToNewServiceSystem.php | 14 ++++++-------- ...15455_ChangeServiceVariablesValidationRules.php | 4 ++-- ...3_12_150648_MoveFunctionsFromFileToDatabase.php | 4 ++-- 10 files changed, 17 insertions(+), 36 deletions(-) diff --git a/app/Http/Controllers/Admin/OptionController.php b/app/Http/Controllers/Admin/OptionController.php index 837e5a8aa..68ddfaf9e 100644 --- a/app/Http/Controllers/Admin/OptionController.php +++ b/app/Http/Controllers/Admin/OptionController.php @@ -26,7 +26,6 @@ namespace Pterodactyl\Http\Controllers\Admin; use Log; use Alert; -use Storage; use Javascript; use Illuminate\Http\Request; use Pterodactyl\Models\Service; @@ -67,7 +66,7 @@ class OptionController extends Controller $option = $repo->create($request->intersect([ 'service_id', 'name', 'description', 'tag', 'docker_image', 'startup', 'config_from', 'config_startup', - 'config_logs', 'config_files', 'config_stop' + 'config_logs', 'config_files', 'config_stop', ])); Alert::success('Successfully created new service option.')->flash(); @@ -195,7 +194,7 @@ class OptionController extends Controller Alert::success("The service variable '{$variable->name}' has been updated.")->flash(); } else { $repo->delete($variable); - Alert::success("That service variable has been deleted.")->flash(); + Alert::success('That service variable has been deleted.')->flash(); } } catch (DisplayValidationException $ex) { return redirect()->route('admin.services.option.variables', $option)->withErrors(json_decode($ex->getMessage())); diff --git a/app/Http/Controllers/Admin/ServiceController.php b/app/Http/Controllers/Admin/ServiceController.php index 523e6bb14..bcf4d9008 100644 --- a/app/Http/Controllers/Admin/ServiceController.php +++ b/app/Http/Controllers/Admin/ServiceController.php @@ -149,16 +149,4 @@ class ServiceController extends Controller return redirect()->route($redirectTo, $id); } - - /** - * Edits function file for a service. - * - * @param Request $request - * @param int $id - * @return \Illuminate\Response\RedirectResponse - */ - public function editFunctions(Request $request, $id) - { - - } } diff --git a/app/Http/Controllers/Daemon/ServiceController.php b/app/Http/Controllers/Daemon/ServiceController.php index 6638f30d6..99d69cf02 100644 --- a/app/Http/Controllers/Daemon/ServiceController.php +++ b/app/Http/Controllers/Daemon/ServiceController.php @@ -66,7 +66,7 @@ class ServiceController extends Controller if ($file === 'index.js') { return response($service->index_file)->header('Content-Type', 'text/plain'); - } else if ($file === 'main.json') { + } elseif ($file === 'main.json') { return response()->json($this->getConfiguration($service->id)); } diff --git a/app/Models/Service.php b/app/Models/Service.php index c521b8035..96798f94c 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -51,7 +51,7 @@ class Service extends Model */ public function defaultIndexFile() { - return <<options as $option) { + foreach ($service->options as $option) { (new OptionRepository)->delete($option->id); } diff --git a/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php b/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php index 535c3b477..71af419b0 100644 --- a/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php +++ b/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php @@ -13,7 +13,7 @@ class AddNewServiceOptionsColumns extends Migration */ public function up() { - DB::transaction(function() { + DB::transaction(function () { Schema::table('service_options', function (Blueprint $table) { $table->dropColumn('executable'); @@ -35,7 +35,7 @@ class AddNewServiceOptionsColumns extends Migration */ public function down() { - DB::transaction(function() { + DB::transaction(function () { Schema::table('service_options', function (Blueprint $table) { $table->dropForeign('config_from'); diff --git a/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php b/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php index c22954150..75d775185 100644 --- a/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php +++ b/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php @@ -23,8 +23,6 @@ */ use Pterodactyl\Models\Service; use Pterodactyl\Models\ServiceOption; -use Illuminate\Support\Facades\Schema; -use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class MigrateToNewServiceSystem extends Migration @@ -69,7 +67,7 @@ class MigrateToNewServiceSystem extends Migration DB::transaction(function () use ($service) { $options = ServiceOption::where('service_id', $service->id)->get(); $options->each(function ($item) use ($options) { - switch($item->tag) { + switch ($item->tag) { case 'vanilla': $item->config_startup = '{"done": ")! For help, type ", "userInteraction": [ "Go to eula.txt for more info."]}'; $item->config_files = '{"server.properties":{"parser": "properties", "find":{"server-ip": "0.0.0.0", "enable-query": "true", "server-port": "{{server.build.default.port}}", "query.port": "{{server.build.default.port}}"}}}'; @@ -117,13 +115,13 @@ class MigrateToNewServiceSystem extends Migration $options->each(function ($item) use ($options) { if ($item->tag === 'srcds' && $item->name === 'Insurgency') { $item->tag = 'insurgency'; - } else if ($item->tag === 'srcds' && $item->name === 'Team Fortress 2') { + } elseif ($item->tag === 'srcds' && $item->name === 'Team Fortress 2') { $item->tag = 'tf2'; - } else if ($item->tag === 'srcds' && $item->name === 'Custom Source Engine Game') { + } elseif ($item->tag === 'srcds' && $item->name === 'Custom Source Engine Game') { $item->tag = 'source'; } - switch($item->tag) { + switch ($item->tag) { case 'source': $item->config_startup = '{"done": "Assigned anonymous gameserver", "userInteraction": []}'; $item->config_files = '{}'; @@ -164,7 +162,7 @@ class MigrateToNewServiceSystem extends Migration DB::transaction(function () use ($service) { $options = ServiceOption::where('service_id', $service->id)->get(); $options->each(function ($item) use ($options) { - switch($item->tag) { + switch ($item->tag) { case 'tshock': $item->startup = null; $item->config_startup = '{"done": "Type \'help\' for a list of commands", "userInteraction": []}'; @@ -195,7 +193,7 @@ class MigrateToNewServiceSystem extends Migration DB::transaction(function () use ($service) { $options = ServiceOption::where('service_id', $service->id)->get(); $options->each(function ($item) use ($options) { - switch($item->tag) { + switch ($item->tag) { case 'mumble': $item->startup = './murmur.x86 -fg'; $item->config_startup = '{"done": "Server listening on", "userInteraction": [ "Generating new server certificate"]}'; diff --git a/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php b/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php index 1ade5b53e..a9c1dae84 100644 --- a/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php +++ b/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php @@ -19,7 +19,7 @@ class ChangeServiceVariablesValidationRules extends Migration }); DB::transaction(function () { - foreach(ServiceVariable::all() as $variable) { + foreach (ServiceVariable::all() as $variable) { $variable->rules = ($variable->required) ? 'required|regex:' . $variable->rules : 'regex:' . $variable->regex; $variable->save(); } @@ -38,7 +38,7 @@ class ChangeServiceVariablesValidationRules extends Migration }); DB::transaction(function () { - foreach(ServiceVariable::all() as $variable) { + foreach (ServiceVariable::all() as $variable) { $variable->regex = str_replace(['required|regex:', 'regex:'], '', $variable->regex); $variable->save(); } diff --git a/database/migrations/2017_03_12_150648_MoveFunctionsFromFileToDatabase.php b/database/migrations/2017_03_12_150648_MoveFunctionsFromFileToDatabase.php index bb37afd9a..b58051736 100644 --- a/database/migrations/2017_03_12_150648_MoveFunctionsFromFileToDatabase.php +++ b/database/migrations/2017_03_12_150648_MoveFunctionsFromFileToDatabase.php @@ -8,7 +8,7 @@ use Illuminate\Database\Migrations\Migration; class MoveFunctionsFromFileToDatabase extends Migration { - private $default = << Date: Sun, 12 Mar 2017 16:35:25 -0400 Subject: [PATCH 11/22] More StyleCI... --- app/Repositories/OptionRepository.php | 2 +- .../2017_03_12_150648_MoveFunctionsFromFileToDatabase.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/Repositories/OptionRepository.php b/app/Repositories/OptionRepository.php index e700f98bf..b5e9510c1 100644 --- a/app/Repositories/OptionRepository.php +++ b/app/Repositories/OptionRepository.php @@ -87,7 +87,7 @@ class OptionRepository } DB::transaction(function () use ($option) { - foreach($option->variables as $variable) { + foreach ($option->variables as $variable) { (new VariableRepository)->delete($variable->id); } diff --git a/database/migrations/2017_03_12_150648_MoveFunctionsFromFileToDatabase.php b/database/migrations/2017_03_12_150648_MoveFunctionsFromFileToDatabase.php index b58051736..bbd5fda42 100644 --- a/database/migrations/2017_03_12_150648_MoveFunctionsFromFileToDatabase.php +++ b/database/migrations/2017_03_12_150648_MoveFunctionsFromFileToDatabase.php @@ -7,7 +7,6 @@ use Illuminate\Database\Migrations\Migration; class MoveFunctionsFromFileToDatabase extends Migration { - private $default = <<<'EOF' 'use strict'; From 97cad1d55e234c9cf8c27184b47678d7ba244591 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 12 Mar 2017 18:02:15 -0400 Subject: [PATCH 12/22] Update seeders to not error out and allow updating values quickly. --- app/Models/Service.php | 2 +- app/Repositories/ServiceRepository.php | 2 +- .../seeds/MinecraftServiceTableSeeder.php | 181 +++++++++++------ database/seeds/SourceServiceTableSeeder.php | 182 +++++++++++------- database/seeds/TerrariaServiceTableSeeder.php | 45 +++-- database/seeds/VoiceServiceTableSeeder.php | 67 ++++--- 6 files changed, 305 insertions(+), 174 deletions(-) diff --git a/app/Models/Service.php b/app/Models/Service.php index 96798f94c..245587afd 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -49,7 +49,7 @@ class Service extends Model * * @return string */ - public function defaultIndexFile() + public static function defaultIndexFile() { return <<<'EOF' 'use strict'; diff --git a/app/Repositories/ServiceRepository.php b/app/Repositories/ServiceRepository.php index 56bfd5148..e8d606ffc 100644 --- a/app/Repositories/ServiceRepository.php +++ b/app/Repositories/ServiceRepository.php @@ -59,7 +59,7 @@ class ServiceRepository 'description' => (isset($data['description'])) ? $data['description'] : null, 'folder' => $data['folder'], 'startup' => (isset($data['startup'])) ? $data['startup'] : null, - 'index_file' => $service->defaultIndexFile(), + 'index_file' => Service::defaultIndexFile(), ])->save(); // It is possible for an event to return false or throw an exception diff --git a/database/seeds/MinecraftServiceTableSeeder.php b/database/seeds/MinecraftServiceTableSeeder.php index 45f251ebd..7595f64bc 100644 --- a/database/seeds/MinecraftServiceTableSeeder.php +++ b/database/seeds/MinecraftServiceTableSeeder.php @@ -21,15 +21,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -use Pterodactyl\Models; use Illuminate\Database\Seeder; +use Pterodactyl\Models\Service; +use Pterodactyl\Models\ServiceOption; +use Pterodactyl\Models\ServiceVariable; class MinecraftServiceTableSeeder extends Seeder { /** * The core service ID. * - * @var Models\Service + * @var \Pterodactyl\Models\Service */ protected $service; @@ -40,6 +42,47 @@ class MinecraftServiceTableSeeder extends Seeder */ protected $option = []; + private $default_mc = <<<'EOF' +'use strict'; + +/** + * Pterodactyl - Daemon + * 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. + */ +const rfr = require('rfr'); +const _ = require('lodash'); + +const Core = rfr('src/services/index.js'); + +class Service extends Core { + onConsole(data) { + // Hide the output spam from Bungeecord getting pinged. + if (_.endsWith(data, '<-> InitialHandler has connected')) return; + return super.onConsole(data); + } +} + +module.exports = Service; +EOF; + /** * Run the database seeds. * @@ -54,55 +97,76 @@ class MinecraftServiceTableSeeder extends Seeder private function addCoreService() { - $this->service = Models\Service::create([ - 'author' => 'ptrdctyl-v040-11e6-8b77-86f30ca893d3', + $this->service = Service::updateOrCreate([ + 'author' => config('pterodactyl.service.core'), + 'folder' => 'minecraft', + ], [ 'name' => 'Minecraft', 'description' => 'Minecraft - the classic game from Mojang. With support for Vanilla MC, Spigot, and many others!', - 'file' => 'minecraft', - 'executable' => 'java', - 'startup' => '-Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}', + 'startup' => 'java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}', + 'index_file' => $this->default_mc, ]); } private function addCoreOptions() { - $this->option['vanilla'] = Models\ServiceOption::create([ + $this->option['vanilla'] = ServiceOption::updateOrCreate([ 'service_id' => $this->service->id, + 'tag' => 'vanilla', + ], [ 'name' => 'Vanilla Minecraft', 'description' => 'Minecraft is a game about placing blocks and going on adventures. Explore randomly generated worlds and build amazing things from the simplest of homes to the grandest of castles. Play in Creative Mode with unlimited resources or mine deep in Survival Mode, crafting weapons and armor to fend off dangerous mobs. Do all this alone or with friends.', - 'tag' => 'vanilla', 'docker_image' => 'quay.io/pterodactyl/minecraft', - 'executable' => null, + 'config_startup' => '{"done": ")! For help, type ", "userInteraction": [ "Go to eula.txt for more info."]}', + 'config_logs' => '{"custom": false, "location": "logs/latest.log"}', + 'config_files' => '{"server.properties":{"parser": "properties", "find":{"server-ip": "0.0.0.0", "enable-query": "true", "server-port": "{{server.build.default.port}}", "query.port": "{{server.build.default.port}}"}}}', + 'config_stop' => 'stop', + 'config_from' => null, 'startup' => null, ]); - $this->option['spigot'] = Models\ServiceOption::create([ + $this->option['spigot'] = ServiceOption::updateOrCreate([ 'service_id' => $this->service->id, + 'tag' => 'spigot', + ], [ 'name' => 'Spigot', 'description' => 'Spigot is the most widely-used modded Minecraft server software in the world. It powers many of the top Minecraft server networks around to ensure they can cope with their huge player base and ensure the satisfaction of their players. Spigot works by reducing and eliminating many causes of lag, as well as adding in handy features and settings that help make your job of server administration easier.', - 'tag' => 'spigot', 'docker_image' => 'quay.io/pterodactyl/minecraft:spigot', - 'executable' => null, - 'startup' => '-Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}', - ]); - - $this->option['sponge'] = Models\ServiceOption::create([ - 'service_id' => $this->service->id, - 'name' => 'Sponge (SpongeVanilla)', - 'description' => 'SpongeVanilla is the SpongeAPI implementation for Vanilla Minecraft.', - 'tag' => 'sponge', - 'docker_image' => 'quay.io/pterodactyl/minecraft:sponge', - 'executable' => null, + 'config_startup' => null, + 'config_files' => '{"spigot.yml":{"parser": "yaml", "find":{"settings.restart-on-crash": "false"}}}', + 'config_logs' => null, + 'config_stop' => null, + 'config_from' => $this->option['vanilla']->id, 'startup' => null, ]); - $this->option['bungeecord'] = Models\ServiceOption::create([ + $this->option['sponge'] = ServiceOption::updateOrCreate([ 'service_id' => $this->service->id, + 'tag' => 'sponge', + ], [ + 'name' => 'Sponge (SpongeVanilla)', + 'description' => 'SpongeVanilla is the SpongeAPI implementation for Vanilla Minecraft.', + 'docker_image' => 'quay.io/pterodactyl/minecraft:sponge', + 'config_startup' => '{"userInteraction": [ "You need to agree to the EULA"]}', + 'config_files' => null, + 'config_logs' => null, + 'config_stop' => null, + 'config_from' => $this->option['vanilla']->id, + 'startup' => null, + ]); + + $this->option['bungeecord'] = ServiceOption::updateOrCreate([ + 'service_id' => $this->service->id, + 'tag' => 'bungeecord', + ], [ 'name' => 'Bungeecord', 'description' => 'For a long time, Minecraft server owners have had a dream that encompasses a free, easy, and reliable way to connect multiple Minecraft servers together. BungeeCord is the answer to said dream. Whether you are a small server wishing to string multiple game-modes together, or the owner of the ShotBow Network, BungeeCord is the ideal solution for you. With the help of BungeeCord, you will be able to unlock your community\'s full potential.', - 'tag' => 'bungeecord', 'docker_image' => 'quay.io/pterodactyl/minecraft:bungeecord', - 'executable' => null, + 'config_startup' => '{"done": "Listening on ", "userInteraction": [ "Listening on /0.0.0.0:25577"]}', + 'config_files' => '{"config.yml":{"parser": "yaml", "find":{"listeners[0].query_enabled": true, "listeners[0].query_port": "{{server.build.default.port}}", "listeners[0].host": "0.0.0.0:{{server.build.default.port}}", "servers.*.address":{"127.0.0.1": "{{config.docker.interface}}", "localhost": "{{config.docker.interface}}"}}}}', + 'config_logs' => '{"custom": false, "location": "proxy.log.0"}', + 'config_stop' => 'end', + 'config_from' => null, 'startup' => null, ]); } @@ -117,121 +181,130 @@ class MinecraftServiceTableSeeder extends Seeder private function addVanillaVariables() { - Models\ServiceVariable::create([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['vanilla']->id, + 'env_variable' => 'SERVER_JARFILE', + ], [ 'name' => 'Server Jar File', 'description' => 'The name of the server jarfile to run the server with.', - 'env_variable' => 'SERVER_JARFILE', 'default_value' => 'server.jar', 'user_viewable' => 1, 'user_editable' => 1, 'required' => 1, - 'regex' => '/^([\w\d._-]+)(\.jar)$/', + 'rules' => 'required|regex:/^([\w\d._-]+)(\.jar)$/', ]); - Models\ServiceVariable::create([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['vanilla']->id, + 'env_variable' => 'VANILLA_VERSION', + ], [ 'name' => 'Server Version', 'description' => 'The version of Minecraft Vanilla to install. Use "latest" to install the latest version.', - 'env_variable' => 'VANILLA_VERSION', 'default_value' => 'latest', 'user_viewable' => 1, 'user_editable' => 1, 'required' => 1, - 'regex' => '/^(latest|[a-zA-Z0-9_\.-]{3,7})$/', + 'rules' => 'required|string|between:3,7', ]); } private function addSpigotVariables() { - Models\ServiceVariable::create([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['spigot']->id, + 'env_variable' => 'SERVER_JARFILE', + ], [ 'name' => 'Server Jar File', 'description' => 'The name of the server jarfile to run the server with.', - 'env_variable' => 'SERVER_JARFILE', 'default_value' => 'server.jar', 'user_viewable' => 1, 'user_editable' => 1, 'required' => 1, - 'regex' => '/^([\w\d._-]+)(\.jar)$/', + 'rules' => 'required|regex:/^([\w\d._-]+)(\.jar)$/', ]); - Models\ServiceVariable::create([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['spigot']->id, + 'env_variable' => 'DL_VERSION', + ], [ 'name' => 'Spigot Version', 'description' => 'The version of Spigot to download (using the --rev tag). Use "latest" for latest.', - 'env_variable' => 'DL_VERSION', 'default_value' => 'latest', 'user_viewable' => 1, 'user_editable' => 1, 'required' => 1, - 'regex' => '/^(latest|[a-zA-Z0-9_\.-]{3,7})$/', + 'rules' => 'required|string|between:3,7', ]); - Models\ServiceVariable::create([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['spigot']->id, + 'env_variable' => 'DL_PATH', + ], [ 'name' => 'Download Path', 'description' => 'A URL to use to download Spigot rather than building it on the server. This is not user viewable. Use {{DL_VERSION}} in the URL to automatically insert the assigned version into the URL. If you do not enter a URL Spigot will build directly in the container (this will fail on low memory containers).', - 'env_variable' => 'DL_PATH', 'default_value' => '', 'user_viewable' => 0, 'user_editable' => 0, 'required' => 0, - 'regex' => '/^(.*)$/', + 'rules' => 'required|string', ]); } private function addSpongeVariables() { - Models\ServiceVariable::create([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['sponge']->id, + 'env_variable' => 'SPONGE_VERSION', + ], [ 'name' => 'Sponge Version', 'description' => 'The version of SpongeVanilla to download and use.', - 'env_variable' => 'SPONGE_VERSION', - 'default_value' => '1.10.2-5.1.0-BETA-359', + 'default_value' => '1.10.2-5.2.0-BETA-381', 'user_viewable' => 1, 'user_editable' => 0, 'required' => 1, - 'regex' => '/^([a-zA-Z0-9.\-_]+)$/', + 'rules' => 'required|regex:/^([a-zA-Z0-9.\-_]+)$/', ]); - Models\ServiceVariable::create([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['sponge']->id, + 'env_variable' => 'SERVER_JARFILE', + ], [ 'name' => 'Server Jar File', 'description' => 'The name of the Jarfile to use when running SpongeVanilla.', - 'env_variable' => 'SERVER_JARFILE', 'default_value' => 'server.jar', 'user_viewable' => 1, 'user_editable' => 1, 'required' => 1, - 'regex' => '/^([\w\d._-]+)(\.jar)$/', + 'rules' => 'required|regex:/^([\w\d._-]+)(\.jar)$/', ]); } private function addBungeecordVariables() { - Models\ServiceVariable::create([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['bungeecord']->id, + 'env_variable' => 'BUNGEE_VERSION', + ], [ 'name' => 'Bungeecord Version', 'description' => 'The version of Bungeecord to download and use.', - 'env_variable' => 'BUNGEE_VERSION', 'default_value' => 'latest', 'user_viewable' => 1, 'user_editable' => 1, 'required' => 1, - 'regex' => '/^(latest|[\d]{1,6})$/', + 'rules' => 'required|alpha_num|between:1,6', ]); - Models\ServiceVariable::create([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['bungeecord']->id, + 'env_variable' => 'SERVER_JARFILE', + ], [ 'name' => 'Bungeecord Jar File', 'description' => 'The name of the Jarfile to use when running Bungeecord.', - 'env_variable' => 'SERVER_JARFILE', 'default_value' => 'bungeecord.jar', 'user_viewable' => 1, 'user_editable' => 1, 'required' => 1, - 'regex' => '/^([\w\d._-]+)(\.jar)$/', + 'rules' => 'required|regex:/^([\w\d._-]+)(\.jar)$/', ]); } } diff --git a/database/seeds/SourceServiceTableSeeder.php b/database/seeds/SourceServiceTableSeeder.php index 87623335c..d565d27d6 100644 --- a/database/seeds/SourceServiceTableSeeder.php +++ b/database/seeds/SourceServiceTableSeeder.php @@ -21,8 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -use Pterodactyl\Models; use Illuminate\Database\Seeder; +use Pterodactyl\Models\Service; +use Pterodactyl\Models\ServiceOption; +use Pterodactyl\Models\ServiceVariable; class SourceServiceTableSeeder extends Seeder { @@ -54,57 +56,78 @@ class SourceServiceTableSeeder extends Seeder private function addCoreService() { - $this->service = Models\Service::create([ - 'author' => 'ptrdctyl-v040-11e6-8b77-86f30ca893d3', + $this->service = Service::updateOrCreate([ + 'author' => config('pterodactyl.service.core'), + 'folder' => 'srcds', + ], [ 'name' => 'Source Engine', 'description' => 'Includes support for most Source Dedicated Server games.', - 'file' => 'srcds', - 'executable' => './srcds_run', - 'startup' => '-game {{SRCDS_GAME}} -console -port {{SERVER_PORT}} -strictportbind -norestart', + 'startup' => './srcds_run -game {{SRCDS_GAME}} -console -port {{SERVER_PORT}} +ip 0.0.0.0 -strictportbind -norestart', + 'index_file' => Service::defaultIndexFile(), ]); } private function addCoreOptions() { - $this->option['insurgency'] = Models\ServiceOption::create([ - 'service_id' => $this->service->id, - 'name' => 'Insurgency', - 'description' => 'Take to the streets for intense close quarters combat, where a team\'s survival depends upon securing crucial strongholds and destroying enemy supply in this multiplayer and cooperative Source Engine based experience.', - 'tag' => 'srcds', - 'docker_image' => 'quay.io/pterodactyl/srcds', - 'executable' => null, - 'startup' => '-game {{SRCDS_GAME}} -console -port {{SERVER_PORT}} +map {{SRCDS_MAP}} -strictportbind -norestart', - ]); - - $this->option['tf2'] = Models\ServiceOption::create([ - 'service_id' => $this->service->id, - 'name' => 'Team Fortress 2', - 'description' => 'Team Fortress 2 is a team-based first-person shooter multiplayer video game developed and published by Valve Corporation. It is the sequel to the 1996 mod Team Fortress for Quake and its 1999 remake.', - 'tag' => 'srcds', - 'docker_image' => 'quay.io/pterodactyl/srcds', - 'executable' => null, - 'startup' => '-game {{SRCDS_GAME}} -console -port {{SERVER_PORT}} +map {{SRCDS_MAP}} -strictportbind -norestart', - ]); - - $this->option['ark'] = Models\ServiceOption::create([ - 'service_id' => $this->service->id, - 'name' => 'Ark: Survival Evolved', - 'description' => 'As a man or woman stranded, naked, freezing, and starving on the unforgiving shores of a mysterious island called ARK, use your skill and cunning to kill or tame and ride the plethora of leviathan dinosaurs and other primeval creatures roaming the land. Hunt, harvest resources, craft items, grow crops, research technologies, and build shelters to withstand the elements and store valuables, all while teaming up with (or preying upon) hundreds of other players to survive, dominate... and escape! — Gamepedia: ARK', - 'tag' => 'ark', - 'docker_image' => 'quay.io/pterodactyl/srcds:ark', - 'executable' => './ShooterGameServer', - 'startup' => 'TheIsland?listen?ServerPassword={{ARK_PASSWORD}}?ServerAdminPassword={{ARK_ADMIN_PASSWORD}}?Port={{SERVER_PORT}}?MaxPlayers={{SERVER_MAX_PLAYERS}}', - ]); - - $this->option['custom'] = Models\ServiceOption::create([ + $this->option['source'] = ServiceOption::updateOrCreate([ 'service_id' => $this->service->id, + 'tag' => 'source', + ], [ 'name' => 'Custom Source Engine Game', 'description' => 'This option allows modifying the startup arguments and other details to run a custo SRCDS based game on the panel.', - 'tag' => 'srcds', 'docker_image' => 'quay.io/pterodactyl/srcds', - 'executable' => null, + 'config_startup' => '{"done": "Assigned anonymous gameserver", "userInteraction": []}', + 'config_files' => '{}', + 'config_logs' => '{"custom": true, "location": "logs/latest.log"}', + 'config_stop' => 'quit', + 'config_from' => null, 'startup' => null, ]); + + $this->option['insurgency'] = ServiceOption::updateOrCreate([ + 'service_id' => $this->service->id, + 'tag' => 'insurgency', + ], [ + 'name' => 'Insurgency', + 'description' => 'Take to the streets for intense close quarters combat, where a team\'s survival depends upon securing crucial strongholds and destroying enemy supply in this multiplayer and cooperative Source Engine based experience.', + 'docker_image' => 'quay.io/pterodactyl/srcds', + 'config_startup' => null, + 'config_files' => null, + 'config_logs' => null, + 'config_stop' => null, + 'config_from' => $this->option['source']->id, + 'startup' => './srcds_run -game {{SRCDS_GAME}} -console -port {{SERVER_PORT}} +map {{SRCDS_MAP}} +ip 0.0.0.0 -strictportbind -norestart', + ]); + + $this->option['tf2'] = ServiceOption::updateOrCreate([ + 'service_id' => $this->service->id, + 'tag' => 'tf2', + ], [ + 'name' => 'Team Fortress 2', + 'description' => 'Team Fortress 2 is a team-based first-person shooter multiplayer video game developed and published by Valve Corporation. It is the sequel to the 1996 mod Team Fortress for Quake and its 1999 remake.', + 'docker_image' => 'quay.io/pterodactyl/srcds', + 'config_startup' => null, + 'config_files' => null, + 'config_logs' => null, + 'config_stop' => null, + 'config_from' => $this->option['source']->id, + 'startup' => './srcds_run -game {{SRCDS_GAME}} -console -port {{SERVER_PORT}} +map {{SRCDS_MAP}} +ip 0.0.0.0 -strictportbind -norestart', + ]); + + $this->option['ark'] = ServiceOption::updateOrCreate([ + 'service_id' => $this->service->id, + 'tag' => 'ark', + ], [ + 'name' => 'Ark: Survival Evolved', + 'description' => 'As a man or woman stranded, naked, freezing, and starving on the unforgiving shores of a mysterious island called ARK, use your skill and cunning to kill or tame and ride the plethora of leviathan dinosaurs and other primeval creatures roaming the land. Hunt, harvest resources, craft items, grow crops, research technologies, and build shelters to withstand the elements and store valuables, all while teaming up with (or preying upon) hundreds of other players to survive, dominate... and escape! — Gamepedia: ARK', + 'docker_image' => 'quay.io/pterodactyl/srcds:ark', + 'config_startup' => '{"done": "Setting breakpad minidump AppID"}', + 'config_files' => null, + 'config_logs' => null, + 'config_stop' => '^C', + 'config_from' => $this->option['source']->id, + 'startup' => './ShooterGame/Binaries/Linux/ShooterGameServer TheIsland?listen?ServerPassword={{ARK_PASSWORD}}?ServerAdminPassword={{ARK_ADMIN_PASSWORD}}?Port={{SERVER_PORT}}?MaxPlayers={{SERVER_MAX_PLAYERS}}', + ]); } private function addVariables() @@ -117,145 +140,156 @@ class SourceServiceTableSeeder extends Seeder private function addInsurgencyVariables() { - Models\ServiceVariable::create([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['insurgency']->id, + 'env_variable' => 'SRCDS_APPID', + ], [ 'name' => 'Game ID', 'description' => 'The ID corresponding to the game to download and run using SRCDS.', - 'env_variable' => 'SRCDS_APPID', 'default_value' => '17705', 'user_viewable' => 1, 'user_editable' => 0, 'required' => 1, - 'regex' => '/^(17705)$/', + 'rules' => 'required|regex:/^(17705)$/', ]); - Models\ServiceVariable::create([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['insurgency']->id, + 'env_variable' => 'SRCDS_GAME', + ], [ 'name' => 'Game Name', 'description' => 'The name corresponding to the game to download and run using SRCDS.', - 'env_variable' => 'SRCDS_GAME', 'default_value' => 'insurgency', 'user_viewable' => 1, 'user_editable' => 0, 'required' => 1, - 'regex' => '/^(insurgency)$/', + 'rules' => 'required|regex:/^(insurgency)$/', ]); - Models\ServiceVariable::create([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['insurgency']->id, + 'env_variable' => 'SRCDS_MAP', + ], [ 'name' => 'Default Map', 'description' => 'The default map to use when starting the server.', - 'env_variable' => 'SRCDS_MAP', 'default_value' => 'sinjar', 'user_viewable' => 1, 'user_editable' => 1, 'required' => 1, - 'regex' => '/^(\w{1,20})$/', + 'rules' => 'required|regex:/^(\w{1,20})$/', ]); } private function addTF2Variables() { - Models\ServiceVariable::create([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['tf2']->id, + 'env_variable' => 'SRCDS_APPID', + ], [ 'name' => 'Game ID', 'description' => 'The ID corresponding to the game to download and run using SRCDS.', - 'env_variable' => 'SRCDS_APPID', 'default_value' => '232250', 'user_viewable' => 1, 'user_editable' => 0, 'required' => 1, - 'regex' => '/^(232250)$/', + 'rules' => 'required|regex:/^(232250)$/', ]); - Models\ServiceVariable::create([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['tf2']->id, + 'env_variable' => 'SRCDS_GAME', + ], [ 'name' => 'Game Name', 'description' => 'The name corresponding to the game to download and run using SRCDS.', - 'env_variable' => 'SRCDS_GAME', 'default_value' => 'tf', 'user_viewable' => 1, 'user_editable' => 0, 'required' => 1, - 'regex' => '/^(tf)$/', + 'rules' => 'required|regex:/^(tf)$/', ]); - Models\ServiceVariable::create([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['tf2']->id, + 'env_variable' => 'SRCDS_MAP', + ], [ 'name' => 'Default Map', 'description' => 'The default map to use when starting the server.', - 'env_variable' => 'SRCDS_MAP', 'default_value' => 'cp_dustbowl', 'user_viewable' => 1, 'user_editable' => 1, 'required' => 1, - 'regex' => '/^(\w{1,20})$/', + 'rules' => 'required|regex:/^(\w{1,20})$/', ]); } private function addArkVariables() { - DB::table('service_variables')->insert([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['ark']->id, + 'env_variable' => 'ARK_PASSWORD', + ], [ 'name' => 'Server Password', 'description' => 'If specified, players must provide this password to join the server.', - 'env_variable' => 'ARK_PASSWORD', 'default_value' => '', 'user_viewable' => 1, 'user_editable' => 1, 'required' => 0, - 'regex' => '/^(\w\.*)$/', + 'rules' => 'alpha_dash|between:1,100', ]); - DB::table('service_variables')->insert([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['ark']->id, + 'env_variable' => 'ARK_ADMIN_PASSWORD', + ], [ 'name' => 'Admin Password', 'description' => 'If specified, players must provide this password (via the in-game console) to gain access to administrator commands on the server.', - 'env_variable' => 'ARK_ADMIN_PASSWORD', 'default_value' => '', 'user_viewable' => 1, 'user_editable' => 1, 'required' => 0, - 'regex' => '/^(\w\.*)$/', + 'rules' => 'alpha_dash|between:1,100', ]); - DB::table('service_variables')->insert([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['ark']->id, + 'env_variable' => 'SERVER_MAX_PLAYERS', + ], [ 'name' => 'Maximum Players', 'description' => 'Specifies the maximum number of players that can play on the server simultaneously.', - 'env_variable' => 'SERVER_MAX_PLAYERS', 'default_value' => 20, 'user_viewable' => 1, 'user_editable' => 1, 'required' => 1, - 'regex' => '/^(\d{1,4})$/', + 'rules' => 'required|numeric|digits_between:1,4', ]); } private function addCustomVariables() { - Models\ServiceVariable::create([ - 'option_id' => $this->option['custom']->id, + ServiceVariable::updateOrCreate([ + 'option_id' => $this->option['source']->id, + 'env_variable' => 'SRCDS_APPID', + ], [ 'name' => 'Game ID', 'description' => 'The ID corresponding to the game to download and run using SRCDS.', - 'env_variable' => 'SRCDS_APPID', 'default_value' => '', 'user_viewable' => 1, 'user_editable' => 0, 'required' => 1, - 'regex' => '/^(\d){1,6}$/', + 'rules' => 'required|numeric|digits_between:1,6', ]); - Models\ServiceVariable::create([ - 'option_id' => $this->option['custom']->id, + ServiceVariable::updateOrCreate([ + 'option_id' => $this->option['source']->id, + 'env_variable' => 'SRCDS_GAME', + ], [ 'name' => 'Game Name', 'description' => 'The name corresponding to the game to download and run using SRCDS.', - 'env_variable' => 'SRCDS_GAME', 'default_value' => '', 'user_viewable' => 1, 'user_editable' => 0, 'required' => 1, - 'regex' => '/^(.*)$/', + 'rules' => 'required|alpha_dash|between:1,100', ]); } } diff --git a/database/seeds/TerrariaServiceTableSeeder.php b/database/seeds/TerrariaServiceTableSeeder.php index 59fd1e519..9bc442738 100644 --- a/database/seeds/TerrariaServiceTableSeeder.php +++ b/database/seeds/TerrariaServiceTableSeeder.php @@ -21,8 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -use Pterodactyl\Models; use Illuminate\Database\Seeder; +use Pterodactyl\Models\Service; +use Pterodactyl\Models\ServiceOption; +use Pterodactyl\Models\ServiceVariable; class TerrariaServiceTableSeeder extends Seeder { @@ -54,53 +56,60 @@ class TerrariaServiceTableSeeder extends Seeder private function addCoreService() { - $this->service = Models\Service::create([ - 'author' => 'ptrdctyl-v040-11e6-8b77-86f30ca893d3', + $this->service = Service::updateOrCreate([ + 'author' => config('pterodactyl.service.core'), + 'folder' => 'terraria', + ], [ 'name' => 'Terraria', 'description' => 'Terraria is a land of adventure! A land of mystery! A land that\'s yours to shape, defend, and enjoy. Your options in Terraria are limitless. Are you an action gamer with an itchy trigger finger? A master builder? A collector? An explorer? There\'s something for everyone.', - 'file' => 'terraria', - 'executable' => 'TerrariaServer.exe', - 'startup' => '-port {{SERVER_PORT}} -autocreate 2 -worldname World', + 'startup' => 'mono TerrariaServer.exe -port {{SERVER_PORT}} -autocreate 2 -worldname World', ]); } private function addCoreOptions() { - $this->option['tshock'] = Models\ServiceOption::create([ + $this->option['tshock'] = ServiceOption::updateOrCreate([ 'service_id' => $this->service->id, + 'tag' => 'tshock', + ], [ 'name' => 'Terraria Server (TShock)', 'description' => 'TShock is a server modification for Terraria, written in C#, and based upon the Terraria Server API. It uses JSON for configuration management, and offers several features not present in the Terraria Server normally.', - 'tag' => 'tshock', 'docker_image' => 'quay.io/pterodactyl/terraria:tshock', - 'executable' => '', - 'startup' => '', + 'config_startup' => '{"userInteraction": [ "You need to agree to the EULA"]}', + 'config_startup' => '{"done": "Type \'help\' for a list of commands", "userInteraction": []}', + 'config_files' => '{"tshock/config.json":{"parser": "json", "find":{"ServerPort": "{{server.build.default.port}}", "MaxSlots": "{{server.build.env.MAX_SLOTS}}"}}}', + 'config_logs' => '{"custom": false, "location": "ServerLog.txt"}', + 'config_stop' => 'exit', + 'startup' => null, ]); } private function addVariables() { - Models\ServiceVariable::create([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['tshock']->id, + 'env_variable' => 'T_VERSION', + ], [ 'name' => 'TShock Version', 'description' => 'Which version of TShock to install and use.', - 'env_variable' => 'T_VERSION', - 'default_value' => '4.3.17', + 'default_value' => '4.3.22', 'user_viewable' => 1, 'user_editable' => 1, 'required' => 1, - 'regex' => '/^([0-9_\.-]{5,10})$/', + 'rules' => 'required|regex:/^([0-9_\.-]{5,10})$/', ]); - Models\ServiceVariable::create([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['tshock']->id, + 'env_variable' => 'MAX_SLOTS', + ], [ 'name' => 'Maximum Slots', 'description' => 'Total number of slots to allow on the server.', - 'env_variable' => 'MAX_SLOTS', - 'default_value' => '20', + 'default_value' => 20, 'user_viewable' => 1, 'user_editable' => 0, 'required' => 1, - 'regex' => '/^(\d){1,3}$/', + 'rules' => 'required|numeric|digits_between:1,3', ]); } } diff --git a/database/seeds/VoiceServiceTableSeeder.php b/database/seeds/VoiceServiceTableSeeder.php index 3856ac76a..22b02b95e 100644 --- a/database/seeds/VoiceServiceTableSeeder.php +++ b/database/seeds/VoiceServiceTableSeeder.php @@ -21,15 +21,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -use Pterodactyl\Models; use Illuminate\Database\Seeder; +use Pterodactyl\Models\Service; +use Pterodactyl\Models\ServiceOption; +use Pterodactyl\Models\ServiceVariable; class VoiceServiceTableSeeder extends Seeder { /** * The core service ID. * - * @var Models\Service + * @var Service */ protected $service; @@ -54,75 +56,88 @@ class VoiceServiceTableSeeder extends Seeder private function addCoreService() { - $this->service = Models\Service::create([ - 'author' => 'ptrdctyl-v040-11e6-8b77-86f30ca893d3', + $this->service = Service::updateOrCreate([ + 'author' => config('pterodactyl.service.core'), + 'folder' => 'voice', + ], [ 'name' => 'Voice Servers', 'description' => 'Voice servers such as Mumble and Teamspeak 3.', - 'file' => 'voice', - 'executable' => '', 'startup' => '', ]); } private function addCoreOptions() { - $this->option['mumble'] = Models\ServiceOption::create([ + $this->option['mumble'] = ServiceOption::updateOrCreate([ 'service_id' => $this->service->id, + 'tag' => 'mumble', + ], [ 'name' => 'Mumble Server', 'description' => 'Mumble is an open source, low-latency, high quality voice chat software primarily intended for use while gaming.', - 'tag' => 'mumble', 'docker_image' => 'quay.io/pterodactyl/voice:mumble', - 'executable' => './murmur.x86', - 'startup' => '-fg', + 'config_startup' => '{"done": "Server listening on", "userInteraction": [ "Generating new server certificate"]}', + 'config_files' => '{"murmur.ini":{"parser": "ini", "find":{"logfile": "murmur.log", "port": "{{server.build.default.port}}", "host": "0.0.0.0", "users": "{{server.build.env.MAX_USERS}}"}}}', + 'config_logs' => '{"custom": true, "location": "logs/murmur.log"}', + 'config_stop' => '^C', + 'config_from' => null, + 'startup' => './murmur.x86 -fg', ]); - $this->option['ts3'] = Models\ServiceOption::create([ + $this->option['ts3'] = ServiceOption::updateOrCreate([ 'service_id' => $this->service->id, + 'tag' => 'ts3', + ], [ 'name' => 'Teamspeak3 Server', 'description' => 'VoIP software designed with security in mind, featuring crystal clear voice quality, endless customization options, and scalabilty up to thousands of simultaneous users.', - 'tag' => 'ts3', 'docker_image' => 'quay.io/pterodactyl/voice:ts3', - 'executable' => './ts3server_minimal_runscript.sh', - 'startup' => 'default_voice_port={{SERVER_PORT}} query_port={{SERVER_PORT}}', + 'config_startup' => '{"done": "listening on 0.0.0.0:", "userInteraction": []}', + 'config_files' => '{"ts3server.ini":{"parser": "ini", "find":{"default_voice_port": "{{server.build.default.port}}", "voice_ip": "0.0.0.0", "query_port": "{{server.build.default.port}}", "query_ip": "0.0.0.0"}}}', + 'config_logs' => '{"custom": true, "location": "logs/ts3.log"}', + 'config_stop' => '^C', + 'config_from' => null, + 'startup' => './ts3server_minimal_runscript.sh default_voice_port={{SERVER_PORT}} query_port={{SERVER_PORT}}', ]); } private function addVariables() { - Models\ServiceVariable::create([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['mumble']->id, + 'env_variable' => 'MAX_USERS', + ], [ 'name' => 'Maximum Users', 'description' => 'Maximum concurrent users on the mumble server.', - 'env_variable' => 'MAX_USERS', - 'default_value' => '100', + 'default_value' => 100, 'user_viewable' => 1, 'user_editable' => 0, 'required' => 1, - 'regex' => '/^(\d){1,6}$/', + 'rules' => 'required|numeric|digits_between:1,5', ]); - Models\ServiceVariable::create([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['mumble']->id, + 'env_variable' => 'MUMBLE_VERSION', + ], [ 'name' => 'Server Version', 'description' => 'Version of Mumble Server to download and use.', - 'env_variable' => 'MUMBLE_VERSION', - 'default_value' => '1.2.16', + 'default_value' => '1.2.19', 'user_viewable' => 1, 'user_editable' => 1, 'required' => 1, - 'regex' => '/^([0-9_\.-]{5,8})$/', + 'rules' => 'required|regex:/^([0-9_\.-]{5,8})$/', ]); - Models\ServiceVariable::create([ + ServiceVariable::updateOrCreate([ 'option_id' => $this->option['ts3']->id, + 'env_variable' => 'T_VERSION', + ], [ 'name' => 'Server Version', 'description' => 'The version of Teamspeak 3 to use when running the server.', - 'env_variable' => 'T_VERSION', - 'default_value' => '3.0.13.4', + 'default_value' => '3.1.1.1', 'user_viewable' => 1, 'user_editable' => 1, 'required' => 1, - 'regex' => '/^([0-9_\.-]{5,10})$/', + 'rules' => 'required|regex:/^([0-9_\.-]{5,10})$/', ]); } } From 63029bb39694b5bf36d3dd174a1ff7b5a2a6a5c4 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 12 Mar 2017 18:05:30 -0400 Subject: [PATCH 13/22] Clean old migration. New seeder method allows most all of this migration to occur in the seeder, aside from renaming a few duplicate keys. --- ...03_10_173607_MigrateToNewServiceSystem.php | 186 ++---------------- 1 file changed, 14 insertions(+), 172 deletions(-) diff --git a/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php b/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php index 75d775185..471ad8078 100644 --- a/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php +++ b/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php @@ -27,8 +27,6 @@ use Illuminate\Database\Migrations\Migration; class MigrateToNewServiceSystem extends Migration { - protected $services; - /** * Run the migrations. * @@ -36,12 +34,21 @@ class MigrateToNewServiceSystem extends Migration */ public function up() { - $this->services = Service::where('author', 'ptrdctyl-v040-11e6-8b77-86f30ca893d3')->get(); + $service = Service::where('author', config('pterodactyl.service.core'))->where('folder', 'srcds')->first(); + if (! $service) { + return; + } - $this->minecraft(); - $this->srcds(); - $this->terraria(); - $this->voice(); + $options = ServiceOption::where('service_id', $service->id)->get(); + $options->each(function ($item) use ($options) { + if ($item->tag === 'srcds' && $item->name === 'Insurgency') { + $item->tag = 'insurgency'; + } elseif ($item->tag === 'srcds' && $item->name === 'Team Fortress 2') { + $item->tag = 'tf2'; + } elseif ($item->tag === 'srcds' && $item->name === 'Custom Source Engine Game') { + $item->tag = 'source'; + } + }); } /** @@ -53,169 +60,4 @@ class MigrateToNewServiceSystem extends Migration { // Not doing reversals right now... } - - public function minecraft() - { - $service = $this->services->where('folder', 'minecraft')->first(); - if (! $service) { - return; - } - - // Set New Default Startup - $service->startup = 'java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}'; - - DB::transaction(function () use ($service) { - $options = ServiceOption::where('service_id', $service->id)->get(); - $options->each(function ($item) use ($options) { - switch ($item->tag) { - case 'vanilla': - $item->config_startup = '{"done": ")! For help, type ", "userInteraction": [ "Go to eula.txt for more info."]}'; - $item->config_files = '{"server.properties":{"parser": "properties", "find":{"server-ip": "0.0.0.0", "enable-query": "true", "server-port": "{{server.build.default.port}}", "query.port": "{{server.build.default.port}}"}}}'; - $item->config_logs = '{"custom": false, "location": "logs/latest.log"}'; - $item->config_stop = 'stop'; - break; - case 'spigot': - $item->startup = null; - $item->config_from = $options->where('tag', 'vanilla')->pluck('id')->pop(); - $item->config_files = '{"spigot.yml":{"parser": "yaml", "find":{"settings.restart-on-crash": "false"}}}'; - break; - case 'bungeecord': - $item->config_startup = '{"done": "Listening on ", "userInteraction": [ "Listening on /0.0.0.0:25577"]}'; - $item->config_files = '{"config.yml":{"parser": "yaml", "find":{"listeners[0].query_enabled": true, "listeners[0].query_port": "{{server.build.default.port}}", "listeners[0].host": "0.0.0.0:{{server.build.default.port}}", "servers.*.address":{"127.0.0.1": "{{config.docker.interface}}", "localhost": "{{config.docker.interface}}"}}}}'; - $item->config_logs = '{"custom": false, "location": "proxy.log.0"}'; - $item->config_stop = 'end'; - break; - case 'sponge': - $item->startup = null; - $item->config_from = $options->where('tag', 'vanilla')->pluck('id')->pop(); - $item->config_startup = '{"userInteraction": [ "You need to agree to the EULA"]}'; - break; - default: - break; - } - - $item->save(); - }); - - $service->save(); - }); - } - - public function srcds() - { - $service = $this->services->where('folder', 'srcds')->first(); - if (! $service) { - return; - } - - $service->startup = './srcds_run -game {{SRCDS_GAME}} -console -port {{SERVER_PORT}} +ip 0.0.0.0 -strictportbind -norestart'; - - DB::transaction(function () use ($service) { - $options = ServiceOption::where('service_id', $service->id)->get(); - $options->each(function ($item) use ($options) { - if ($item->tag === 'srcds' && $item->name === 'Insurgency') { - $item->tag = 'insurgency'; - } elseif ($item->tag === 'srcds' && $item->name === 'Team Fortress 2') { - $item->tag = 'tf2'; - } elseif ($item->tag === 'srcds' && $item->name === 'Custom Source Engine Game') { - $item->tag = 'source'; - } - - switch ($item->tag) { - case 'source': - $item->config_startup = '{"done": "Assigned anonymous gameserver", "userInteraction": []}'; - $item->config_files = '{}'; - $item->config_logs = '{"custom": true, "location": "logs/latest.log"}'; - $item->config_stop = 'quit'; - break; - case 'insurgency': - case 'tf2': - $item->startup = './srcds_run -game {{SRCDS_GAME}} -console -port {{SERVER_PORT}} +map {{SRCDS_MAP}} +ip 0.0.0.0 -strictportbind -norestart'; - $item->config_from = $options->where('name', 'Custom Source Engine Game')->pluck('id')->pop(); - break; - case 'ark': - $item->startup = './ShooterGame/Binaries/Linux/ShooterGameServer TheIsland?listen?ServerPassword={{ARK_PASSWORD}}?ServerAdminPassword={{ARK_ADMIN_PASSWORD}}?Port={{SERVER_PORT}}?MaxPlayers={{SERVER_MAX_PLAYERS}}'; - $item->config_from = $options->where('name', 'Custom Source Engine Game')->pluck('id')->pop(); - $item->config_startup = '{"done": "Setting breakpad minidump AppID"}'; - $item->config_stop = '^C'; - break; - default: - break; - } - - $item->save(); - }); - - $service->save(); - }); - } - - public function terraria() - { - $service = $this->services->where('folder', 'terraria')->first(); - if (! $service) { - return; - } - - $service->startup = 'mono TerrariaServer.exe -port {{SERVER_PORT}} -autocreate 2 -worldname World'; - - DB::transaction(function () use ($service) { - $options = ServiceOption::where('service_id', $service->id)->get(); - $options->each(function ($item) use ($options) { - switch ($item->tag) { - case 'tshock': - $item->startup = null; - $item->config_startup = '{"done": "Type \'help\' for a list of commands", "userInteraction": []}'; - $item->config_files = '{"tshock/config.json":{"parser": "json", "find":{"ServerPort": "{{server.build.default.port}}", "MaxSlots": "{{server.build.env.MAX_SLOTS}}"}}}'; - $item->config_logs = '{"custom": false, "location": "ServerLog.txt"}'; - $item->config_stop = 'exit'; - break; - default: - break; - } - - $item->save(); - }); - - $service->save(); - }); - } - - public function voice() - { - $service = $this->services->where('folder', 'voice')->first(); - if (! $service) { - return; - } - - $service->startup = null; - - DB::transaction(function () use ($service) { - $options = ServiceOption::where('service_id', $service->id)->get(); - $options->each(function ($item) use ($options) { - switch ($item->tag) { - case 'mumble': - $item->startup = './murmur.x86 -fg'; - $item->config_startup = '{"done": "Server listening on", "userInteraction": [ "Generating new server certificate"]}'; - $item->config_files = '{"murmur.ini":{"parser": "ini", "find":{"logfile": "murmur.log", "port": "{{server.build.default.port}}", "host": "0.0.0.0", "users": "{{server.build.env.MAX_USERS}}"}}}'; - $item->config_logs = '{"custom": true, "location": "logs/murmur.log"}'; - $item->config_stop = '^C'; - break; - case 'ts3': - $item->startup = './ts3server_minimal_runscript.sh default_voice_port={{SERVER_PORT}} query_port={{SERVER_PORT}}'; - $item->config_startup = '{"done": "listening on 0.0.0.0:", "userInteraction": []}'; - $item->config_files = '{"ts3server.ini":{"parser": "ini", "find":{"default_voice_port": "{{server.build.default.port}}", "voice_ip": "0.0.0.0", "query_port": "{{server.build.default.port}}", "query_ip": "0.0.0.0"}}}'; - $item->config_logs = '{"custom": true, "location": "logs/ts3.log"}'; - $item->config_stop = '^C'; - break; - default: - break; - } - - $item->save(); - }); - - $service->save(); - }); - } } From d67f65bb71067584f86a230f6f743559d7301828 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 12 Mar 2017 18:26:36 -0400 Subject: [PATCH 14/22] Attempt to properly handle new start lines on servers for migration --- ...5_212803_DeleteServiceExecutableOption.php | 28 ++++++++++++--- ..._10_162934_AddNewServiceOptionsColumns.php | 36 +++++++++---------- ...03_10_173607_MigrateToNewServiceSystem.php | 28 ++++++++------- 3 files changed, 54 insertions(+), 38 deletions(-) diff --git a/database/migrations/2017_03_05_212803_DeleteServiceExecutableOption.php b/database/migrations/2017_03_05_212803_DeleteServiceExecutableOption.php index 374219539..5a1df2afb 100644 --- a/database/migrations/2017_03_05_212803_DeleteServiceExecutableOption.php +++ b/database/migrations/2017_03_05_212803_DeleteServiceExecutableOption.php @@ -13,11 +13,29 @@ class DeleteServiceExecutableOption extends Migration */ public function up() { - Schema::table('services', function (Blueprint $table) { - $table->dropColumn('executable'); - $table->renameColumn('file', 'folder'); - $table->text('description')->nullable()->change(); - $table->text('startup')->nullable()->change(); + DB::transaction(function () { + Schema::table('services', function (Blueprint $table) { + $table->renameColumn('file', 'folder'); + $table->text('description')->nullable()->change(); + $table->text('startup')->nullable()->change(); + }); + + // Attempt to fix any startup commands for servers + // that we possibly can. + foreach (ServiceOption::with('servers')->get() as $option) { + $option->servers->each(function ($s) use ($option) { + $prepend = $option->display_executable; + $prepend = ($prepend === './ShooterGameServer') ? './ShooterGame/Binaries/Linux/ShooterGameServer' : $prepend; + $prepend = ($prepend === 'TerrariaServer.exe') ? 'mono TerrariaServer.exe' : $prepend; + + $s->startup = $prepend . ' ' . $s->startup; + $s->save(); + }); + } + + Schema::table('services', function (Blueprint $table) { + $table->dropColumn('executable'); + }); }); } diff --git a/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php b/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php index 71af419b0..f2c50cdd7 100644 --- a/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php +++ b/database/migrations/2017_03_10_162934_AddNewServiceOptionsColumns.php @@ -13,18 +13,16 @@ class AddNewServiceOptionsColumns extends Migration */ public function up() { - DB::transaction(function () { - Schema::table('service_options', function (Blueprint $table) { - $table->dropColumn('executable'); + Schema::table('service_options', function (Blueprint $table) { + $table->dropColumn('executable'); - $table->unsignedInteger('config_from')->nullable()->after('docker_image'); - $table->string('config_stop')->nullable()->after('docker_image'); - $table->text('config_logs')->nullable()->after('docker_image'); - $table->text('config_startup')->nullable()->after('docker_image'); - $table->text('config_files')->nullable()->after('docker_image'); + $table->unsignedInteger('config_from')->nullable()->after('docker_image'); + $table->string('config_stop')->nullable()->after('docker_image'); + $table->text('config_logs')->nullable()->after('docker_image'); + $table->text('config_startup')->nullable()->after('docker_image'); + $table->text('config_files')->nullable()->after('docker_image'); - $table->foreign('config_from')->references('id')->on('service_options'); - }); + $table->foreign('config_from')->references('id')->on('service_options'); }); } @@ -35,18 +33,16 @@ class AddNewServiceOptionsColumns extends Migration */ public function down() { - DB::transaction(function () { - Schema::table('service_options', function (Blueprint $table) { - $table->dropForeign('config_from'); + Schema::table('service_options', function (Blueprint $table) { + $table->dropForeign('config_from'); - $table->dropColumn('config_from'); - $table->dropColumn('config_stop'); - $table->dropColumn('config_logs'); - $table->dropColumn('config_startup'); - $table->dropColumn('config_files'); + $table->dropColumn('config_from'); + $table->dropColumn('config_stop'); + $table->dropColumn('config_logs'); + $table->dropColumn('config_startup'); + $table->dropColumn('config_files'); - $table->string('executable')->after('docker_image')->nullable(); - }); + $table->string('executable')->after('docker_image')->nullable(); }); } } diff --git a/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php b/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php index 471ad8078..9f935fff8 100644 --- a/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php +++ b/database/migrations/2017_03_10_173607_MigrateToNewServiceSystem.php @@ -34,20 +34,22 @@ class MigrateToNewServiceSystem extends Migration */ public function up() { - $service = Service::where('author', config('pterodactyl.service.core'))->where('folder', 'srcds')->first(); - if (! $service) { - return; - } - - $options = ServiceOption::where('service_id', $service->id)->get(); - $options->each(function ($item) use ($options) { - if ($item->tag === 'srcds' && $item->name === 'Insurgency') { - $item->tag = 'insurgency'; - } elseif ($item->tag === 'srcds' && $item->name === 'Team Fortress 2') { - $item->tag = 'tf2'; - } elseif ($item->tag === 'srcds' && $item->name === 'Custom Source Engine Game') { - $item->tag = 'source'; + DB::transaction(function () { + $service = Service::where('author', config('pterodactyl.service.core'))->where('folder', 'srcds')->first(); + if (! $service) { + return; } + + $options = ServiceOption::where('service_id', $service->id)->get(); + $options->each(function ($item) use ($options) { + if ($item->tag === 'srcds' && $item->name === 'Insurgency') { + $item->tag = 'insurgency'; + } elseif ($item->tag === 'srcds' && $item->name === 'Team Fortress 2') { + $item->tag = 'tf2'; + } elseif ($item->tag === 'srcds' && $item->name === 'Custom Source Engine Game') { + $item->tag = 'source'; + } + }); }); } From 66e94dd7c04b369b958236a8eac0302384075b26 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 12 Mar 2017 18:31:56 -0400 Subject: [PATCH 15/22] combine validation --- app/Repositories/OptionRepository.php | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/app/Repositories/OptionRepository.php b/app/Repositories/OptionRepository.php index b5e9510c1..80a0a52a7 100644 --- a/app/Repositories/OptionRepository.php +++ b/app/Repositories/OptionRepository.php @@ -130,7 +130,9 @@ class OptionRepository 'config_from' => 'sometimes|required|numeric|exists:service_options,id', ]); - $validator->sometimes('config_startup', 'required_without:config_from|json', function ($input) use ($option) { + $validator->sometimes([ + 'config_startup', 'config_logs', 'config_files', + ], 'required_without:config_from|json', function ($input) use ($option) { return ! (! $input->config_from && ! is_null($option->config_from)); }); @@ -138,14 +140,6 @@ class OptionRepository return ! (! $input->config_from && ! is_null($option->config_from)); }); - $validator->sometimes('config_logs', 'required_without:config_from|json', function ($input) use ($option) { - return ! (! $input->config_from && ! is_null($option->config_from)); - }); - - $validator->sometimes('config_files', 'required_without:config_from|json', function ($input) use ($option) { - return ! (! $input->config_from && ! is_null($option->config_from)); - }); - if ($validator->fails()) { throw new DisplayValidationException($validator->errors()); } From fcadee7e673f68aa1c36b0f5a55bb6aeb92f475e Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 12 Mar 2017 19:34:06 -0400 Subject: [PATCH 16/22] Fix front and backend views with new service variable setups --- .../Controllers/Admin/ServersController.php | 2 + .../Controllers/Server/ServerController.php | 41 ++++++------------- app/Models/Server.php | 2 +- app/Models/ServerVariable.php | 30 ++++++++++++++ app/Models/ServiceOption.php | 11 ----- app/Models/ServiceVariable.php | 28 ++++++++++--- app/Repositories/ServerRepository.php | 24 ++++++----- app/Repositories/VariableRepository.php | 2 - ..._ChangeServiceVariablesValidationRules.php | 5 +++ .../seeds/MinecraftServiceTableSeeder.php | 2 +- resources/lang/en/server.php | 2 +- .../admin/servers/view/startup.blade.php | 7 +--- .../services/options/variables.blade.php | 2 - .../server/settings/startup.blade.php | 39 +++++++----------- 14 files changed, 104 insertions(+), 93 deletions(-) diff --git a/app/Http/Controllers/Admin/ServersController.php b/app/Http/Controllers/Admin/ServersController.php index 6b1808516..56dfdbad3 100644 --- a/app/Http/Controllers/Admin/ServersController.php +++ b/app/Http/Controllers/Admin/ServersController.php @@ -493,6 +493,8 @@ class ServersController extends Controller $repo->updateStartup($id, $request->except('_token'), true); Alert::success('Startup variables were successfully modified and assigned for this server.')->flash(); + } catch (DisplayValidationException $ex) { + return redirect()->route('admin.servers.view.startup', $id)->withErrors(json_decode($ex->getMessage())); } catch (DisplayException $ex) { Alert::danger($ex->getMessage())->flash(); } catch (TransferException $ex) { diff --git a/app/Http/Controllers/Server/ServerController.php b/app/Http/Controllers/Server/ServerController.php index 7be93ff61..c2a597928 100644 --- a/app/Http/Controllers/Server/ServerController.php +++ b/app/Http/Controllers/Server/ServerController.php @@ -209,37 +209,20 @@ class ServerController extends Controller public function getStartup(Request $request, $uuid) { $server = Models\Server::byUuid($uuid); - $server->load(['allocations' => function ($query) use ($server) { - $query->where('id', $server->allocation_id); - }]); + $server->load(['node', 'allocation', 'variables.variable']); + $this->authorize('view-startup', $server); - $variables = Models\ServiceVariable::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('service_variables.option_id', $server->option_id) - ->where('server_variables.server_id', $server->id) - ->get(); - - $service = Models\Service::select( - DB::raw('IFNULL(service_options.executable, services.executable) as executable') - )->leftJoin('service_options', 'service_options.service_id', '=', 'services.id') - ->where('service_options.id', $server->option_id) - ->where('services.id', $server->service_id) - ->first(); - - $allocation = $server->allocations->pop(); - $ServerVariable = [ + $replacements = [ '{{SERVER_MEMORY}}' => $server->memory, - '{{SERVER_IP}}' => $allocation->ip, - '{{SERVER_PORT}}' => $allocation->port, + '{{SERVER_IP}}' => $server->allocation->ip, + '{{SERVER_PORT}}' => $server->allocation->port, ]; - $processed = str_replace(array_keys($ServerVariable), array_values($ServerVariable), $server->startup); - foreach ($variables as &$variable) { - $replace = ($variable->user_viewable === 1) ? $variable->a_serverValue : '[hidden]'; - $processed = str_replace('{{' . $variable->env_variable . '}}', $replace, $processed); + $processed = str_replace(array_keys($replacements), array_values($replacements), $server->startup); + foreach ($server->variables as $v) { + $replace = ($v->user_can_view) ? $v->variable_value : '[hidden]'; + $processed = str_replace('{{' . $v->variable->env_variable . '}}', $replace, $processed); } $server->js(); @@ -247,8 +230,8 @@ class ServerController extends Controller return view('server.settings.startup', [ 'server' => $server, 'node' => $server->node, - 'variables' => $variables->where('user_viewable', 1), - 'service' => $service, + 'variables' => $server->variables->where('user_can_view', true), + 'service' => $server->service, 'processedStartup' => $processed, ]); } @@ -311,6 +294,8 @@ class ServerController extends Controller $repo = new ServerRepository; $repo->updateStartup($server->id, $request->except('_token')); Alert::success('Server startup variables were successfully updated.')->flash(); + } catch (DisplayValidationException $ex) { + return redirect()->route('server.settings.startup', $uuid)->withErrors(json_decode($ex->getMessage())); } catch (DisplayException $ex) { Alert::danger($ex->getMessage())->flash(); } catch (\Exception $ex) { diff --git a/app/Models/Server.php b/app/Models/Server.php index 524708cde..4e0ae9e2c 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -111,7 +111,7 @@ class Server extends Model * @param string $uuid The Short-UUID of the server to return an object about. * @return \Illuminate\Database\Eloquent\Collection */ - public static function byUuid($uuid) + public static function byUuid($uuid, array $with = [], array $withCount = []) { if (! Auth::check()) { throw new \Exception('You must call Server:byUuid as an authenticated user.'); diff --git a/app/Models/ServerVariable.php b/app/Models/ServerVariable.php index e92c5caf2..165d5b3df 100644 --- a/app/Models/ServerVariable.php +++ b/app/Models/ServerVariable.php @@ -52,6 +52,36 @@ class ServerVariable extends Model 'variable_id' => 'integer', ]; + /** + * Determine if variable is viewable by users. + * + * @return bool + */ + public function getUserCanViewAttribute() + { + return (bool) $this->variable->user_viewable; + } + + /** + * Determine if variable is editable by users. + * + * @return bool + */ + public function getUserCanEditAttribute() + { + return (bool) $this->variable->user_editable; + } + + /** + * Determine if variable is required. + * + * @return bool + */ + public function getRequiredAttribute() + { + return $this->variable->required; + } + /** * Returns information about a given variables parent. * diff --git a/app/Models/ServiceOption.php b/app/Models/ServiceOption.php index adf646d88..521efef39 100644 --- a/app/Models/ServiceOption.php +++ b/app/Models/ServiceOption.php @@ -51,17 +51,6 @@ class ServiceOption extends Model 'service_id' => 'integer', ]; - /** - * Returns the display executable for the option and will use the parent - * service one if the option does not have one defined. - * - * @return string - */ - public function getDisplayExecutableAttribute($value) - { - return (is_null($this->executable)) ? $this->service->executable : $this->executable; - } - /** * Returns the display startup string for the option and will use the parent * service one if the option does not have one defined. diff --git a/app/Models/ServiceVariable.php b/app/Models/ServiceVariable.php index aedfc6351..7e35e48dc 100644 --- a/app/Models/ServiceVariable.php +++ b/app/Models/ServiceVariable.php @@ -47,13 +47,29 @@ class ServiceVariable extends Model * * @var array */ - protected $casts = [ - 'option_id' => 'integer', - 'user_viewable' => 'integer', - 'user_editable' => 'integer', - 'required' => 'integer', - ]; + protected $casts = [ + 'option_id' => 'integer', + 'user_viewable' => 'integer', + 'user_editable' => 'integer', + 'required' => 'integer', + ]; + /** + * Returns the display executable for the option and will use the parent + * service one if the option does not have one defined. + * + * @return string + */ + public function getRequiredAttribute($value) + { + return ($this->rules === 'required' || str_contains($this->rules, ['required|', '|required'])); + } + + /** + * Return server variables associated with this variable. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ public function serverVariable() { return $this->hasMany(ServerVariable::class, 'variable_id'); diff --git a/app/Repositories/ServerRepository.php b/app/Repositories/ServerRepository.php index 0e4e0f099..478bddc11 100644 --- a/app/Repositories/ServerRepository.php +++ b/app/Repositories/ServerRepository.php @@ -627,23 +627,25 @@ class ServerRepository foreach ($server->option->variables as &$variable) { $set = isset($data['env_' . $variable->id]); - // Variable is required but was not passed into the function. - if ($variable->required && ! $set) { - throw new DisplayException('A required variable (' . $variable->env_variable . ') was not passed in the request.'); - } - // If user is not an admin and are trying to edit a non-editable field // or an invisible field just silently skip the variable. if (! $admin && (! $variable->user_editable || ! $variable->user_viewable)) { continue; } - // Confirm value is valid when compared aganist regex. - // @TODO: switch to Laravel validation rules. - if ($set && ! is_null($variable->regex)) { - if (! preg_match($variable->regex, $data['env_' . $variable->id])) { - throw new DisplayException('The value passed for a variable (' . $variable->env_variable . ') could not be matched aganist the regex for that field (' . $variable->regex . ').'); - } + // Perform Field Validation + $validator = Validator::make([ + 'variable_value' => ($set) ? $data['env_' . $variable->id] : null, + ], [ + 'variable_value' => $variable->rules, + ]); + + if ($validator->fails()) { + throw new DisplayValidationException(json_encode( + collect([ + 'notice' => ['There was a validation error with the `' . $variable->name . '` variable.'] + ])->merge($validator->errors()->toArray()) + )); } $svar = Models\ServerVariable::firstOrNew([ diff --git a/app/Repositories/VariableRepository.php b/app/Repositories/VariableRepository.php index 91490b4b1..c707788d3 100644 --- a/app/Repositories/VariableRepository.php +++ b/app/Repositories/VariableRepository.php @@ -80,7 +80,6 @@ class VariableRepository $data['option_id'] = $option->id; $data['user_viewable'] = (in_array('user_viewable', $data['options'])); $data['user_editable'] = (in_array('user_editable', $data['options'])); - $data['required'] = (in_array('required', $data['options'])); // Remove field that isn't used. unset($data['options']); @@ -156,7 +155,6 @@ class VariableRepository $data['user_viewable'] = (in_array('user_viewable', $data['options'])); $data['user_editable'] = (in_array('user_editable', $data['options'])); - $data['required'] = (in_array('required', $data['options'])); // Remove field that isn't used. unset($data['options']); diff --git a/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php b/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php index a9c1dae84..225a1edc0 100644 --- a/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php +++ b/database/migrations/2017_03_11_215455_ChangeServiceVariablesValidationRules.php @@ -24,6 +24,10 @@ class ChangeServiceVariablesValidationRules extends Migration $variable->save(); } }); + + Schema::table('service_variables', function (Blueprint $table) { + $table->dropColumn('required'); + }); } /** @@ -35,6 +39,7 @@ class ChangeServiceVariablesValidationRules extends Migration { Schema::table('service_variables', function (Blueprint $table) { $table->renameColumn('rules', 'regex'); + $table->boolean('required')->default(true)->before('regex'); }); DB::transaction(function () { diff --git a/database/seeds/MinecraftServiceTableSeeder.php b/database/seeds/MinecraftServiceTableSeeder.php index 7595f64bc..0a004947a 100644 --- a/database/seeds/MinecraftServiceTableSeeder.php +++ b/database/seeds/MinecraftServiceTableSeeder.php @@ -246,7 +246,7 @@ EOF; 'user_viewable' => 0, 'user_editable' => 0, 'required' => 0, - 'rules' => 'required|string', + 'rules' => 'string', ]); } diff --git a/resources/lang/en/server.php b/resources/lang/en/server.php index d8cae3ae2..43913b64e 100644 --- a/resources/lang/en/server.php +++ b/resources/lang/en/server.php @@ -234,7 +234,7 @@ return [ 'edit_params' => 'Edit Parameters', 'update' => 'Update Startup Parameters', 'startup_var' => 'Startup Command Variable', - 'startup_regex' => 'Verification Regex', + 'startup_regex' => 'Input Rules', ], 'sftp' => [ 'header' => 'SFTP Configuration', diff --git a/resources/themes/pterodactyl/admin/servers/view/startup.blade.php b/resources/themes/pterodactyl/admin/servers/view/startup.blade.php index 5e4175ad2..61cb650f3 100644 --- a/resources/themes/pterodactyl/admin/servers/view/startup.blade.php +++ b/resources/themes/pterodactyl/admin/servers/view/startup.blade.php @@ -62,10 +62,7 @@
    -
    - {{ $server->option->display_executable }} - -
    +

    Edit your server's startup command here. The following variables are available by default: @{{SERVER_MEMORY}}, @{{SERVER_IP}}, and @{{SERVER_PORT}}.

    diff --git a/resources/themes/pterodactyl/admin/services/options/variables.blade.php b/resources/themes/pterodactyl/admin/services/options/variables.blade.php index c763e6cee..830bec458 100644 --- a/resources/themes/pterodactyl/admin/services/options/variables.blade.php +++ b/resources/themes/pterodactyl/admin/services/options/variables.blade.php @@ -81,7 +81,6 @@
    @@ -135,7 +134,6 @@
    diff --git a/resources/themes/pterodactyl/server/settings/startup.blade.php b/resources/themes/pterodactyl/server/settings/startup.blade.php index 981d8e60c..b9377fa6e 100644 --- a/resources/themes/pterodactyl/server/settings/startup.blade.php +++ b/resources/themes/pterodactyl/server/settings/startup.blade.php @@ -42,9 +42,8 @@

    @lang('server.config.startup.command')

    -
    - {{ $service->executable }} - +
    +
    @can('edit-startup', $server) @@ -56,35 +55,35 @@
    @can('edit-startup', $server) - @foreach($variables as $variable) + @foreach($variables as $v)
    -

    {{ $variable->name }}

    +

    {{ $v->variable->name }}

    - user_editable) - name="env_{{ $variable->id }}" + user_can_edit) + name="env_{{ $v->variable->id }}" @else readonly @endif - class="form-control" type="text" value="{{ old('env_' . $variable->id, $variable->server_value) }}" /> -

    {{ $variable->description }}

    + class="form-control" type="text" value="{{ old('env_' . $v->id, $v->variable_value) }}" /> +

    {{ $v->variable->description }}

    - @if($variable->required && $variable->user_editable) + @if($v->required && $v->user_can_edit) @lang('strings.required') - @elseif(! $variable->required && $variable->user_editable) + @elseif(! $v->required && $v->user_can_edit) @lang('strings.optional') @endif - @if(! $variable->user_editable) + @if(! $v->user_can_edit) @lang('strings.read_only') @endif

    @@ -97,14 +96,4 @@ @section('footer-scripts') @parent {!! Theme::js('js/frontend/server.socket.js') !!} - @endsection From 668dc9216a682fab45e3e504d5a88bf4bc3df857 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 12 Mar 2017 19:36:13 -0400 Subject: [PATCH 17/22] Update seeders to reflect removed table column --- database/seeds/MinecraftServiceTableSeeder.php | 9 --------- database/seeds/SourceServiceTableSeeder.php | 11 ----------- database/seeds/TerrariaServiceTableSeeder.php | 2 -- database/seeds/VoiceServiceTableSeeder.php | 3 --- 4 files changed, 25 deletions(-) diff --git a/database/seeds/MinecraftServiceTableSeeder.php b/database/seeds/MinecraftServiceTableSeeder.php index 0a004947a..d1e7b844a 100644 --- a/database/seeds/MinecraftServiceTableSeeder.php +++ b/database/seeds/MinecraftServiceTableSeeder.php @@ -190,7 +190,6 @@ EOF; 'default_value' => 'server.jar', 'user_viewable' => 1, 'user_editable' => 1, - 'required' => 1, 'rules' => 'required|regex:/^([\w\d._-]+)(\.jar)$/', ]); @@ -203,7 +202,6 @@ EOF; 'default_value' => 'latest', 'user_viewable' => 1, 'user_editable' => 1, - 'required' => 1, 'rules' => 'required|string|between:3,7', ]); } @@ -219,7 +217,6 @@ EOF; 'default_value' => 'server.jar', 'user_viewable' => 1, 'user_editable' => 1, - 'required' => 1, 'rules' => 'required|regex:/^([\w\d._-]+)(\.jar)$/', ]); @@ -232,7 +229,6 @@ EOF; 'default_value' => 'latest', 'user_viewable' => 1, 'user_editable' => 1, - 'required' => 1, 'rules' => 'required|string|between:3,7', ]); @@ -245,7 +241,6 @@ EOF; 'default_value' => '', 'user_viewable' => 0, 'user_editable' => 0, - 'required' => 0, 'rules' => 'string', ]); } @@ -261,7 +256,6 @@ EOF; 'default_value' => '1.10.2-5.2.0-BETA-381', 'user_viewable' => 1, 'user_editable' => 0, - 'required' => 1, 'rules' => 'required|regex:/^([a-zA-Z0-9.\-_]+)$/', ]); @@ -274,7 +268,6 @@ EOF; 'default_value' => 'server.jar', 'user_viewable' => 1, 'user_editable' => 1, - 'required' => 1, 'rules' => 'required|regex:/^([\w\d._-]+)(\.jar)$/', ]); } @@ -290,7 +283,6 @@ EOF; 'default_value' => 'latest', 'user_viewable' => 1, 'user_editable' => 1, - 'required' => 1, 'rules' => 'required|alpha_num|between:1,6', ]); @@ -303,7 +295,6 @@ EOF; 'default_value' => 'bungeecord.jar', 'user_viewable' => 1, 'user_editable' => 1, - 'required' => 1, 'rules' => 'required|regex:/^([\w\d._-]+)(\.jar)$/', ]); } diff --git a/database/seeds/SourceServiceTableSeeder.php b/database/seeds/SourceServiceTableSeeder.php index d565d27d6..08e941ed5 100644 --- a/database/seeds/SourceServiceTableSeeder.php +++ b/database/seeds/SourceServiceTableSeeder.php @@ -149,7 +149,6 @@ class SourceServiceTableSeeder extends Seeder 'default_value' => '17705', 'user_viewable' => 1, 'user_editable' => 0, - 'required' => 1, 'rules' => 'required|regex:/^(17705)$/', ]); @@ -162,7 +161,6 @@ class SourceServiceTableSeeder extends Seeder 'default_value' => 'insurgency', 'user_viewable' => 1, 'user_editable' => 0, - 'required' => 1, 'rules' => 'required|regex:/^(insurgency)$/', ]); @@ -175,7 +173,6 @@ class SourceServiceTableSeeder extends Seeder 'default_value' => 'sinjar', 'user_viewable' => 1, 'user_editable' => 1, - 'required' => 1, 'rules' => 'required|regex:/^(\w{1,20})$/', ]); } @@ -191,7 +188,6 @@ class SourceServiceTableSeeder extends Seeder 'default_value' => '232250', 'user_viewable' => 1, 'user_editable' => 0, - 'required' => 1, 'rules' => 'required|regex:/^(232250)$/', ]); @@ -204,7 +200,6 @@ class SourceServiceTableSeeder extends Seeder 'default_value' => 'tf', 'user_viewable' => 1, 'user_editable' => 0, - 'required' => 1, 'rules' => 'required|regex:/^(tf)$/', ]); @@ -217,7 +212,6 @@ class SourceServiceTableSeeder extends Seeder 'default_value' => 'cp_dustbowl', 'user_viewable' => 1, 'user_editable' => 1, - 'required' => 1, 'rules' => 'required|regex:/^(\w{1,20})$/', ]); } @@ -233,7 +227,6 @@ class SourceServiceTableSeeder extends Seeder 'default_value' => '', 'user_viewable' => 1, 'user_editable' => 1, - 'required' => 0, 'rules' => 'alpha_dash|between:1,100', ]); @@ -246,7 +239,6 @@ class SourceServiceTableSeeder extends Seeder 'default_value' => '', 'user_viewable' => 1, 'user_editable' => 1, - 'required' => 0, 'rules' => 'alpha_dash|between:1,100', ]); @@ -259,7 +251,6 @@ class SourceServiceTableSeeder extends Seeder 'default_value' => 20, 'user_viewable' => 1, 'user_editable' => 1, - 'required' => 1, 'rules' => 'required|numeric|digits_between:1,4', ]); } @@ -275,7 +266,6 @@ class SourceServiceTableSeeder extends Seeder 'default_value' => '', 'user_viewable' => 1, 'user_editable' => 0, - 'required' => 1, 'rules' => 'required|numeric|digits_between:1,6', ]); @@ -288,7 +278,6 @@ class SourceServiceTableSeeder extends Seeder 'default_value' => '', 'user_viewable' => 1, 'user_editable' => 0, - 'required' => 1, 'rules' => 'required|alpha_dash|between:1,100', ]); } diff --git a/database/seeds/TerrariaServiceTableSeeder.php b/database/seeds/TerrariaServiceTableSeeder.php index 9bc442738..036e42e0e 100644 --- a/database/seeds/TerrariaServiceTableSeeder.php +++ b/database/seeds/TerrariaServiceTableSeeder.php @@ -95,7 +95,6 @@ class TerrariaServiceTableSeeder extends Seeder 'default_value' => '4.3.22', 'user_viewable' => 1, 'user_editable' => 1, - 'required' => 1, 'rules' => 'required|regex:/^([0-9_\.-]{5,10})$/', ]); @@ -108,7 +107,6 @@ class TerrariaServiceTableSeeder extends Seeder 'default_value' => 20, 'user_viewable' => 1, 'user_editable' => 0, - 'required' => 1, 'rules' => 'required|numeric|digits_between:1,3', ]); } diff --git a/database/seeds/VoiceServiceTableSeeder.php b/database/seeds/VoiceServiceTableSeeder.php index 22b02b95e..2e1c26a8b 100644 --- a/database/seeds/VoiceServiceTableSeeder.php +++ b/database/seeds/VoiceServiceTableSeeder.php @@ -110,7 +110,6 @@ class VoiceServiceTableSeeder extends Seeder 'default_value' => 100, 'user_viewable' => 1, 'user_editable' => 0, - 'required' => 1, 'rules' => 'required|numeric|digits_between:1,5', ]); @@ -123,7 +122,6 @@ class VoiceServiceTableSeeder extends Seeder 'default_value' => '1.2.19', 'user_viewable' => 1, 'user_editable' => 1, - 'required' => 1, 'rules' => 'required|regex:/^([0-9_\.-]{5,8})$/', ]); @@ -136,7 +134,6 @@ class VoiceServiceTableSeeder extends Seeder 'default_value' => '3.1.1.1', 'user_viewable' => 1, 'user_editable' => 1, - 'required' => 1, 'rules' => 'required|regex:/^([0-9_\.-]{5,10})$/', ]); } From 2d90187c83b77b4d38ffd02cec7b11f9421dfba8 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 12 Mar 2017 19:38:50 -0400 Subject: [PATCH 18/22] StyleCI fixes --- app/Http/Controllers/Server/ServerController.php | 1 - app/Models/ServiceVariable.php | 13 ++++++------- app/Repositories/ServerRepository.php | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/app/Http/Controllers/Server/ServerController.php b/app/Http/Controllers/Server/ServerController.php index c2a597928..c1514e334 100644 --- a/app/Http/Controllers/Server/ServerController.php +++ b/app/Http/Controllers/Server/ServerController.php @@ -24,7 +24,6 @@ namespace Pterodactyl\Http\Controllers\Server; -use DB; use Log; use Uuid; use Alert; diff --git a/app/Models/ServiceVariable.php b/app/Models/ServiceVariable.php index 7e35e48dc..0e7746312 100644 --- a/app/Models/ServiceVariable.php +++ b/app/Models/ServiceVariable.php @@ -42,16 +42,15 @@ class ServiceVariable extends Model */ protected $guarded = ['id', 'created_at', 'updated_at']; - /** - * Cast values to correct type. - * - * @var array - */ + /** + * Cast values to correct type. + * + * @var array + */ protected $casts = [ 'option_id' => 'integer', 'user_viewable' => 'integer', 'user_editable' => 'integer', - 'required' => 'integer', ]; /** @@ -62,7 +61,7 @@ class ServiceVariable extends Model */ public function getRequiredAttribute($value) { - return ($this->rules === 'required' || str_contains($this->rules, ['required|', '|required'])); + return $this->rules === 'required' || str_contains($this->rules, ['required|', '|required']); } /** diff --git a/app/Repositories/ServerRepository.php b/app/Repositories/ServerRepository.php index 478bddc11..51806042c 100644 --- a/app/Repositories/ServerRepository.php +++ b/app/Repositories/ServerRepository.php @@ -643,7 +643,7 @@ class ServerRepository if ($validator->fails()) { throw new DisplayValidationException(json_encode( collect([ - 'notice' => ['There was a validation error with the `' . $variable->name . '` variable.'] + 'notice' => ['There was a validation error with the `' . $variable->name . '` variable.'], ])->merge($validator->errors()->toArray()) )); } From 50558db7c3a1d9d7ba25ebc3e72cba1dd53ba7d8 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Tue, 14 Mar 2017 21:18:36 -0400 Subject: [PATCH 19/22] Add initial pack creation and overview pages --- app/Http/Controllers/Admin/PackController.php | 233 +++++++----------- .../Controllers/Daemon/PackController.php | 4 +- app/Http/Routes/AdminRoutes.php | 52 ++-- app/Models/Pack.php | 100 ++++++++ app/Models/Server.php | 77 +++--- app/Models/Service.php | 2 +- app/Models/ServiceOption.php | 2 +- app/Models/ServicePack.php | 69 ------ app/Models/User.php | 5 + app/Repositories/PackRepository.php | 124 ++++++---- app/Repositories/ServerRepository.php | 2 +- ...5631_RenameServicePacksToSingluarPacks.php | 44 ++++ ...17_03_14_200326_AddLockedStatusToTable.php | 32 +++ public/js/laroute.js | 2 +- public/themes/pterodactyl/css/checkbox.css | 231 +++++++++++++++++ public/themes/pterodactyl/css/pterodactyl.css | 6 + .../pterodactyl/admin/packs/index.blade.php | 84 +++++++ .../pterodactyl/admin/packs/modal.blade.php | 47 ++++ .../pterodactyl/admin/packs/new.blade.php | 158 ++++++++++++ .../admin/services/packs/byoption.blade.php | 90 ------- .../admin/services/packs/byservice.blade.php | 67 ----- 21 files changed, 944 insertions(+), 487 deletions(-) create mode 100644 app/Models/Pack.php delete mode 100644 app/Models/ServicePack.php create mode 100644 database/migrations/2017_03_14_175631_RenameServicePacksToSingluarPacks.php create mode 100644 database/migrations/2017_03_14_200326_AddLockedStatusToTable.php create mode 100644 public/themes/pterodactyl/css/checkbox.css create mode 100644 resources/themes/pterodactyl/admin/packs/index.blade.php create mode 100644 resources/themes/pterodactyl/admin/packs/modal.blade.php create mode 100644 resources/themes/pterodactyl/admin/packs/new.blade.php delete mode 100644 resources/views/admin/services/packs/byoption.blade.php delete mode 100644 resources/views/admin/services/packs/byservice.blade.php diff --git a/app/Http/Controllers/Admin/PackController.php b/app/Http/Controllers/Admin/PackController.php index f4e577d0e..1ef60b8c8 100644 --- a/app/Http/Controllers/Admin/PackController.php +++ b/app/Http/Controllers/Admin/PackController.php @@ -27,179 +27,124 @@ namespace Pterodactyl\Http\Controllers\Admin; use Log; use Alert; use Storage; -use Pterodactyl\Models; use Illuminate\Http\Request; +use Pterodactyl\Models\Pack; +use Pterodactyl\Models\Service; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Repositories\ServiceRepository\Pack; +use Pterodactyl\Repositories\PackRepository; use Pterodactyl\Exceptions\DisplayValidationException; class PackController extends Controller { - public function __construct() + /** + * Display listing of all packs on the system. + * + * @param Request $request + * @return \Illuminate\View\View + */ + public function index(Request $request) { - // + $packs = Pack::with('option')->withCount('servers'); + + if (! is_null($request->input('query'))) { + $packs->search($request->input('query')); + } + + return view('admin.packs.index', ['packs' => $packs->paginate(50)]); } - public function listAll(Request $request) + /** + * Display new pack creation form. + * + * @param Request $request + * @return \Illuminate\View\View + */ + public function new(Request $request) { - return view('admin.services.packs.index', ['services' => Models\Service::all()]); - } - - public function listByOption(Request $request, $id) - { - return view('admin.services.packs.byoption', [ - 'option' => Models\ServiceOption::with('service', 'packs')->findOrFail($id), + return view('admin.packs.new', [ + 'services' => Service::with('options')->get(), ]); } - public function listByService(Request $request, $id) + /** + * Display new pack creation modal for use with template upload. + * + * @param Request $request + * @return \Illuminate\View\View + */ + public function newTemplate(Request $request) { - return view('admin.services.packs.byservice', [ - 'service' => Models\Service::with('options', 'options.packs')->findOrFail($id), - ]); - } - - public function new(Request $request, $opt = null) - { - return view('admin.services.packs.new', [ - 'services' => Models\Service::with('options')->get(), + return view('admin.packs.modal', [ + 'services' => Service::with('options')->get(), ]); } public function create(Request $request) { + $repo = new PackRepository; + try { - $repo = new Pack; - $pack = $repo->create($request->only([ - 'name', 'version', 'description', - 'option', 'selectable', 'visible', - 'file_upload', - ])); - Alert::success('Successfully created new service!')->flash(); - - return redirect()->route('admin.services.packs.edit', $pack->id)->withInput(); - } catch (DisplayValidationException $ex) { - return redirect()->route('admin.services.packs.new', $request->input('option'))->withErrors(json_decode($ex->getMessage()))->withInput(); - } catch (DisplayException $ex) { - Alert::danger($ex->getMessage())->flash(); - } catch (\Exception $ex) { - Log::error($ex); - Alert::danger('An error occured while attempting to add a new service pack.')->flash(); - } - - return redirect()->route('admin.services.packs.new', $request->input('option'))->withInput(); - } - - public function edit(Request $request, $id) - { - $pack = Models\ServicePack::with('option.service')->findOrFail($id); - - return view('admin.services.packs.edit', [ - 'pack' => $pack, - 'services' => Models\Service::all()->load('options'), - 'files' => Storage::files('packs/' . $pack->uuid), - ]); - } - - public function update(Request $request, $id) - { - if (! is_null($request->input('action_delete'))) { - try { - $repo = new Pack; - $repo->delete($id); - Alert::success('The requested service pack has been deleted from the system.')->flash(); - - return redirect()->route('admin.services.packs'); - } catch (DisplayException $ex) { - Alert::danger($ex->getMessage())->flash(); - } catch (\Exception $ex) { - Log::error($ex); - Alert::danger('An error occured while attempting to delete this pack.')->flash(); - } - - return redirect()->route('admin.services.packs.edit', $id); - } else { - try { - $repo = new Pack; - $repo->update($id, $request->only([ - 'name', 'version', 'description', - 'option', 'selectable', 'visible', + if ($request->input('action') === 'from_template') { + $pack = $repo->createWithTemplate($request->intersect(['option_id', 'file_upload'])); + } else { + $pack = $repo->create($request->intersect([ + 'name', 'description', 'version', 'option_id', + 'selectable', 'visible', 'locked', 'file_upload', ])); - Alert::success('Service pack has been successfully updated.')->flash(); - } catch (DisplayValidationException $ex) { - return redirect()->route('admin.services.packs.edit', $id)->withErrors(json_decode($ex->getMessage()))->withInput(); - } catch (\Exception $ex) { - Log::error($ex); - Alert::danger('An error occured while attempting to add edit this pack.')->flash(); } + Alert::success('Pack successfully created on the system.')->flash(); - return redirect()->route('admin.services.packs.edit', $id); - } - } - - public function export(Request $request, $id, $files = false) - { - $pack = Models\ServicePack::findOrFail($id); - $json = [ - 'name' => $pack->name, - 'version' => $pack->version, - 'description' => $pack->dscription, - 'selectable' => (bool) $pack->selectable, - 'visible' => (bool) $pack->visible, - ]; - - $filename = tempnam(sys_get_temp_dir(), 'pterodactyl_'); - if ((bool) $files) { - $zip = new \ZipArchive; - if (! $zip->open($filename, \ZipArchive::CREATE)) { - abort(503, 'Unable to open file for writing.'); - } - - $files = Storage::files('packs/' . $pack->uuid); - foreach ($files as $file) { - $zip->addFile(storage_path('app/' . $file), basename(storage_path('app/' . $file))); - } - - $zip->addFromString('import.json', json_encode($json, JSON_PRETTY_PRINT)); - $zip->close(); - - return response()->download($filename, 'pack-' . $pack->name . '.zip')->deleteFileAfterSend(true); - } else { - $fp = fopen($filename, 'a+'); - fwrite($fp, json_encode($json, JSON_PRETTY_PRINT)); - fclose($fp); - - return response()->download($filename, 'pack-' . $pack->name . '.json', [ - 'Content-Type' => 'application/json', - ])->deleteFileAfterSend(true); - } - } - - public function uploadForm(Request $request, $for = null) - { - return view('admin.services.packs.upload', [ - 'services' => Models\Service::all()->load('options'), - ]); - } - - public function postUpload(Request $request) - { - try { - $repo = new Pack; - $pack = $repo->createWithTemplate($request->only(['option', 'file_upload'])); - Alert::success('Successfully created new service!')->flash(); - - return redirect()->route('admin.services.packs.edit', $pack->id)->withInput(); - } catch (DisplayValidationException $ex) { - return redirect()->back()->withErrors(json_decode($ex->getMessage()))->withInput(); + return redirect()->route('admin.packs.view', $pack->id); + } catch(DisplayValidationException $ex) { + return redirect()->route('admin.packs.new')->withErrors(json_decode($ex->getMessage()))->withInput(); } catch (DisplayException $ex) { Alert::danger($ex->getMessage())->flash(); } catch (\Exception $ex) { Log::error($ex); - Alert::danger('An error occured while attempting to add a new service pack.')->flash(); + Alert::danger('An error occured while attempting to add a new service pack. This error has been logged.')->flash(); } - return redirect()->back(); + return redirect()->route('admin.packs.new')->withInput(); } + + + // public function export(Request $request, $id, $files = false) + // { + // $pack = Models\Pack::findOrFail($id); + // $json = [ + // 'name' => $pack->name, + // 'version' => $pack->version, + // 'description' => $pack->dscription, + // 'selectable' => (bool) $pack->selectable, + // 'visible' => (bool) $pack->visible, + // ]; + + // $filename = tempnam(sys_get_temp_dir(), 'pterodactyl_'); + // if ((bool) $files) { + // $zip = new \ZipArchive; + // if (! $zip->open($filename, \ZipArchive::CREATE)) { + // abort(503, 'Unable to open file for writing.'); + // } + + // $files = Storage::files('packs/' . $pack->uuid); + // foreach ($files as $file) { + // $zip->addFile(storage_path('app/' . $file), basename(storage_path('app/' . $file))); + // } + + // $zip->addFromString('import.json', json_encode($json, JSON_PRETTY_PRINT)); + // $zip->close(); + + // return response()->download($filename, 'pack-' . $pack->name . '.zip')->deleteFileAfterSend(true); + // } else { + // $fp = fopen($filename, 'a+'); + // fwrite($fp, json_encode($json, JSON_PRETTY_PRINT)); + // fclose($fp); + + // return response()->download($filename, 'pack-' . $pack->name . '.json', [ + // 'Content-Type' => 'application/json', + // ])->deleteFileAfterSend(true); + // } + // } + } diff --git a/app/Http/Controllers/Daemon/PackController.php b/app/Http/Controllers/Daemon/PackController.php index e96aa6ee5..4d35f6d77 100644 --- a/app/Http/Controllers/Daemon/PackController.php +++ b/app/Http/Controllers/Daemon/PackController.php @@ -47,7 +47,7 @@ class PackController extends Controller */ public function pull(Request $request, $uuid) { - $pack = Models\ServicePack::where('uuid', $uuid)->first(); + $pack = Models\Pack::where('uuid', $uuid)->first(); if (! $pack) { return response()->json(['error' => 'No such pack.'], 404); @@ -68,7 +68,7 @@ class PackController extends Controller */ public function hash(Request $request, $uuid) { - $pack = Models\ServicePack::where('uuid', $uuid)->first(); + $pack = Models\Pack::where('uuid', $uuid)->first(); if (! $pack) { return response()->json(['error' => 'No such pack.'], 404); diff --git a/app/Http/Routes/AdminRoutes.php b/app/Http/Routes/AdminRoutes.php index 634568728..f714f632d 100644 --- a/app/Http/Routes/AdminRoutes.php +++ b/app/Http/Routes/AdminRoutes.php @@ -453,43 +453,27 @@ class AdminRoutes 'csrf', ], ], function () use ($router) { - // $router->get('/new/{option?}', [ - // 'as' => 'admin.packs.new', - // 'uses' => 'Admin\PackController@new', - // ]); - // $router->post('/new', [ - // 'uses' => 'Admin\PackController@create', - // ]); - // $router->get('/upload/{option?}', [ - // 'as' => 'admin.packs.uploadForm', - // 'uses' => 'Admin\PackController@uploadForm', - // ]); - // $router->post('/upload', [ - // 'uses' => 'Admin\PackController@postUpload', - // ]); $router->get('/', [ 'as' => 'admin.packs', - 'uses' => 'Admin\PackController@listAll', + 'uses' => 'Admin\PackController@index', + ]); + + $router->get('/new', [ + 'as' => 'admin.packs.new', + 'uses' => 'Admin\PackController@new', + ]); + + $router->post('/new', 'Admin\PackController@create'); + + $router->get('/new/template', [ + 'as' => 'admin.packs.new.template', + 'uses' => 'Admin\PackController@newTemplate', + ]); + + $router->get('/view/{id}', [ + 'as' => 'admin.packs.view', + 'uses' => 'Admin\PackController@view', ]); - // $router->get('/for/option/{option}', [ - // 'as' => 'admin.packs.option', - // 'uses' => 'Admin\PackController@listByOption', - // ]); - // $router->get('/for/service/{service}', [ - // 'as' => 'admin.packs.service', - // 'uses' => 'Admin\PackController@listByService', - // ]); - // $router->get('/edit/{pack}', [ - // 'as' => 'admin.packs.edit', - // 'uses' => 'Admin\PackController@edit', - // ]); - // $router->post('/edit/{pack}', [ - // 'uses' => 'Admin\PackController@update', - // ]); - // $router->get('/edit/{pack}/export/{archive?}', [ - // 'as' => 'admin.packs.export', - // 'uses' => 'Admin\PackController@export', - // ]); }); } } diff --git a/app/Models/Pack.php b/app/Models/Pack.php new file mode 100644 index 000000000..5675fb25a --- /dev/null +++ b/app/Models/Pack.php @@ -0,0 +1,100 @@ +. + * + * 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\Models; + +use Illuminate\Database\Eloquent\Model; +use Nicolaslopezj\Searchable\SearchableTrait; + +class Pack extends Model +{ + use SearchableTrait; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'packs'; + + /** + * Fields that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'option_id', 'name', 'version', 'description', 'selectable', 'visible', 'locked', + ]; + + /** + * Cast values to correct type. + * + * @var array + */ + protected $casts = [ + 'option_id' => 'integer', + 'selectable' => 'boolean', + 'visible' => 'boolean', + 'locked' => 'boolean', + ]; + + /** + * Parameters for search querying. + * + * @var array + */ + protected $searchable = [ + 'columns' => [ + 'packs.name' => 10, + 'packs.uuid' => 8, + 'service_options.name' => 6, + 'service_options.tag' => 5, + 'service_options.docker_image' => 5, + 'packs.version' => 2, + ], + 'joins' => [ + 'service_options' => ['packs.option_id', 'service_options.id'], + ], + ]; + + /** + * Gets option associated with a service pack. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function option() + { + return $this->belongsTo(ServiceOption::class); + } + + /** + * Gets servers associated with a pack. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function servers() + { + return $this->hasMany(Server::class); + } +} diff --git a/app/Models/Server.php b/app/Models/Server.php index 4e0ae9e2c..c341aad70 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -65,43 +65,50 @@ class Server extends Model */ protected $guarded = ['id', 'installed', 'created_at', 'updated_at', 'deleted_at']; - /** - * Cast values to correct type. - * - * @var array - */ - protected $casts = [ - 'node_id' => 'integer', - 'suspended' => 'integer', - 'owner_id' => 'integer', - 'memory' => 'integer', - 'swap' => 'integer', - 'disk' => 'integer', - 'io' => 'integer', - 'cpu' => 'integer', - 'oom_disabled' => 'integer', - 'allocation_id' => 'integer', - 'service_id' => 'integer', - 'option_id' => 'integer', - 'pack_id' => 'integer', - 'installed' => 'integer', - ]; + /** + * Cast values to correct type. + * + * @var array + */ + protected $casts = [ + 'node_id' => 'integer', + 'suspended' => 'integer', + 'owner_id' => 'integer', + 'memory' => 'integer', + 'swap' => 'integer', + 'disk' => 'integer', + 'io' => 'integer', + 'cpu' => 'integer', + 'oom_disabled' => 'integer', + 'allocation_id' => 'integer', + 'service_id' => 'integer', + 'option_id' => 'integer', + 'pack_id' => 'integer', + 'installed' => 'integer', + ]; + /** + * Parameters for search querying. + * + * @var array + */ protected $searchable = [ - 'columns' => [ - 'servers.name' => 10, - 'servers.username' => 10, - 'servers.uuidShort' => 9, - 'servers.uuid' => 8, - 'users.email' => 6, - 'users.username' => 6, - 'nodes.name' => 2, - ], - 'joins' => [ + 'columns' => [ + 'servers.name' => 10, + 'servers.username' => 10, + 'servers.uuidShort' => 9, + 'servers.uuid' => 8, + 'packs.name' => 7, + 'users.email' => 6, + 'users.username' => 6, + 'nodes.name' => 2, + ], + 'joins' => [ + 'packs' => ['packs.id', 'servers.pack_id'], 'users' => ['users.id', 'servers.owner_id'], 'nodes' => ['nodes.id', 'servers.node_id'], - ], - ]; + ], + ]; /** * Returns a single server specified by UUID. @@ -236,11 +243,11 @@ class Server extends Model /** * Gets information for the pack associated with this server. * - * @return \Illuminate\Database\Eloquent\Relations\HasOne + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function pack() { - return $this->hasOne(ServicePack::class, 'id', 'pack_id'); + return $this->belongsTo(Pack::class); } /** diff --git a/app/Models/Service.php b/app/Models/Service.php index 245587afd..b05366882 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -105,7 +105,7 @@ EOF; public function packs() { return $this->hasManyThrough( - 'Pterodactyl\Models\ServicePack', 'Pterodactyl\Models\ServiceOption', + 'Pterodactyl\Models\Pack', 'Pterodactyl\Models\ServiceOption', 'service_id', 'option_id' ); } diff --git a/app/Models/ServiceOption.php b/app/Models/ServiceOption.php index 521efef39..a0f76df23 100644 --- a/app/Models/ServiceOption.php +++ b/app/Models/ServiceOption.php @@ -99,6 +99,6 @@ class ServiceOption extends Model */ public function packs() { - return $this->hasMany(ServicePack::class, 'option_id'); + return $this->hasMany(Pack::class, 'option_id'); } } diff --git a/app/Models/ServicePack.php b/app/Models/ServicePack.php deleted file mode 100644 index e82f72411..000000000 --- a/app/Models/ServicePack.php +++ /dev/null @@ -1,69 +0,0 @@ -. - * - * 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\Models; - -use Illuminate\Database\Eloquent\Model; - -class ServicePack extends Model -{ - /** - * The table associated with the model. - * - * @var string - */ - protected $table = 'service_packs'; - - /** - * Fields that are not mass assignable. - * - * @var array - */ - protected $guarded = ['id', 'created_at', 'updated_at']; - - /** - * Cast values to correct type. - * - * @var array - */ - protected $casts = [ - 'option' => 'integer', - 'build_memory' => 'integer', - 'build_swap' => 'integer', - 'build_cpu' => 'integer', - 'build_io' => 'integer', - 'selectable' => 'boolean', - 'visible' => 'boolean', - ]; - - /** - * Gets option associated with a service pack. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function option() - { - return $this->belongsTo(ServiceOption::class); - } -} diff --git a/app/Models/User.php b/app/Models/User.php index cb78ad4a8..62ced4e72 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -88,6 +88,11 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac */ protected $hidden = ['password', 'remember_token', 'totp_secret']; + /** + * Parameters for search querying. + * + * @var array + */ protected $searchable = [ 'columns' => [ 'email' => 10, diff --git a/app/Repositories/PackRepository.php b/app/Repositories/PackRepository.php index 842bfdac6..3fc6b2571 100644 --- a/app/Repositories/PackRepository.php +++ b/app/Repositories/PackRepository.php @@ -28,31 +28,36 @@ use DB; use Uuid; use Storage; use Validator; -use Pterodactyl\Models; +use Pterodactyl\Models\Pack; use Pterodactyl\Services\UuidService; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Exceptions\DisplayValidationException; class PackRepository { - public function __construct() - { - // - } - + /** + * Creates a new pack on the system. + * + * @param array $data + * @return \Pterodactyl\Models\Pack + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\DisplayValidationException + */ public function create(array $data) { $validator = Validator::make($data, [ 'name' => 'required|string', 'version' => 'required|string', 'description' => 'sometimes|nullable|string', - 'option' => 'required|exists:service_options,id', - 'selectable' => 'sometimes|boolean', - 'visible' => 'sometimes|boolean', + 'selectable' => 'sometimes|required|boolean', + 'visible' => 'sometimes|required|boolean', + 'locked' => 'sometimes|required|boolean', + 'option_id' => 'required|exists:service_options,id', ]); if ($validator->fails()) { - throw new DisplayValidationException($validator->errors()); + throw new DisplayValidationException(json_encode($validator->errors())); } if (isset($data['file_upload'])) { @@ -65,33 +70,42 @@ class PackRepository } } - DB::beginTransaction(); - try { - $uuid = new UuidService; - $pack = Models\ServicePack::create([ - 'option_id' => $data['option'], - 'uuid' => $uuid->generate('service_packs', 'uuid'), + return DB::transaction(function () use ($data) { + $uuid = new UuidService(); + + $pack = new Pack; + $pack->uuid = $uuid->generate('packs', 'uuid'); + $pack->fill([ + 'option_id' => $data['option_id'], 'name' => $data['name'], 'version' => $data['version'], 'description' => (empty($data['description'])) ? null : $data['description'], 'selectable' => isset($data['selectable']), 'visible' => isset($data['visible']), - ]); + 'locked' => isset($data['locked']), + ])->save(); + + if (! $pack->exists) { + throw new DisplayException('Model does not exist after creation. Did an event prevent it from saving?'); + } Storage::makeDirectory('packs/' . $pack->uuid); if (isset($data['file_upload'])) { $data['file_upload']->storeAs('packs/' . $pack->uuid, 'archive.tar.gz'); } - DB::commit(); - } catch (\Exception $ex) { - DB::rollBack(); - throw $ex; - } - - return $pack; + return $pack; + }); } + /** + * Creates a new pack on the system given a template file. + * + * @param array $data + * @return \Pterodactyl\Models\Pack + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ public function createWithTemplate(array $data) { if (! isset($data['file_upload'])) { @@ -127,9 +141,10 @@ class PackRepository 'name' => $json->name, 'version' => $json->version, 'description' => $json->description, - 'option' => $data['option'], + 'option_id' => $data['option_id'], 'selectable' => $json->selectable, 'visible' => $json->visible, + 'locked' => $json->locked, ]); if (! $zip->extractTo(storage_path('app/packs/' . $pack->uuid), 'archive.tar.gz')) { @@ -147,42 +162,67 @@ class PackRepository 'name' => $json->name, 'version' => $json->version, 'description' => $json->description, - 'option' => $data['option'], + 'option_id' => $data['option_id'], 'selectable' => $json->selectable, 'visible' => $json->visible, + 'locked' => $json->locked, ]); } } + /** + * Updates a pack on the system. + * + * @param int $id + * @param array $data + * @return \Pterodactyl\Models\Pack + * + * @throws \Pterodactyl\Exceptions\DisplayValidationException + */ public function update($id, array $data) { $validator = Validator::make($data, [ - 'name' => 'required|string', - 'version' => 'required|string', - 'description' => 'string', - 'option' => 'required|exists:service_options,id', - 'selectable' => 'sometimes|boolean', - 'visible' => 'sometimes|boolean', + 'name' => 'sometimes|required|string', + 'version' => 'sometimes|required|string', + 'description' => 'sometimes|string', + 'selectable' => 'sometimes|required|boolean', + 'visible' => 'sometimes|required|boolean', + 'locked' => 'sometimes|required|boolean', ]); if ($validator->fails()) { - throw new DisplayValidationException($validator->errors()); + throw new DisplayValidationException(json_encode($validator->errors())); } - Models\ServicePack::findOrFail($id)->update([ - 'option_id' => $data['option'], - 'name' => $data['name'], - 'version' => $data['version'], + $pack = Pack::findOrFail($id); + $pack->fill([ + 'name' => isset($data['name']) ? $data['name'] : $pack->name, + 'version' => isset($data['version']) ? $data['version'] : $pack->version, 'description' => (empty($data['description'])) ? null : $data['description'], - 'selectable' => isset($data['selectable']), - 'visible' => isset($data['visible']), - ]); + 'selectable' => isset($data['selectable']) ? $data['selectable'] : $data->selectable, + 'visible' => isset($data['visible']) ? $data['visible'] : $data->visible, + 'locked' => isset($data['locked']) ? $data['locked'] : $data->locked, + ])->save(); + + return $pack; } + /** + * Deletes a pack and files from the system. + * + * @param int $id + * @return void + * + * @throws \Pterodactyl\Exceptions\DisplayException + */ public function delete($id) { - $pack = Models\ServicePack::findOrFail($id); - // @TODO Check for linked servers; foreign key should block this. + $pack = Models\Pack::withCount('servers')->findOrFail($id); + + if ($pack->servers_count > 0) { + throw new DisplayException('Cannot delete a pack from the system if servers are assocaited with it.'); + } + DB::transaction(function () use ($pack) { $pack->delete(); Storage::deleteDirectory('packs/' . $pack->uuid); diff --git a/app/Repositories/ServerRepository.php b/app/Repositories/ServerRepository.php index 51806042c..f95d45431 100644 --- a/app/Repositories/ServerRepository.php +++ b/app/Repositories/ServerRepository.php @@ -156,7 +156,7 @@ class ServerRepository if (! isset($data['pack_id']) || (int) $data['pack_id'] < 1) { $data['pack_id'] = null; } else { - $pack = Models\ServicePack::where('id', $data['pack_id'])->where('option_id', $data['option_id'])->first(); + $pack = Models\Pack::where('id', $data['pack_id'])->where('option_id', $data['option_id'])->first(); if (! $pack) { throw new DisplayException('The requested service pack does not seem to exist for this combination.'); } diff --git a/database/migrations/2017_03_14_175631_RenameServicePacksToSingluarPacks.php b/database/migrations/2017_03_14_175631_RenameServicePacksToSingluarPacks.php new file mode 100644 index 000000000..910fb79df --- /dev/null +++ b/database/migrations/2017_03_14_175631_RenameServicePacksToSingluarPacks.php @@ -0,0 +1,44 @@ +dropForeign(['option_id']); + }); + + Schema::rename('service_packs', 'packs'); + + Schema::table('packs', function (Blueprint $table) { + $table->foreign('option_id')->references('id')->on('service_options'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('packs', function (Blueprint $table) { + $table->dropForeign(['option_id']); + }); + + Schema::rename('packs', 'service_packs'); + + Schema::table('service_packs', function (Blueprint $table) { + $table->foreign('option_id')->references('id')->on('service_options'); + }); + } +} diff --git a/database/migrations/2017_03_14_200326_AddLockedStatusToTable.php b/database/migrations/2017_03_14_200326_AddLockedStatusToTable.php new file mode 100644 index 000000000..4916cd028 --- /dev/null +++ b/database/migrations/2017_03_14_200326_AddLockedStatusToTable.php @@ -0,0 +1,32 @@ +boolean('locked')->default(false)->after('visible'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('packs', function (Blueprint $table) { + $table->dropColumn('locked'); + }); + } +} diff --git a/public/js/laroute.js b/public/js/laroute.js index b4f553912..dde3acaf8 100644 --- a/public/js/laroute.js +++ b/public/js/laroute.js @@ -6,7 +6,7 @@ absolute: false, rootUrl: 'http://pterodactyl.app', - routes : [{"host":null,"methods":["GET","HEAD"],"uri":"admin","name":"admin.index","action":"Pterodactyl\Http\Controllers\Admin\BaseController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/settings","name":"admin.settings","action":"Pterodactyl\Http\Controllers\Admin\BaseController@getSettings"},{"host":null,"methods":["POST"],"uri":"admin\/settings","name":null,"action":"Pterodactyl\Http\Controllers\Admin\BaseController@postSettings"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users","name":"admin.users","action":"Pterodactyl\Http\Controllers\Admin\UserController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/accounts.json","name":"admin.users.json","action":"Pterodactyl\Http\Controllers\Admin\UserController@getJson"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/view\/{id}","name":"admin.users.view","action":"Pterodactyl\Http\Controllers\Admin\UserController@getView"},{"host":null,"methods":["POST"],"uri":"admin\/users\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@updateUser"},{"host":null,"methods":["DELETE"],"uri":"admin\/users\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@deleteUser"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/new","name":"admin.users.new","action":"Pterodactyl\Http\Controllers\Admin\UserController@getNew"},{"host":null,"methods":["POST"],"uri":"admin\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@postNew"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers","name":"admin.servers","action":"Pterodactyl\Http\Controllers\Admin\ServersController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/new","name":"admin.servers.new","action":"Pterodactyl\Http\Controllers\Admin\ServersController@new"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@create"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/new\/nodes","name":"admin.servers.new.nodes","action":"Pterodactyl\Http\Controllers\Admin\ServersController@newServerNodes"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}","name":"admin.servers.view","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}\/details","name":"admin.servers.view.details","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDetails"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/details","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@setDetails"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/details\/container","name":"admin.servers.view.details.container","action":"Pterodactyl\Http\Controllers\Admin\ServersController@setContainer"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}\/build","name":"admin.servers.view.build","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewBuild"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/build","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@updateBuild"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}\/startup","name":"admin.servers.view.startup","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewStartup"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/startup","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@saveStartup"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}\/database","name":"admin.servers.view.database","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDatabase"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/database","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@newDatabase"},{"host":null,"methods":["PATCH"],"uri":"admin\/servers\/view\/{id}\/database","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@resetDatabasePassword"},{"host":null,"methods":["DELETE"],"uri":"admin\/servers\/view\/{id}\/database\/{database}\/delete","name":"admin.servers.view.database.delete","action":"Pterodactyl\Http\Controllers\Admin\ServersController@deleteDatabase"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}\/manage","name":"admin.servers.view.manage","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewManage"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/manage\/toggle","name":"admin.servers.view.manage.toggle","action":"Pterodactyl\Http\Controllers\Admin\ServersController@toggleInstall"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/manage\/rebuild","name":"admin.servers.view.manage.rebuild","action":"Pterodactyl\Http\Controllers\Admin\ServersController@rebuildContainer"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/manage\/suspension","name":"admin.servers.view.manage.suspension","action":"Pterodactyl\Http\Controllers\Admin\ServersController@manageSuspension"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}\/delete","name":"admin.servers.view.delete","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDelete"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/delete","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@delete"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/delete\/continue\/{force?}","name":"admin.servers.view.delete.continue","action":"Pterodactyl\Http\Controllers\Admin\ServersController@continueDeletion"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/delete\/cancel","name":"admin.servers.view.delete.cancel","action":"Pterodactyl\Http\Controllers\Admin\ServersController@cancelDeletion"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes","name":"admin.nodes","action":"Pterodactyl\Http\Controllers\Admin\NodesController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/new","name":"admin.nodes.new","action":"Pterodactyl\Http\Controllers\Admin\NodesController@new"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}","name":"admin.nodes.view","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}\/settings","name":"admin.nodes.view.settings","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewSettings"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{id}\/settings","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@updateSettings"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}\/configuration","name":"admin.nodes.view.configuration","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewConfiguration"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}\/allocation","name":"admin.nodes.view.allocation","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewAllocation"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{id}\/allocation","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@createAllocation"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}\/servers","name":"admin.nodes.view.servers","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewServers"},{"host":null,"methods":["DELETE"],"uri":"admin\/nodes\/view\/{id}\/delete","name":"admin.nodes.view.delete","action":"Pterodactyl\Http\Controllers\Admin\NodesController@delete"},{"host":null,"methods":["DELETE"],"uri":"admin\/nodes\/view\/{id}\/allocation\/remove\/{allocation}","name":"admin.nodes.view.allocation.removeSingle","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationRemoveSingle"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{id}\/allocation\/remove","name":"admin.nodes.view.allocation.removeBlock","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationRemoveBlock"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{id}\/allocation\/alias","name":"admin.nodes.view.allocation.setAlias","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationSetAlias"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}\/settings\/token","name":"admin.nodes.view.configuration.token","action":"Pterodactyl\Http\Controllers\Admin\NodesController@setToken"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/locations","name":"admin.locations","action":"Pterodactyl\Http\Controllers\Admin\LocationsController@getIndex"},{"host":null,"methods":["DELETE"],"uri":"admin\/locations\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationsController@deleteLocation"},{"host":null,"methods":["PATCH"],"uri":"admin\/locations\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationsController@patchLocation"},{"host":null,"methods":["POST"],"uri":"admin\/locations","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationsController@postLocation"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/databases","name":"admin.databases","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/databases\/new","name":"admin.databases.new","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@getNew"},{"host":null,"methods":["POST"],"uri":"admin\/databases\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@postNew"},{"host":null,"methods":["DELETE"],"uri":"admin\/databases\/delete\/{id}","name":"admin.databases.delete","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@deleteDatabase"},{"host":null,"methods":["DELETE"],"uri":"admin\/databases\/delete-server\/{id}","name":"admin.databases.delete-server","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@deleteServer"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services","name":"admin.services","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/new","name":"admin.services.new","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@getNew"},{"host":null,"methods":["POST"],"uri":"admin\/services\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postNew"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/service\/{id}","name":"admin.services.service","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@getService"},{"host":null,"methods":["POST"],"uri":"admin\/services\/service\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postService"},{"host":null,"methods":["DELETE"],"uri":"admin\/services\/service\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@deleteService"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/service\/{id}\/configuration","name":"admin.services.service.config","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@getConfiguration"},{"host":null,"methods":["POST"],"uri":"admin\/services\/service\/{id}\/configuration","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postConfiguration"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/service\/{service}\/option\/new","name":"admin.services.option.new","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@newOption"},{"host":null,"methods":["POST"],"uri":"admin\/services\/service\/{service}\/option\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postNewOption"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/service\/{service}\/option\/{option}","name":"admin.services.option","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@getOption"},{"host":null,"methods":["POST"],"uri":"admin\/services\/service\/{service}\/option\/{option}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postOption"},{"host":null,"methods":["DELETE"],"uri":"admin\/services\/service\/{service}\/option\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@deleteOption"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/service\/{service}\/option\/{option}\/variable\/new","name":"admin.services.option.variable.new","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@getNewVariable"},{"host":null,"methods":["POST"],"uri":"admin\/services\/service\/{service}\/option\/{option}\/variable\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postNewVariable"},{"host":null,"methods":["POST"],"uri":"admin\/services\/service\/{service}\/option\/{option}\/variable\/{variable}","name":"admin.services.option.variable","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@postOptionVariable"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/service\/{service}\/option\/{option}\/variable\/{variable}\/delete","name":"admin.services.option.variable.delete","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@deleteVariable"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs\/new\/{option?}","name":"admin.services.packs.new","action":"Pterodactyl\Http\Controllers\Admin\PackController@new"},{"host":null,"methods":["POST"],"uri":"admin\/services\/packs\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs\/upload\/{option?}","name":"admin.services.packs.uploadForm","action":"Pterodactyl\Http\Controllers\Admin\PackController@uploadForm"},{"host":null,"methods":["POST"],"uri":"admin\/services\/packs\/upload","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@postUpload"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs","name":"admin.services.packs","action":"Pterodactyl\Http\Controllers\Admin\PackController@listAll"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs\/for\/option\/{option}","name":"admin.services.packs.option","action":"Pterodactyl\Http\Controllers\Admin\PackController@listByOption"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs\/for\/service\/{service}","name":"admin.services.packs.service","action":"Pterodactyl\Http\Controllers\Admin\PackController@listByService"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs\/edit\/{pack}","name":"admin.services.packs.edit","action":"Pterodactyl\Http\Controllers\Admin\PackController@edit"},{"host":null,"methods":["POST"],"uri":"admin\/services\/packs\/edit\/{pack}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/packs\/edit\/{pack}\/export\/{archive?}","name":"admin.services.packs.export","action":"Pterodactyl\Http\Controllers\Admin\PackController@export"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/login","name":"auth.login","action":"Pterodactyl\Http\Controllers\Auth\LoginController@showLoginForm"},{"host":null,"methods":["POST"],"uri":"auth\/login","name":null,"action":"Pterodactyl\Http\Controllers\Auth\LoginController@login"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/login\/totp","name":"auth.totp","action":"Pterodactyl\Http\Controllers\Auth\LoginController@totp"},{"host":null,"methods":["POST"],"uri":"auth\/login\/totp","name":null,"action":"Pterodactyl\Http\Controllers\Auth\LoginController@totpCheckpoint"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/password","name":"auth.password","action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm"},{"host":null,"methods":["POST"],"uri":"auth\/password","name":null,"action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/password\/reset\/{token}","name":"auth.reset","action":"Pterodactyl\Http\Controllers\Auth\ResetPasswordController@showResetForm"},{"host":null,"methods":["POST"],"uri":"auth\/password\/reset","name":"auth.reset.post","action":"Pterodactyl\Http\Controllers\Auth\ResetPasswordController@reset"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/logout","name":"auth.logout","action":"Pterodactyl\Http\Controllers\Auth\LoginController@logout"},{"host":null,"methods":["GET","HEAD"],"uri":"\/","name":"index","action":"Pterodactyl\Http\Controllers\Base\IndexController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"index","name":null,"action":"Closure"},{"host":null,"methods":["GET","HEAD"],"uri":"password-gen\/{length}","name":"password-gen","action":"Pterodactyl\Http\Controllers\Base\IndexController@getPassword"},{"host":null,"methods":["GET","HEAD"],"uri":"account","name":"account","action":"Pterodactyl\Http\Controllers\Base\AccountController@index"},{"host":null,"methods":["POST"],"uri":"account","name":null,"action":"Pterodactyl\Http\Controllers\Base\AccountController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/api","name":"account.api","action":"Pterodactyl\Http\Controllers\Base\APIController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/api\/new","name":"account.api.new","action":"Pterodactyl\Http\Controllers\Base\APIController@create"},{"host":null,"methods":["POST"],"uri":"account\/api\/new","name":null,"action":"Pterodactyl\Http\Controllers\Base\APIController@save"},{"host":null,"methods":["DELETE"],"uri":"account\/api\/revoke\/{key}","name":"account.api.revoke","action":"Pterodactyl\Http\Controllers\Base\APIController@revoke"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/security","name":"account.security","action":"Pterodactyl\Http\Controllers\Base\SecurityController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/security\/revoke\/{id}","name":"account.security.revoke","action":"Pterodactyl\Http\Controllers\Base\SecurityController@revoke"},{"host":null,"methods":["PUT"],"uri":"account\/security\/totp","name":"account.security.totp","action":"Pterodactyl\Http\Controllers\Base\SecurityController@generateTotp"},{"host":null,"methods":["POST"],"uri":"account\/security\/totp","name":null,"action":"Pterodactyl\Http\Controllers\Base\SecurityController@setTotp"},{"host":null,"methods":["DELETE"],"uri":"account\/security\/totp","name":null,"action":"Pterodactyl\Http\Controllers\Base\SecurityController@disableTotp"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/services","name":"daemon.services","action":"Pterodactyl\Http\Controllers\Daemon\ServiceController@list"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/services\/pull\/{service}\/{file}","name":"remote.install","action":"Pterodactyl\Http\Controllers\Daemon\ServiceController@pull"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/packs\/pull\/{uuid}","name":"daemon.pack.pull","action":"Pterodactyl\Http\Controllers\Daemon\PackController@pull"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/packs\/pull\/{uuid}\/hash","name":"daemon.pack.hash","action":"Pterodactyl\Http\Controllers\Daemon\PackController@hash"},{"host":null,"methods":["GET","HEAD"],"uri":"language\/{lang}","name":"langauge.set","action":"Pterodactyl\Http\Controllers\Base\LanguageController@setLanguage"},{"host":null,"methods":["POST"],"uri":"remote\/download","name":"remote.download","action":"Pterodactyl\Http\Controllers\Remote\RemoteController@postDownload"},{"host":null,"methods":["POST"],"uri":"remote\/install","name":"remote.install","action":"Pterodactyl\Http\Controllers\Remote\RemoteController@postInstall"},{"host":null,"methods":["GET","HEAD"],"uri":"remote\/configuration\/{token}","name":"remote.configuration","action":"Pterodactyl\Http\Controllers\Remote\RemoteController@getConfiguration"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/ajax\/status","name":"server.ajax.status","action":"Pterodactyl\Http\Controllers\Server\AjaxController@getStatus"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}","name":"server.index","action":"Pterodactyl\Http\Controllers\Server\ServerController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/databases","name":"server.settings.databases","action":"Pterodactyl\Http\Controllers\Server\ServerController@getDatabases"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/sftp","name":"server.settings.sftp","action":"Pterodactyl\Http\Controllers\Server\ServerController@getSFTP"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/settings\/sftp","name":null,"action":"Pterodactyl\Http\Controllers\Server\ServerController@postSettingsSFTP"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/startup","name":"server.settings.startup","action":"Pterodactyl\Http\Controllers\Server\ServerController@getStartup"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/settings\/startup","name":null,"action":"Pterodactyl\Http\Controllers\Server\ServerController@postSettingsStartup"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/allocation","name":"server.settings.allocation","action":"Pterodactyl\Http\Controllers\Server\ServerController@getAllocation"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files","name":"server.files.index","action":"Pterodactyl\Http\Controllers\Server\ServerController@getFiles"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/edit\/{file}","name":"server.files.edit","action":"Pterodactyl\Http\Controllers\Server\ServerController@getEditFile"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/download\/{file}","name":"server.files.download","action":"Pterodactyl\Http\Controllers\Server\ServerController@getDownloadFile"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/add","name":"server.files.add","action":"Pterodactyl\Http\Controllers\Server\ServerController@getAddFile"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/files\/directory-list","name":"server.files.directory-list","action":"Pterodactyl\Http\Controllers\Server\AjaxController@postDirectoryList"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/files\/save","name":"server.files.save","action":"Pterodactyl\Http\Controllers\Server\AjaxController@postSaveFile"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users","name":"server.subusers","action":"Pterodactyl\Http\Controllers\Server\SubuserController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users\/new","name":"server.subusers.new","action":"Pterodactyl\Http\Controllers\Server\SubuserController@getNew"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@postNew"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users\/view\/{id}","name":"server.subusers.view","action":"Pterodactyl\Http\Controllers\Server\SubuserController@getView"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/users\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@postView"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/users\/delete\/{id}","name":"server.subusers.delete","action":"Pterodactyl\Http\Controllers\Server\SubuserController@deleteSubuser"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/tasks","name":"server.tasks","action":"Pterodactyl\Http\Controllers\Server\TaskController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/tasks\/view\/{id}","name":"server.tasks.view","action":"Pterodactyl\Http\Controllers\Server\TaskController@getView"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/tasks\/new","name":"server.tasks.new","action":"Pterodactyl\Http\Controllers\Server\TaskController@getNew"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/tasks\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\TaskController@postNew"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/tasks\/delete\/{id}","name":"server.tasks.delete","action":"Pterodactyl\Http\Controllers\Server\TaskController@deleteTask"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/tasks\/toggle\/{id}","name":"server.tasks.toggle","action":"Pterodactyl\Http\Controllers\Server\TaskController@toggleTask"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/ajax\/set-primary","name":null,"action":"Pterodactyl\Http\Controllers\Server\AjaxController@postSetPrimary"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/ajax\/settings\/reset-database-password","name":"server.ajax.reset-database-password","action":"Pterodactyl\Http\Controllers\Server\AjaxController@postResetDatabasePassword"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/open","name":"debugbar.openhandler","action":"Barryvdh\Debugbar\Controllers\OpenHandlerController@handle"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/clockwork\/{id}","name":"debugbar.clockwork","action":"Barryvdh\Debugbar\Controllers\OpenHandlerController@clockwork"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/assets\/stylesheets","name":"debugbar.assets.css","action":"Barryvdh\Debugbar\Controllers\AssetController@css"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/assets\/javascript","name":"debugbar.assets.js","action":"Barryvdh\Debugbar\Controllers\AssetController@js"}], + routes : [{"host":null,"methods":["GET","HEAD"],"uri":"admin","name":"admin.index","action":"Pterodactyl\Http\Controllers\Admin\BaseController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/settings","name":"admin.settings","action":"Pterodactyl\Http\Controllers\Admin\BaseController@getSettings"},{"host":null,"methods":["POST"],"uri":"admin\/settings","name":null,"action":"Pterodactyl\Http\Controllers\Admin\BaseController@postSettings"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users","name":"admin.users","action":"Pterodactyl\Http\Controllers\Admin\UserController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/accounts.json","name":"admin.users.json","action":"Pterodactyl\Http\Controllers\Admin\UserController@getJson"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/view\/{id}","name":"admin.users.view","action":"Pterodactyl\Http\Controllers\Admin\UserController@getView"},{"host":null,"methods":["POST"],"uri":"admin\/users\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@updateUser"},{"host":null,"methods":["DELETE"],"uri":"admin\/users\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@deleteUser"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/new","name":"admin.users.new","action":"Pterodactyl\Http\Controllers\Admin\UserController@getNew"},{"host":null,"methods":["POST"],"uri":"admin\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@postNew"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers","name":"admin.servers","action":"Pterodactyl\Http\Controllers\Admin\ServersController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/new","name":"admin.servers.new","action":"Pterodactyl\Http\Controllers\Admin\ServersController@new"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@create"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/new\/nodes","name":"admin.servers.new.nodes","action":"Pterodactyl\Http\Controllers\Admin\ServersController@newServerNodes"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}","name":"admin.servers.view","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}\/details","name":"admin.servers.view.details","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDetails"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/details","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@setDetails"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/details\/container","name":"admin.servers.view.details.container","action":"Pterodactyl\Http\Controllers\Admin\ServersController@setContainer"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}\/build","name":"admin.servers.view.build","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewBuild"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/build","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@updateBuild"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}\/startup","name":"admin.servers.view.startup","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewStartup"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/startup","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@saveStartup"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}\/database","name":"admin.servers.view.database","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDatabase"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/database","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@newDatabase"},{"host":null,"methods":["PATCH"],"uri":"admin\/servers\/view\/{id}\/database","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@resetDatabasePassword"},{"host":null,"methods":["DELETE"],"uri":"admin\/servers\/view\/{id}\/database\/{database}\/delete","name":"admin.servers.view.database.delete","action":"Pterodactyl\Http\Controllers\Admin\ServersController@deleteDatabase"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}\/manage","name":"admin.servers.view.manage","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewManage"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/manage\/toggle","name":"admin.servers.view.manage.toggle","action":"Pterodactyl\Http\Controllers\Admin\ServersController@toggleInstall"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/manage\/rebuild","name":"admin.servers.view.manage.rebuild","action":"Pterodactyl\Http\Controllers\Admin\ServersController@rebuildContainer"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/manage\/suspension","name":"admin.servers.view.manage.suspension","action":"Pterodactyl\Http\Controllers\Admin\ServersController@manageSuspension"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}\/delete","name":"admin.servers.view.delete","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDelete"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/delete","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@delete"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/delete\/continue\/{force?}","name":"admin.servers.view.delete.continue","action":"Pterodactyl\Http\Controllers\Admin\ServersController@continueDeletion"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/delete\/cancel","name":"admin.servers.view.delete.cancel","action":"Pterodactyl\Http\Controllers\Admin\ServersController@cancelDeletion"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes","name":"admin.nodes","action":"Pterodactyl\Http\Controllers\Admin\NodesController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/new","name":"admin.nodes.new","action":"Pterodactyl\Http\Controllers\Admin\NodesController@new"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}","name":"admin.nodes.view","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}\/settings","name":"admin.nodes.view.settings","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewSettings"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{id}\/settings","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@updateSettings"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}\/configuration","name":"admin.nodes.view.configuration","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewConfiguration"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}\/allocation","name":"admin.nodes.view.allocation","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewAllocation"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{id}\/allocation","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@createAllocation"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}\/servers","name":"admin.nodes.view.servers","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewServers"},{"host":null,"methods":["DELETE"],"uri":"admin\/nodes\/view\/{id}\/delete","name":"admin.nodes.view.delete","action":"Pterodactyl\Http\Controllers\Admin\NodesController@delete"},{"host":null,"methods":["DELETE"],"uri":"admin\/nodes\/view\/{id}\/allocation\/remove\/{allocation}","name":"admin.nodes.view.allocation.removeSingle","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationRemoveSingle"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{id}\/allocation\/remove","name":"admin.nodes.view.allocation.removeBlock","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationRemoveBlock"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{id}\/allocation\/alias","name":"admin.nodes.view.allocation.setAlias","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationSetAlias"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}\/settings\/token","name":"admin.nodes.view.configuration.token","action":"Pterodactyl\Http\Controllers\Admin\NodesController@setToken"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/locations","name":"admin.locations","action":"Pterodactyl\Http\Controllers\Admin\LocationsController@getIndex"},{"host":null,"methods":["DELETE"],"uri":"admin\/locations\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationsController@deleteLocation"},{"host":null,"methods":["PATCH"],"uri":"admin\/locations\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationsController@patchLocation"},{"host":null,"methods":["POST"],"uri":"admin\/locations","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationsController@postLocation"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/databases","name":"admin.databases","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/databases\/new","name":"admin.databases.new","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@getNew"},{"host":null,"methods":["POST"],"uri":"admin\/databases\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@postNew"},{"host":null,"methods":["DELETE"],"uri":"admin\/databases\/delete\/{id}","name":"admin.databases.delete","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@deleteDatabase"},{"host":null,"methods":["DELETE"],"uri":"admin\/databases\/delete-server\/{id}","name":"admin.databases.delete-server","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@deleteServer"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services","name":"admin.services","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/new","name":"admin.services.new","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@new"},{"host":null,"methods":["POST"],"uri":"admin\/services\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/view\/{id}","name":"admin.services.view","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@view"},{"host":null,"methods":["POST"],"uri":"admin\/services\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@edit"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/view\/{id}\/functions","name":"admin.services.view.functions","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@viewFunctions"},{"host":null,"methods":["DELETE"],"uri":"admin\/services\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/option\/new","name":"admin.services.option.new","action":"Pterodactyl\Http\Controllers\Admin\OptionController@new"},{"host":null,"methods":["POST"],"uri":"admin\/services\/option\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\OptionController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/option\/{id}","name":"admin.services.option.view","action":"Pterodactyl\Http\Controllers\Admin\OptionController@viewConfiguration"},{"host":null,"methods":["POST"],"uri":"admin\/services\/option\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\OptionController@editConfiguration"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/option\/{id}\/variables","name":"admin.services.option.variables","action":"Pterodactyl\Http\Controllers\Admin\OptionController@viewVariables"},{"host":null,"methods":["POST"],"uri":"admin\/services\/option\/{id}\/variables","name":null,"action":"Pterodactyl\Http\Controllers\Admin\OptionController@createVariable"},{"host":null,"methods":["POST"],"uri":"admin\/services\/option\/{id}\/variables\/{variable}","name":"admin.services.option.variables.edit","action":"Pterodactyl\Http\Controllers\Admin\OptionController@editVariable"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/packs","name":"admin.packs","action":"Pterodactyl\Http\Controllers\Admin\PackController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/packs\/new","name":"admin.packs.new","action":"Pterodactyl\Http\Controllers\Admin\PackController@new"},{"host":null,"methods":["POST"],"uri":"admin\/packs\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/packs\/new\/template","name":"admin.packs.new.template","action":"Pterodactyl\Http\Controllers\Admin\PackController@newTemplate"},{"host":null,"methods":["POST"],"uri":"admin\/packs\/new\/template","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@createTemplate"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/packs\/view\/{id}","name":"admin.packs.view","action":"Pterodactyl\Http\Controllers\Admin\PackController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/login","name":"auth.login","action":"Pterodactyl\Http\Controllers\Auth\LoginController@showLoginForm"},{"host":null,"methods":["POST"],"uri":"auth\/login","name":null,"action":"Pterodactyl\Http\Controllers\Auth\LoginController@login"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/login\/totp","name":"auth.totp","action":"Pterodactyl\Http\Controllers\Auth\LoginController@totp"},{"host":null,"methods":["POST"],"uri":"auth\/login\/totp","name":null,"action":"Pterodactyl\Http\Controllers\Auth\LoginController@totpCheckpoint"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/password","name":"auth.password","action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm"},{"host":null,"methods":["POST"],"uri":"auth\/password","name":null,"action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/password\/reset\/{token}","name":"auth.reset","action":"Pterodactyl\Http\Controllers\Auth\ResetPasswordController@showResetForm"},{"host":null,"methods":["POST"],"uri":"auth\/password\/reset","name":"auth.reset.post","action":"Pterodactyl\Http\Controllers\Auth\ResetPasswordController@reset"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/logout","name":"auth.logout","action":"Pterodactyl\Http\Controllers\Auth\LoginController@logout"},{"host":null,"methods":["GET","HEAD"],"uri":"\/","name":"index","action":"Pterodactyl\Http\Controllers\Base\IndexController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"index","name":null,"action":"Closure"},{"host":null,"methods":["GET","HEAD"],"uri":"password-gen\/{length}","name":"password-gen","action":"Pterodactyl\Http\Controllers\Base\IndexController@getPassword"},{"host":null,"methods":["GET","HEAD"],"uri":"account","name":"account","action":"Pterodactyl\Http\Controllers\Base\AccountController@index"},{"host":null,"methods":["POST"],"uri":"account","name":null,"action":"Pterodactyl\Http\Controllers\Base\AccountController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/api","name":"account.api","action":"Pterodactyl\Http\Controllers\Base\APIController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/api\/new","name":"account.api.new","action":"Pterodactyl\Http\Controllers\Base\APIController@create"},{"host":null,"methods":["POST"],"uri":"account\/api\/new","name":null,"action":"Pterodactyl\Http\Controllers\Base\APIController@save"},{"host":null,"methods":["DELETE"],"uri":"account\/api\/revoke\/{key}","name":"account.api.revoke","action":"Pterodactyl\Http\Controllers\Base\APIController@revoke"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/security","name":"account.security","action":"Pterodactyl\Http\Controllers\Base\SecurityController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/security\/revoke\/{id}","name":"account.security.revoke","action":"Pterodactyl\Http\Controllers\Base\SecurityController@revoke"},{"host":null,"methods":["PUT"],"uri":"account\/security\/totp","name":"account.security.totp","action":"Pterodactyl\Http\Controllers\Base\SecurityController@generateTotp"},{"host":null,"methods":["POST"],"uri":"account\/security\/totp","name":null,"action":"Pterodactyl\Http\Controllers\Base\SecurityController@setTotp"},{"host":null,"methods":["DELETE"],"uri":"account\/security\/totp","name":null,"action":"Pterodactyl\Http\Controllers\Base\SecurityController@disableTotp"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/services","name":"daemon.services","action":"Pterodactyl\Http\Controllers\Daemon\ServiceController@list"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/services\/pull\/{service}\/{file}","name":"remote.install","action":"Pterodactyl\Http\Controllers\Daemon\ServiceController@pull"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/packs\/pull\/{uuid}","name":"daemon.pack.pull","action":"Pterodactyl\Http\Controllers\Daemon\PackController@pull"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/packs\/pull\/{uuid}\/hash","name":"daemon.pack.hash","action":"Pterodactyl\Http\Controllers\Daemon\PackController@hash"},{"host":null,"methods":["GET","HEAD"],"uri":"language\/{lang}","name":"langauge.set","action":"Pterodactyl\Http\Controllers\Base\LanguageController@setLanguage"},{"host":null,"methods":["POST"],"uri":"remote\/download","name":"remote.download","action":"Pterodactyl\Http\Controllers\Remote\RemoteController@postDownload"},{"host":null,"methods":["POST"],"uri":"remote\/install","name":"remote.install","action":"Pterodactyl\Http\Controllers\Remote\RemoteController@postInstall"},{"host":null,"methods":["GET","HEAD"],"uri":"remote\/configuration\/{token}","name":"remote.configuration","action":"Pterodactyl\Http\Controllers\Remote\RemoteController@getConfiguration"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/ajax\/status","name":"server.ajax.status","action":"Pterodactyl\Http\Controllers\Server\AjaxController@getStatus"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}","name":"server.index","action":"Pterodactyl\Http\Controllers\Server\ServerController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/databases","name":"server.settings.databases","action":"Pterodactyl\Http\Controllers\Server\ServerController@getDatabases"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/sftp","name":"server.settings.sftp","action":"Pterodactyl\Http\Controllers\Server\ServerController@getSFTP"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/settings\/sftp","name":null,"action":"Pterodactyl\Http\Controllers\Server\ServerController@postSettingsSFTP"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/startup","name":"server.settings.startup","action":"Pterodactyl\Http\Controllers\Server\ServerController@getStartup"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/settings\/startup","name":null,"action":"Pterodactyl\Http\Controllers\Server\ServerController@postSettingsStartup"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/allocation","name":"server.settings.allocation","action":"Pterodactyl\Http\Controllers\Server\ServerController@getAllocation"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files","name":"server.files.index","action":"Pterodactyl\Http\Controllers\Server\ServerController@getFiles"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/edit\/{file}","name":"server.files.edit","action":"Pterodactyl\Http\Controllers\Server\ServerController@getEditFile"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/download\/{file}","name":"server.files.download","action":"Pterodactyl\Http\Controllers\Server\ServerController@getDownloadFile"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/add","name":"server.files.add","action":"Pterodactyl\Http\Controllers\Server\ServerController@getAddFile"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/files\/directory-list","name":"server.files.directory-list","action":"Pterodactyl\Http\Controllers\Server\AjaxController@postDirectoryList"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/files\/save","name":"server.files.save","action":"Pterodactyl\Http\Controllers\Server\AjaxController@postSaveFile"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users","name":"server.subusers","action":"Pterodactyl\Http\Controllers\Server\SubuserController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users\/new","name":"server.subusers.new","action":"Pterodactyl\Http\Controllers\Server\SubuserController@getNew"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@postNew"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users\/view\/{id}","name":"server.subusers.view","action":"Pterodactyl\Http\Controllers\Server\SubuserController@getView"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/users\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@postView"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/users\/delete\/{id}","name":"server.subusers.delete","action":"Pterodactyl\Http\Controllers\Server\SubuserController@deleteSubuser"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/tasks","name":"server.tasks","action":"Pterodactyl\Http\Controllers\Server\TaskController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/tasks\/view\/{id}","name":"server.tasks.view","action":"Pterodactyl\Http\Controllers\Server\TaskController@getView"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/tasks\/new","name":"server.tasks.new","action":"Pterodactyl\Http\Controllers\Server\TaskController@getNew"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/tasks\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\TaskController@postNew"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/tasks\/delete\/{id}","name":"server.tasks.delete","action":"Pterodactyl\Http\Controllers\Server\TaskController@deleteTask"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/tasks\/toggle\/{id}","name":"server.tasks.toggle","action":"Pterodactyl\Http\Controllers\Server\TaskController@toggleTask"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/ajax\/set-primary","name":null,"action":"Pterodactyl\Http\Controllers\Server\AjaxController@postSetPrimary"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/ajax\/settings\/reset-database-password","name":"server.ajax.reset-database-password","action":"Pterodactyl\Http\Controllers\Server\AjaxController@postResetDatabasePassword"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/open","name":"debugbar.openhandler","action":"Barryvdh\Debugbar\Controllers\OpenHandlerController@handle"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/clockwork\/{id}","name":"debugbar.clockwork","action":"Barryvdh\Debugbar\Controllers\OpenHandlerController@clockwork"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/assets\/stylesheets","name":"debugbar.assets.css","action":"Barryvdh\Debugbar\Controllers\AssetController@css"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/assets\/javascript","name":"debugbar.assets.js","action":"Barryvdh\Debugbar\Controllers\AssetController@js"}], prefix: '', route : function (name, parameters, route) { diff --git a/public/themes/pterodactyl/css/checkbox.css b/public/themes/pterodactyl/css/checkbox.css new file mode 100644 index 000000000..fd4a855ac --- /dev/null +++ b/public/themes/pterodactyl/css/checkbox.css @@ -0,0 +1,231 @@ +/** + * Bootsnip - "Bootstrap Checkboxes/Radios" + * Bootstrap 3.2.0 Snippet by i-heart-php + * + * Copyright (c) 2013 Bootsnipp.com + * + * 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. + */ + .checkbox { + padding-left: 20px; +} +.checkbox label { + display: inline-block; + position: relative; + padding-left: 5px; +} +.checkbox label::before { + content: ""; + display: inline-block; + position: absolute; + width: 17px; + height: 17px; + left: 0; + margin-left: -20px; + border: 1px solid #cccccc; + border-radius: 3px; + background-color: #fff; + -webkit-transition: border 0.15s ease-in-out, color 0.15s ease-in-out; + -o-transition: border 0.15s ease-in-out, color 0.15s ease-in-out; + transition: border 0.15s ease-in-out, color 0.15s ease-in-out; +} +.checkbox label::after { + display: inline-block; + position: absolute; + width: 16px; + height: 16px; + left: 0; + top: 0; + margin-left: -20px; + padding-left: 3px; + padding-top: 1px; + font-size: 11px; + color: #555555; +} +.checkbox input[type="checkbox"] { + opacity: 0; +} +.checkbox input[type="checkbox"]:focus + label::before { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.checkbox input[type="checkbox"]:checked + label::after { + font-family: 'FontAwesome'; + content: "\f00c"; +} +.checkbox input[type="checkbox"]:disabled + label { + opacity: 0.65; +} +.checkbox input[type="checkbox"]:disabled + label::before { + background-color: #eeeeee; + cursor: not-allowed; +} +.checkbox.checkbox-circle label::before { + border-radius: 50%; +} +.checkbox.checkbox-inline { + margin-top: 0; +} +.checkbox-primary input[type="checkbox"]:checked + label::before { + background-color: #428bca; + border-color: #428bca; +} +.checkbox-primary input[type="checkbox"]:checked + label::after { + color: #fff; +} +.checkbox-danger input[type="checkbox"]:checked + label::before { + background-color: #d9534f; + border-color: #d9534f; +} +.checkbox-danger input[type="checkbox"]:checked + label::after { + color: #fff; +} +.checkbox-info input[type="checkbox"]:checked + label::before { + background-color: #5bc0de; + border-color: #5bc0de; +} +.checkbox-info input[type="checkbox"]:checked + label::after { + color: #fff; +} +.checkbox-warning input[type="checkbox"]:checked + label::before { + background-color: #f0ad4e; + border-color: #f0ad4e; +} +.checkbox-warning input[type="checkbox"]:checked + label::after { + color: #fff; +} +.checkbox-success input[type="checkbox"]:checked + label::before { + background-color: #5cb85c; + border-color: #5cb85c; +} +.checkbox-success input[type="checkbox"]:checked + label::after { + color: #fff; +} +.radio { + padding-left: 20px; +} +.radio label { + display: inline-block; + position: relative; + padding-left: 5px; +} +.radio label::before { + content: ""; + display: inline-block; + position: absolute; + width: 17px; + height: 17px; + left: 0; + margin-left: -20px; + border: 1px solid #cccccc; + border-radius: 50%; + background-color: #fff; + -webkit-transition: border 0.15s ease-in-out; + -o-transition: border 0.15s ease-in-out; + transition: border 0.15s ease-in-out; +} +.radio label::after { + display: inline-block; + position: absolute; + content: " "; + width: 11px; + height: 11px; + left: 3px; + top: 3px; + margin-left: -20px; + border-radius: 50%; + background-color: #555555; + -webkit-transform: scale(0, 0); + -ms-transform: scale(0, 0); + -o-transform: scale(0, 0); + transform: scale(0, 0); + -webkit-transition: -webkit-transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33); + -moz-transition: -moz-transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33); + -o-transition: -o-transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33); + transition: transform 0.1s cubic-bezier(0.8, -0.33, 0.2, 1.33); +} +.radio input[type="radio"] { + opacity: 0; +} +.radio input[type="radio"]:focus + label::before { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.radio input[type="radio"]:checked + label::after { + -webkit-transform: scale(1, 1); + -ms-transform: scale(1, 1); + -o-transform: scale(1, 1); + transform: scale(1, 1); +} +.radio input[type="radio"]:disabled + label { + opacity: 0.65; +} +.radio input[type="radio"]:disabled + label::before { + cursor: not-allowed; +} +.radio.radio-inline { + margin-top: 0; +} +.radio-primary input[type="radio"] + label::after { + background-color: #428bca; +} +.radio-primary input[type="radio"]:checked + label::before { + border-color: #428bca; +} +.radio-primary input[type="radio"]:checked + label::after { + background-color: #428bca; +} +.radio-danger input[type="radio"] + label::after { + background-color: #d9534f; +} +.radio-danger input[type="radio"]:checked + label::before { + border-color: #d9534f; +} +.radio-danger input[type="radio"]:checked + label::after { + background-color: #d9534f; +} +.radio-info input[type="radio"] + label::after { + background-color: #5bc0de; +} +.radio-info input[type="radio"]:checked + label::before { + border-color: #5bc0de; +} +.radio-info input[type="radio"]:checked + label::after { + background-color: #5bc0de; +} +.radio-warning input[type="radio"] + label::after { + background-color: #f0ad4e; +} +.radio-warning input[type="radio"]:checked + label::before { + border-color: #f0ad4e; +} +.radio-warning input[type="radio"]:checked + label::after { + background-color: #f0ad4e; +} +.radio-success input[type="radio"] + label::after { + background-color: #5cb85c; +} +.radio-success input[type="radio"]:checked + label::before { + border-color: #5cb85c; +} +.radio-success input[type="radio"]:checked + label::after { + background-color: #5cb85c; +} diff --git a/public/themes/pterodactyl/css/pterodactyl.css b/public/themes/pterodactyl/css/pterodactyl.css index ca34e46a5..9d91c3192 100644 --- a/public/themes/pterodactyl/css/pterodactyl.css +++ b/public/themes/pterodactyl/css/pterodactyl.css @@ -20,6 +20,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + @import 'checkbox.css'; + .login-box, .register-box { width: 40%; margin: 7% auto @@ -266,3 +268,7 @@ span[aria-labelledby="select2-pUserId-container"] { .terminal-notify:hover { opacity: .9; } + +.no-margin-bottom { + margin-bottom: 0 !important; +} diff --git a/resources/themes/pterodactyl/admin/packs/index.blade.php b/resources/themes/pterodactyl/admin/packs/index.blade.php new file mode 100644 index 000000000..a4da22809 --- /dev/null +++ b/resources/themes/pterodactyl/admin/packs/index.blade.php @@ -0,0 +1,84 @@ +{{-- 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') + List Packs +@endsection + +@section('content-header') +

    PacksAll service packs available on the system.

    + +@endsection + +@section('content') +
    +
    +
    +
    +

    Pack List

    +
    +
    +
    + +
    + + +
    +
    +
    +
    +
    +
    + + + + + + + + + + @foreach ($packs as $pack) + + + + + + + + + @endforeach + +
    IDPack NameVersionDescriptionOption + Servers
    {{ $pack->id }}{{ $pack->name }}{{ $pack->version }}{{ str_limit($pack->description, 150) }}{{ $pack->option->name }}{{ $pack->servers_count }}
    +
    + @if ($packs->hasPages()) + + @endif +
    +
    +
    +@endsection diff --git a/resources/themes/pterodactyl/admin/packs/modal.blade.php b/resources/themes/pterodactyl/admin/packs/modal.blade.php new file mode 100644 index 000000000..fd4263575 --- /dev/null +++ b/resources/themes/pterodactyl/admin/packs/modal.blade.php @@ -0,0 +1,47 @@ + diff --git a/resources/themes/pterodactyl/admin/packs/new.blade.php b/resources/themes/pterodactyl/admin/packs/new.blade.php new file mode 100644 index 000000000..540717ef4 --- /dev/null +++ b/resources/themes/pterodactyl/admin/packs/new.blade.php @@ -0,0 +1,158 @@ +{{-- 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') + Packs → New +@endsection + +@section('content-header') +

    New PackCreate a new pack on the system.

    + +@endsection + +@section('content') + +
    +
    +
    +
    +
    +

    Pack Details

    +
    +
    +
    + + +

    A short but descriptive name of what this pack is. For example, Counter Strike: Source if it is a Counter Strike package.

    +
    +
    + + +
    +
    + + +

    The version of this package, or the version of the files contained within the package.

    +
    +
    + + +

    The option that this pack is assocaited with. Only servers that are assigned this option will be able to access this pack.

    +
    +
    +
    +
    +
    +
    +
    +

    Pack Configuration

    +
    +
    +
    +
    + + +
    +

    Check this box if user should be able to select this pack to install on their servers.

    +
    +
    +
    + + +
    +

    Check this box if this pack is visible in the dropdown menu. If this pack is assigned to a server it will be visible regardless of this setting.

    +
    +
    +
    + + +
    +

    Check this box if servers assigned this pack should not be able to switch to a different pack.

    +
    +
    +
    + + +

    This package file must be a .tar.gz archive of pack files to be decompressed into the server folder.

    +

    If your file is larger than 50MB it is recommended to upload it using SFTP. Once you have added this pack to the system, a path will be provided where you should upload the file.

    +
    +

    This server is currently configured with the following limits:
    upload_max_filesize={{ ini_get('upload_max_filesize') }}
    post_max_size={{ ini_get('post_max_size') }}

    If your file is larger than either of those values this request will fail.

    +
    +
    +
    + +
    +
    +
    +
    +@endsection + +@section('footer-scripts') + @parent + +@endsection diff --git a/resources/views/admin/services/packs/byoption.blade.php b/resources/views/admin/services/packs/byoption.blade.php deleted file mode 100644 index f781a3a3c..000000000 --- a/resources/views/admin/services/packs/byoption.blade.php +++ /dev/null @@ -1,90 +0,0 @@ -{{-- 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') - Service Packs for {{ $option->name }} -@endsection - -@section('content') -
    - -

    Service Packs


    - - - - - - - - - - - - @foreach ($option->packs as $pack) - - - - - - - - @endforeach - - - - -
    Pack NameVersionUUIDSelectableVisible
    {{ $pack->name }}{{ $pack->version }}{{ $pack->uuid }}@if($pack->selectable)@else@endif@if($pack->visible)@else@endif
    - - - - - - -
    -
    - -@endsection diff --git a/resources/views/admin/services/packs/byservice.blade.php b/resources/views/admin/services/packs/byservice.blade.php deleted file mode 100644 index f36ce4e4c..000000000 --- a/resources/views/admin/services/packs/byservice.blade.php +++ /dev/null @@ -1,67 +0,0 @@ -{{-- 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') - Service Packs for {{ $service->name }} -@endsection - -@section('content') -
    - -

    Service Packs


    - - - - - - - - - @foreach ($service->options as $option) - - - - - @endforeach - - - - -
    Service OptionTotal Packs
    {{ $option->name }}{{ $option->packs->count() }}
    - - - - - - -
    -
    - -@endsection From 47303095897a87b9b260084c8baeb4885a693f3c Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Wed, 15 Mar 2017 19:47:44 -0400 Subject: [PATCH 20/22] Add IDE helper for all functions --- .gitignore | 4 + app/Providers/AppServiceProvider.php | 4 +- composer.json | 3 +- composer.lock | 175 ++++++++++++++++++++++++++- 4 files changed, 182 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 9f02de4ca..efbbccdcf 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,7 @@ Vagrantfile node_modules yarn.lock node_modules + +_ide_helper_models.php + +_ide_helper.php diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index a1181fb10..5a9a0d925 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -49,6 +49,8 @@ class AppServiceProvider extends ServiceProvider */ public function register() { - // + if ($this->app->environment() !== 'production') { + $this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class); + } } } diff --git a/composer.json b/composer.json index a95d82df1..a58fc7f82 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,8 @@ "phpunit/phpunit": "~5.0", "symfony/css-selector": "3.1.*", "symfony/dom-crawler": "3.1.*", - "laravel/homestead": "3.0.*" + "laravel/homestead": "3.0.*", + "barryvdh/laravel-ide-helper": "^2.3" }, "autoload": { "classmap": [ diff --git a/composer.lock b/composer.lock index 535af7aa2..8975e85a0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "6a9656aff0fb3809d27a2a093a810197", - "content-hash": "8affaad10f155172b5079a72015b8bc5", + "hash": "7c01d337ebab3d47ae3479360c63e68f", + "content-hash": "c2addd888e0a4fd26518ea7c691f6f25", "packages": [ { "name": "aws/aws-sdk-php", @@ -3747,6 +3747,121 @@ } ], "packages-dev": [ + { + "name": "barryvdh/laravel-ide-helper", + "version": "v2.3.2", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/laravel-ide-helper.git", + "reference": "e82de98cef0d6597b1b686be0b5813a3a4bb53c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/e82de98cef0d6597b1b686be0b5813a3a4bb53c5", + "reference": "e82de98cef0d6597b1b686be0b5813a3a4bb53c5", + "shasum": "" + }, + "require": { + "barryvdh/reflection-docblock": "^2.0.4", + "illuminate/console": "^5.0,<5.5", + "illuminate/filesystem": "^5.0,<5.5", + "illuminate/support": "^5.0,<5.5", + "php": ">=5.4.0", + "symfony/class-loader": "^2.3|^3.0" + }, + "require-dev": { + "doctrine/dbal": "~2.3", + "phpunit/phpunit": "4.*", + "scrutinizer/ocular": "~1.1", + "squizlabs/php_codesniffer": "~2.3" + }, + "suggest": { + "doctrine/dbal": "Load information from the database about models for phpdocs (~2.3)" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-4": { + "Barryvdh\\LaravelIdeHelper\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.", + "keywords": [ + "autocomplete", + "codeintel", + "helper", + "ide", + "laravel", + "netbeans", + "phpdoc", + "phpstorm", + "sublime" + ], + "time": "2017-02-22 12:27:33" + }, + { + "name": "barryvdh/reflection-docblock", + "version": "v2.0.4", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/ReflectionDocBlock.git", + "reference": "3dcbd98b5d9384a5357266efba8fd29884458e5c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/3dcbd98b5d9384a5357266efba8fd29884458e5c", + "reference": "3dcbd98b5d9384a5357266efba8fd29884458e5c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0,<4.5" + }, + "suggest": { + "dflydev/markdown": "~1.0", + "erusev/parsedown": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Barryvdh": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "mike.vanriel@naenius.com" + } + ], + "time": "2016-06-13 19:28:20" + }, { "name": "doctrine/instantiator", "version": "1.0.5", @@ -5008,6 +5123,62 @@ "homepage": "https://github.com/sebastianbergmann/version", "time": "2016-10-03 07:35:21" }, + { + "name": "symfony/class-loader", + "version": "v3.2.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/class-loader.git", + "reference": "c29a5bc6ca14cfff1f5e3d7781ed74b6e898d2b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/class-loader/zipball/c29a5bc6ca14cfff1f5e3d7781ed74b6e898d2b9", + "reference": "c29a5bc6ca14cfff1f5e3d7781ed74b6e898d2b9", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "require-dev": { + "symfony/finder": "~2.8|~3.0", + "symfony/polyfill-apcu": "~1.1" + }, + "suggest": { + "symfony/polyfill-apcu": "For using ApcClassLoader on HHVM" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\ClassLoader\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony ClassLoader Component", + "homepage": "https://symfony.com", + "time": "2017-02-18 17:28:00" + }, { "name": "symfony/css-selector", "version": "v3.1.10", From 1c47b2ed5551b7c29d66766d4846497edc3eda9c Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Wed, 15 Mar 2017 20:52:37 -0400 Subject: [PATCH 21/22] Finish pack management in Admin CP --- app/Http/Controllers/Admin/PackController.php | 129 +++++++++--- .../Controllers/Remote/RemoteController.php | 4 - app/Http/Routes/AdminRoutes.php | 7 + app/Models/Pack.php | 25 +++ .../MacroServiceProvider.php} | 46 ++--- app/Repositories/PackRepository.php | 16 +- app/Services/FileService.php | 0 config/app.php | 1 + .../pterodactyl/admin/packs/view.blade.php | 192 ++++++++++++++++++ .../views/admin/services/packs/edit.blade.php | 160 --------------- .../admin/services/packs/index.blade.php | 47 ----- .../views/admin/services/packs/new.blade.php | 135 ------------ .../admin/services/packs/upload.blade.php | 45 ---- 13 files changed, 352 insertions(+), 455 deletions(-) rename app/{Services/NotificationService.php => Providers/MacroServiceProvider.php} (58%) create mode 100644 app/Services/FileService.php create mode 100644 resources/themes/pterodactyl/admin/packs/view.blade.php delete mode 100644 resources/views/admin/services/packs/edit.blade.php delete mode 100644 resources/views/admin/services/packs/index.blade.php delete mode 100644 resources/views/admin/services/packs/new.blade.php delete mode 100644 resources/views/admin/services/packs/upload.blade.php diff --git a/app/Http/Controllers/Admin/PackController.php b/app/Http/Controllers/Admin/PackController.php index 1ef60b8c8..675ccc738 100644 --- a/app/Http/Controllers/Admin/PackController.php +++ b/app/Http/Controllers/Admin/PackController.php @@ -80,6 +80,12 @@ class PackController extends Controller ]); } + /** + * Handle create pack request and route user to location. + * + * @param Request $request + * @return \Illuminate\View\View + */ public function create(Request $request) { $repo = new PackRepository; @@ -108,43 +114,102 @@ class PackController extends Controller return redirect()->route('admin.packs.new')->withInput(); } + /** + * Display pack view template to user. + * + * @param Request $request + * @param int $id + * @return \Illuminate\View\View + */ + public function view(Request $request, $id) + { + return view('admin.packs.view', [ + 'pack' => Pack::with('servers.node', 'servers.user')->findOrFail($id), + 'services' => Service::with('options')->get(), + ]); + } - // public function export(Request $request, $id, $files = false) - // { - // $pack = Models\Pack::findOrFail($id); - // $json = [ - // 'name' => $pack->name, - // 'version' => $pack->version, - // 'description' => $pack->dscription, - // 'selectable' => (bool) $pack->selectable, - // 'visible' => (bool) $pack->visible, - // ]; + /** + * Handle updating or deleting pack information. + * + * @param Request $request + * @param int $id + * @return \Illuminate\Response\RedirectResponse + */ + public function update(Request $request, $id) + { + $repo = new PackRepository; - // $filename = tempnam(sys_get_temp_dir(), 'pterodactyl_'); - // if ((bool) $files) { - // $zip = new \ZipArchive; - // if (! $zip->open($filename, \ZipArchive::CREATE)) { - // abort(503, 'Unable to open file for writing.'); - // } + try { + if ($request->input('action') !== 'delete') { + $pack = $repo->update($id, $request->intersect([ + 'name', 'description', 'version', + 'option_id', 'selectable', 'visible', 'locked' + ])); + Alert::success('Pack successfully updated.')->flash(); + } else { + $repo->delete($id); + Alert::success('Pack was successfully deleted from the system.')->flash(); - // $files = Storage::files('packs/' . $pack->uuid); - // foreach ($files as $file) { - // $zip->addFile(storage_path('app/' . $file), basename(storage_path('app/' . $file))); - // } + return redirect()->route('admin.packs'); + } + } catch(DisplayValidationException $ex) { + return redirect()->route('admin.packs.view', $id)->withErrors(json_decode($ex->getMessage())); + } catch (DisplayException $ex) { + Alert::danger($ex->getMessage())->flash(); + } catch (\Exception $ex) { + Log::error($ex); + Alert::danger('An error occured while attempting to edit this service pack. This error has been logged.')->flash(); + } - // $zip->addFromString('import.json', json_encode($json, JSON_PRETTY_PRINT)); - // $zip->close(); + return redirect()->route('admin.packs.view', $id); + } - // return response()->download($filename, 'pack-' . $pack->name . '.zip')->deleteFileAfterSend(true); - // } else { - // $fp = fopen($filename, 'a+'); - // fwrite($fp, json_encode($json, JSON_PRETTY_PRINT)); - // fclose($fp); + /** + * Creates an archive of the pack and downloads it to the browser. + * + * @param Request $request + * @param int $id + * @param bool $files + * @return \Illuminate\Response\BinaryFileResponse + */ + public function export(Request $request, $id, $files = false) + { + $pack = Pack::findOrFail($id); + $json = [ + 'name' => $pack->name, + 'version' => $pack->version, + 'description' => $pack->description, + 'selectable' => $pack->selectable, + 'visible' => $pack->visible, + 'locked' => $pack->locked, + ]; - // return response()->download($filename, 'pack-' . $pack->name . '.json', [ - // 'Content-Type' => 'application/json', - // ])->deleteFileAfterSend(true); - // } - // } + $filename = tempnam(sys_get_temp_dir(), 'pterodactyl_'); + if ($files === 'with-files') { + $zip = new \ZipArchive; + if (! $zip->open($filename, \ZipArchive::CREATE)) { + abort(503, 'Unable to open file for writing.'); + } + + $files = Storage::files('packs/' . $pack->uuid); + foreach ($files as $file) { + $zip->addFile(storage_path('app/' . $file), basename(storage_path('app/' . $file))); + } + + $zip->addFromString('import.json', json_encode($json, JSON_PRETTY_PRINT)); + $zip->close(); + + return response()->download($filename, 'pack-' . $pack->name . '.zip')->deleteFileAfterSend(true); + } else { + $fp = fopen($filename, 'a+'); + fwrite($fp, json_encode($json, JSON_PRETTY_PRINT)); + fclose($fp); + + return response()->download($filename, 'pack-' . $pack->name . '.json', [ + 'Content-Type' => 'application/json', + ])->deleteFileAfterSend(true); + } + } } diff --git a/app/Http/Controllers/Remote/RemoteController.php b/app/Http/Controllers/Remote/RemoteController.php index 2e8b782a1..93dd672e1 100644 --- a/app/Http/Controllers/Remote/RemoteController.php +++ b/app/Http/Controllers/Remote/RemoteController.php @@ -98,10 +98,6 @@ class RemoteController extends Controller ], 403); } - // Passes Validation, Setup Notifications - $notify = new NotificationService($server); - $notify->pass($request->input('notification')); - return response('', 201); } diff --git a/app/Http/Routes/AdminRoutes.php b/app/Http/Routes/AdminRoutes.php index f714f632d..f8cbc17fa 100644 --- a/app/Http/Routes/AdminRoutes.php +++ b/app/Http/Routes/AdminRoutes.php @@ -474,6 +474,13 @@ class AdminRoutes 'as' => 'admin.packs.view', 'uses' => 'Admin\PackController@view', ]); + + $router->post('/view/{id}', 'Admin\PackController@update'); + + $router->post('/view/{id}/export/{files?}', [ + 'as' => 'admin.packs.view.export', + 'uses' => 'Admin\PackController@export', + ]); }); } } diff --git a/app/Models/Pack.php b/app/Models/Pack.php index 5675fb25a..0a6310660 100644 --- a/app/Models/Pack.php +++ b/app/Models/Pack.php @@ -24,6 +24,8 @@ namespace Pterodactyl\Models; +use File; +use Storage; use Illuminate\Database\Eloquent\Model; use Nicolaslopezj\Searchable\SearchableTrait; @@ -78,6 +80,29 @@ class Pack extends Model ], ]; + /** + * Returns all of the archived files for a given pack. + * + * @param bool $collection + * @return \Illuminate\Support\Collection|object + */ + public function files($collection = false) + { + $files = collect(Storage::files('packs/' . $this->uuid)); + + $files = $files->map(function ($item) { + $path = storage_path('app/' . $item); + + return (object) [ + 'name' => basename($item), + 'hash' => sha1_file($path), + 'size' => File::humanReadableSize($path), + ]; + }); + + return ($collection) ? $files : (object) $files->all(); + } + /** * Gets option associated with a service pack. * diff --git a/app/Services/NotificationService.php b/app/Providers/MacroServiceProvider.php similarity index 58% rename from app/Services/NotificationService.php rename to app/Providers/MacroServiceProvider.php index 7912cf65d..ad23aaf74 100644 --- a/app/Services/NotificationService.php +++ b/app/Providers/MacroServiceProvider.php @@ -22,41 +22,31 @@ * SOFTWARE. */ -namespace Pterodactyl\Services; +namespace Pterodactyl\Providers; -use Pterodactyl\Models\Server; -use Pterodactyl\Notifications\Daemon; +use File; +use Illuminate\Support\ServiceProvider; -class NotificationService +class MacroServiceProvider extends ServiceProvider { - protected $server; - - protected $user; - /** - * Daemon will pass an event name, this matches that event name with the notification to send. - * @var array + * Bootstrap the application services. + * + * @return void */ - protected $types = [ - // 'crashed' => 'CrashNotification', - // 'started' => 'StartNotification', - // 'stopped' => 'StopNotification', - // 'rebuild' => 'RebuildNotification' - ]; - - public function __construct(Server $server) + public function boot() { - $this->server = $server; - } + File::macro('humanReadableSize', function ($path, $precision = 2) { + $size = File::size($path); + static $units = ['B', 'kB', 'MB', 'GB', 'TB']; - public function pass(array $notification) - { - if (! $notification->type) { - return; - } + $i = 0; + while (($size / 1024) > 0.9) { + $size = $size / 1024; + $i++; + } - if (class_exists($this->types[$notification->type]::class)) { - $user->notify(new $this->types[$notification->type]($notification->payload)); - } + return round($size, ($i < 2) ? 0 : $precision) . ' ' . $units[$i]; + }); } } diff --git a/app/Repositories/PackRepository.php b/app/Repositories/PackRepository.php index 3fc6b2571..fdc5fbd5d 100644 --- a/app/Repositories/PackRepository.php +++ b/app/Repositories/PackRepository.php @@ -177,12 +177,14 @@ class PackRepository * @param array $data * @return \Pterodactyl\Models\Pack * + * @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\DisplayValidationException */ public function update($id, array $data) { $validator = Validator::make($data, [ 'name' => 'sometimes|required|string', + 'option_id' => 'sometimes|required|exists:service_options,id', 'version' => 'sometimes|required|string', 'description' => 'sometimes|string', 'selectable' => 'sometimes|required|boolean', @@ -194,14 +196,20 @@ class PackRepository throw new DisplayValidationException(json_encode($validator->errors())); } - $pack = Pack::findOrFail($id); + $pack = Pack::withCount('servers')->findOrFail($id); + + if ($pack->servers_count > 0 && (isset($data['option_id']) && (int) $data['option_id'] !== $pack->option_id)) { + throw new DisplayException('You cannot modify the associated option if servers are attached to a pack.'); + } + $pack->fill([ 'name' => isset($data['name']) ? $data['name'] : $pack->name, + 'option_id' => isset($data['option_id']) ? $data['option_id'] : $pack->option_id, 'version' => isset($data['version']) ? $data['version'] : $pack->version, 'description' => (empty($data['description'])) ? null : $data['description'], - 'selectable' => isset($data['selectable']) ? $data['selectable'] : $data->selectable, - 'visible' => isset($data['visible']) ? $data['visible'] : $data->visible, - 'locked' => isset($data['locked']) ? $data['locked'] : $data->locked, + 'selectable' => isset($data['selectable']), + 'visible' => isset($data['visible']), + 'locked' => isset($data['locked']), ])->save(); return $pack; diff --git a/app/Services/FileService.php b/app/Services/FileService.php new file mode 100644 index 000000000..e69de29bb diff --git a/config/app.php b/config/app.php index 622098a9b..4721bb3d2 100644 --- a/config/app.php +++ b/config/app.php @@ -150,6 +150,7 @@ return [ Pterodactyl\Providers\AuthServiceProvider::class, Pterodactyl\Providers\EventServiceProvider::class, Pterodactyl\Providers\RouteServiceProvider::class, + Pterodactyl\Providers\MacroServiceProvider::class, Pterodactyl\Providers\PhraseAppTranslationProvider::class, /* diff --git a/resources/themes/pterodactyl/admin/packs/view.blade.php b/resources/themes/pterodactyl/admin/packs/view.blade.php new file mode 100644 index 000000000..03b331bee --- /dev/null +++ b/resources/themes/pterodactyl/admin/packs/view.blade.php @@ -0,0 +1,192 @@ +{{-- 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') + Packs → View → {{ $pack->name }} +@endsection + +@section('content-header') +

    {{ $pack->name }}{{ str_limit($pack->description, 60) }}

    + +@endsection + +@section('content') +
    +
    +
    +
    +
    +

    Pack Details

    +
    +
    +
    + + +

    A short but descriptive name of what this pack is. For example, Counter Strike: Source if it is a Counter Strike package.

    +
    +
    + + +
    +
    + + +

    The version of this package, or the version of the files contained within the package.

    +
    +
    +
    +
    +
    +
    +
    +

    Pack Configuration

    +
    +
    +
    + + +

    The option that this pack is assocaited with. Only servers that are assigned this option will be able to access this pack. This assigned option cannot be changed if servers are attached to this pack.

    +
    +
    +
    + selectable ?: 'checked' }}/> + +
    +

    Check this box if user should be able to select this pack to install on their servers.

    +
    +
    +
    + visible ?: 'checked' }}/> + +
    +

    Check this box if this pack is visible in the dropdown menu. If this pack is assigned to a server it will be visible regardless of this setting.

    +
    +
    +
    + locked ?: 'checked' }}/> + +
    +

    Check this box if servers assigned this pack should not be able to switch to a different pack.

    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +

    Stored Files

    +
    +
    + + + + + + + @foreach($pack->files() as $file) + + + + + + @endforeach +
    NameSHA1 HashFile Size
    {{ $file->name }}{{ $file->hash }}{{ $file->size }}
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    Servers Using This Pack

    +
    +
    + + + + + + + + @foreach($pack->servers as $server) + + + + + + + @endforeach +
    IDServer NameNodeOwner
    {{ $server->uuidShort }}{{ $server->name }}{{ $server->node->name }}{{ $server->user->email }}
    +
    +
    +
    +
    +
    +
    +
    + {!! csrf_field() !!} + +
    +
    + {!! csrf_field() !!} + +
    +
    +
    +@endsection + +@section('footer-scripts') + @parent + +@endsection diff --git a/resources/views/admin/services/packs/edit.blade.php b/resources/views/admin/services/packs/edit.blade.php deleted file mode 100644 index cc6470b3b..000000000 --- a/resources/views/admin/services/packs/edit.blade.php +++ /dev/null @@ -1,160 +0,0 @@ -{{-- 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') - Add New Service Pack -@endsection - -@section('content') -
    - -

    Manage Service Pack


    -
    -
    -
    - -
    - -

    The name of the pack which will be seen in dropdown menus and to users.

    -
    -
    -
    - -
    - -

    The version of the program included in this pack.

    -
    -
    -
    - -
    - -

    Provide a description of the pack which will be shown to users.

    -
    -
    -
    -
    -
    - - -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    -
    -
    -
    -
    Package Archive
    -
    -
    -
    - @if(count($files) > 1) -
    Warning! Service packs should only contain a single pack archive in .tar.gz format. We've detected more than one file for this pack.
    - @endif - - - - - - - - - - - @foreach($files as &$file) - - - - - - - @endforeach - -
    FilenameFile SizeSHA1 HashLast Modified
    {{ basename($file) }}{{ Storage::size($file) }} Bytes{{ sha1_file(storage_path('app/' . $file)) }}{{ Carbon::createFromTimestamp(Storage::lastModified($file))->toDateTimeString() }}
    -

    If you wish to modify or upload a new file it should be uploaded to {{ storage_path('app/packs/' . $pack->uuid) }} as archive.tar.gz.

    -
    -
    -
    -
    -
    -
    -
    -
    -
    - {!! csrf_field() !!} - - - - -
    -
    - -
    -{!! Theme::js('js/vendor/ace/ace.js') !!} -{!! Theme::js('js/vendor/ace/ext-modelist.js') !!} - -@endsection diff --git a/resources/views/admin/services/packs/index.blade.php b/resources/views/admin/services/packs/index.blade.php deleted file mode 100644 index 6456ca317..000000000 --- a/resources/views/admin/services/packs/index.blade.php +++ /dev/null @@ -1,47 +0,0 @@ -{{-- 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') - Service Packs -@endsection - -@section('content') -
    - -

    Service Packs


    -
    - @foreach ($services as $service) - - @endforeach -
    -
    - -@endsection diff --git a/resources/views/admin/services/packs/new.blade.php b/resources/views/admin/services/packs/new.blade.php deleted file mode 100644 index d5b0440da..000000000 --- a/resources/views/admin/services/packs/new.blade.php +++ /dev/null @@ -1,135 +0,0 @@ -{{-- 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') - Add New Service Pack -@endsection - -@section('content') -
    - -

    New Service Pack


    -
    -
    -
    - -
    - -

    The name of the pack which will be seen in dropdown menus and to users.

    -
    -
    -
    - -
    - -

    The version of the program included in this pack.

    -
    -
    -
    - -
    - -

    Provide a description of the pack which will be shown to users.

    -
    -
    -
    -
    -
    - - -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    -
    -
    -
    -
    File Upload
    -
    -
    -
    - - -

    This package file must be a .tar.gz archive of files to use for either building or running this pack.

    If your file is larger than 20MB we recommend uploading it using SFTP. Once you have added this pack to the system, a path will be provided where you should upload the file. - This server is currently configured with the following limits: upload_max_filesize={{ ini_get('upload_max_filesize') }} and post_max_size={{ ini_get('post_max_size') }}. If your file is larger than either of those values this request will fail.

    -
    -
    -
    -
    -
    -
    -
    -
    -
    - {!! csrf_field() !!} - -
    -
    - -
    -{!! Theme::js('js/vendor/ace/ace.js') !!} -{!! Theme::js('js/vendor/ace/ext-modelist.js') !!} - -@endsection diff --git a/resources/views/admin/services/packs/upload.blade.php b/resources/views/admin/services/packs/upload.blade.php deleted file mode 100644 index cb5e548d8..000000000 --- a/resources/views/admin/services/packs/upload.blade.php +++ /dev/null @@ -1,45 +0,0 @@ - From 436ba814a999df0bc99e8eb82509939a86cfabc1 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Wed, 15 Mar 2017 20:53:49 -0400 Subject: [PATCH 22/22] Apply fixes from StyleCI (#351) --- app/Http/Controllers/Admin/PackController.php | 7 +++---- app/Models/ServiceVariable.php | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/app/Http/Controllers/Admin/PackController.php b/app/Http/Controllers/Admin/PackController.php index 675ccc738..46674ae02 100644 --- a/app/Http/Controllers/Admin/PackController.php +++ b/app/Http/Controllers/Admin/PackController.php @@ -102,7 +102,7 @@ class PackController extends Controller Alert::success('Pack successfully created on the system.')->flash(); return redirect()->route('admin.packs.view', $pack->id); - } catch(DisplayValidationException $ex) { + } catch (DisplayValidationException $ex) { return redirect()->route('admin.packs.new')->withErrors(json_decode($ex->getMessage()))->withInput(); } catch (DisplayException $ex) { Alert::danger($ex->getMessage())->flash(); @@ -144,7 +144,7 @@ class PackController extends Controller if ($request->input('action') !== 'delete') { $pack = $repo->update($id, $request->intersect([ 'name', 'description', 'version', - 'option_id', 'selectable', 'visible', 'locked' + 'option_id', 'selectable', 'visible', 'locked', ])); Alert::success('Pack successfully updated.')->flash(); } else { @@ -153,7 +153,7 @@ class PackController extends Controller return redirect()->route('admin.packs'); } - } catch(DisplayValidationException $ex) { + } catch (DisplayValidationException $ex) { return redirect()->route('admin.packs.view', $id)->withErrors(json_decode($ex->getMessage())); } catch (DisplayException $ex) { Alert::danger($ex->getMessage())->flash(); @@ -211,5 +211,4 @@ class PackController extends Controller ])->deleteFileAfterSend(true); } } - } diff --git a/app/Models/ServiceVariable.php b/app/Models/ServiceVariable.php index 0e7746312..3d4647583 100644 --- a/app/Models/ServiceVariable.php +++ b/app/Models/ServiceVariable.php @@ -43,10 +43,10 @@ class ServiceVariable extends Model protected $guarded = ['id', 'created_at', 'updated_at']; /** - * Cast values to correct type. - * - * @var array - */ + * Cast values to correct type. + * + * @var array + */ protected $casts = [ 'option_id' => 'integer', 'user_viewable' => 'integer',