Skip to content

Commit 036bea2

Browse files
committed
Update schedule process to allow toggling/triggering via UI
1 parent 02fe498 commit 036bea2

File tree

18 files changed

+280
-221
lines changed

18 files changed

+280
-221
lines changed

app/Contracts/Repository/ScheduleRepositoryInterface.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@ interface ScheduleRepositoryInterface extends RepositoryInterface
1515
*/
1616
public function findServerSchedules(int $server): Collection;
1717

18+
/**
19+
* Load the tasks relationship onto the Schedule module if they are not
20+
* already present.
21+
*
22+
* @param \Pterodactyl\Models\Schedule $schedule
23+
* @param bool $refresh
24+
* @return \Pterodactyl\Models\Schedule
25+
*/
26+
public function loadTasks(Schedule $schedule, bool $refresh = false): Schedule;
27+
1828
/**
1929
* Return a schedule model with all of the associated tasks as a relationship.
2030
*

app/Contracts/Repository/TaskRepositoryInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ interface TaskRepositoryInterface extends RepositoryInterface
1414
*
1515
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
1616
*/
17-
public function getTaskWithServer(int $id): Task;
17+
public function getTaskForJobProcess(int $id): Task;
1818

1919
/**
2020
* Returns the next task in a schedule.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
namespace Pterodactyl\Http\Controllers\Server\Tasks;
4+
5+
use Carbon\Carbon;
6+
use Illuminate\Http\Request;
7+
use Illuminate\Http\Response;
8+
use Pterodactyl\Http\Controllers\Controller;
9+
use Pterodactyl\Services\Schedules\ProcessScheduleService;
10+
use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface;
11+
12+
class ActionController extends Controller
13+
{
14+
private $processScheduleService;
15+
/**
16+
* @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface
17+
*/
18+
private $repository;
19+
20+
public function __construct(ProcessScheduleService $processScheduleService, ScheduleRepositoryInterface $repository)
21+
{
22+
$this->processScheduleService = $processScheduleService;
23+
$this->repository = $repository;
24+
}
25+
26+
/**
27+
* Toggle a task to be active or inactive for a given server.
28+
*
29+
* @param \Illuminate\Http\Request $request
30+
* @return \Illuminate\Http\Response
31+
*
32+
* @throws \Illuminate\Auth\Access\AuthorizationException
33+
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
34+
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
35+
*/
36+
public function toggle(Request $request): Response
37+
{
38+
$server = $request->attributes->get('server');
39+
$schedule = $request->attributes->get('schedule');
40+
$this->authorize('toggle-schedule', $server);
41+
42+
$this->repository->update($schedule->id, [
43+
'is_active' => ! $schedule->is_active,
44+
]);
45+
46+
return response('', 204);
47+
}
48+
49+
/**
50+
* Trigger a schedule to run now.
51+
*
52+
* @param \Illuminate\Http\Request $request
53+
* @return \Illuminate\Http\Response
54+
*
55+
* @throws \Illuminate\Auth\Access\AuthorizationException
56+
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
57+
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
58+
*/
59+
public function trigger(Request $request): Response
60+
{
61+
$server = $request->attributes->get('server');
62+
$this->authorize('toggle-schedule', $server);
63+
64+
$this->processScheduleService->setRunTimeOverride(Carbon::now())->handle(
65+
$request->attributes->get('schedule')
66+
);
67+
68+
return response('', 204);
69+
}
70+
}

app/Http/Controllers/Server/Tasks/TaskManagementController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ public function store(ScheduleCreationFormRequest $request): RedirectResponse
112112
$server = $request->attributes->get('server');
113113

114114
$schedule = $this->creationService->handle($server, $request->normalize(), $request->getTasks());
115-
$this->alert->success(trans('server.schedules.task_created'))->flash();
115+
$this->alert->success(trans('server.schedule.task_created'))->flash();
116116

117117
return redirect()->route('server.schedules.view', [
118118
'server' => $server->uuidShort,

app/Jobs/Schedule/RunTaskJob.php

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
11
<?php
2-
/**
3-
* Pterodactyl - Panel
4-
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
5-
*
6-
* This software is licensed under the terms of the MIT license.
7-
* https://opensource.org/licenses/MIT
8-
*/
92

103
namespace Pterodactyl\Jobs\Schedule;
114

125
use Exception;
136
use Carbon\Carbon;
147
use Pterodactyl\Jobs\Job;
15-
use Webmozart\Assert\Assert;
168
use InvalidArgumentException;
179
use Illuminate\Queue\SerializesModels;
1810
use Illuminate\Queue\InteractsWithQueue;
@@ -59,12 +51,9 @@ class RunTaskJob extends Job implements ShouldQueue
5951
* @param int $task
6052
* @param int $schedule
6153
*/
62-
public function __construct($task, $schedule)
54+
public function __construct(int $task, int $schedule)
6355
{
64-
Assert::integerish($task, 'First argument passed to constructor must be integer, received %s.');
65-
Assert::integerish($schedule, 'Second argument passed to constructor must be integer, received %s.');
66-
67-
$this->queue = app()->make('config')->get('pterodactyl.queues.standard');
56+
$this->queue = config('pterodactyl.queues.standard');
6857
$this->task = $task;
6958
$this->schedule = $schedule;
7059
}
@@ -91,10 +80,18 @@ public function handle(
9180
$this->powerRepository = $powerRepository;
9281
$this->taskRepository = $taskRepository;
9382

94-
$task = $this->taskRepository->getTaskWithServer($this->task);
83+
$task = $this->taskRepository->getTaskForJobProcess($this->task);
9584
$server = $task->getRelation('server');
9685
$user = $server->getRelation('user');
9786

87+
// Do not process a task that is not set to active.
88+
if (! $task->getRelation('schedule')->is_active) {
89+
$this->markTaskNotQueued();
90+
$this->markScheduleComplete();
91+
92+
return;
93+
}
94+
9895
// Perform the provided task aganist the daemon.
9996
switch ($task->action) {
10097
case 'power':
@@ -108,7 +105,7 @@ public function handle(
108105
->send($task->payload);
109106
break;
110107
default:
111-
throw new InvalidArgumentException('Cannot run a task that points to a non-existant action.');
108+
throw new InvalidArgumentException('Cannot run a task that points to a non-existent action.');
112109
}
113110

114111
$this->markTaskNotQueued();

app/Repositories/Eloquent/ScheduleRepository.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,23 @@ public function findServerSchedules(int $server): Collection
3131
return $this->getBuilder()->withCount('tasks')->where('server_id', '=', $server)->get($this->getColumns());
3232
}
3333

34+
/**
35+
* Load the tasks relationship onto the Schedule module if they are not
36+
* already present.
37+
*
38+
* @param \Pterodactyl\Models\Schedule $schedule
39+
* @param bool $refresh
40+
* @return \Pterodactyl\Models\Schedule
41+
*/
42+
public function loadTasks(Schedule $schedule, bool $refresh = false): Schedule
43+
{
44+
if (! $schedule->relationLoaded('tasks') || $refresh) {
45+
$schedule->load('tasks');
46+
}
47+
48+
return $schedule;
49+
}
50+
3451
/**
3552
* Return a schedule model with all of the associated tasks as a relationship.
3653
*

app/Repositories/Eloquent/TaskRepository.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ public function model()
2727
*
2828
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
2929
*/
30-
public function getTaskWithServer(int $id): Task
30+
public function getTaskForJobProcess(int $id): Task
3131
{
3232
try {
33-
return $this->getBuilder()->with('server.user')->findOrFail($id, $this->getColumns());
33+
return $this->getBuilder()->with('server.user', 'schedule')->findOrFail($id, $this->getColumns());
3434
} catch (ModelNotFoundException $exception) {
3535
throw new RecordNotFoundException;
3636
}
Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
11
<?php
2-
/**
3-
* Pterodactyl - Panel
4-
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
5-
*
6-
* This software is licensed under the terms of the MIT license.
7-
* https://opensource.org/licenses/MIT
8-
*/
92

103
namespace Pterodactyl\Services\Schedules;
114

5+
use Carbon\Carbon;
126
use Cron\CronExpression;
13-
use Webmozart\Assert\Assert;
147
use Pterodactyl\Models\Schedule;
158
use Pterodactyl\Services\Schedules\Tasks\RunTaskService;
169
use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface;
@@ -20,12 +13,17 @@ class ProcessScheduleService
2013
/**
2114
* @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface
2215
*/
23-
protected $repository;
16+
private $repository;
2417

2518
/**
2619
* @var \Pterodactyl\Services\Schedules\Tasks\RunTaskService
2720
*/
28-
protected $runnerService;
21+
private $runnerService;
22+
23+
/**
24+
* @var \Carbon\Carbon|null
25+
*/
26+
private $runTimeOverride;
2927

3028
/**
3129
* ProcessScheduleService constructor.
@@ -39,23 +37,31 @@ public function __construct(RunTaskService $runnerService, ScheduleRepositoryInt
3937
$this->runnerService = $runnerService;
4038
}
4139

40+
/**
41+
* Set the time that this schedule should be run at. This will override the time
42+
* defined on the schedule itself. Useful for triggering one-off task runs.
43+
*
44+
* @param \Carbon\Carbon $time
45+
* @return $this
46+
*/
47+
public function setRunTimeOverride(Carbon $time)
48+
{
49+
$this->runTimeOverride = $time;
50+
51+
return $this;
52+
}
53+
4254
/**
4355
* Process a schedule and push the first task onto the queue worker.
4456
*
45-
* @param int|\Pterodactyl\Models\Schedule $schedule
57+
* @param \Pterodactyl\Models\Schedule $schedule
4658
*
4759
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
4860
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
4961
*/
50-
public function handle($schedule)
62+
public function handle(Schedule $schedule)
5163
{
52-
Assert::true(($schedule instanceof Schedule || is_digit($schedule)),
53-
'First argument passed to handle must be instance of \Pterodactyl\Models\Schedule or an integer, received %s.'
54-
);
55-
56-
if (($schedule instanceof Schedule && ! $schedule->relationLoaded('tasks')) || ! $schedule instanceof Schedule) {
57-
$schedule = $this->repository->getScheduleWithTasks(is_digit($schedule) ? $schedule : $schedule->id);
58-
}
64+
$this->repository->loadTasks($schedule);
5965

6066
$formattedCron = sprintf('%s %s %s * %s *',
6167
$schedule->cron_minute,
@@ -66,10 +72,25 @@ public function handle($schedule)
6672

6773
$this->repository->update($schedule->id, [
6874
'is_processing' => true,
69-
'next_run_at' => CronExpression::factory($formattedCron)->getNextRunDate(),
75+
'next_run_at' => $this->getRunAtTime($formattedCron),
7076
]);
7177

72-
$task = $schedule->tasks->where('sequence_id', 1)->first();
78+
$task = $schedule->getRelation('tasks')->where('sequence_id', 1)->first();
7379
$this->runnerService->handle($task);
7480
}
81+
82+
/**
83+
* Get the timestamp to store in the database as the next_run time for a schedule.
84+
*
85+
* @param string $formatted
86+
* @return \DateTime|string
87+
*/
88+
private function getRunAtTime(string $formatted)
89+
{
90+
if ($this->runTimeOverride instanceof Carbon) {
91+
return $this->runTimeOverride->toDateTimeString();
92+
}
93+
94+
return CronExpression::factory($formatted)->getNextRunDate();
95+
}
7596
}

app/Services/Schedules/ScheduleCreationService.php

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,8 @@
11
<?php
2-
/**
3-
* Pterodactyl - Panel
4-
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
5-
*
6-
* This software is licensed under the terms of the MIT license.
7-
* https://opensource.org/licenses/MIT
8-
*/
92

103
namespace Pterodactyl\Services\Schedules;
114

125
use Cron\CronExpression;
13-
use Webmozart\Assert\Assert;
146
use Pterodactyl\Models\Server;
157
use Illuminate\Database\ConnectionInterface;
168
use Pterodactyl\Services\Schedules\Tasks\TaskCreationService;
@@ -53,37 +45,32 @@ public function __construct(
5345
/**
5446
* Create a new schedule for a specific server.
5547
*
56-
* @param int|\Pterodactyl\Models\Server $server
57-
* @param array $data
58-
* @param array $tasks
48+
* @param \Pterodactyl\Models\Server $server
49+
* @param array $data
50+
* @param array $tasks
5951
* @return \Pterodactyl\Models\Schedule
6052
*
6153
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
6254
* @throws \Pterodactyl\Exceptions\Service\Schedule\Task\TaskIntervalTooLongException
6355
*/
64-
public function handle($server, array $data, array $tasks = [])
56+
public function handle(Server $server, array $data, array $tasks = [])
6557
{
66-
Assert::true(($server instanceof Server || is_digit($server)),
67-
'First argument passed to handle must be numeric or instance of \Pterodactyl\Models\Server, received %s.'
68-
);
69-
70-
$server = ($server instanceof Server) ? $server->id : $server;
71-
$data['server_id'] = $server;
72-
$data['next_run_at'] = $this->getCronTimestamp($data);
58+
$data = array_merge($data, [
59+
'server_id' => $server->id,
60+
'next_run_at' => $this->getCronTimestamp($data),
61+
]);
7362

7463
$this->connection->beginTransaction();
7564
$schedule = $this->repository->create($data);
7665

77-
if (! empty($tasks)) {
78-
foreach ($tasks as $index => $task) {
79-
$this->taskCreationService->handle($schedule, [
80-
'time_interval' => array_get($task, 'time_interval'),
81-
'time_value' => array_get($task, 'time_value'),
82-
'sequence_id' => $index + 1,
83-
'action' => array_get($task, 'action'),
84-
'payload' => array_get($task, 'payload'),
85-
], false);
86-
}
66+
foreach ($tasks as $index => $task) {
67+
$this->taskCreationService->handle($schedule, [
68+
'time_interval' => array_get($task, 'time_interval'),
69+
'time_value' => array_get($task, 'time_value'),
70+
'sequence_id' => $index + 1,
71+
'action' => array_get($task, 'action'),
72+
'payload' => array_get($task, 'payload'),
73+
], false);
8774
}
8875

8976
$this->connection->commit();

public/js/laroute.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)