diff --git a/app/Actions/Database/StartMongodb.php b/app/Actions/Database/StartMongodb.php index 6d67bdde6..26a0f82d0 100644 --- a/app/Actions/Database/StartMongodb.php +++ b/app/Actions/Database/StartMongodb.php @@ -25,6 +25,10 @@ class StartMongodb $container_name = $this->database->uuid; $this->configuration_dir = database_configuration_dir().'/'.$container_name; + if (isDev()) { + $this->configuration_dir = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/databases/'.$container_name; + } + $this->commands = [ "echo 'Starting {$database->name}.'", "mkdir -p $this->configuration_dir", diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 9aa87e8dd..3fb4de60b 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -48,7 +48,7 @@ class Kernel extends ConsoleKernel // Instance Jobs $schedule->command('horizon:snapshot')->everyMinute(); $schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer(); - $schedule->job(new CheckHelperImageJob)->everyFiveMinutes()->onOneServer(); + $schedule->job(new CheckHelperImageJob)->everyTenMinutes()->onOneServer(); // Server Jobs $this->checkResources($schedule); @@ -120,7 +120,6 @@ class Kernel extends ConsoleKernel } else { $servers = $this->allServers->get(); } - // $schedule->job(new \App\Jobs\ResourcesCheck)->everyMinute()->onOneServer(); foreach ($servers as $server) { $serverTimezone = $server->settings->server_timezone; diff --git a/app/Events/ScheduledTaskDone.php b/app/Events/ScheduledTaskDone.php new file mode 100644 index 000000000..c8b5547f6 --- /dev/null +++ b/app/Events/ScheduledTaskDone.php @@ -0,0 +1,34 @@ +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}"), + ]; + } +} diff --git a/app/Jobs/CleanupInstanceStuffsJob.php b/app/Jobs/CleanupInstanceStuffsJob.php index acbe82338..84f14ed02 100644 --- a/app/Jobs/CleanupInstanceStuffsJob.php +++ b/app/Jobs/CleanupInstanceStuffsJob.php @@ -3,14 +3,15 @@ namespace App\Jobs; use App\Models\TeamInvitation; -use App\Models\Waitlist; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldBeEncrypted; use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\Log; class CleanupInstanceStuffsJob implements ShouldBeEncrypted, ShouldBeUnique, ShouldQueue { @@ -18,34 +19,21 @@ class CleanupInstanceStuffsJob implements ShouldBeEncrypted, ShouldBeUnique, Sho public function __construct() {} - // public function uniqueId(): string - // { - // return $this->container_name; - // } + public function middleware(): array + { + return [(new WithoutOverlapping('cleanup-instance-stuffs'))->dontRelease()]; + } public function handle(): void { try { - // $this->cleanup_waitlist(); + $this->cleanupInvitationLink(); } catch (\Throwable $e) { - send_internal_notification('CleanupInstanceStuffsJob failed with error: '.$e->getMessage()); - } - try { - $this->cleanup_invitation_link(); - } catch (\Throwable $e) { - send_internal_notification('CleanupInstanceStuffsJob failed with error: '.$e->getMessage()); + Log::error('CleanupInstanceStuffsJob failed with error: '.$e->getMessage()); } } - private function cleanup_waitlist() - { - $waitlist = Waitlist::whereVerified(false)->where('created_at', '<', now()->subMinutes(config('constants.waitlist.expiration')))->get(); - foreach ($waitlist as $item) { - $item->delete(); - } - } - - private function cleanup_invitation_link() + private function cleanupInvitationLink() { $invitation = TeamInvitation::all(); foreach ($invitation as $item) { diff --git a/app/Jobs/DockerCleanupJob.php b/app/Jobs/DockerCleanupJob.php index 900bae99c..0d7e63dd2 100644 --- a/app/Jobs/DockerCleanupJob.php +++ b/app/Jobs/DockerCleanupJob.php @@ -10,6 +10,7 @@ use Illuminate\Contracts\Queue\ShouldBeEncrypted; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Log; @@ -23,6 +24,11 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue public ?string $usageBefore = null; + public function middleware(): array + { + return [(new WithoutOverlapping($this->server->id))->dontRelease()]; + } + public function __construct(public Server $server, public bool $manualCleanup = false) {} public function handle(): void diff --git a/app/Jobs/ScheduledTaskJob.php b/app/Jobs/ScheduledTaskJob.php index 827641e8c..7bfc29af3 100644 --- a/app/Jobs/ScheduledTaskJob.php +++ b/app/Jobs/ScheduledTaskJob.php @@ -2,6 +2,7 @@ namespace App\Jobs; +use App\Events\ScheduledTaskDone; use App\Models\Application; use App\Models\ScheduledTask; use App\Models\ScheduledTaskExecution; @@ -19,7 +20,7 @@ class ScheduledTaskJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - public ?Team $team = null; + public Team $team; public Server $server; @@ -47,7 +48,7 @@ class ScheduledTaskJob implements ShouldQueue } else { 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(); } @@ -125,6 +126,7 @@ class ScheduledTaskJob implements ShouldQueue // send_internal_notification('ScheduledTaskJob failed with: ' . $e->getMessage()); throw $e; } finally { + ScheduledTaskDone::dispatch($this->team->id); } } } diff --git a/app/Jobs/ServerCheckNewJob.php b/app/Jobs/ServerCheckNewJob.php index 9ce52759c..3e8e60a31 100644 --- a/app/Jobs/ServerCheckNewJob.php +++ b/app/Jobs/ServerCheckNewJob.php @@ -2,6 +2,7 @@ namespace App\Jobs; +use App\Actions\Server\ResourcesCheck; use App\Actions\Server\ServerCheck; use App\Models\Server; use Illuminate\Bus\Queueable; @@ -25,6 +26,7 @@ class ServerCheckNewJob implements ShouldBeEncrypted, ShouldQueue { try { ServerCheck::run($this->server); + ResourcesCheck::dispatch($this->server); } catch (\Throwable $e) { return handleError($e); } diff --git a/app/Livewire/Destination/New/Docker.php b/app/Livewire/Destination/New/Docker.php index 8cf58ec41..f86f42e34 100644 --- a/app/Livewire/Destination/New/Docker.php +++ b/app/Livewire/Destination/New/Docker.php @@ -36,8 +36,10 @@ class Docker extends Component $this->servers = Server::isUsable()->get(); if ($server_id) { $this->selectedServer = $this->servers->find($server_id); + $this->serverId = $this->selectedServer->id; } else { $this->selectedServer = $this->servers->first(); + $this->serverId = $this->selectedServer->id; } $this->generateName(); } diff --git a/app/Livewire/Project/Shared/ScheduledTask/Executions.php b/app/Livewire/Project/Shared/ScheduledTask/Executions.php index 05d9a7a13..0710e37ff 100644 --- a/app/Livewire/Project/Shared/ScheduledTask/Executions.php +++ b/app/Livewire/Project/Shared/ScheduledTask/Executions.php @@ -2,23 +2,60 @@ 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; 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() { + $teamId = Auth::user()->currentTeam()->id; + 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 { if ($key == $this->selectedKey) { @@ -29,38 +66,9 @@ class Executions extends Component $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) { - $serverTimezone = $this->getServerTimezone(); + $serverTimezone = $this->serverTimezone; $dateObj = new \DateTime($date); try { $dateObj->setTimezone(new \DateTimeZone($serverTimezone)); diff --git a/app/Livewire/Project/Shared/ScheduledTask/Show.php b/app/Livewire/Project/Shared/ScheduledTask/Show.php index 36194edb7..0900a1d70 100644 --- a/app/Livewire/Project/Shared/ScheduledTask/Show.php +++ b/app/Livewire/Project/Shared/ScheduledTask/Show.php @@ -2,74 +2,124 @@ namespace App\Livewire\Project\Shared\ScheduledTask; +use App\Jobs\ScheduledTaskJob; use App\Models\Application; -use App\Models\ScheduledTask as ModelsScheduledTask; +use App\Models\ScheduledTask; use App\Models\Service; +use Livewire\Attributes\Locked; +use Livewire\Attributes\Validate; use Livewire\Component; -use Visus\Cuid2\Cuid2; class Show extends Component { - public $parameters; - public Application|Service $resource; - public ModelsScheduledTask $task; + public ScheduledTask $task; - public ?string $modalId = null; + #[Locked] + public array $parameters; + #[Locked] public string $type; - public string $scheduledTaskName; + #[Validate(['boolean'])] + public bool $isEnabled = false; - protected $rules = [ - 'task.enabled' => 'required|boolean', - 'task.name' => 'required|string', - 'task.command' => 'required|string', - 'task.frequency' => 'required|string', - 'task.container' => 'nullable|string', - ]; + #[Validate(['string', 'required'])] + public string $name; - protected $validationAttributes = [ - 'name' => 'name', - 'command' => 'command', - 'frequency' => 'frequency', - 'container' => 'container', - ]; + #[Validate(['string', 'required'])] + public string $command; - 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 ($application_uuid) { + $this->type = 'application'; + $this->application_uuid = $application_uuid; + $this->resource = Application::ownedByCurrentTeam()->where('uuid', $application_uuid)->firstOrFail(); + } elseif ($service_uuid) { + $this->type = 'service'; + $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, + ]; - if (data_get($this->parameters, 'application_uuid')) { - $this->type = 'application'; - $this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail(); - } elseif (data_get($this->parameters, 'service_uuid')) { - $this->type = 'service'; - $this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail(); + $this->task = $this->resource->scheduled_tasks()->where('uuid', $task_uuid)->firstOrFail(); + $this->syncData(); + } catch (\Exception $e) { + return handleError($e); } + } - $this->modalId = new Cuid2; - $this->task = ModelsScheduledTask::where('uuid', request()->route('task_uuid'))->first(); - $this->scheduledTaskName = $this->task->name; + public function syncData(bool $toModel = false) + { + 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() { - $this->validateOnly('task.enabled'); - $this->task->save(['enabled' => $this->task->enabled]); - $this->dispatch('success', 'Scheduled task updated.'); - $this->dispatch('refreshTasks'); + try { + $this->syncData(true); + $this->dispatch('success', 'Scheduled task updated.'); + $this->refreshTasks(); + } catch (\Exception $e) { + return handleError($e); + } } public function submit() { - $this->validate(); - $this->task->name = str($this->task->name)->trim()->value(); - $this->task->container = str($this->task->container)->trim()->value(); - $this->task->save(); - $this->dispatch('success', 'Scheduled task updated.'); - $this->dispatch('refreshTasks'); + try { + $this->syncData(true); + $this->dispatch('success', 'Scheduled task updated.'); + } catch (\Exception $e) { + return handleError($e); + } + } + + public function refreshTasks() + { + try { + $this->task->refresh(); + } catch (\Exception $e) { + return handleError($e); + } } public function delete() @@ -78,12 +128,22 @@ class Show extends Component $this->task->delete(); 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 { - return redirect()->route('project.service.configuration', $this->parameters, $this->scheduledTaskName); + return redirect()->route('project.service.configuration', $this->parameters, $this->task->name); } } catch (\Exception $e) { return handleError($e); } } + + public function executeNow() + { + try { + ScheduledTaskJob::dispatch($this->task); + $this->dispatch('success', 'Scheduled task executed.'); + } catch (\Exception $e) { + return handleError($e); + } + } } diff --git a/app/Models/Application.php b/app/Models/Application.php index 91abf2e3a..0ef787b2e 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -172,6 +172,11 @@ class Application extends BaseModel 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 { $containers = $previewDeployments diff --git a/app/Models/Service.php b/app/Models/Service.php index f88a23641..0c9e081a1 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -133,6 +133,11 @@ class Service extends BaseModel 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 { $containersToStop = []; diff --git a/app/Models/ServiceApplication.php b/app/Models/ServiceApplication.php index 305913068..5cafc9042 100644 --- a/app/Models/ServiceApplication.php +++ b/app/Models/ServiceApplication.php @@ -37,6 +37,11 @@ class ServiceApplication extends BaseModel 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() { return str($this->status)->contains('running'); diff --git a/app/Models/ServiceDatabase.php b/app/Models/ServiceDatabase.php index 6641509dd..5fdd52637 100644 --- a/app/Models/ServiceDatabase.php +++ b/app/Models/ServiceDatabase.php @@ -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() { $container_id = $this->name.'-'.$this->service->uuid; 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 e6a8b317f..d3f6cb5dc 100644 --- a/resources/views/livewire/project/shared/scheduled-task/show.blade.php +++ b/resources/views/livewire/project/shared/scheduled-task/show.blade.php @@ -16,6 +16,11 @@ Save + @if ($resource->isRunning()) + + Execute Now + + @endif
- +
- - - + + + @if ($type === 'application') @elseif ($type === 'service') + id="container" label="Service name" /> @endif

Recent executions (click to check output)

- +