From 2ac90b50f2677c9039ed1f4363c2acc11f9a1c9d Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Tue, 12 Sep 2017 23:45:19 -0500 Subject: [PATCH] Begin refactoring Tasks to be apart of the Scheduler system --- app/Console/Commands/MakeUser.php | 5 +- .../ScheduleRepositoryInterface.php} | 35 ++- .../Repository/TaskRepositoryInterface.php | 18 -- .../Task/TaskIntervalTooLongException.php | 31 +++ .../Server/Tasks/TaskManagementController.php | 113 ++++++--- app/Http/Middleware/Server/ScheduleAccess.php | 91 ++++++++ ...st.php => ScheduleCreationFormRequest.php} | 38 ++-- app/Models/Schedule.php | 153 +++++++++++++ app/Models/Task.php | 106 ++++----- app/Providers/RepositoryServiceProvider.php | 3 + .../Eloquent/ScheduleRepository.php | 55 +++++ .../Schedules/ScheduleCreationService.php | 106 +++++++++ .../Schedules/Tasks/TaskCreationService.php | 84 +++++++ app/Services/Tasks/TaskCreationService.php | 108 --------- ...2017_09_10_225941_CreateSchedulesTable.php | 4 +- public/js/laroute.js | 2 +- public/themes/pterodactyl/css/pterodactyl.css | 17 ++ .../{tasks.js => tasks/management-actions.js} | 30 +-- .../js/frontend/tasks/view-actions.js | 61 +++++ resources/lang/en/exceptions.php | 3 + resources/lang/en/server.php | 22 ++ resources/lang/en/strings.php | 2 +- .../pterodactyl/layouts/master.blade.php | 2 +- .../task-template.blade.php} | 16 +- .../pterodactyl/server/tasks/index.blade.php | 37 ++- .../pterodactyl/server/tasks/new.blade.php | 214 +++++++----------- .../pterodactyl/server/tasks/view.blade.php | 2 +- routes/server.php | 12 +- 28 files changed, 902 insertions(+), 468 deletions(-) rename app/{Services/Tasks/TaskUpdateService.php => Contracts/Repository/ScheduleRepositoryInterface.php} (67%) create mode 100644 app/Exceptions/Service/Schedule/Task/TaskIntervalTooLongException.php create mode 100644 app/Http/Middleware/Server/ScheduleAccess.php rename app/Http/Requests/Server/{TaskCreationFormRequest.php => ScheduleCreationFormRequest.php} (63%) create mode 100644 app/Models/Schedule.php create mode 100644 app/Repositories/Eloquent/ScheduleRepository.php create mode 100644 app/Services/Schedules/ScheduleCreationService.php create mode 100644 app/Services/Schedules/Tasks/TaskCreationService.php delete mode 100644 app/Services/Tasks/TaskCreationService.php rename public/themes/pterodactyl/js/frontend/{tasks.js => tasks/management-actions.js} (76%) create mode 100644 public/themes/pterodactyl/js/frontend/tasks/view-actions.js rename resources/themes/pterodactyl/partials/{tasks/chain-template.blade.php => schedules/task-template.blade.php} (71%) diff --git a/app/Console/Commands/MakeUser.php b/app/Console/Commands/MakeUser.php index b7c7c78d7..3625e7bec 100644 --- a/app/Console/Commands/MakeUser.php +++ b/app/Console/Commands/MakeUser.php @@ -25,7 +25,6 @@ namespace Pterodactyl\Console\Commands; use Illuminate\Console\Command; -use Pterodactyl\Repositories\UserRepository; use Pterodactyl\Services\Users\UserCreationService; class MakeUser extends Command @@ -60,8 +59,7 @@ class MakeUser extends Command */ public function __construct( UserCreationService $creationService - ) - { + ) { parent::__construct(); $this->creationService = $creationService; } @@ -87,7 +85,6 @@ class MakeUser extends Command $data['root_admin'] = is_null($this->option('admin')) ? $this->confirm('Is this user a root administrator?') : $this->option('admin'); try { - $this->creationService->handle($data); return $this->info('User successfully created.'); diff --git a/app/Services/Tasks/TaskUpdateService.php b/app/Contracts/Repository/ScheduleRepositoryInterface.php similarity index 67% rename from app/Services/Tasks/TaskUpdateService.php rename to app/Contracts/Repository/ScheduleRepositoryInterface.php index af227fdcf..36760b2db 100644 --- a/app/Services/Tasks/TaskUpdateService.php +++ b/app/Contracts/Repository/ScheduleRepositoryInterface.php @@ -22,26 +22,23 @@ * SOFTWARE. */ -namespace Pterodactyl\Services\Tasks; +namespace Pterodactyl\Contracts\Repository; -use Pterodactyl\Contracts\Repository\TaskRepositoryInterface; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; - -class TaskUpdateService +interface ScheduleRepositoryInterface extends RepositoryInterface { - protected $repository; + /** + * Return all of the schedules for a given server. + * + * @param int $server + * @return \Illuminate\Database\Eloquent\Collection + */ + public function getServerSchedules($server); - protected $serverRepository; - - public function __construct( - ServerRepositoryInterface $serverRepository, - TaskRepositoryInterface $repository - ) { - $this->repository = $repository; - $this->serverRepository = $serverRepository; - } - - public function handle($server, array $data, array $chain = null) - { - } + /** + * Return a schedule model with all of the associated tasks as a relationship. + * + * @param int $schedule + * @return \Illuminate\Support\Collection + */ + public function getScheduleWithTasks($schedule); } diff --git a/app/Contracts/Repository/TaskRepositoryInterface.php b/app/Contracts/Repository/TaskRepositoryInterface.php index 8d8b32c33..f105747b8 100644 --- a/app/Contracts/Repository/TaskRepositoryInterface.php +++ b/app/Contracts/Repository/TaskRepositoryInterface.php @@ -26,22 +26,4 @@ namespace Pterodactyl\Contracts\Repository; interface TaskRepositoryInterface extends RepositoryInterface { - /** - * Return the parent tasks and the count of children attached to that task. - * - * @param int $server - * @return mixed - */ - public function getParentTasksWithChainCount($server); - - /** - * Return a single task for a given server including all of the chained tasks. - * - * @param int $task - * @param int $server - * @return mixed - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function getTaskForServer($task, $server); } diff --git a/app/Exceptions/Service/Schedule/Task/TaskIntervalTooLongException.php b/app/Exceptions/Service/Schedule/Task/TaskIntervalTooLongException.php new file mode 100644 index 000000000..16cd3f212 --- /dev/null +++ b/app/Exceptions/Service/Schedule/Task/TaskIntervalTooLongException.php @@ -0,0 +1,31 @@ +. + * + * 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\Exceptions\Service\Schedule\Task; + +use Pterodactyl\Exceptions\DisplayException; + +class TaskIntervalTooLongException extends DisplayException +{ +} diff --git a/app/Http/Controllers/Server/Tasks/TaskManagementController.php b/app/Http/Controllers/Server/Tasks/TaskManagementController.php index 80c2e6679..953815887 100644 --- a/app/Http/Controllers/Server/Tasks/TaskManagementController.php +++ b/app/Http/Controllers/Server/Tasks/TaskManagementController.php @@ -24,20 +24,27 @@ namespace Pterodactyl\Http\Controllers\Server\Tasks; +use Prologue\Alerts\AlertsMessageBag; +use Pterodactyl\Http\Requests\Request; use Illuminate\Contracts\Session\Session; use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Services\Tasks\TaskCreationService; use Pterodactyl\Contracts\Extensions\HashidsInterface; use Pterodactyl\Traits\Controllers\JavascriptInjection; -use Pterodactyl\Contracts\Repository\TaskRepositoryInterface; -use Pterodactyl\Http\Requests\Server\TaskCreationFormRequest; +use Pterodactyl\Services\Schedules\ScheduleCreationService; +use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface; +use Pterodactyl\Http\Requests\Server\ScheduleCreationFormRequest; class TaskManagementController extends Controller { use JavascriptInjection; /** - * @var \Pterodactyl\Services\Tasks\TaskCreationService + * @var \Prologue\Alerts\AlertsMessageBag + */ + protected $alert; + + /** + * @var \Pterodactyl\Services\Schedules\ScheduleCreationService */ protected $creationService; @@ -47,7 +54,7 @@ class TaskManagementController extends Controller protected $hashids; /** - * @var \Pterodactyl\Contracts\Repository\TaskRepositoryInterface + * @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface */ protected $repository; @@ -59,17 +66,20 @@ class TaskManagementController extends Controller /** * TaskManagementController constructor. * - * @param \Pterodactyl\Contracts\Extensions\HashidsInterface $hashids - * @param \Illuminate\Contracts\Session\Session $session - * @param \Pterodactyl\Services\Tasks\TaskCreationService $creationService - * @param \Pterodactyl\Contracts\Repository\TaskRepositoryInterface $repository + * @param \Prologue\Alerts\AlertsMessageBag $alert + * @param \Pterodactyl\Contracts\Extensions\HashidsInterface $hashids + * @param \Illuminate\Contracts\Session\Session $session + * @param \Pterodactyl\Services\Schedules\ScheduleCreationService $creationService + * @param \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface $repository */ public function __construct( + AlertsMessageBag $alert, HashidsInterface $hashids, Session $session, - TaskCreationService $creationService, - TaskRepositoryInterface $repository + ScheduleCreationService $creationService, + ScheduleRepositoryInterface $repository ) { + $this->alert = $alert; $this->creationService = $creationService; $this->hashids = $hashids; $this->repository = $repository; @@ -86,11 +96,11 @@ class TaskManagementController extends Controller public function index() { $server = $this->session->get('server_data.model'); - $this->authorize('list-tasks', $server); + $this->authorize('list-schedules', $server); $this->injectJavascript(); return view('server.tasks.index', [ - 'tasks' => $this->repository->getParentTasksWithChainCount($server->id), + 'schedules' => $this->repository->getServerSchedules($server->id), 'actions' => [ 'command' => trans('server.tasks.actions.command'), 'power' => trans('server.tasks.actions.power'), @@ -108,64 +118,95 @@ class TaskManagementController extends Controller public function create() { $server = $this->session->get('server_data.model'); - $this->authorize('create-task', $server); + $this->authorize('create-schedule', $server); $this->injectJavascript(); return view('server.tasks.new'); } /** - * @param \Pterodactyl\Http\Requests\Server\TaskCreationFormRequest $request - * + * @param \Pterodactyl\Http\Requests\Server\ScheduleCreationFormRequest $request * @return \Illuminate\Http\RedirectResponse * - * @throws \Exception * @throws \Illuminate\Auth\Access\AuthorizationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Pterodactyl\Exceptions\Service\Schedule\Task\TaskIntervalTooLongException */ - public function store(TaskCreationFormRequest $request) + public function store(ScheduleCreationFormRequest $request) { $server = $this->session->get('server_data.model'); - $this->authorize('create-task', $server); + $this->authorize('create-schedule', $server); - $task = $this->creationService->handle($server, $request->normalize(), $request->getChainedTasks()); + $schedule = $this->creationService->handle($server, $request->normalize(), $request->getTasks()); + $this->alert->success(trans('server.tasks.task_created'))->flash(); return redirect()->route('server.tasks.view', [ 'server' => $server->uuidShort, - 'task' => $task->id, + 'task' => $schedule->hashid, ]); } /** - * Return a view to modify task settings. + * Return a view to modify a schedule. * - * @param string $uuid - * @param string $task + * @param \Pterodactyl\Http\Requests\Request $request * @return \Illuminate\View\View - * * @throws \Illuminate\Auth\Access\AuthorizationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function view($uuid, $task) + public function view(Request $request) { $server = $this->session->get('server_data.model'); - $this->authorize('edit-task', $server); - $task = $this->repository->getTaskForServer($this->hashids->decodeFirst($task, 0), $server->id); + $schedule = $request->attributes->get('schedule'); + $this->authorize('view-schedule', $server); $this->injectJavascript([ - 'chained' => $task->chained->map(function ($chain) { - return collect($chain->toArray())->only('action', 'chain_delay', 'data')->all(); + 'tasks' => $schedule->tasks->map(function ($schedule) { + return collect($schedule->toArray())->only('action', 'time_offset', 'payload')->all(); }), ]); - return view('server.tasks.view', ['task' => $task]); + return view('server.tasks.view', ['schedule' => $schedule]); } - public function update(TaskCreationFormRequest $request, $uuid, $task) + /** + * Update a specific parent task on the system. + * + * @param \Pterodactyl\Http\Requests\Server\ScheduleCreationFormRequest $request + * @return \Illuminate\Http\RedirectResponse + * + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function update(ScheduleCreationFormRequest $request) { $server = $this->session->get('server_data.model'); - $this->authorize('edit-task', $server); - $task = $this->repository->getTaskForServer($this->hashids->decodeFirst($task, 0), $server->id); + $schedule = $request->attributes->get('schedule'); + $this->authorize('edit-schedule', $server); + + // $this->updateService->handle($task, $request->normalize(), $request->getChainedTasks()); + $this->alert->success(trans('server.tasks.task_updated'))->flash(); + + return redirect()->route('server.tasks.view', [ + 'server' => $server->uuidShort, + 'task' => $schedule->hashid, + ]); + } + + /** + * Delete a parent task from the Panel. + * + * @param \Pterodactyl\Http\Requests\Request $request + * @return \Illuminate\Http\RedirectResponse + * + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function delete(Request $request) + { + $server = $this->session->get('server_data.model'); + $schedule = $request->attributes->get('schedule'); + $this->authorize('delete-schedule', $server); + + $this->repository->delete($task->id); + + return response('', 204); } } diff --git a/app/Http/Middleware/Server/ScheduleAccess.php b/app/Http/Middleware/Server/ScheduleAccess.php new file mode 100644 index 000000000..7629f1ad5 --- /dev/null +++ b/app/Http/Middleware/Server/ScheduleAccess.php @@ -0,0 +1,91 @@ +. + * + * 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\Middleware\Server; + +use Closure; +use Illuminate\Contracts\Session\Session; +use Pterodactyl\Contracts\Extensions\HashidsInterface; +use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface; + +class ScheduleAccess +{ + /** + * @var \Pterodactyl\Contracts\Extensions\HashidsInterface + */ + protected $hashids; + + /** + * @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface + */ + protected $repository; + + /** + * @var \Illuminate\Contracts\Session\Session + */ + protected $session; + + /** + * TaskAccess constructor. + * + * @param \Pterodactyl\Contracts\Extensions\HashidsInterface $hashids + * @param \Illuminate\Contracts\Session\Session $session + * @param \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface $repository + */ + public function __construct( + HashidsInterface $hashids, + Session $session, + ScheduleRepositoryInterface $repository + ) { + $this->hashids = $hashids; + $this->repository = $repository; + $this->session = $session; + } + + /** + * Determine if a task is assigned to the active server. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @return mixed + * + * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function handle($request, Closure $next) + { + $server = $this->session->get('server_data.model'); + + $scheduleId = $this->hashids->decodeFirst($request->route()->parameter('task'), 0); + $schedule = $this->repository->getScheduleWithTasks($scheduleId); + + if ($schedule->server_id !== $server->id) { + abort(404); + } + + $request->attributes->set('schedule', $schedule); + + return $next($request); + } +} diff --git a/app/Http/Requests/Server/TaskCreationFormRequest.php b/app/Http/Requests/Server/ScheduleCreationFormRequest.php similarity index 63% rename from app/Http/Requests/Server/TaskCreationFormRequest.php rename to app/Http/Requests/Server/ScheduleCreationFormRequest.php index 486e04b36..0c0770994 100644 --- a/app/Http/Requests/Server/TaskCreationFormRequest.php +++ b/app/Http/Requests/Server/ScheduleCreationFormRequest.php @@ -26,7 +26,7 @@ namespace Pterodactyl\Http\Requests\Server; use Pterodactyl\Http\Requests\FrontendUserFormRequest; -class TaskCreationFormRequest extends FrontendUserFormRequest +class ScheduleCreationFormRequest extends FrontendUserFormRequest { /** * Validation rules to apply to the request. @@ -37,21 +37,19 @@ class TaskCreationFormRequest extends FrontendUserFormRequest { return [ 'name' => 'string|max:255', - 'day_of_week' => 'required|string', - 'day_of_month' => 'required|string', - 'hour' => 'required|string', - 'minute' => 'required|string', - 'action' => 'required|string|in:power,command', - 'data' => 'required|string', - 'chain' => 'sometimes|array|size:4', - 'chain.time_value' => 'required_with:chain|max:5', - 'chain.time_interval' => 'required_with:chain|max:5', - 'chain.action' => 'required_with:chain|max:5', - 'chain.payload' => 'required_with:chain|max:5', - 'chain.time_value.*' => 'numeric|between:1,60', - 'chain.time_interval.*' => 'string|in:s,m', - 'chain.action.*' => 'string|in:power,command', - 'chain.payload.*' => 'string', + 'cron_day_of_week' => 'required|string', + 'cron_day_of_month' => 'required|string', + 'cron_hour' => 'required|string', + 'cron_minute' => 'required|string', + 'tasks' => 'sometimes|array|size:4', + 'tasks.time_value' => 'required_with:chain|max:5', + 'tasks.time_interval' => 'required_with:chain|max:5', + 'tasks.action' => 'required_with:chain|max:5', + 'tasks.payload' => 'required_with:chain|max:5', + 'tasks.time_value.*' => 'numeric|between:1,60', + 'tasks.time_interval.*' => 'string|in:s,m', + 'tasks.action.*' => 'string|in:power,command', + 'tasks.payload.*' => 'string', ]; } @@ -62,18 +60,18 @@ class TaskCreationFormRequest extends FrontendUserFormRequest */ public function normalize() { - return $this->only('name', 'day_of_week', 'day_of_month', 'hour', 'minute', 'action', 'data'); + return $this->only('name', 'cron_day_of_week', 'cron_day_of_month', 'cron_hour', 'cron_minute'); } /** - * Return the chained tasks provided in the request. + * Return the tasks provided in the request that are associated with this schedule. * * @return array|null */ - public function getChainedTasks() + public function getTasks() { $restructured = []; - foreach (array_get($this->all(), 'chain', []) as $key => $values) { + foreach (array_get($this->all(), 'tasks', []) as $key => $values) { for ($i = 0; $i < count($values); ++$i) { $restructured[$i][$key] = $values[$i]; } diff --git a/app/Models/Schedule.php b/app/Models/Schedule.php new file mode 100644 index 000000000..aefea1984 --- /dev/null +++ b/app/Models/Schedule.php @@ -0,0 +1,153 @@ +. + * + * 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 Sofa\Eloquence\Eloquence; +use Sofa\Eloquence\Validable; +use Illuminate\Database\Eloquent\Model; +use Sofa\Eloquence\Contracts\CleansAttributes; +use Sofa\Eloquence\Contracts\Validable as ValidableContract; + +class Schedule extends Model implements CleansAttributes, ValidableContract +{ + use Eloquence, Validable; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'schedules'; + + /** + * Mass assignable attributes on this model. + * + * @var array + */ + protected $fillable = [ + 'server_id', + 'name', + 'cron_day_of_week', + 'cron_day_of_month', + 'cron_hour', + 'cron_minute', + 'is_active', + 'is_processing', + 'last_run_at', + 'next_run_at', + ]; + + /** + * @var array + */ + protected $casts = [ + 'id' => 'integer', + 'server_id' => 'integer', + 'is_active' => 'boolean', + 'is_processing' => 'boolean', + ]; + + /** + * Columns to mutate to a date. + * + * @var array + */ + protected $dates = [ + self::CREATED_AT, + self::UPDATED_AT, + 'last_run_at', + 'next_run_at', + ]; + + /** + * @var array + */ + protected $attributes = [ + 'name' => null, + 'cron_day_of_week' => '*', + 'cron_day_of_month' => '*', + 'cron_hour' => '*', + 'cron_minute' => '*', + 'is_active' => true, + 'is_processing' => false, + ]; + + /** + * @var array + */ + protected static $applicationRules = [ + 'server_id' => 'required', + 'cron_day_of_week' => 'required', + 'cron_day_of_month' => 'required', + 'cron_hour' => 'required', + 'cron_minute' => 'required', + ]; + + /** + * @var array + */ + protected static $dataIntegrityRules = [ + 'server_id' => 'exists:servers,id', + 'name' => 'nullable|string|max:255', + 'cron_day_of_week' => 'string', + 'cron_day_of_month' => 'string', + 'cron_hour' => 'string', + 'cron_minute' => 'string', + 'is_active' => 'boolean', + 'is_processing' => 'boolean', + 'last_run_at' => 'nullable|timestamp', + 'next_run_at' => 'nullable|timestamp', + ]; + + /** + * Return a hashid encoded string to represent the ID of the schedule. + * + * @return string + */ + public function getHashidAttribute() + { + return app()->make('hashids')->encode($this->id); + } + + /** + * Return tasks belonging to a schedule. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function tasks() + { + return $this->hasMany(Task::class); + } + + /** + * Return the server model that a schedule belongs to. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function server() + { + return $this->belongsTo(Server::class); + } +} diff --git a/app/Models/Task.php b/app/Models/Task.php index 71a582510..40f562c16 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -42,11 +42,25 @@ class Task extends Model implements CleansAttributes, ValidableContract protected $table = 'tasks'; /** - * Fields that are not mass assignable. + * Relationships to be updated when this model is updated. * * @var array */ - protected $guarded = ['id', 'created_at', 'updated_at']; + protected $touches = ['schedule']; + + /** + * Fields that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'schedule_id', + 'squence_id', + 'action', + 'payload', + 'time_offset', + 'is_queued', + ]; /** * Cast values to correct type. @@ -55,10 +69,10 @@ class Task extends Model implements CleansAttributes, ValidableContract */ protected $casts = [ 'id' => 'integer', - 'user_id' => 'integer', - 'server_id' => 'integer', - 'queued' => 'boolean', - 'active' => 'boolean', + 'schedule_id' => 'integer', + 'squence_id' => 'integer', + 'time_offset' => 'integer', + 'is_queued' => 'boolean', ]; /** @@ -67,54 +81,32 @@ class Task extends Model implements CleansAttributes, ValidableContract * @var array */ protected $attributes = [ - 'parent_task_id' => null, - 'chain_order' => null, - 'active' => true, - 'day_of_week' => '*', - 'day_of_month' => '*', - 'hour' => '*', - 'minute' => '*', - 'chain_delay' => null, - 'queued' => false, + 'is_queued' => false, ]; /** * @var array */ protected static $applicationRules = [ - 'server_id' => 'required', + 'schedule_id' => 'required', + 'squence_id' => 'required', 'action' => 'required', - 'data' => 'required', + 'payload' => 'required', + 'time_offset' => 'required', ]; /** * @var array */ protected static $dataIntegrityRules = [ - 'name' => 'nullable|string|max:255', - 'parent_task_id' => 'nullable|numeric|exists:tasks,id', - 'chain_order' => 'nullable|numeric|min:1', - 'server_id' => 'numeric|exists:servers,id', - 'active' => 'boolean', + 'schedule_id' => 'numeric|exists:schedules,id', + 'squence_id' => 'numeric|min:1', 'action' => 'string', - 'data' => 'string', - 'queued' => 'boolean', - 'day_of_month' => 'string', - 'day_of_week' => 'string', - 'hour' => 'string', - 'minute' => 'string', - 'chain_delay' => 'nullable|numeric|between:1,900', - 'last_run' => 'nullable|timestamp', - 'next_run' => 'nullable|timestamp', + 'payload' => 'string', + 'time_offset' => 'numeric|between:0,900', + 'is_queued' => 'boolean', ]; - /** - * The attributes that should be mutated to dates. - * - * @var array - */ - protected $dates = ['last_run', 'next_run', 'created_at', 'updated_at']; - /** * Return a hashid encoded string to represent the ID of the task. * @@ -126,32 +118,26 @@ class Task extends Model implements CleansAttributes, ValidableContract } /** - * Gets the server associated with a task. + * Return the schedule that a task belongs to. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function schedule() + { + return $this->belongsTo(Schedule::class); + } + + /** + * Return the server a task is assigned to, acts as a belongsToThrough. * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function server() { - return $this->belongsTo(Server::class); - } - - /** - * Gets the user associated with a task. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function user() - { - return $this->belongsTo(User::class); - } - - /** - * Return chained tasks for a parent task. - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function chained() - { - return $this->hasMany(self::class, 'parent_task_id')->orderBy('chain_order', 'asc'); + if ($schedule = $this->schedule) { + return $schedule->server(); + } else { + throw new \InvalidArgumentException('Instance of Task must have an associated Schedule in the database.'); + } } } diff --git a/app/Providers/RepositoryServiceProvider.php b/app/Providers/RepositoryServiceProvider.php index f44715dfb..4167b95da 100644 --- a/app/Providers/RepositoryServiceProvider.php +++ b/app/Providers/RepositoryServiceProvider.php @@ -39,6 +39,7 @@ use Pterodactyl\Repositories\Eloquent\SessionRepository; use Pterodactyl\Repositories\Eloquent\SubuserRepository; use Pterodactyl\Repositories\Eloquent\DatabaseRepository; use Pterodactyl\Repositories\Eloquent\LocationRepository; +use Pterodactyl\Repositories\Eloquent\ScheduleRepository; use Pterodactyl\Repositories\Eloquent\AllocationRepository; use Pterodactyl\Repositories\Eloquent\PermissionRepository; use Pterodactyl\Repositories\Daemon\ConfigurationRepository; @@ -59,6 +60,7 @@ use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface; use Pterodactyl\Repositories\Eloquent\ServiceVariableRepository; use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; use Pterodactyl\Contracts\Repository\LocationRepositoryInterface; +use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface; use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface; use Pterodactyl\Contracts\Repository\PermissionRepositoryInterface; use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface; @@ -92,6 +94,7 @@ class RepositoryServiceProvider extends ServiceProvider $this->app->bind(OptionVariableRepositoryInterface::class, OptionVariableRepository::class); $this->app->bind(PackRepositoryInterface::class, PackRepository::class); $this->app->bind(PermissionRepositoryInterface::class, PermissionRepository::class); + $this->app->bind(ScheduleRepositoryInterface::class, ScheduleRepository::class); $this->app->bind(ServerRepositoryInterface::class, ServerRepository::class); $this->app->bind(ServerVariableRepositoryInterface::class, ServerVariableRepository::class); $this->app->bind(ServiceRepositoryInterface::class, ServiceRepository::class); diff --git a/app/Repositories/Eloquent/ScheduleRepository.php b/app/Repositories/Eloquent/ScheduleRepository.php new file mode 100644 index 000000000..f30c80d1c --- /dev/null +++ b/app/Repositories/Eloquent/ScheduleRepository.php @@ -0,0 +1,55 @@ +. + * + * 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\Eloquent; + +use Pterodactyl\Models\Schedule; +use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface; + +class ScheduleRepository extends EloquentRepository implements ScheduleRepositoryInterface +{ + /** + * {@inheritdoc} + */ + public function model() + { + return Schedule::class; + } + + /** + * {@inheritdoc} + */ + public function getServerSchedules($server) + { + return $this->getBuilder()->withCount('tasks')->where('server_id', '=', $server)->get($this->getColumns()); + } + + /** + * {@inheritdoc} + */ + public function getScheduleWithTasks($schedule) + { + return $this->getBuilder()->with('tasks')->find($schedule, $this->getColumns()); + } +} diff --git a/app/Services/Schedules/ScheduleCreationService.php b/app/Services/Schedules/ScheduleCreationService.php new file mode 100644 index 000000000..3a1920aac --- /dev/null +++ b/app/Services/Schedules/ScheduleCreationService.php @@ -0,0 +1,106 @@ +. + * + * 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\Services\Schedules; + +use Webmozart\Assert\Assert; +use Pterodactyl\Models\Server; +use Illuminate\Database\ConnectionInterface; +use Pterodactyl\Services\Schedules\Tasks\TaskCreationService; +use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface; + +class ScheduleCreationService +{ + /** + * @var \Illuminate\Database\ConnectionInterface + */ + protected $connection; + + /** + * @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface + */ + protected $repository; + + /** + * @var \Pterodactyl\Services\Schedules\Tasks\TaskCreationService + */ + protected $taskCreationService; + + /** + * ScheduleCreationService constructor. + * + * @param \Illuminate\Database\ConnectionInterface $connection + * @param \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface $repository + * @param \Pterodactyl\Services\Schedules\Tasks\TaskCreationService $taskCreationService + */ + public function __construct( + ConnectionInterface $connection, + ScheduleRepositoryInterface $repository, + TaskCreationService $taskCreationService + ) { + $this->connection = $connection; + $this->repository = $repository; + $this->taskCreationService = $taskCreationService; + } + + /** + * Create a new schedule for a specific server. + * + * @param int|\Pterodactyl\Models\Server $server + * @param array $data + * @param array $tasks + * @return \Pterodactyl\Models\Schedule + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Service\Schedule\Task\TaskIntervalTooLongException + */ + public function handle($server, array $data, array $tasks = []) + { + Assert::true(($server instanceof Server || is_numeric($server)), + 'First argument passed to handle must be numeric or instance of \Pterodactyl\Models\Server, received %s.' + ); + + $server = ($server instanceof Server) ? $server->id : $server; + $data['server_id'] = $server; + + $this->connection->beginTransaction(); + $schedule = $this->repository->create($data); + + if (! empty($tasks)) { + foreach ($tasks as $index => $task) { + $this->taskCreationService->handle($schedule, [ + 'time_interval' => array_get($task, 'time_interval'), + 'time_value' => array_get($task, 'time_value'), + 'sequence_id' => $index + 1, + 'action' => array_get($task, 'action'), + 'payload' => array_get($task, 'payload'), + ], false); + } + } + + $this->connection->commit(); + + return $schedule; + } +} diff --git a/app/Services/Schedules/Tasks/TaskCreationService.php b/app/Services/Schedules/Tasks/TaskCreationService.php new file mode 100644 index 000000000..556528782 --- /dev/null +++ b/app/Services/Schedules/Tasks/TaskCreationService.php @@ -0,0 +1,84 @@ +. + * + * 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\Services\Schedules\Tasks; + +use Webmozart\Assert\Assert; +use Pterodactyl\Models\Schedule; +use Pterodactyl\Contracts\Repository\TaskRepositoryInterface; +use Pterodactyl\Exceptions\Service\Schedule\Task\TaskIntervalTooLongException; + +class TaskCreationService +{ + /** + * @var \Pterodactyl\Contracts\Repository\TaskRepositoryInterface + */ + protected $repository; + + /** + * TaskCreationService constructor. + * + * @param \Pterodactyl\Contracts\Repository\TaskRepositoryInterface $repository + */ + public function __construct(TaskRepositoryInterface $repository) + { + $this->repository = $repository; + } + + /** + * Create a new task that is assigned to a schedule. + * + * @param int|\Pterodactyl\Models\Schedule $schedule + * @param array $data + * @param bool $returnModel + * @return bool|\Pterodactyl\Models\Task + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Service\Schedule\Task\TaskIntervalTooLongException + */ + public function handle($schedule, array $data, $returnModel = true) + { + Assert::true(($schedule instanceof Schedule || is_numeric($schedule)), + 'First argument passed to handle must be numeric or instance of \Pterodactyl\Models\Schedule, received %s.' + ); + + $schedule = ($schedule instanceof Schedule) ? $schedule->id : $schedule; + if ($data['time_interval'] === 'm' && $data['time_value'] > 15) { + throw new TaskIntervalTooLongException(trans('exceptions.tasks.chain_interval_too_long')); + } + + $delay = $data['time_interval'] === 'm' ? $data['time_value'] * 60 : $data['time_value']; + + $repository = ($returnModel) ? $this->repository : $this->repository->withoutFresh(); + $task = $repository->create([ + 'schedule_id' => $schedule, + 'sequence_id' => $data['sequence_id'], + 'action' => $data['action'], + 'payload' => $data['payload'], + 'time_offset' => $delay, + ]); + + return $task; + } +} diff --git a/app/Services/Tasks/TaskCreationService.php b/app/Services/Tasks/TaskCreationService.php deleted file mode 100644 index d7549bf02..000000000 --- a/app/Services/Tasks/TaskCreationService.php +++ /dev/null @@ -1,108 +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\Services\Tasks; - -use Pterodactyl\Models\Server; -use Illuminate\Database\ConnectionInterface; -use Pterodactyl\Contracts\Repository\TaskRepositoryInterface; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; - -class TaskCreationService -{ - /** - * @var \Illuminate\Database\ConnectionInterface - */ - protected $connection; - - /** - * @var \Pterodactyl\Contracts\Repository\TaskRepositoryInterface - */ - protected $repository; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - protected $serverRepository; - - /** - * TaskCreationService constructor. - * - * @param \Illuminate\Database\ConnectionInterface $connection - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository - * @param \Pterodactyl\Contracts\Repository\TaskRepositoryInterface $repository - */ - public function __construct( - ConnectionInterface $connection, - ServerRepositoryInterface $serverRepository, - TaskRepositoryInterface $repository - ) { - $this->connection = $connection; - $this->repository = $repository; - $this->serverRepository = $serverRepository; - } - - /** - * @param int|\Pterodactyl\Models\Server $server - * @param array $data - * @param array|null $chain - * @return \Pterodactyl\Models\Task - * - * @throws \Exception - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function handle($server, array $data, array $chain = null) - { - if (! $server instanceof Server) { - $server = $this->serverRepository->find($server); - } - - $this->connection->beginTransaction(); - - $data['server_id'] = $server->id; - $task = $this->repository->create($data); - - if (is_array($chain)) { - foreach ($chain as $index => $values) { - if ($values['time_interval'] === 'm' && $values['time_value'] > 15) { - throw new \Exception('I should fix this.'); - } - - $delay = $values['time_interval'] === 'm' ? $values['time_value'] * 60 : $values['time_value']; - $this->repository->withoutFresh()->create([ - 'parent_task_id' => $task->id, - 'chain_order' => $index + 1, - 'server_id' => $server->id, - 'action' => $values['action'], - 'data' => $values['payload'], - 'chain_delay' => $delay, - ]); - } - } - $this->connection->commit(); - - return $task; - } -} diff --git a/database/migrations/2017_09_10_225941_CreateSchedulesTable.php b/database/migrations/2017_09_10_225941_CreateSchedulesTable.php index 1b8ec3456..3d5baa6d3 100644 --- a/database/migrations/2017_09_10_225941_CreateSchedulesTable.php +++ b/database/migrations/2017_09_10_225941_CreateSchedulesTable.php @@ -21,8 +21,8 @@ class CreateSchedulesTable extends Migration $table->string('cron_minute'); $table->boolean('is_active'); $table->boolean('is_processing'); - $table->timestamp('last_run_at'); - $table->timestamp('next_run_at'); + $table->timestamp('last_run_at')->nullable(); + $table->timestamp('next_run_at')->nullable(); $table->timestamps(); $table->foreign('server_id')->references('id')->on('servers')->onDelete('cascade'); diff --git a/public/js/laroute.js b/public/js/laroute.js index 10af670a1..dbdf65c97 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":"api\/user","name":"api.user","action":"Pterodactyl\Http\Controllers\API\User\CoreController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/user\/server\/{server}","name":"api.user.server","action":"Pterodactyl\Http\Controllers\API\User\ServerController@index"},{"host":null,"methods":["POST"],"uri":"api\/user\/server\/{server}\/power","name":"api.user.server.power","action":"Pterodactyl\Http\Controllers\API\User\ServerController@power"},{"host":null,"methods":["POST"],"uri":"api\/user\/server\/{server}\/command","name":"api.user.server.command","action":"Pterodactyl\Http\Controllers\API\User\ServerController@command"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\CoreController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/servers","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/servers\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@view"},{"host":null,"methods":["POST"],"uri":"api\/admin\/servers","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@store"},{"host":null,"methods":["PUT"],"uri":"api\/admin\/servers\/{id}\/details","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@details"},{"host":null,"methods":["PUT"],"uri":"api\/admin\/servers\/{id}\/container","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@container"},{"host":null,"methods":["PUT"],"uri":"api\/admin\/servers\/{id}\/build","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@build"},{"host":null,"methods":["PUT"],"uri":"api\/admin\/servers\/{id}\/startup","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@startup"},{"host":null,"methods":["PATCH"],"uri":"api\/admin\/servers\/{id}\/install","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@install"},{"host":null,"methods":["PATCH"],"uri":"api\/admin\/servers\/{id}\/rebuild","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@rebuild"},{"host":null,"methods":["PATCH"],"uri":"api\/admin\/servers\/{id}\/suspend","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@suspend"},{"host":null,"methods":["DELETE"],"uri":"api\/admin\/servers\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/locations","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\LocationController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/nodes","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\NodeController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/nodes\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\NodeController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/nodes\/{id}\/config","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\NodeController@viewConfig"},{"host":null,"methods":["POST"],"uri":"api\/admin\/nodes","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\NodeController@store"},{"host":null,"methods":["DELETE"],"uri":"api\/admin\/nodes\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\NodeController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/users","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\UserController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/users\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\UserController@view"},{"host":null,"methods":["POST"],"uri":"api\/admin\/users","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\UserController@store"},{"host":null,"methods":["PUT"],"uri":"api\/admin\/users\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\UserController@update"},{"host":null,"methods":["DELETE"],"uri":"api\/admin\/users\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\UserController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/services","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServiceController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/services\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServiceController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"\/","name":"index","action":"Pterodactyl\Http\Controllers\Base\IndexController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"status\/{server}","name":"index.status","action":"Pterodactyl\Http\Controllers\Base\IndexController@status"},{"host":null,"methods":["GET","HEAD"],"uri":"index","name":null,"action":"Closure"},{"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@store"},{"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":"admin","name":"admin.index","action":"Pterodactyl\Http\Controllers\Admin\BaseController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/locations","name":"admin.locations","action":"Pterodactyl\Http\Controllers\Admin\LocationController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/locations\/view\/{id}","name":"admin.locations.view","action":"Pterodactyl\Http\Controllers\Admin\LocationController@view"},{"host":null,"methods":["POST"],"uri":"admin\/locations","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationController@create"},{"host":null,"methods":["POST"],"uri":"admin\/locations\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/databases","name":"admin.databases","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/databases\/view\/{id}","name":"admin.databases.view","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@view"},{"host":null,"methods":["POST"],"uri":"admin\/databases","name":null,"action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@create"},{"host":null,"methods":["POST"],"uri":"admin\/databases\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@update"},{"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@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/accounts.json","name":"admin.users.json","action":"Pterodactyl\Http\Controllers\Admin\UserController@json"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/new","name":"admin.users.new","action":"Pterodactyl\Http\Controllers\Admin\UserController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/view\/{id}","name":"admin.users.view","action":"Pterodactyl\Http\Controllers\Admin\UserController@view"},{"host":null,"methods":["POST"],"uri":"admin\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@store"},{"host":null,"methods":["POST"],"uri":"admin\/users\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/users\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@delete"},{"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@create"},{"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":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}\/build","name":"admin.servers.view.build","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewBuild"},{"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":["GET","HEAD"],"uri":"admin\/servers\/view\/{id}\/database","name":"admin.servers.view.database","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDatabase"},{"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":["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\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@store"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/new\/nodes","name":"admin.servers.new.nodes","action":"Pterodactyl\Http\Controllers\Admin\ServersController@nodes"},{"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":["POST"],"uri":"admin\/servers\/view\/{id}\/build","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@updateBuild"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/startup","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@saveStartup"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/database","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@newDatabase"},{"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":["POST"],"uri":"admin\/servers\/view\/{id}\/manage\/reinstall","name":"admin.servers.view.manage.reinstall","action":"Pterodactyl\Http\Controllers\Admin\ServersController@reinstallServer"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{id}\/delete","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@delete"},{"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\/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@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":["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":["GET","HEAD"],"uri":"admin\/nodes\/view\/{id}\/servers","name":"admin.nodes.view.servers","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewServers"},{"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":["POST"],"uri":"admin\/nodes\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@store"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{id}\/settings","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@updateSettings"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{id}\/allocation","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@createAllocation"},{"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":["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":["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@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":["GET","HEAD"],"uri":"admin\/services\/view\/{id}\/functions","name":"admin.services.view.functions","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@viewFunctions"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/option\/new","name":"admin.services.option.new","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":["GET","HEAD"],"uri":"admin\/services\/option\/{id}\/variables","name":"admin.services.option.variables","action":"Pterodactyl\Http\Controllers\Admin\OptionController@viewVariables"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/option\/{id}\/scripts","name":"admin.services.option.scripts","action":"Pterodactyl\Http\Controllers\Admin\OptionController@viewScripts"},{"host":null,"methods":["POST"],"uri":"admin\/services\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@store"},{"host":null,"methods":["POST"],"uri":"admin\/services\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@edit"},{"host":null,"methods":["POST"],"uri":"admin\/services\/option\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\OptionController@store"},{"host":null,"methods":["POST"],"uri":"admin\/services\/option\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\OptionController@editConfiguration"},{"host":null,"methods":["POST"],"uri":"admin\/services\/option\/{id}\/scripts","name":null,"action":"Pterodactyl\Http\Controllers\Admin\OptionController@updateScripts"},{"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":["DELETE"],"uri":"admin\/services\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@delete"},{"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@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":["GET","HEAD"],"uri":"admin\/packs\/view\/{id}","name":"admin.packs.view","action":"Pterodactyl\Http\Controllers\Admin\PackController@view"},{"host":null,"methods":["POST"],"uri":"admin\/packs\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@store"},{"host":null,"methods":["POST"],"uri":"admin\/packs\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@update"},{"host":null,"methods":["POST"],"uri":"admin\/packs\/view\/{id}\/export\/{files?}","name":"admin.packs.view.export","action":"Pterodactyl\Http\Controllers\Admin\PackController@export"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/logout","name":"auth.logout","action":"Pterodactyl\Http\Controllers\Auth\LoginController@logout"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/login","name":"auth.login","action":"Pterodactyl\Http\Controllers\Auth\LoginController@showLoginForm"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/login\/totp","name":"auth.totp","action":"Pterodactyl\Http\Controllers\Auth\LoginController@totp"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/password","name":"auth.password","action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm"},{"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\/login","name":null,"action":"Pterodactyl\Http\Controllers\Auth\LoginController@login"},{"host":null,"methods":["POST"],"uri":"auth\/login\/totp","name":null,"action":"Pterodactyl\Http\Controllers\Auth\LoginController@totpCheckpoint"},{"host":null,"methods":["POST"],"uri":"auth\/password","name":null,"action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail"},{"host":null,"methods":["POST"],"uri":"auth\/password\/reset","name":"auth.reset.post","action":"Pterodactyl\Http\Controllers\Auth\ResetPasswordController@reset"},{"host":null,"methods":["POST"],"uri":"auth\/password\/reset\/{token}","name":null,"action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail"},{"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}\/console","name":"server.console","action":"Pterodactyl\Http\Controllers\Server\ServerController@getConsole"},{"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":["GET","HEAD"],"uri":"server\/{server}\/settings\/startup","name":"server.settings.startup","action":"Pterodactyl\Http\Controllers\Server\ServerController@getStartup"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/allocation","name":"server.settings.allocation","action":"Pterodactyl\Http\Controllers\Server\ServerController@getAllocation"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/settings\/sftp","name":null,"action":"Pterodactyl\Http\Controllers\Server\ServerController@postSettingsSFTP"},{"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}\/files","name":"server.files.index","action":"Pterodactyl\Http\Controllers\Server\ServerController@getFiles"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/add","name":"server.files.add","action":"Pterodactyl\Http\Controllers\Server\ServerController@getAddFile"},{"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.edit","action":"Pterodactyl\Http\Controllers\Server\ServerController@getDownloadFile"},{"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@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users\/new","name":"server.subusers.new","action":"Pterodactyl\Http\Controllers\Server\SubuserController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users\/view\/{id}","name":"server.subusers.view","action":"Pterodactyl\Http\Controllers\Server\SubuserController@view"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@store"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/users\/view\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@update"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/users\/delete\/{id}","name":"server.subusers.delete","action":"Pterodactyl\Http\Controllers\Server\SubuserController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/tasks","name":"server.tasks","action":"Pterodactyl\Http\Controllers\Server\TaskController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/tasks\/new","name":"server.tasks.new","action":"Pterodactyl\Http\Controllers\Server\TaskController@create"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/tasks\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\TaskController@store"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/tasks\/toggle\/{id}","name":"server.tasks.toggle","action":"Pterodactyl\Http\Controllers\Server\TaskController@toggle"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/tasks\/delete\/{id}","name":"server.tasks.delete","action":"Pterodactyl\Http\Controllers\Server\TaskController@delete"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/ajax\/set-primary","name":"server.ajax.set-primary","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":"daemon\/services","name":"daemon.services","action":"Pterodactyl\Http\Controllers\Daemon\ServiceController@listServices"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/services\/pull\/{service}\/{file}","name":"daemon.pull","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":"daemon\/details\/option\/{server}","name":"daemon.option.details","action":"Pterodactyl\Http\Controllers\Daemon\OptionController@details"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/configure\/{token}","name":"daemon.configuration","action":"Pterodactyl\Http\Controllers\Daemon\ActionController@configuration"},{"host":null,"methods":["POST"],"uri":"daemon\/download","name":"daemon.download","action":"Pterodactyl\Http\Controllers\Daemon\ActionController@authenticateDownload"},{"host":null,"methods":["POST"],"uri":"daemon\/install","name":"daemon.install","action":"Pterodactyl\Http\Controllers\Daemon\ActionController@markInstall"},{"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":"api\/user","name":"api.user","action":"Pterodactyl\Http\Controllers\API\User\CoreController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/user\/server\/{server}","name":"api.user.server","action":"Pterodactyl\Http\Controllers\API\User\ServerController@index"},{"host":null,"methods":["POST"],"uri":"api\/user\/server\/{server}\/power","name":"api.user.server.power","action":"Pterodactyl\Http\Controllers\API\User\ServerController@power"},{"host":null,"methods":["POST"],"uri":"api\/user\/server\/{server}\/command","name":"api.user.server.command","action":"Pterodactyl\Http\Controllers\API\User\ServerController@command"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\CoreController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/servers","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/servers\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@view"},{"host":null,"methods":["POST"],"uri":"api\/admin\/servers","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@store"},{"host":null,"methods":["PUT"],"uri":"api\/admin\/servers\/{id}\/details","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@details"},{"host":null,"methods":["PUT"],"uri":"api\/admin\/servers\/{id}\/container","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@container"},{"host":null,"methods":["PUT"],"uri":"api\/admin\/servers\/{id}\/build","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@build"},{"host":null,"methods":["PUT"],"uri":"api\/admin\/servers\/{id}\/startup","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@startup"},{"host":null,"methods":["PATCH"],"uri":"api\/admin\/servers\/{id}\/install","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@install"},{"host":null,"methods":["PATCH"],"uri":"api\/admin\/servers\/{id}\/rebuild","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@rebuild"},{"host":null,"methods":["PATCH"],"uri":"api\/admin\/servers\/{id}\/suspend","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@suspend"},{"host":null,"methods":["DELETE"],"uri":"api\/admin\/servers\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/locations","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\LocationController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/nodes","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\NodeController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/nodes\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\NodeController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/nodes\/{id}\/config","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\NodeController@viewConfig"},{"host":null,"methods":["POST"],"uri":"api\/admin\/nodes","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\NodeController@store"},{"host":null,"methods":["DELETE"],"uri":"api\/admin\/nodes\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\NodeController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/users","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\UserController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/users\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\UserController@view"},{"host":null,"methods":["POST"],"uri":"api\/admin\/users","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\UserController@store"},{"host":null,"methods":["PUT"],"uri":"api\/admin\/users\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\UserController@update"},{"host":null,"methods":["DELETE"],"uri":"api\/admin\/users\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\UserController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/services","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServiceController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/services\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServiceController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"\/","name":"index","action":"Pterodactyl\Http\Controllers\Base\IndexController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"status\/{server}","name":"index.status","action":"Pterodactyl\Http\Controllers\Base\IndexController@status"},{"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@store"},{"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":"admin","name":"admin.index","action":"Pterodactyl\Http\Controllers\Admin\BaseController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/locations","name":"admin.locations","action":"Pterodactyl\Http\Controllers\Admin\LocationController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/locations\/view\/{location}","name":"admin.locations.view","action":"Pterodactyl\Http\Controllers\Admin\LocationController@view"},{"host":null,"methods":["POST"],"uri":"admin\/locations","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationController@create"},{"host":null,"methods":["PATCH"],"uri":"admin\/locations\/view\/{location}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/databases","name":"admin.databases","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/databases\/view\/{host}","name":"admin.databases.view","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@view"},{"host":null,"methods":["POST"],"uri":"admin\/databases","name":null,"action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@create"},{"host":null,"methods":["PATCH"],"uri":"admin\/databases\/view\/{host}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@update"},{"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@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/accounts.json","name":"admin.users.json","action":"Pterodactyl\Http\Controllers\Admin\UserController@json"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/new","name":"admin.users.new","action":"Pterodactyl\Http\Controllers\Admin\UserController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/view\/{user}","name":"admin.users.view","action":"Pterodactyl\Http\Controllers\Admin\UserController@view"},{"host":null,"methods":["POST"],"uri":"admin\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@store"},{"host":null,"methods":["PATCH"],"uri":"admin\/users\/view\/{user}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/users\/view\/{user}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@delete"},{"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@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}","name":"admin.servers.view","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/details","name":"admin.servers.view.details","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDetails"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/build","name":"admin.servers.view.build","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewBuild"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/startup","name":"admin.servers.view.startup","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewStartup"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/database","name":"admin.servers.view.database","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDatabase"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/manage","name":"admin.servers.view.manage","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewManage"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/delete","name":"admin.servers.view.delete","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDelete"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@store"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/new\/nodes","name":"admin.servers.new.nodes","action":"Pterodactyl\Http\Controllers\Admin\ServersController@nodes"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/build","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@updateBuild"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/startup","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@saveStartup"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/database","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@newDatabase"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/manage\/toggle","name":"admin.servers.view.manage.toggle","action":"Pterodactyl\Http\Controllers\Admin\ServersController@toggleInstall"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/manage\/rebuild","name":"admin.servers.view.manage.rebuild","action":"Pterodactyl\Http\Controllers\Admin\ServersController@rebuildContainer"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/manage\/suspension","name":"admin.servers.view.manage.suspension","action":"Pterodactyl\Http\Controllers\Admin\ServersController@manageSuspension"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/manage\/reinstall","name":"admin.servers.view.manage.reinstall","action":"Pterodactyl\Http\Controllers\Admin\ServersController@reinstallServer"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/delete","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@delete"},{"host":null,"methods":["PATCH"],"uri":"admin\/servers\/view\/{server}\/details","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@setDetails"},{"host":null,"methods":["PATCH"],"uri":"admin\/servers\/view\/{server}\/details\/container","name":"admin.servers.view.details.container","action":"Pterodactyl\Http\Controllers\Admin\ServersController@setContainer"},{"host":null,"methods":["PATCH"],"uri":"admin\/servers\/view\/{server}\/database","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@resetDatabasePassword"},{"host":null,"methods":["DELETE"],"uri":"admin\/servers\/view\/{server}\/database\/{database}\/delete","name":"admin.servers.view.database.delete","action":"Pterodactyl\Http\Controllers\Admin\ServersController@deleteDatabase"},{"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@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}","name":"admin.nodes.view","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/settings","name":"admin.nodes.view.settings","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewSettings"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/configuration","name":"admin.nodes.view.configuration","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewConfiguration"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/allocation","name":"admin.nodes.view.allocation","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewAllocation"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/servers","name":"admin.nodes.view.servers","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewServers"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/settings\/token","name":"admin.nodes.view.configuration.token","action":"Pterodactyl\Http\Controllers\Admin\NodesController@setToken"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@store"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{node}\/allocation","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@createAllocation"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{node}\/allocation\/remove","name":"admin.nodes.view.allocation.removeBlock","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationRemoveBlock"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{node}\/allocation\/alias","name":"admin.nodes.view.allocation.setAlias","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationSetAlias"},{"host":null,"methods":["PATCH"],"uri":"admin\/nodes\/view\/{node}\/settings","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@updateSettings"},{"host":null,"methods":["DELETE"],"uri":"admin\/nodes\/view\/{node}\/delete","name":"admin.nodes.view.delete","action":"Pterodactyl\Http\Controllers\Admin\NodesController@delete"},{"host":null,"methods":["DELETE"],"uri":"admin\/nodes\/view\/{node}\/allocation\/remove\/{allocation}","name":"admin.nodes.view.allocation.removeSingle","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationRemoveSingle"},{"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@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/view\/{service}","name":"admin.services.view","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/view\/{service}\/functions","name":"admin.services.view.functions","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@viewFunctions"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/option\/new","name":"admin.services.option.new","action":"Pterodactyl\Http\Controllers\Admin\OptionController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/option\/{option}","name":"admin.services.option.view","action":"Pterodactyl\Http\Controllers\Admin\OptionController@viewConfiguration"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/option\/{option}\/variables","name":"admin.services.option.variables","action":"Pterodactyl\Http\Controllers\Admin\VariableController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/option\/{option}\/scripts","name":"admin.services.option.scripts","action":"Pterodactyl\Http\Controllers\Admin\OptionController@viewScripts"},{"host":null,"methods":["POST"],"uri":"admin\/services\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@store"},{"host":null,"methods":["POST"],"uri":"admin\/services\/option\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\OptionController@store"},{"host":null,"methods":["POST"],"uri":"admin\/services\/option\/{option}\/variables","name":null,"action":"Pterodactyl\Http\Controllers\Admin\VariableController@store"},{"host":null,"methods":["PATCH"],"uri":"admin\/services\/view\/{service}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/services\/view\/{service}\/functions","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@updateFunctions"},{"host":null,"methods":["PATCH"],"uri":"admin\/services\/option\/{option}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\OptionController@editConfiguration"},{"host":null,"methods":["PATCH"],"uri":"admin\/services\/option\/{option}\/scripts","name":null,"action":"Pterodactyl\Http\Controllers\Admin\OptionController@updateScripts"},{"host":null,"methods":["PATCH"],"uri":"admin\/services\/option\/{option}\/variables\/{variable}","name":"admin.services.option.variables.edit","action":"Pterodactyl\Http\Controllers\Admin\VariableController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/services\/view\/{service}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@destroy"},{"host":null,"methods":["DELETE"],"uri":"admin\/services\/option\/{option}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\OptionController@destroy"},{"host":null,"methods":["DELETE"],"uri":"admin\/services\/option\/{option}\/variables\/{variable}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\VariableController@delete"},{"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@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":["GET","HEAD"],"uri":"admin\/packs\/view\/{pack}","name":"admin.packs.view","action":"Pterodactyl\Http\Controllers\Admin\PackController@view"},{"host":null,"methods":["POST"],"uri":"admin\/packs\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@store"},{"host":null,"methods":["POST"],"uri":"admin\/packs\/view\/{pack}\/export\/{files?}","name":"admin.packs.view.export","action":"Pterodactyl\Http\Controllers\Admin\PackController@export"},{"host":null,"methods":["PATCH"],"uri":"admin\/packs\/view\/{pack}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/packs\/view\/{pack}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@destroy"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/logout","name":"auth.logout","action":"Pterodactyl\Http\Controllers\Auth\LoginController@logout"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/login","name":"auth.login","action":"Pterodactyl\Http\Controllers\Auth\LoginController@showLoginForm"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/login\/totp","name":"auth.totp","action":"Pterodactyl\Http\Controllers\Auth\LoginController@totp"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/password","name":"auth.password","action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm"},{"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\/login","name":null,"action":"Pterodactyl\Http\Controllers\Auth\LoginController@login"},{"host":null,"methods":["POST"],"uri":"auth\/login\/totp","name":null,"action":"Pterodactyl\Http\Controllers\Auth\LoginController@totpCheckpoint"},{"host":null,"methods":["POST"],"uri":"auth\/password","name":null,"action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail"},{"host":null,"methods":["POST"],"uri":"auth\/password\/reset","name":"auth.reset.post","action":"Pterodactyl\Http\Controllers\Auth\ResetPasswordController@reset"},{"host":null,"methods":["POST"],"uri":"auth\/password\/reset\/{token}","name":null,"action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}","name":"server.index","action":"Pterodactyl\Http\Controllers\Server\ConsoleController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/console","name":"server.console","action":"Pterodactyl\Http\Controllers\Server\ConsoleController@console"},{"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":["GET","HEAD"],"uri":"server\/{server}\/settings\/startup","name":"server.settings.startup","action":"Pterodactyl\Http\Controllers\Server\ServerController@getStartup"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/allocation","name":"server.settings.allocation","action":"Pterodactyl\Http\Controllers\Server\ServerController@getAllocation"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/settings\/sftp","name":null,"action":"Pterodactyl\Http\Controllers\Server\ServerController@postSettingsSFTP"},{"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}\/files","name":"server.files.index","action":"Pterodactyl\Http\Controllers\Server\Files\FileActionsController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/add","name":"server.files.add","action":"Pterodactyl\Http\Controllers\Server\Files\FileActionsController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/edit\/{file}","name":"server.files.edit","action":"Pterodactyl\Http\Controllers\Server\Files\FileActionsController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/download\/{file}","name":"server.files.edit","action":"Pterodactyl\Http\Controllers\Server\Files\DownloadController@index"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/files\/directory-list","name":"server.files.directory-list","action":"Pterodactyl\Http\Controllers\Server\Files\RemoteRequestController@directory"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/files\/save","name":"server.files.save","action":"Pterodactyl\Http\Controllers\Server\Files\RemoteRequestController@store"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users","name":"server.subusers","action":"Pterodactyl\Http\Controllers\Server\SubuserController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users\/new","name":"server.subusers.new","action":"Pterodactyl\Http\Controllers\Server\SubuserController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users\/view\/{subuser}","name":"server.subusers.view","action":"Pterodactyl\Http\Controllers\Server\SubuserController@view"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@store"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/users\/view\/{subuser}","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@update"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/users\/view\/{subuser}\/delete","name":"server.subusers.delete","action":"Pterodactyl\Http\Controllers\Server\SubuserController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/tasks","name":"server.tasks","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/tasks\/new","name":"server.tasks.new","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/tasks\/view\/{task}","name":"server.tasks.view","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@view"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/tasks\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@store"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/tasks\/view\/{task}","name":null,"action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@update"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/tasks\/view\/{task}\/toggle","name":"server.tasks.toggle","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskToggleController@index"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/tasks\/view\/{task}\/delete","name":"server.tasks.delete","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@delete"},{"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":"daemon\/services","name":"daemon.services","action":"Pterodactyl\Http\Controllers\Daemon\ServiceController@listServices"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/services\/pull\/{service}\/{file}","name":"daemon.pull","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":"daemon\/details\/option\/{server}","name":"daemon.option.details","action":"Pterodactyl\Http\Controllers\Daemon\OptionController@details"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/configure\/{token}","name":"daemon.configuration","action":"Pterodactyl\Http\Controllers\Daemon\ActionController@configuration"},{"host":null,"methods":["POST"],"uri":"daemon\/download","name":"daemon.download","action":"Pterodactyl\Http\Controllers\Daemon\ActionController@authenticateDownload"},{"host":null,"methods":["POST"],"uri":"daemon\/install","name":"daemon.install","action":"Pterodactyl\Http\Controllers\Daemon\ActionController@markInstall"},{"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/pterodactyl.css b/public/themes/pterodactyl/css/pterodactyl.css index 1c60da7d5..73178cbf6 100644 --- a/public/themes/pterodactyl/css/pterodactyl.css +++ b/public/themes/pterodactyl/css/pterodactyl.css @@ -343,3 +343,20 @@ input.form-autocomplete-stop[readonly] { bottom: 1px; margin-right: 5px !important; } + +label.control-label > span { + font-size: 80%; + font-weight: 400; + font-style: italic; + color: #dd4b39; +} + +label.control-label > span.field-required:before { + content: "required"; + color: #dd4b39; +} + +label.control-label > span.field-optional:before { + content: "optional"; + color: #bbbbbb; +} diff --git a/public/themes/pterodactyl/js/frontend/tasks.js b/public/themes/pterodactyl/js/frontend/tasks/management-actions.js similarity index 76% rename from public/themes/pterodactyl/js/frontend/tasks.js rename to public/themes/pterodactyl/js/frontend/tasks/management-actions.js index 8b600a268..7301719c6 100644 --- a/public/themes/pterodactyl/js/frontend/tasks.js +++ b/public/themes/pterodactyl/js/frontend/tasks/management-actions.js @@ -19,32 +19,6 @@ // SOFTWARE. $(document).ready(function () { - $('select[name="action"]').select2(); - $('[data-action="update-field"]').on('change', function (event) { - event.preventDefault(); - var updateField = $(this).data('field'); - var selected = $(this).map(function (i, opt) { - return $(opt).val(); - }).toArray(); - if (selected.length === $(this).find('option').length) { - $('input[name=' + updateField + ']').val('*'); - } else { - $('input[name=' + updateField + ']').val(selected.join(',')); - } - }); - - $('button[data-action="add-chain"]').on('click', function () { - var clone = $('div[data-target="chain-clone"]').clone(); - clone.insertBefore('#chainLastSegment').removeAttr('data-target').removeClass('hidden'); - clone.find('select[name="chain[time_value][]"]').select2(); - clone.find('select[name="chain[time_interval][]"]').select2(); - clone.find('select[name="chain[action][]"]').select2(); - clone.find('button[data-action="remove-chain-element"]').on('click', function () { - clone.remove(); - }); - $(this).data('element', clone); - }); - $('[data-action="delete-task"]').click(function () { var self = $(this); swal({ @@ -62,7 +36,7 @@ $(document).ready(function () { method: 'DELETE', url: Router.route('server.tasks.delete', { server: Pterodactyl.server.uuidShort, - id: self.data('id'), + task: self.data('taskid'), }), headers: { 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content'), @@ -101,7 +75,7 @@ $(document).ready(function () { method: 'POST', url: Router.route('server.tasks.toggle', { server: Pterodactyl.server.uuidShort, - id: self.data('id'), + task: self.data('taskid'), }), headers: { 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content'), diff --git a/public/themes/pterodactyl/js/frontend/tasks/view-actions.js b/public/themes/pterodactyl/js/frontend/tasks/view-actions.js new file mode 100644 index 000000000..77a30eef6 --- /dev/null +++ b/public/themes/pterodactyl/js/frontend/tasks/view-actions.js @@ -0,0 +1,61 @@ +// 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. + +$(document).ready(function () { + function setupSelect2() { + $('select[name="chain[time_value][]"]').select2(); + $('select[name="chain[time_interval][]"]').select2(); + $('select[name="chain[action][]"]').select2(); + } + + setupSelect2(); + + $('[data-action="update-field"]').on('change', function (event) { + event.preventDefault(); + var updateField = $(this).data('field'); + var selected = $(this).map(function (i, opt) { + return $(opt).val(); + }).toArray(); + if (selected.length === $(this).find('option').length) { + $('input[name=' + updateField + ']').val('*'); + } else { + $('input[name=' + updateField + ']').val(selected.join(',')); + } + }); + + $('button[data-action="add-new-task"]').on('click', function () { + if ($('#containsTaskList').find('.task-list-item').length >= 5) { + swal('Task Limit Reached', 'You may only assign a maximum of 5 tasks to one schedule.'); + return; + } + + var clone = $('div[data-target="task-clone"]').clone(); + clone.insertBefore('#taskAppendBefore').removeAttr('data-target'); + clone.find('select:first').attr('selected'); + clone.find('input').val(''); + clone.find('span.select2-container').remove(); + clone.find('div[data-attribute="remove-task-element"]').addClass('input-group').find('div.input-group-btn').removeClass('hidden'); + clone.find('button[data-action="remove-task"]').on('click', function () { + clone.remove(); + }); + setupSelect2(); + $(this).data('element', clone); + }); +}); diff --git a/resources/lang/en/exceptions.php b/resources/lang/en/exceptions.php index 59950e2c2..bf876019a 100644 --- a/resources/lang/en/exceptions.php +++ b/resources/lang/en/exceptions.php @@ -62,4 +62,7 @@ return [ 'databases' => [ 'delete_has_databases' => 'Cannot delete a database host server that has active databases linked to it.', ], + 'tasks' => [ + 'chain_interval_too_long' => 'The maximum interval time for a chained task is 15 minutes.', + ], ]; diff --git a/resources/lang/en/server.php b/resources/lang/en/server.php index 8e7503243..f433aad10 100644 --- a/resources/lang/en/server.php +++ b/resources/lang/en/server.php @@ -6,7 +6,29 @@ return [ 'header' => 'Server Console', 'header_sub' => 'Control your server in real time.', ], + 'schedule' => [ + 'new' => [ + 'header' => 'Create New Schedule', + 'header_sub' => 'Create a new set of scheduled tasks for this server.', + 'submit' => 'Create Schedule', + ], + 'task' => [ + 'time' => 'After', + 'action' => 'Perform Action', + 'payload' => 'With Payload', + 'add_more' => 'Add Another Task', + ], + 'setup' => 'Schedule Setup', + 'day_of_week' => 'Day of Week', + 'day_of_month' => 'Day of Month', + 'hour' => 'Hour of Day', + 'minute' => 'Minute of Hour', + 'time_help' => 'The schedule system supports the use of Cronjob syntax when defining when tasks should begin running. Use the fields above to specify when these tasks should begin running or select options from the multiple select menus.', + 'task_help' => 'Times for tasks are relative to the previously defined task. Each schedule may have no more than 5 tasks assigned to it and tasks may not be scheduled more than 15 minutes apart.', + ], 'tasks' => [ + 'task_created' => 'Successfully created a new task on the Panel.', + 'task_updated' => 'Task has successfully been updated. Any currently queued task actions will be cancelled and run again at the next defined time.', 'header' => 'Scheduled Tasks', 'header_sub' => 'Automate your server.', 'current' => 'Current Scheduled Tasks', diff --git a/resources/lang/en/strings.php b/resources/lang/en/strings.php index 8cdb400c1..15608b83f 100644 --- a/resources/lang/en/strings.php +++ b/resources/lang/en/strings.php @@ -71,7 +71,7 @@ return [ 'admin' => 'Admin', 'subuser' => 'Subuser', 'captcha_invalid' => 'The provided captcha is invalid.', - 'child_tasks' => 'Child Tasks', + 'tasks' => 'Tasks', 'seconds' => 'Seconds', 'minutes' => 'Minutes', ]; diff --git a/resources/themes/pterodactyl/layouts/master.blade.php b/resources/themes/pterodactyl/layouts/master.blade.php index d817db823..a3f18a46e 100644 --- a/resources/themes/pterodactyl/layouts/master.blade.php +++ b/resources/themes/pterodactyl/layouts/master.blade.php @@ -164,7 +164,7 @@ @lang('navigation.server.task_management') - {{ \Pterodactyl\Models\Task::select('id')->where('server_id', $server->id)->where('active', 1)->count() }} + {{ \Pterodactyl\Models\Schedule::select('id')->where('server_id', $server->id)->where('is_active', 1)->count() }} diff --git a/resources/themes/pterodactyl/partials/tasks/chain-template.blade.php b/resources/themes/pterodactyl/partials/schedules/task-template.blade.php similarity index 71% rename from resources/themes/pterodactyl/partials/tasks/chain-template.blade.php rename to resources/themes/pterodactyl/partials/schedules/task-template.blade.php index 0ba47e2c9..b8cc0d7dc 100644 --- a/resources/themes/pterodactyl/partials/tasks/chain-template.blade.php +++ b/resources/themes/pterodactyl/partials/schedules/task-template.blade.php @@ -1,12 +1,12 @@ @section('tasks::chain-template') -