diff --git a/app/Actions/Docker/GetContainersStatus.php b/app/Actions/Docker/GetContainersStatus.php index 1d240d88a..f0e1de8f6 100644 --- a/app/Actions/Docker/GetContainersStatus.php +++ b/app/Actions/Docker/GetContainersStatus.php @@ -6,14 +6,12 @@ use App\Actions\Database\StartDatabaseProxy; use App\Actions\Proxy\CheckProxy; use App\Actions\Proxy\StartProxy; use App\Actions\Shared\ComplexStatusCheck; -use App\Models\Application; use App\Models\ApplicationPreview; use App\Models\Server; use App\Models\ServiceDatabase; use App\Notifications\Container\ContainerRestarted; use App\Notifications\Container\ContainerStopped; use Illuminate\Support\Arr; -use Illuminate\Support\Facades\Log; use Lorisleiva\Actions\Concerns\AsAction; class GetContainersStatus diff --git a/app/Jobs/ScheduledTaskJob.php b/app/Jobs/ScheduledTaskJob.php index 6e2ad06b0..a28f85901 100644 --- a/app/Jobs/ScheduledTaskJob.php +++ b/app/Jobs/ScheduledTaskJob.php @@ -8,14 +8,13 @@ use App\Models\Server; use App\Models\Application; use App\Models\Service; use App\Models\Team; +use App\Notifications\ScheduledTask\TaskFailed; use Illuminate\Bus\Queueable; 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\Collection; -use Throwable; class ScheduledTaskJob implements ShouldQueue { @@ -114,6 +113,7 @@ class ScheduledTaskJob implements ShouldQueue 'message' => $this->task_output ?? $e->getMessage(), ]); } + $this->team?->notify(new TaskFailed($this->task, $e->getMessage())); // send_internal_notification('ScheduledTaskJob failed with: ' . $e->getMessage()); throw $e; } diff --git a/app/Jobs/SendMessageToDiscordJob.php b/app/Jobs/SendMessageToDiscordJob.php index 31f54f6d6..ddd6bd271 100644 --- a/app/Jobs/SendMessageToDiscordJob.php +++ b/app/Jobs/SendMessageToDiscordJob.php @@ -41,7 +41,6 @@ class SendMessageToDiscordJob implements ShouldQueue, ShouldBeEncrypted $payload = [ 'content' => $this->text, ]; - ray($payload); Http::post($this->webhookUrl, $payload); } } diff --git a/app/Livewire/Notifications/Discord.php b/app/Livewire/Notifications/Discord.php index 8aad8ccf0..88705437b 100644 --- a/app/Livewire/Notifications/Discord.php +++ b/app/Livewire/Notifications/Discord.php @@ -16,6 +16,7 @@ class Discord extends Component 'team.discord_notifications_deployments' => 'nullable|boolean', 'team.discord_notifications_status_changes' => 'nullable|boolean', 'team.discord_notifications_database_backups' => 'nullable|boolean', + 'team.discord_notifications_scheduled_tasks' => 'nullable|boolean', ]; protected $validationAttributes = [ 'team.discord_webhook_url' => 'Discord Webhook', diff --git a/app/Livewire/Notifications/Email.php b/app/Livewire/Notifications/Email.php index 343cbda3e..6ef9b2255 100644 --- a/app/Livewire/Notifications/Email.php +++ b/app/Livewire/Notifications/Email.php @@ -28,6 +28,7 @@ class Email extends Component 'team.smtp_notifications_deployments' => 'nullable|boolean', 'team.smtp_notifications_status_changes' => 'nullable|boolean', 'team.smtp_notifications_database_backups' => 'nullable|boolean', + 'team.smtp_notifications_scheduled_tasks' => 'nullable|boolean', 'team.use_instance_email_settings' => 'boolean', 'team.resend_enabled' => 'nullable|boolean', 'team.resend_api_key' => 'nullable', diff --git a/app/Livewire/Notifications/Telegram.php b/app/Livewire/Notifications/Telegram.php index 35b868527..685c9e8eb 100644 --- a/app/Livewire/Notifications/Telegram.php +++ b/app/Livewire/Notifications/Telegram.php @@ -18,10 +18,12 @@ class Telegram extends Component 'team.telegram_notifications_deployments' => 'nullable|boolean', 'team.telegram_notifications_status_changes' => 'nullable|boolean', 'team.telegram_notifications_database_backups' => 'nullable|boolean', + 'team.telegram_notifications_scheduled_tasks' => 'nullable|boolean', 'team.telegram_notifications_test_message_thread_id' => 'nullable|string', 'team.telegram_notifications_deployments_message_thread_id' => 'nullable|string', 'team.telegram_notifications_status_changes_message_thread_id' => 'nullable|string', 'team.telegram_notifications_database_backups_message_thread_id' => 'nullable|string', + 'team.telegram_notifications_scheduled_tasks_thread_id' => 'nullable|string', ]; protected $validationAttributes = [ 'team.telegram_token' => 'Token', diff --git a/app/Models/Application.php b/app/Models/Application.php index 07c45b83c..0f3425dd6 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -113,6 +113,18 @@ class Application extends BaseModel } return null; } + public function failedTaskLink($task_uuid) + { + if (data_get($this, 'environment.project.uuid')) { + return route('project.application.scheduled-tasks', [ + 'project_uuid' => data_get($this, 'environment.project.uuid'), + 'environment_name' => data_get($this, 'environment.name'), + 'application_uuid' => data_get($this, 'uuid'), + 'task_uuid' => $task_uuid + ]); + } + return null; + } public function settings() { return $this->hasOne(ApplicationSetting::class); diff --git a/app/Models/Service.php b/app/Models/Service.php index d8950137b..4c20b71de 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -653,6 +653,18 @@ class Service extends BaseModel } return null; } + public function failedTaskLink($task_uuid) + { + if (data_get($this, 'environment.project.uuid')) { + return route('project.service.scheduled-tasks', [ + 'project_uuid' => data_get($this, 'environment.project.uuid'), + 'environment_name' => data_get($this, 'environment.name'), + 'application_uuid' => data_get($this, 'uuid'), + 'task_uuid' => $task_uuid + ]); + } + return null; + } public function documentation() { $services = getServiceTemplates(); diff --git a/app/Notifications/Channels/TelegramChannel.php b/app/Notifications/Channels/TelegramChannel.php index 12695497a..6101ef208 100644 --- a/app/Notifications/Channels/TelegramChannel.php +++ b/app/Notifications/Channels/TelegramChannel.php @@ -32,6 +32,9 @@ class TelegramChannel case 'App\Notifications\Database\BackupFailed': $topicId = data_get($notifiable, 'telegram_notifications_database_backups_message_thread_id'); break; + case 'App\Notifications\ScheduledTask\TaskFailed': + $topicId = data_get($notifiable, 'telegram_notifications_scheduled_tasks_thread_id'); + break; } if (!$telegramToken || !$chatId || !$message) { return; diff --git a/app/Notifications/Container/ContainerRestarted.php b/app/Notifications/Container/ContainerRestarted.php index 21dc799f8..d9c524da4 100644 --- a/app/Notifications/Container/ContainerRestarted.php +++ b/app/Notifications/Container/ContainerRestarted.php @@ -31,7 +31,7 @@ class ContainerRestarted extends Notification implements ShouldQueue $mail->view('emails.container-restarted', [ 'containerName' => $this->name, 'serverName' => $this->server->name, - 'url' => $this->url , + 'url' => $this->url, ]); return $mail; } diff --git a/app/Notifications/ScheduledTask/TaskFailed.php b/app/Notifications/ScheduledTask/TaskFailed.php new file mode 100644 index 000000000..f61b1f573 --- /dev/null +++ b/app/Notifications/ScheduledTask/TaskFailed.php @@ -0,0 +1,64 @@ +application) { + $this->url = $task->application->failedTaskLink($task->uuid); + } else if ($task->service) { + $this->url = $task->service->failedTaskLink($task->uuid); + } + } + + public function via(object $notifiable): array + { + + return setNotificationChannels($notifiable, 'scheduled_tasks'); + } + + public function toMail(): MailMessage + { + $mail = new MailMessage(); + $mail->subject("Coolify: [ACTION REQUIRED] Scheduled task ({$this->task->name}) failed."); + $mail->view('emails.scheduled-task-failed', [ + 'task' => $this->task, + 'url' => $this->url, + 'output' => $this->output, + ]); + return $mail; + } + + public function toDiscord(): string + { + return "Coolify: Scheduled task ({$this->task->name}, [link]({$this->url})) failed with output: {$this->output}"; + } + public function toTelegram(): array + { + $message = "Coolify: Scheduled task ({$this->task->name}) failed with output: {$this->output}"; + if ($this->url) { + $buttons[] = [ + "text" => "Open task in Coolify", + "url" => (string) $this->url + ]; + } + return [ + "message" => $message, + ]; + } +} diff --git a/database/migrations/2024_05_21_125739_add_scheduled_tasks_notification_to_teams.php b/database/migrations/2024_05_21_125739_add_scheduled_tasks_notification_to_teams.php new file mode 100644 index 000000000..0fcbb0655 --- /dev/null +++ b/database/migrations/2024_05_21_125739_add_scheduled_tasks_notification_to_teams.php @@ -0,0 +1,34 @@ +boolean('telegram_notifications_scheduled_tasks')->default(true); + $table->boolean('smtp_notifications_scheduled_tasks')->default(false)->after('smtp_notifications_status_changes'); + $table->boolean('discord_notifications_scheduled_tasks')->default(true)->after('discord_notifications_status_changes'); + $table->text('telegram_notifications_scheduled_tasks_thread_id')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('teams', function (Blueprint $table) { + $table->dropColumn('telegram_notifications_scheduled_tasks'); + $table->dropColumn('smtp_notifications_scheduled_tasks'); + $table->dropColumn('discord_notifications_scheduled_tasks'); + $table->dropColumn('telegram_notifications_scheduled_tasks_thread_id'); + }); + } +}; diff --git a/resources/views/emails/scheduled-task-failed.blade.php b/resources/views/emails/scheduled-task-failed.blade.php new file mode 100644 index 000000000..60e451823 --- /dev/null +++ b/resources/views/emails/scheduled-task-failed.blade.php @@ -0,0 +1,9 @@ + +Scheduled task ({{ $task->name }}) was FAILED with the following error: + +
+{{ $output }}
+
+ +Click [here]({{ $url }}) to view the task. +
diff --git a/resources/views/livewire/notifications/discord.blade.php b/resources/views/livewire/notifications/discord.blade.php index 95fc9aae9..d85f59600 100644 --- a/resources/views/livewire/notifications/discord.blade.php +++ b/resources/views/livewire/notifications/discord.blade.php @@ -32,6 +32,8 @@ label="Application Deployments" /> + @endif diff --git a/resources/views/livewire/notifications/email.blade.php b/resources/views/livewire/notifications/email.blade.php index 9a89057a7..cccd4c26e 100644 --- a/resources/views/livewire/notifications/email.blade.php +++ b/resources/views/livewire/notifications/email.blade.php @@ -111,6 +111,8 @@ label="Application Deployments" /> + @endif diff --git a/resources/views/livewire/notifications/telegram.blade.php b/resources/views/livewire/notifications/telegram.blade.php index 1d8e1231c..01d8b1329 100644 --- a/resources/views/livewire/notifications/telegram.blade.php +++ b/resources/views/livewire/notifications/telegram.blade.php @@ -60,6 +60,15 @@ helper="If you are using Group chat with Topics, you can specify the topics ID. If empty, General topic will be used." id="team.telegram_notifications_database_backups_message_thread_id" label="Custom Topic ID" /> +
+

Scheduled Tasks Status

+ + +
+ @endif