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 @@
+
+{{ $output }}
+
+
+Click [here]({{ $url }}) to view the task.
+