enable/disable cronjobs (#203)

This commit is contained in:
Saeed Vaziry 2024-05-11 13:19:19 +02:00 committed by GitHub
parent 1067a5fd33
commit 88223a61f9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 198 additions and 11 deletions

View File

@ -2,6 +2,7 @@
namespace App\Actions\CronJob;
use App\Enums\CronjobStatus;
use App\Models\CronJob;
use App\Models\Server;
@ -10,7 +11,9 @@ class DeleteCronJob
public function delete(Server $server, CronJob $cronJob): void
{
$user = $cronJob->user;
$cronJob->delete();
$cronJob->status = CronjobStatus::DELETING;
$cronJob->save();
$server->cron()->update($cronJob->user, CronJob::crontab($server, $user));
$cronJob->delete();
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace App\Actions\CronJob;
use App\Enums\CronjobStatus;
use App\Models\CronJob;
use App\Models\Server;
class DisableCronJob
{
public function disable(Server $server, CronJob $cronJob): void
{
$cronJob->status = CronjobStatus::DISABLING;
$cronJob->save();
$server->cron()->update($cronJob->user, CronJob::crontab($server, $cronJob->user));
$cronJob->status = CronjobStatus::DISABLED;
$cronJob->save();
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace App\Actions\CronJob;
use App\Enums\CronjobStatus;
use App\Models\CronJob;
use App\Models\Server;
class EnableCronJob
{
public function enable(Server $server, CronJob $cronJob): void
{
$cronJob->status = CronjobStatus::ENABLING;
$cronJob->save();
$server->cron()->update($cronJob->user, CronJob::crontab($server, $cronJob->user));
$cronJob->status = CronjobStatus::READY;
$cronJob->save();
}
}

View File

@ -9,4 +9,10 @@ final class CronjobStatus
const READY = 'ready';
const DELETING = 'deleting';
const ENABLING = 'enabling';
const DISABLING = 'disabling';
const DISABLED = 'disabled';
}

View File

@ -4,6 +4,8 @@
use App\Actions\CronJob\CreateCronJob;
use App\Actions\CronJob\DeleteCronJob;
use App\Actions\CronJob\DisableCronJob;
use App\Actions\CronJob\EnableCronJob;
use App\Facades\Toast;
use App\Helpers\HtmxResponse;
use App\Models\CronJob;
@ -45,4 +47,26 @@ public function destroy(Server $server, CronJob $cronJob): RedirectResponse
return back();
}
public function enable(Server $server, CronJob $cronJob): RedirectResponse
{
$this->authorize('manage', $server);
app(EnableCronJob::class)->enable($server, $cronJob);
Toast::success('Cronjob enabled successfully.');
return back();
}
public function disable(Server $server, CronJob $cronJob): RedirectResponse
{
$this->authorize('manage', $server);
app(DisableCronJob::class)->disable($server, $cronJob);
Toast::success('Cronjob disabled successfully.');
return back();
}
}

View File

@ -2,6 +2,7 @@
namespace App\Models;
use App\Enums\CronjobStatus;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -41,7 +42,14 @@ public function server(): BelongsTo
public static function crontab(Server $server, string $user): string
{
$data = '';
$cronJobs = $server->cronJobs()->where('user', $user)->get();
$cronJobs = $server->cronJobs()
->where('user', $user)
->whereIn('status', [
CronjobStatus::READY,
CronjobStatus::CREATING,
CronjobStatus::ENABLING,
])
->get();
foreach ($cronJobs as $key => $cronJob) {
$data .= $cronJob->frequency.' '.$cronJob->command;
if ($key != count($cronJobs) - 1) {

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{
"resources/css/app.css": {
"file": "assets/app-1c10ca5c.css",
"file": "assets/app-4e6a754c.css",
"isEntry": true,
"src": "resources/css/app.css"
},

View File

@ -432,7 +432,7 @@ class="-ml-1 mr-1.5 h-[18px] w-[18px]"
></path>
</svg>
<p
class="font-medium leading-none text-gray-800 dark:text-white"
class="text-sm font-medium leading-none text-gray-800 dark:text-white"
x-text="toast.message"
></p>
</div>

View File

@ -30,6 +30,11 @@ class="p-6"
type="text"
class="mt-1 w-full"
/>
<x-input-help class="mt-2">
<a href="https://vitodeploy.com/servers/cronjobs.html" target="_blank" class="text-primary-500">
How the command should look like?
</a>
</x-input-help>
@error("command")
<x-input-error class="mt-2" :messages="$message" />
@enderror

View File

@ -12,19 +12,50 @@
<div x-data="" class="space-y-3">
@if (count($cronjobs) > 0)
@foreach ($cronjobs as $cronjob)
<x-item-card>
<x-item-card id="cronjob-{{ $cronjob->id }}">
<div class="flex flex-grow flex-col items-start justify-center">
<span class="mb-1 flex items-center lowercase text-red-600">
<div class="mb-1 text-left text-xs lowercase text-red-600 md:text-sm">
{{ $cronjob->command }}
</span>
<span class="text-sm text-gray-400">
</div>
<div class="text-xs text-gray-400 md:text-sm">
{{ $cronjob->frequencyLabel() }}
</span>
</div>
</div>
<div class="flex items-center">
@include("cronjobs.partials.status", ["status" => $cronjob->status])
<div class="inline">
<div class="ml-1 inline">
@if ($cronjob->status == \App\Enums\CronjobStatus::READY)
<x-icon-button
id="disable-cronjob-{{ $cronjob->id }}"
data-tooltip="Disable Cronjob"
hx-post="{{ route('servers.cronjobs.disable', ['server' => $server, 'cronJob' => $cronjob]) }}"
hx-target="#cronjob-{{ $cronjob->id }}"
hx-select="#cronjob-{{ $cronjob->id }}"
hx-swap="outerHTML"
hx-ext="disable-element"
hx-disable-element="#disable-cronjob-{{ $cronjob->id }}"
>
<x-heroicon name="o-stop" class="h-5 w-5" />
</x-icon-button>
@endif
@if ($cronjob->status == \App\Enums\CronjobStatus::DISABLED)
<x-icon-button
id="enable-cronjob-{{ $cronjob->id }}"
data-tooltip="Enable Cronjob"
hx-post="{{ route('servers.cronjobs.enable', ['server' => $server, 'cronJob' => $cronjob]) }}"
hx-target="#cronjob-{{ $cronjob->id }}"
hx-select="#cronjob-{{ $cronjob->id }}"
hx-swap="outerHTML"
hx-ext="disable-element"
hx-disable-element="#enable-cronjob-{{ $cronjob->id }}"
>
<x-heroicon name="o-play" class="h-5 w-5" />
</x-icon-button>
@endif
<x-icon-button
data-tooltip="Delete Cronjob"
x-on:click="deleteAction = '{{ route('servers.cronjobs.destroy', ['server' => $server, 'cronJob' => $cronjob]) }}'; $dispatch('open-modal', 'delete-cronjob')"
>
<x-heroicon name="o-trash" class="h-5 w-5" />

View File

@ -6,6 +6,18 @@
<x-status status="warning">{{ $status }}</x-status>
@endif
@if ($status == \App\Enums\CronjobStatus::DISABLING)
<x-status status="warning">{{ $status }}</x-status>
@endif
@if ($status == \App\Enums\CronjobStatus::DISABLED)
<x-status status="disabled">{{ $status }}</x-status>
@endif
@if ($status == \App\Enums\CronjobStatus::ENABLING)
<x-status status="warning">{{ $status }}</x-status>
@endif
@if ($status == \App\Enums\CronjobStatus::DELETING)
<x-status status="danger">{{ $status }}</x-status>
@endif

View File

@ -113,6 +113,8 @@
Route::get('/{server}/cronjobs', [CronjobController::class, 'index'])->name('servers.cronjobs');
Route::post('/{server}/cronjobs', [CronjobController::class, 'store'])->name('servers.cronjobs.store');
Route::delete('/{server}/cronjobs/{cronJob}', [CronjobController::class, 'destroy'])->name('servers.cronjobs.destroy');
Route::post('/{server}/cronjobs/{cronJob}/enable', [CronjobController::class, 'enable'])->name('servers.cronjobs.enable');
Route::post('/{server}/cronjobs/{cronJob}/disable', [CronjobController::class, 'disable'])->name('servers.cronjobs.disable');
// ssh keys
Route::get('/{server}/ssh-keys', [SSHKeyController::class, 'index'])->name('servers.ssh-keys');

View File

@ -99,4 +99,60 @@ public function test_create_custom_cronjob()
SSH::assertExecutedContains("echo '* * * 1 1 ls -la' | sudo -u vito crontab -");
SSH::assertExecutedContains('sudo -u vito crontab -l');
}
public function test_enable_cronjob()
{
SSH::fake();
$this->actingAs($this->user);
/** @var CronJob $cronjob */
$cronjob = CronJob::factory()->create([
'server_id' => $this->server->id,
'user' => 'vito',
'command' => 'ls -la',
'frequency' => '* * * 1 1',
'status' => CronjobStatus::DISABLED,
]);
$this->post(route('servers.cronjobs.enable', [
'server' => $this->server,
'cronJob' => $cronjob,
]))->assertSessionDoesntHaveErrors();
$cronjob->refresh();
$this->assertEquals(CronjobStatus::READY, $cronjob->status);
SSH::assertExecutedContains("echo '* * * 1 1 ls -la' | sudo -u vito crontab -");
SSH::assertExecutedContains('sudo -u vito crontab -l');
}
public function test_disable_cronjob()
{
SSH::fake();
$this->actingAs($this->user);
/** @var CronJob $cronjob */
$cronjob = CronJob::factory()->create([
'server_id' => $this->server->id,
'user' => 'vito',
'command' => 'ls -la',
'frequency' => '* * * 1 1',
'status' => CronjobStatus::READY,
]);
$this->post(route('servers.cronjobs.disable', [
'server' => $this->server,
'cronJob' => $cronjob,
]))->assertSessionDoesntHaveErrors();
$cronjob->refresh();
$this->assertEquals(CronjobStatus::DISABLED, $cronjob->status);
SSH::assertExecutedContains("echo '' | sudo -u vito crontab -");
SSH::assertExecutedContains('sudo -u vito crontab -l');
}
}