feat: scheduled task failed notification

This commit is contained in:
Andras Bacsai 2024-05-21 15:36:26 +02:00
parent 98b6aec203
commit a3d73634e7
16 changed files with 154 additions and 6 deletions

View File

@ -6,14 +6,12 @@
use App\Actions\Proxy\CheckProxy; use App\Actions\Proxy\CheckProxy;
use App\Actions\Proxy\StartProxy; use App\Actions\Proxy\StartProxy;
use App\Actions\Shared\ComplexStatusCheck; use App\Actions\Shared\ComplexStatusCheck;
use App\Models\Application;
use App\Models\ApplicationPreview; use App\Models\ApplicationPreview;
use App\Models\Server; use App\Models\Server;
use App\Models\ServiceDatabase; use App\Models\ServiceDatabase;
use App\Notifications\Container\ContainerRestarted; use App\Notifications\Container\ContainerRestarted;
use App\Notifications\Container\ContainerStopped; use App\Notifications\Container\ContainerStopped;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
class GetContainersStatus class GetContainersStatus

View File

@ -8,14 +8,13 @@
use App\Models\Application; use App\Models\Application;
use App\Models\Service; use App\Models\Service;
use App\Models\Team; use App\Models\Team;
use App\Notifications\ScheduledTask\TaskFailed;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
use Throwable;
class ScheduledTaskJob implements ShouldQueue class ScheduledTaskJob implements ShouldQueue
{ {
@ -114,6 +113,7 @@ public function handle(): void
'message' => $this->task_output ?? $e->getMessage(), 'message' => $this->task_output ?? $e->getMessage(),
]); ]);
} }
$this->team?->notify(new TaskFailed($this->task, $e->getMessage()));
// send_internal_notification('ScheduledTaskJob failed with: ' . $e->getMessage()); // send_internal_notification('ScheduledTaskJob failed with: ' . $e->getMessage());
throw $e; throw $e;
} }

View File

@ -41,7 +41,6 @@ public function handle(): void
$payload = [ $payload = [
'content' => $this->text, 'content' => $this->text,
]; ];
ray($payload);
Http::post($this->webhookUrl, $payload); Http::post($this->webhookUrl, $payload);
} }
} }

View File

@ -16,6 +16,7 @@ class Discord extends Component
'team.discord_notifications_deployments' => 'nullable|boolean', 'team.discord_notifications_deployments' => 'nullable|boolean',
'team.discord_notifications_status_changes' => 'nullable|boolean', 'team.discord_notifications_status_changes' => 'nullable|boolean',
'team.discord_notifications_database_backups' => 'nullable|boolean', 'team.discord_notifications_database_backups' => 'nullable|boolean',
'team.discord_notifications_scheduled_tasks' => 'nullable|boolean',
]; ];
protected $validationAttributes = [ protected $validationAttributes = [
'team.discord_webhook_url' => 'Discord Webhook', 'team.discord_webhook_url' => 'Discord Webhook',

View File

@ -28,6 +28,7 @@ class Email extends Component
'team.smtp_notifications_deployments' => 'nullable|boolean', 'team.smtp_notifications_deployments' => 'nullable|boolean',
'team.smtp_notifications_status_changes' => 'nullable|boolean', 'team.smtp_notifications_status_changes' => 'nullable|boolean',
'team.smtp_notifications_database_backups' => 'nullable|boolean', 'team.smtp_notifications_database_backups' => 'nullable|boolean',
'team.smtp_notifications_scheduled_tasks' => 'nullable|boolean',
'team.use_instance_email_settings' => 'boolean', 'team.use_instance_email_settings' => 'boolean',
'team.resend_enabled' => 'nullable|boolean', 'team.resend_enabled' => 'nullable|boolean',
'team.resend_api_key' => 'nullable', 'team.resend_api_key' => 'nullable',

View File

@ -18,10 +18,12 @@ class Telegram extends Component
'team.telegram_notifications_deployments' => 'nullable|boolean', 'team.telegram_notifications_deployments' => 'nullable|boolean',
'team.telegram_notifications_status_changes' => 'nullable|boolean', 'team.telegram_notifications_status_changes' => 'nullable|boolean',
'team.telegram_notifications_database_backups' => '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_test_message_thread_id' => 'nullable|string',
'team.telegram_notifications_deployments_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_status_changes_message_thread_id' => 'nullable|string',
'team.telegram_notifications_database_backups_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 = [ protected $validationAttributes = [
'team.telegram_token' => 'Token', 'team.telegram_token' => 'Token',

View File

@ -113,6 +113,18 @@ public function link()
} }
return null; 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() public function settings()
{ {
return $this->hasOne(ApplicationSetting::class); return $this->hasOne(ApplicationSetting::class);

View File

@ -653,6 +653,18 @@ public function link()
} }
return null; 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() public function documentation()
{ {
$services = getServiceTemplates(); $services = getServiceTemplates();

View File

@ -32,6 +32,9 @@ public function send($notifiable, $notification): void
case 'App\Notifications\Database\BackupFailed': case 'App\Notifications\Database\BackupFailed':
$topicId = data_get($notifiable, 'telegram_notifications_database_backups_message_thread_id'); $topicId = data_get($notifiable, 'telegram_notifications_database_backups_message_thread_id');
break; break;
case 'App\Notifications\ScheduledTask\TaskFailed':
$topicId = data_get($notifiable, 'telegram_notifications_scheduled_tasks_thread_id');
break;
} }
if (!$telegramToken || !$chatId || !$message) { if (!$telegramToken || !$chatId || !$message) {
return; return;

View File

@ -31,7 +31,7 @@ public function toMail(): MailMessage
$mail->view('emails.container-restarted', [ $mail->view('emails.container-restarted', [
'containerName' => $this->name, 'containerName' => $this->name,
'serverName' => $this->server->name, 'serverName' => $this->server->name,
'url' => $this->url , 'url' => $this->url,
]); ]);
return $mail; return $mail;
} }

View File

@ -0,0 +1,64 @@
<?php
namespace App\Notifications\ScheduledTask;
use App\Models\ScheduledTask;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class TaskFailed extends Notification implements ShouldQueue
{
use Queueable;
public $backoff = 10;
public $tries = 2;
public ?string $url = null;
public function __construct(public ScheduledTask $task, public string $output)
{
if ($task->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,
];
}
}

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('teams', function (Blueprint $table) {
$table->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');
});
}
};

View File

@ -0,0 +1,9 @@
<x-emails.layout>
Scheduled task ({{ $task->name }}) was FAILED with the following error:
<pre>
{{ $output }}
</pre>
Click [here]({{ $url }}) to view the task.
</x-emails.layout>

View File

@ -32,6 +32,8 @@
label="Application Deployments" /> label="Application Deployments" />
<x-forms.checkbox instantSave="saveModel" id="team.discord_notifications_database_backups" <x-forms.checkbox instantSave="saveModel" id="team.discord_notifications_database_backups"
label="Backup Status" /> label="Backup Status" />
<x-forms.checkbox instantSave="saveModel" id="team.discord_notifications_scheduled_tasks"
label="Scheduled Tasks Status" />
</div> </div>
@endif @endif
</div> </div>

View File

@ -111,6 +111,8 @@
label="Application Deployments" /> label="Application Deployments" />
<x-forms.checkbox instantSave="saveModel" id="team.smtp_notifications_database_backups" <x-forms.checkbox instantSave="saveModel" id="team.smtp_notifications_database_backups"
label="Backup Status" /> label="Backup Status" />
<x-forms.checkbox instantSave="saveModel" id="team.smtp_notifications_scheduled_tasks"
label="Scheduled Tasks Status" />
</div> </div>
@endif @endif
</div> </div>

View File

@ -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." 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" /> id="team.telegram_notifications_database_backups_message_thread_id" label="Custom Topic ID" />
</div> </div>
<div class="flex flex-col">
<h4>Scheduled Tasks Status</h4>
<x-forms.checkbox instantSave="saveModel" id="team.telegram_notifications_scheduled_tasks"
label="Enabled" />
<x-forms.input
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_scheduled_tasks_thread_id" label="Custom Topic ID" />
</div>
</div> </div>
@endif @endif
</form> </form>