diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 2a8e857e2..267572b39 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -5,12 +5,14 @@ use App\Jobs\CheckLogDrainContainerJob; use App\Jobs\CleanupInstanceStuffsJob; use App\Jobs\DatabaseBackupJob; +use App\Jobs\ScheduledTaskJob; use App\Jobs\InstanceAutoUpdateJob; use App\Jobs\ContainerStatusJob; use App\Jobs\PullHelperImageJob; use App\Jobs\ServerStatusJob; use App\Models\InstanceSettings; use App\Models\ScheduledDatabaseBackup; +use App\Models\ScheduledTask; use App\Models\Server; use App\Models\Team; use Illuminate\Console\Scheduling\Schedule; @@ -30,6 +32,7 @@ protected function schedule(Schedule $schedule): void $this->check_resources($schedule); $this->check_scheduled_backups($schedule); $this->pull_helper_image($schedule); + $this->check_scheduled_tasks($schedule); } else { // Instance Jobs $schedule->command('horizon:snapshot')->everyFiveMinutes(); @@ -41,6 +44,7 @@ protected function schedule(Schedule $schedule): void $this->check_scheduled_backups($schedule); $this->check_resources($schedule); $this->pull_helper_image($schedule); + $this->check_scheduled_tasks($schedule); } } private function pull_helper_image($schedule) @@ -107,6 +111,32 @@ private function check_scheduled_backups($schedule) } } + private function check_scheduled_tasks($schedule) { + $scheduled_tasks = ScheduledTask::all(); + if ($scheduled_tasks->isEmpty()) { + ray('no scheduled tasks'); + return; + } + foreach ($scheduled_tasks as $scheduled_task) { + $service = $scheduled_task->service()->get(); + $application = $scheduled_task->application()->get(); + + if (!$application && !$service) { + ray('application/service attached to scheduled task does not exist'); + $scheduled_task->delete(); + continue; + } + + if (isset(VALID_CRON_STRINGS[$scheduled_task->frequency])) { + $scheduled_task->frequency = VALID_CRON_STRINGS[$scheduled_task->frequency]; + } + $schedule->job(new ScheduledTaskJob( + task: $scheduled_task + ))->cron($scheduled_task->frequency)->onOneServer(); + } + + } + protected function commands(): void { $this->load(__DIR__ . '/Commands'); diff --git a/app/Livewire/Project/Shared/ScheduledTask/Add.php b/app/Livewire/Project/Shared/ScheduledTask/Add.php index 84f790ad4..3cc5428b8 100644 --- a/app/Livewire/Project/Shared/ScheduledTask/Add.php +++ b/app/Livewire/Project/Shared/ScheduledTask/Add.php @@ -33,7 +33,6 @@ public function mount() public function submit() { - error_log("*** IN SUBMIT"); $this->validate(); $isValid = validate_cron_expression($this->frequency); if (!$isValid) { diff --git a/app/Livewire/Project/Shared/ScheduledTask/All.php b/app/Livewire/Project/Shared/ScheduledTask/All.php index 1562a4cae..7f61c30cc 100644 --- a/app/Livewire/Project/Shared/ScheduledTask/All.php +++ b/app/Livewire/Project/Shared/ScheduledTask/All.php @@ -10,102 +10,23 @@ class All extends Component { public $resource; - public bool $showPreview = false; public string|null $modalId = null; public ?string $variables = null; - public ?string $variablesPreview = null; + public array $parameters; protected $listeners = ['refreshTasks', 'saveScheduledTask' => 'submit']; public function mount() { - $resourceClass = get_class($this->resource); - $resourceWithPreviews = ['App\Models\Application']; - $simpleDockerfile = !is_null(data_get($this->resource, 'dockerfile')); - if (Str::of($resourceClass)->contains($resourceWithPreviews) && !$simpleDockerfile) { - $this->showPreview = true; - } + $this->parameters = get_route_parameters(); $this->modalId = new Cuid2(7); - $this->getDevView(); - } - public function getDevView() - { - $this->variables = $this->resource->scheduled_tasks->map(function ($item) { - error_log("** got one"); - return "$item->name=$item->command"; - })->sort(); - - error_log(print_r($this->variables,1)); - } - public function saveVariables($isPreview) - { - if ($isPreview) { - $variables = parseEnvFormatToArray($this->variablesPreview); - $this->resource->environment_variables_preview()->whereNotIn('key', array_keys($variables))->delete(); - } else { - $variables = parseEnvFormatToArray($this->variables); - $this->resource->environment_variables()->whereNotIn('key', array_keys($variables))->delete(); - } - foreach ($variables as $key => $variable) { - if ($isPreview) { - $found = $this->resource->environment_variables_preview()->where('key', $key)->first(); - } else { - $found = $this->resource->environment_variables()->where('key', $key)->first(); - } - if ($found) { - if ($found->is_shown_once) { - continue; - } - $found->value = $variable; - $found->save(); - continue; - } else { - $task = new ScheduledTask(); - $task->key = $key; - $task->value = $variable; - $task->is_build_time = false; - $task->is_preview = $isPreview ? true : false; - switch ($this->resource->type()) { - case 'application': - $task->application_id = $this->resource->id; - break; - case 'standalone-postgresql': - $task->standalone_postgresql_id = $this->resource->id; - break; - case 'standalone-redis': - $task->standalone_redis_id = $this->resource->id; - break; - case 'standalone-mongodb': - $task->standalone_mongodb_id = $this->resource->id; - break; - case 'standalone-mysql': - $task->standalone_mysql_id = $this->resource->id; - break; - case 'standalone-mariadb': - $task->standalone_mariadb_id = $this->resource->id; - break; - case 'service': - $task->service_id = $this->resource->id; - break; - } - $task->save(); - } - } - if ($isPreview) { - $this->dispatch('success', 'Preview environment variables updated successfully.'); - } else { - $this->dispatch('success', 'Environment variables updated successfully.'); - } - $this->refreshTasks(); } public function refreshTasks() { $this->resource->refresh(); - $this->getDevView(); } public function submit($data) { - error_log("** submitting the beast"); try { $task = new ScheduledTask(); $task->name = $data['name']; diff --git a/app/Livewire/Project/Shared/ScheduledTask/Show.php b/app/Livewire/Project/Shared/ScheduledTask/Show.php index b65c36531..23cb0e41a 100644 --- a/app/Livewire/Project/Shared/ScheduledTask/Show.php +++ b/app/Livewire/Project/Shared/ScheduledTask/Show.php @@ -4,47 +4,52 @@ use App\Models\ScheduledTask as ModelsScheduledTask; use Livewire\Component; +use App\Models\Application; +use App\Models\Service; use Visus\Cuid2\Cuid2; class Show extends Component { public $parameters; + public Application|Service $resource; public ModelsScheduledTask $task; public ?string $modalId = null; - public bool $isDisabled = false; - public bool $isLocked = false; public string $type; protected $rules = [ 'task.name' => 'required|string', 'task.command' => 'required|string', + 'task.frequency' => 'required|string', + 'task.container' => 'nullable|string', ]; protected $validationAttributes = [ - 'name' => 'Name', - 'command' => 'Command', + 'name' => 'name', + 'command' => 'command', + 'frequency' => 'frequency', + 'container' => 'container', ]; public function mount() { - $this->modalId = new Cuid2(7); $this->parameters = get_route_parameters(); + + if (data_get($this->parameters, 'application_uuid')) { + $this->type = 'application'; + $this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail(); + } else if (data_get($this->parameters, 'service_uuid')) { + $this->type = 'service'; + $this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail(); + } + + $this->modalId = new Cuid2(7); + $this->task = ModelsScheduledTask::where('uuid', request()->route('task_uuid'))->first(); } - public function lock() - { - $this->task->is_shown_once = true; - $this->task->save(); - $this->dispatch('refreshTasks'); - } - public function instantSave() - { - $this->submit(); - } public function submit() { $this->validate(); $this->task->save(); - $this->dispatch('success', 'Environment variable updated successfully.'); + $this->dispatch('success', 'Scheduled task updated successfully.'); $this->dispatch('refreshTasks'); } @@ -52,7 +57,14 @@ public function delete() { try { $this->task->delete(); - $this->dispatch('refreshTasks'); + + if ($this->type == 'application') { + return redirect()->route('project.application.configuration', $this->parameters); + } + else { + return redirect()->route('project.service.configuration', $this->parameters); + } + } catch (\Exception $e) { return handleError($e); } diff --git a/app/Models/ScheduledTask.php b/app/Models/ScheduledTask.php index 79468dd2a..2ff391c59 100644 --- a/app/Models/ScheduledTask.php +++ b/app/Models/ScheduledTask.php @@ -2,87 +2,27 @@ namespace App\Models; -use App\Models\ScheduledTask as ModelsScheduledTask; -use Illuminate\Database\Eloquent\Casts\Attribute; -use Illuminate\Database\Eloquent\Model; -use Illuminate\Support\Str; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\HasOne; class ScheduledTask extends BaseModel { protected $guarded = []; - protected $casts = [ - 'name' => 'string', - 'command' => 'string', - 'frequency' => 'string', - 'container' => 'string', - ]; - // protected static function booted() - // { - // static::created(function ($scheduled_task) { - // error_log("*** IN CREATED"); - // if ($scheduled_task->application_id) { - // $found = ModelsScheduledTask::where('id', $scheduled_task->id)->where('application_id', $scheduled_task->application_id)->first(); - // $application = Application::find($scheduled_task->application_id); - // if (!$found) { - // ModelsScheduledTask::create([ - // 'name' => $scheduled_task->name, - // 'command' => $scheduled_task->command, - // 'frequency' => $scheduled_task->frequency, - // 'container' => $scheduled_task->container, - // 'application_id' => $scheduled_task->application_id, - // ]); - // } - // } - // }); - // } public function service() { return $this->belongsTo(Service::class); } - // protected function value(): Attribute - // { - // return Attribute::make( - // get: fn (?string $value = null) => $this->get_scheduled_tasks($value), - // set: fn (?string $value = null) => $this->set_scheduled_tasks($value), - // ); - // } - - private function get_scheduled_tasks(?string $scheduled_task = null): string|null + public function application() { - error_log("** in get_scheduled_tasks"); - // // $team_id = currentTeam()->id; - // if (!$scheduled_task) { - // return null; - // } - // $scheduled_task = trim(decrypt($scheduled_task)); - // if (Str::startsWith($scheduled_task, '{{') && Str::endsWith($scheduled_task, '}}') && Str::contains($scheduled_task, 'global.')) { - // $variable = Str::after($scheduled_task, 'global.'); - // $variable = Str::before($variable, '}}'); - // $variable = Str::of($variable)->trim()->value; - // // $scheduled_task = GlobalScheduledTask::where('name', $scheduled_task)->where('team_id', $team_id)->first()?->value; - // ray('global env variable'); - // return $scheduled_task; - // } - // return $scheduled_task; + return $this->belongsTo(Application::class); } - - private function set_scheduled_tasks(?string $scheduled_task = null): string|null + public function latest_log(): HasOne { - error_log("** in set_scheduled_tasks"); - // if (is_null($scheduled_task) && $scheduled_task == '') { - // return null; - // } - // $scheduled_task = trim($scheduled_task); - // return encrypt($scheduled_task); + return $this->hasOne(ScheduledTaskExecution::class)->latest(); + } + public function executions(): HasMany + { + return $this->hasMany(ScheduledTaskExecution::class); } - - // protected function key(): Attribute - // { - // error_log("** in key()"); - - // // return Attribute::make( - // // set: fn (string $value) => Str::of($value)->trim(), - // // ); - // } } diff --git a/resources/views/livewire/project/shared/scheduled-task/all.blade.php b/resources/views/livewire/project/shared/scheduled-task/all.blade.php index 1c42cdc00..7a1f359ef 100644 --- a/resources/views/livewire/project/shared/scheduled-task/all.blade.php +++ b/resources/views/livewire/project/shared/scheduled-task/all.blade.php @@ -1,16 +1,31 @@ -
-
-
-

Scheduled Tasks

- + Add - -
-
Scheduled Tasks for this resource.
+
+
+

Scheduled Tasks

+ + Add +
- @forelse ($resource->scheduled_tasks as $task) - - @empty -
No scheduled tasks found.
- @endforelse + +
+ @forelse($resource->scheduled_tasks as $task) + +
{{ $task->name }}
+
Frequency: {{ $task->frequency }}
+
Last run: {{ data_get($task->latest_log, 'status', 'No runs yet') }}
+
Next run: @todo
+
+ @empty +
No scheduled tasks configured.
+ @endforelse +
+ + {{-- @if ($type === 'service-database' && $selectedBackup) +
+ +

Executions

+ +
+ @endif --}}
diff --git a/resources/views/livewire/project/shared/scheduled-task/show.blade.php b/resources/views/livewire/project/shared/scheduled-task/show.blade.php index d3a9bce47..a53ed6fbb 100644 --- a/resources/views/livewire/project/shared/scheduled-task/show.blade.php +++ b/resources/views/livewire/project/shared/scheduled-task/show.blade.php @@ -5,17 +5,32 @@ class="font-bold text-warning">({{ $task->name }})?

-
- - -
- - Update - - - Delete - + +

Scheduled Backup

+ + + +
+
+

Scheduled Task

+ + Save + + + {{-- @if (Str::of($status)->startsWith('running')) + + @endif --}} + + + Delete + + +
+ + + + +
diff --git a/routes/web.php b/routes/web.php index ffd907344..126ff5bef 100644 --- a/routes/web.php +++ b/routes/web.php @@ -16,6 +16,7 @@ use App\Livewire\Project\EnvironmentEdit; use App\Livewire\Project\Shared\ExecuteContainerCommand; use App\Livewire\Project\Shared\Logs; +use App\Livewire\Project\Shared\ScheduledTask\Show as ScheduledTaskShow; use App\Livewire\Security\ApiTokens; use App\Livewire\Server\All; use App\Livewire\Server\Create; @@ -127,6 +128,8 @@ Route::get('/project/{project_uuid}/{environment_name}/application/{application_uuid}/logs', Logs::class)->name('project.application.logs'); Route::get('/project/{project_uuid}/{environment_name}/application/{application_uuid}/command', ExecuteContainerCommand::class)->name('project.application.command'); + Route::get('/project/{project_uuid}/{environment_name}/application/{application_uuid}/tasks/{task_uuid}', ScheduledTaskShow::class)->name('project.application.scheduled-tasks'); + // Databases Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}', [DatabaseController::class, 'configuration'])->name('project.database.configuration');