From a27ea3d1bc8694231f96c4e68e9d021574898d09 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Tue, 24 Jan 2023 13:48:34 -0700 Subject: [PATCH 1/4] config(queue): default to `redis` driver Updates the default `QUEUE_CONNECTION` value to be `redis` instead of `sync`. This can cause problems if users skip the initial setup or select the prefilled options rather than the recommended ones. Closes #4660 --- .env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 533799999..fc97db959 100644 --- a/.env.example +++ b/.env.example @@ -23,7 +23,7 @@ REDIS_PASSWORD=null REDIS_PORT=6379 CACHE_DRIVER=file -QUEUE_CONNECTION=sync +QUEUE_CONNECTION=redis SESSION_DRIVER=file HASHIDS_SALT= From 20f23a0b272db785a8e514ad919e82b9382948a8 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Tue, 24 Jan 2023 14:02:41 -0700 Subject: [PATCH 2/4] db: add `uuid` column to `failed_jobs` table Refer to for more information regarding this change. Closes #4652 --- ...1_add_uuid_column_to_failed_jobs_table.php | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 database/migrations/2023_01_24_210051_add_uuid_column_to_failed_jobs_table.php diff --git a/database/migrations/2023_01_24_210051_add_uuid_column_to_failed_jobs_table.php b/database/migrations/2023_01_24_210051_add_uuid_column_to_failed_jobs_table.php new file mode 100644 index 000000000..e3482e278 --- /dev/null +++ b/database/migrations/2023_01_24_210051_add_uuid_column_to_failed_jobs_table.php @@ -0,0 +1,34 @@ +string('uuid')->after('id')->nullable()->unique(); + }); + + DB::table('failed_jobs')->whereNull('uuid')->cursor()->each(function ($job) { + DB::table('failed_jobs') + ->where('id', $job->id) + ->update(['uuid' => (string) Illuminate\Support\Str::uuid()]); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('failed_jobs', function (Blueprint $table) { + $table->dropColumn('uuid'); + }); + } +}; From 2b14e46eec4a71d3c67380f15587f2e5dc8f2f41 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Tue, 24 Jan 2023 15:57:24 -0700 Subject: [PATCH 3/4] api: fix `sequence_id` being ignored in server task API Closes #4434 --- .../Client/Servers/ScheduleTaskController.php | 76 ++++++++++++++----- .../Servers/Schedules/StoreTaskRequest.php | 1 + 2 files changed, 57 insertions(+), 20 deletions(-) diff --git a/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php b/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php index addca2876..53f5f7dc8 100644 --- a/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php +++ b/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php @@ -9,6 +9,7 @@ use Pterodactyl\Models\Schedule; use Illuminate\Http\JsonResponse; use Pterodactyl\Facades\Activity; use Pterodactyl\Models\Permission; +use Illuminate\Database\ConnectionInterface; use Pterodactyl\Repositories\Eloquent\TaskRepository; use Pterodactyl\Exceptions\Http\HttpForbiddenException; use Pterodactyl\Transformers\Api\Client\TaskTransformer; @@ -23,8 +24,10 @@ class ScheduleTaskController extends ClientApiController /** * ScheduleTaskController constructor. */ - public function __construct(private TaskRepository $repository) - { + public function __construct( + private ConnectionInterface $connection, + private TaskRepository $repository + ) { parent::__construct(); } @@ -49,14 +52,30 @@ class ScheduleTaskController extends ClientApiController $lastTask = $schedule->tasks()->orderByDesc('sequence_id')->first(); /** @var \Pterodactyl\Models\Task $task */ - $task = $this->repository->create([ - 'schedule_id' => $schedule->id, - 'sequence_id' => ($lastTask->sequence_id ?? 0) + 1, - 'action' => $request->input('action'), - 'payload' => $request->input('payload') ?? '', - 'time_offset' => $request->input('time_offset'), - 'continue_on_failure' => (bool) $request->input('continue_on_failure'), - ]); + $task = $this->connection->transaction(function () use ($request, $schedule, $lastTask) { + $sequenceId = ($lastTask->sequence_id ?? 0) + 1; + $requestSequenceId = $request->integer('sequence_id', $sequenceId); + + // If the sequence id from the request is greater than or equal to the next available + // sequence id, we don't need to do anything special. Otherwise, we need to update + // the sequence id of all tasks that are greater than or equal to the request sequence + // id to be one greater than the current value. + if ($requestSequenceId < $sequenceId) { + $schedule->tasks() + ->where('sequence_id', '>=', $requestSequenceId) + ->increment('sequence_id'); + $sequenceId = $requestSequenceId; + } + + return $this->repository->create([ + 'schedule_id' => $schedule->id, + 'sequence_id' => $sequenceId, + 'action' => $request->input('action'), + 'payload' => $request->input('payload') ?? '', + 'time_offset' => $request->input('time_offset'), + 'continue_on_failure' => $request->boolean('continue_on_failure'), + ]); + }); Activity::event('server:task.create') ->subject($schedule, $task) @@ -84,12 +103,30 @@ class ScheduleTaskController extends ClientApiController throw new HttpForbiddenException("A backup task cannot be created when the server's backup limit is set to 0."); } - $this->repository->update($task->id, [ - 'action' => $request->input('action'), - 'payload' => $request->input('payload') ?? '', - 'time_offset' => $request->input('time_offset'), - 'continue_on_failure' => (bool) $request->input('continue_on_failure'), - ]); + $this->connection->transaction(function () use ($request, $schedule, $task) { + $sequenceId = $request->integer('sequence_id', $task->sequence_id); + + // Shift all other tasks in the schedule up or down to make room for the new task. + if ($sequenceId < $task->sequence_id) { + $schedule->tasks() + ->where('sequence_id', '>=', $sequenceId) + ->where('sequence_id', '<', $task->sequence_id) + ->increment('sequence_id'); + } elseif ($sequenceId > $task->sequence_id) { + $schedule->tasks() + ->where('sequence_id', '>', $task->sequence_id) + ->where('sequence_id', '<=', $sequenceId) + ->decrement('sequence_id'); + } + + $this->repository->update($task->id, [ + 'sequence_id' => $sequenceId, + 'action' => $request->input('action'), + 'payload' => $request->input('payload') ?? '', + 'time_offset' => $request->input('time_offset'), + 'continue_on_failure' => $request->boolean('continue_on_failure'), + ]); + }); Activity::event('server:task.update') ->subject($schedule, $task) @@ -117,10 +154,9 @@ class ScheduleTaskController extends ClientApiController throw new HttpForbiddenException('You do not have permission to perform this action.'); } - $schedule->tasks()->where('sequence_id', '>', $task->sequence_id)->update([ - 'sequence_id' => $schedule->tasks()->getConnection()->raw('(sequence_id - 1)'), - ]); - + $schedule->tasks() + ->where('sequence_id', '>', $task->sequence_id) + ->decrement('sequence_id'); $task->delete(); Activity::event('server:task.delete')->subject($schedule, $task)->property('name', $schedule->name)->log(); diff --git a/app/Http/Requests/Api/Client/Servers/Schedules/StoreTaskRequest.php b/app/Http/Requests/Api/Client/Servers/Schedules/StoreTaskRequest.php index 811b87dfb..5ceb7c8e0 100644 --- a/app/Http/Requests/Api/Client/Servers/Schedules/StoreTaskRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Schedules/StoreTaskRequest.php @@ -23,6 +23,7 @@ class StoreTaskRequest extends ViewScheduleRequest 'payload' => 'required_unless:action,backup|string|nullable', 'time_offset' => 'required|numeric|min:0|max:900', 'sequence_id' => 'sometimes|required|numeric|min:1', + 'continue_on_failure' => 'sometimes|required|boolean', ]; } } From 866b6df4b09daa00e08b06427bf9e6285a982a44 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Tue, 24 Jan 2023 16:18:38 -0700 Subject: [PATCH 4/4] api(task): ensure `sequence_id` always starts at `1` --- .../Api/Client/Servers/ScheduleTaskController.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php b/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php index 53f5f7dc8..e2e4389a1 100644 --- a/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php +++ b/app/Http/Controllers/Api/Client/Servers/ScheduleTaskController.php @@ -56,6 +56,11 @@ class ScheduleTaskController extends ClientApiController $sequenceId = ($lastTask->sequence_id ?? 0) + 1; $requestSequenceId = $request->integer('sequence_id', $sequenceId); + // Ensure that the sequence id is at least 1. + if ($requestSequenceId < 1) { + $requestSequenceId = 1; + } + // If the sequence id from the request is greater than or equal to the next available // sequence id, we don't need to do anything special. Otherwise, we need to update // the sequence id of all tasks that are greater than or equal to the request sequence @@ -105,6 +110,10 @@ class ScheduleTaskController extends ClientApiController $this->connection->transaction(function () use ($request, $schedule, $task) { $sequenceId = $request->integer('sequence_id', $task->sequence_id); + // Ensure that the sequence id is at least 1. + if ($sequenceId < 1) { + $sequenceId = 1; + } // Shift all other tasks in the schedule up or down to make room for the new task. if ($sequenceId < $task->sequence_id) {