refactor scheduled task job (and related stuffs)
This commit is contained in:
34
app/Events/ScheduledTaskDone.php
Normal file
34
app/Events/ScheduledTaskDone.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class ScheduledTaskDone implements ShouldBroadcast
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
|
public $teamId;
|
||||||
|
|
||||||
|
public function __construct($teamId = null)
|
||||||
|
{
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
$teamId = auth()->user()->currentTeam()->id ?? null;
|
||||||
|
}
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
throw new \Exception('Team id is null');
|
||||||
|
}
|
||||||
|
$this->teamId = $teamId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function broadcastOn(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new PrivateChannel("team.{$this->teamId}"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Events\ScheduledTaskDone;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ScheduledTask;
|
use App\Models\ScheduledTask;
|
||||||
use App\Models\ScheduledTaskExecution;
|
use App\Models\ScheduledTaskExecution;
|
||||||
@@ -19,7 +20,7 @@ class ScheduledTaskJob implements ShouldQueue
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public ?Team $team = null;
|
public Team $team;
|
||||||
|
|
||||||
public Server $server;
|
public Server $server;
|
||||||
|
|
||||||
@@ -47,7 +48,7 @@ class ScheduledTaskJob implements ShouldQueue
|
|||||||
} else {
|
} else {
|
||||||
throw new \RuntimeException('ScheduledTaskJob failed: No resource found.');
|
throw new \RuntimeException('ScheduledTaskJob failed: No resource found.');
|
||||||
}
|
}
|
||||||
$this->team = Team::find($task->team_id);
|
$this->team = Team::findOrFail($task->team_id);
|
||||||
$this->server_timezone = $this->getServerTimezone();
|
$this->server_timezone = $this->getServerTimezone();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +126,7 @@ class ScheduledTaskJob implements ShouldQueue
|
|||||||
// send_internal_notification('ScheduledTaskJob failed with: ' . $e->getMessage());
|
// send_internal_notification('ScheduledTaskJob failed with: ' . $e->getMessage());
|
||||||
throw $e;
|
throw $e;
|
||||||
} finally {
|
} finally {
|
||||||
|
ScheduledTaskDone::dispatch($this->team->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,23 +2,60 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Project\Shared\ScheduledTask;
|
namespace App\Livewire\Project\Shared\ScheduledTask;
|
||||||
|
|
||||||
|
use App\Models\ScheduledTask;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Livewire\Attributes\Locked;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Executions extends Component
|
class Executions extends Component
|
||||||
{
|
{
|
||||||
public $executions = [];
|
public ScheduledTask $task;
|
||||||
|
|
||||||
public $selectedKey;
|
#[Locked]
|
||||||
|
public int $taskId;
|
||||||
|
|
||||||
public $task;
|
#[Locked]
|
||||||
|
public Collection $executions;
|
||||||
|
|
||||||
|
#[Locked]
|
||||||
|
public ?int $selectedKey = null;
|
||||||
|
|
||||||
|
#[Locked]
|
||||||
|
public ?string $serverTimezone = null;
|
||||||
|
|
||||||
public function getListeners()
|
public function getListeners()
|
||||||
{
|
{
|
||||||
|
$teamId = Auth::user()->currentTeam()->id;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'selectTask',
|
"echo-private:team.{$teamId},ScheduledTaskDone" => 'refreshExecutions',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function mount($taskId)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->taskId = $taskId;
|
||||||
|
$this->task = ScheduledTask::findOrFail($taskId);
|
||||||
|
$this->executions = $this->task->executions()->take(20)->get();
|
||||||
|
$this->serverTimezone = data_get($this->task, 'application.destination.server.settings.server_timezone');
|
||||||
|
if (! $this->serverTimezone) {
|
||||||
|
$this->serverTimezone = data_get($this->task, 'service.destination.server.settings.server_timezone');
|
||||||
|
}
|
||||||
|
if (! $this->serverTimezone) {
|
||||||
|
$this->serverTimezone = 'UTC';
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function refreshExecutions(): void
|
||||||
|
{
|
||||||
|
$this->executions = $this->task->executions()->take(20)->get();
|
||||||
|
}
|
||||||
|
|
||||||
public function selectTask($key): void
|
public function selectTask($key): void
|
||||||
{
|
{
|
||||||
if ($key == $this->selectedKey) {
|
if ($key == $this->selectedKey) {
|
||||||
@@ -29,38 +66,9 @@ class Executions extends Component
|
|||||||
$this->selectedKey = $key;
|
$this->selectedKey = $key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function server()
|
|
||||||
{
|
|
||||||
if (! $this->task) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->task->application) {
|
|
||||||
if ($this->task->application->destination && $this->task->application->destination->server) {
|
|
||||||
return $this->task->application->destination->server;
|
|
||||||
}
|
|
||||||
} elseif ($this->task->service) {
|
|
||||||
if ($this->task->service->destination && $this->task->service->destination->server) {
|
|
||||||
return $this->task->service->destination->server;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getServerTimezone()
|
|
||||||
{
|
|
||||||
$server = $this->server();
|
|
||||||
if (! $server) {
|
|
||||||
return 'UTC';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $server->settings->server_timezone;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function formatDateInServerTimezone($date)
|
public function formatDateInServerTimezone($date)
|
||||||
{
|
{
|
||||||
$serverTimezone = $this->getServerTimezone();
|
$serverTimezone = $this->serverTimezone;
|
||||||
$dateObj = new \DateTime($date);
|
$dateObj = new \DateTime($date);
|
||||||
try {
|
try {
|
||||||
$dateObj->setTimezone(new \DateTimeZone($serverTimezone));
|
$dateObj->setTimezone(new \DateTimeZone($serverTimezone));
|
||||||
|
@@ -2,74 +2,124 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Project\Shared\ScheduledTask;
|
namespace App\Livewire\Project\Shared\ScheduledTask;
|
||||||
|
|
||||||
|
use App\Jobs\ScheduledTaskJob;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ScheduledTask as ModelsScheduledTask;
|
use App\Models\ScheduledTask;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
|
use Livewire\Attributes\Locked;
|
||||||
|
use Livewire\Attributes\Validate;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
|
||||||
|
|
||||||
class Show extends Component
|
class Show extends Component
|
||||||
{
|
{
|
||||||
public $parameters;
|
|
||||||
|
|
||||||
public Application|Service $resource;
|
public Application|Service $resource;
|
||||||
|
|
||||||
public ModelsScheduledTask $task;
|
public ScheduledTask $task;
|
||||||
|
|
||||||
public ?string $modalId = null;
|
#[Locked]
|
||||||
|
public array $parameters;
|
||||||
|
|
||||||
|
#[Locked]
|
||||||
public string $type;
|
public string $type;
|
||||||
|
|
||||||
public string $scheduledTaskName;
|
#[Validate(['boolean'])]
|
||||||
|
public bool $isEnabled = false;
|
||||||
|
|
||||||
protected $rules = [
|
#[Validate(['string', 'required'])]
|
||||||
'task.enabled' => 'required|boolean',
|
public string $name;
|
||||||
'task.name' => 'required|string',
|
|
||||||
'task.command' => 'required|string',
|
|
||||||
'task.frequency' => 'required|string',
|
|
||||||
'task.container' => 'nullable|string',
|
|
||||||
];
|
|
||||||
|
|
||||||
protected $validationAttributes = [
|
#[Validate(['string', 'required'])]
|
||||||
'name' => 'name',
|
public string $command;
|
||||||
'command' => 'command',
|
|
||||||
'frequency' => 'frequency',
|
|
||||||
'container' => 'container',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function mount()
|
#[Validate(['string', 'required'])]
|
||||||
|
public string $frequency;
|
||||||
|
|
||||||
|
#[Validate(['string', 'nullable'])]
|
||||||
|
public ?string $container = null;
|
||||||
|
|
||||||
|
#[Locked]
|
||||||
|
public ?string $application_uuid;
|
||||||
|
|
||||||
|
#[Locked]
|
||||||
|
public ?string $service_uuid;
|
||||||
|
|
||||||
|
#[Locked]
|
||||||
|
public string $task_uuid;
|
||||||
|
|
||||||
|
public function mount(string $task_uuid, string $project_uuid, string $environment_name, ?string $application_uuid = null, ?string $service_uuid = null)
|
||||||
{
|
{
|
||||||
$this->parameters = get_route_parameters();
|
try {
|
||||||
|
$this->task_uuid = $task_uuid;
|
||||||
if (data_get($this->parameters, 'application_uuid')) {
|
if ($application_uuid) {
|
||||||
$this->type = 'application';
|
$this->type = 'application';
|
||||||
$this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail();
|
$this->application_uuid = $application_uuid;
|
||||||
} elseif (data_get($this->parameters, 'service_uuid')) {
|
$this->resource = Application::ownedByCurrentTeam()->where('uuid', $application_uuid)->firstOrFail();
|
||||||
|
} elseif ($service_uuid) {
|
||||||
$this->type = 'service';
|
$this->type = 'service';
|
||||||
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
|
$this->service_uuid = $service_uuid;
|
||||||
|
$this->resource = Service::ownedByCurrentTeam()->where('uuid', $service_uuid)->firstOrFail();
|
||||||
|
}
|
||||||
|
$this->parameters = [
|
||||||
|
'environment_name' => $environment_name,
|
||||||
|
'project_uuid' => $project_uuid,
|
||||||
|
'application_uuid' => $application_uuid,
|
||||||
|
'service_uuid' => $service_uuid,
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->task = $this->resource->scheduled_tasks()->where('uuid', $task_uuid)->firstOrFail();
|
||||||
|
$this->syncData();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->modalId = new Cuid2;
|
public function syncData(bool $toModel = false)
|
||||||
$this->task = ModelsScheduledTask::where('uuid', request()->route('task_uuid'))->first();
|
{
|
||||||
$this->scheduledTaskName = $this->task->name;
|
if ($toModel) {
|
||||||
|
$this->validate();
|
||||||
|
$this->task->enabled = $this->isEnabled;
|
||||||
|
$this->task->name = str($this->name)->trim()->value();
|
||||||
|
$this->task->command = str($this->command)->trim()->value();
|
||||||
|
$this->task->frequency = str($this->frequency)->trim()->value();
|
||||||
|
$this->task->container = str($this->container)->trim()->value();
|
||||||
|
$this->task->save();
|
||||||
|
} else {
|
||||||
|
$this->isEnabled = $this->task->enabled;
|
||||||
|
$this->name = $this->task->name;
|
||||||
|
$this->command = $this->task->command;
|
||||||
|
$this->frequency = $this->task->frequency;
|
||||||
|
$this->container = $this->task->container;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
$this->validateOnly('task.enabled');
|
try {
|
||||||
$this->task->save(['enabled' => $this->task->enabled]);
|
$this->syncData(true);
|
||||||
$this->dispatch('success', 'Scheduled task updated.');
|
$this->dispatch('success', 'Scheduled task updated.');
|
||||||
$this->dispatch('refreshTasks');
|
$this->refreshTasks();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
$this->validate();
|
try {
|
||||||
$this->task->name = str($this->task->name)->trim()->value();
|
$this->syncData(true);
|
||||||
$this->task->container = str($this->task->container)->trim()->value();
|
|
||||||
$this->task->save();
|
|
||||||
$this->dispatch('success', 'Scheduled task updated.');
|
$this->dispatch('success', 'Scheduled task updated.');
|
||||||
$this->dispatch('refreshTasks');
|
} catch (\Exception $e) {
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function refreshTasks()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->task->refresh();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete()
|
public function delete()
|
||||||
@@ -78,12 +128,22 @@ class Show extends Component
|
|||||||
$this->task->delete();
|
$this->task->delete();
|
||||||
|
|
||||||
if ($this->type === 'application') {
|
if ($this->type === 'application') {
|
||||||
return redirect()->route('project.application.configuration', $this->parameters, $this->scheduledTaskName);
|
return redirect()->route('project.application.configuration', $this->parameters, $this->task->name);
|
||||||
} else {
|
} else {
|
||||||
return redirect()->route('project.service.configuration', $this->parameters, $this->scheduledTaskName);
|
return redirect()->route('project.service.configuration', $this->parameters, $this->task->name);
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return handleError($e);
|
return handleError($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function executeNow()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
ScheduledTaskJob::dispatch($this->task);
|
||||||
|
$this->dispatch('success', 'Scheduled task executed.');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -172,6 +172,11 @@ class Application extends BaseModel
|
|||||||
return Application::whereRelation('environment.project.team', 'id', $teamId)->orderBy('name');
|
return Application::whereRelation('environment.project.team', 'id', $teamId)->orderBy('name');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function ownedByCurrentTeam()
|
||||||
|
{
|
||||||
|
return Application::whereRelation('environment.project.team', 'id', currentTeam()->id)->orderBy('name');
|
||||||
|
}
|
||||||
|
|
||||||
public function getContainersToStop(bool $previewDeployments = false): array
|
public function getContainersToStop(bool $previewDeployments = false): array
|
||||||
{
|
{
|
||||||
$containers = $previewDeployments
|
$containers = $previewDeployments
|
||||||
|
@@ -133,6 +133,11 @@ class Service extends BaseModel
|
|||||||
return $this->morphToMany(Tag::class, 'taggable');
|
return $this->morphToMany(Tag::class, 'taggable');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function ownedByCurrentTeam()
|
||||||
|
{
|
||||||
|
return Service::whereRelation('environment.project.team', 'id', currentTeam()->id)->orderBy('name');
|
||||||
|
}
|
||||||
|
|
||||||
public function getContainersToStop(): array
|
public function getContainersToStop(): array
|
||||||
{
|
{
|
||||||
$containersToStop = [];
|
$containersToStop = [];
|
||||||
|
@@ -37,6 +37,11 @@ class ServiceApplication extends BaseModel
|
|||||||
return ServiceApplication::whereRelation('service.environment.project.team', 'id', $teamId)->orderBy('name');
|
return ServiceApplication::whereRelation('service.environment.project.team', 'id', $teamId)->orderBy('name');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function ownedByCurrentTeam()
|
||||||
|
{
|
||||||
|
return ServiceApplication::whereRelation('service.environment.project.team', 'id', currentTeam()->id)->orderBy('name');
|
||||||
|
}
|
||||||
|
|
||||||
public function isRunning()
|
public function isRunning()
|
||||||
{
|
{
|
||||||
return str($this->status)->contains('running');
|
return str($this->status)->contains('running');
|
||||||
|
@@ -24,6 +24,16 @@ class ServiceDatabase extends BaseModel
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function ownedByCurrentTeamAPI(int $teamId)
|
||||||
|
{
|
||||||
|
return ServiceDatabase::whereRelation('service.environment.project.team', 'id', $teamId)->orderBy('name');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function ownedByCurrentTeam()
|
||||||
|
{
|
||||||
|
return ServiceDatabase::whereRelation('service.environment.project.team', 'id', currentTeam()->id)->orderBy('name');
|
||||||
|
}
|
||||||
|
|
||||||
public function restart()
|
public function restart()
|
||||||
{
|
{
|
||||||
$container_id = $this->name.'-'.$this->service->uuid;
|
$container_id = $this->name.'-'.$this->service->uuid;
|
||||||
|
@@ -16,6 +16,11 @@
|
|||||||
<x-forms.button type="submit">
|
<x-forms.button type="submit">
|
||||||
Save
|
Save
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
|
@if ($resource->isRunning())
|
||||||
|
<x-forms.button type="button" wire:click="executeNow">
|
||||||
|
Execute Now
|
||||||
|
</x-forms.button>
|
||||||
|
@endif
|
||||||
<x-modal-confirmation title="Confirm Scheduled Task Deletion?" isErrorButton buttonTitle="Delete"
|
<x-modal-confirmation title="Confirm Scheduled Task Deletion?" isErrorButton buttonTitle="Delete"
|
||||||
submitAction="delete({{ $task->id }})" :actions="['The selected scheduled task will be permanently deleted.']" confirmationText="{{ $task->name }}"
|
submitAction="delete({{ $task->id }})" :actions="['The selected scheduled task will be permanently deleted.']" confirmationText="{{ $task->name }}"
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Scheduled Task Name below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Scheduled Task Name below"
|
||||||
@@ -24,27 +29,26 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="w-48">
|
<div class="w-48">
|
||||||
<x-forms.checkbox instantSave id="task.enabled" label="Enabled" />
|
<x-forms.checkbox instantSave id="isEnabled" label="Enabled" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2 w-full">
|
<div class="flex gap-2 w-full">
|
||||||
<x-forms.input placeholder="Name" id="task.name" label="Name" required />
|
<x-forms.input placeholder="Name" id="name" label="Name" required />
|
||||||
<x-forms.input placeholder="php artisan schedule:run" id="task.command" label="Command" required />
|
<x-forms.input placeholder="php artisan schedule:run" id="command" label="Command" required />
|
||||||
<x-forms.input placeholder="0 0 * * * or daily" id="task.frequency" label="Frequency" required />
|
<x-forms.input placeholder="0 0 * * * or daily" id="frequency" label="Frequency" required />
|
||||||
@if ($type === 'application')
|
@if ($type === 'application')
|
||||||
<x-forms.input placeholder="php"
|
<x-forms.input placeholder="php"
|
||||||
helper="You can leave this empty if your resource only has one container." id="task.container"
|
helper="You can leave this empty if your resource only has one container." id="container"
|
||||||
label="Container name" />
|
label="Container name" />
|
||||||
@elseif ($type === 'service')
|
@elseif ($type === 'service')
|
||||||
<x-forms.input placeholder="php"
|
<x-forms.input placeholder="php"
|
||||||
helper="You can leave this empty if your resource only has one service in your stack. Otherwise use the stack name, without the random generated ID. So if you have a mysql service in your stack, use mysql."
|
helper="You can leave this empty if your resource only has one service in your stack. Otherwise use the stack name, without the random generated ID. So if you have a mysql service in your stack, use mysql."
|
||||||
id="task.container" label="Service name" />
|
id="container" label="Service name" />
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="pt-4">
|
<div class="pt-4">
|
||||||
<h3 class="py-4">Recent executions <span class="text-xs text-neutral-500">(click to check output)</span></h3>
|
<h3 class="py-4">Recent executions <span class="text-xs text-neutral-500">(click to check output)</span></h3>
|
||||||
<livewire:project.shared.scheduled-task.executions :task="$task" key="{{ $task->id }}" selectedKey=""
|
<livewire:project.shared.scheduled-task.executions :taskId="$task->id" />
|
||||||
:executions="$task->executions->take(20)" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Reference in New Issue
Block a user