feat(notifications): deduplicate repeated email alerts

Add notification-level deduplication keys and TTLs for deployment, backup,
server, container, scheduled task, SSL, token, and transactional emails.
Apply deduplication in email channels before sending rendered messages.
This commit is contained in:
Andras Bacsai committed 2026-07-08 15:23:14 +02:00
1 parent ee620a5413
commit 8c1405e168
33 files changed
+516 -14

No files matched your search

@@ -21,6 +21,16 @@ class DockerCleanupFailed extends CustomEmailNotification
return $notifiable->getEnabledChannels('docker_cleanup_failure');
}
public function deduplicationKey(object $notifiable, string $channel): ?string
{
return "docker-cleanup-failed:server:{$this->server->uuid}:message:".hash('sha256', $this->message);
}
public function deduplicateFor(): int
{
return 21600;
}
public function toMail(): MailMessage
{
$mail = new MailMessage;
@@ -21,6 +21,16 @@ class DockerCleanupSuccess extends CustomEmailNotification
return $notifiable->getEnabledChannels('docker_cleanup_success');
}
public function deduplicationKey(object $notifiable, string $channel): ?string
{
return "docker-cleanup-success:server:{$this->server->uuid}:message:".hash('sha256', $this->message);
}
public function deduplicateFor(): int
{
return 21600;
}
public function toMail(): MailMessage
{
$mail = new MailMessage;
@@ -21,6 +21,16 @@ class ForceDisabled extends CustomEmailNotification
return $notifiable->getEnabledChannels('server_force_disabled');
}
public function deduplicationKey(object $notifiable, string $channel): ?string
{
return "server-force-disabled:{$this->server->uuid}";
}
public function deduplicateFor(): int
{
return 86400;
}
public function toMail(): MailMessage
{
$mail = new MailMessage;
+10
View File
@@ -21,6 +21,16 @@ class ForceEnabled extends CustomEmailNotification
return $notifiable->getEnabledChannels('server_force_enabled');
}
public function deduplicationKey(object $notifiable, string $channel): ?string
{
return "server-force-enabled:{$this->server->uuid}";
}
public function deduplicateFor(): int
{
return 86400;
}
public function toMail(): MailMessage
{
$mail = new MailMessage;
@@ -21,6 +21,16 @@ class HetznerDeletionFailed extends CustomEmailNotification
return $notifiable->getEnabledChannels('hetzner_deletion_failed');
}
public function deduplicationKey(object $notifiable, string $channel): ?string
{
return "hetzner-deletion-failed:{$this->hetznerServerId}:error:".hash('sha256', $this->errorMessage);
}
public function deduplicateFor(): int
{
return 86400;
}
public function toMail(): MailMessage
{
$mail = new MailMessage;
@@ -21,6 +21,16 @@ class HighDiskUsage extends CustomEmailNotification
return $notifiable->getEnabledChannels('server_disk_usage');
}
public function deduplicationKey(object $notifiable, string $channel): ?string
{
return "high-disk-usage:server:{$this->server->uuid}:threshold:{$this->server_disk_usage_notification_threshold}";
}
public function deduplicateFor(): int
{
return 21600;
}
public function toMail(): MailMessage
{
$mail = new MailMessage;
+10
View File
@@ -30,6 +30,16 @@ class Reachable extends CustomEmailNotification
return $notifiable->getEnabledChannels('server_reachable');
}
public function deduplicationKey(object $notifiable, string $channel): ?string
{
return "server-reachable:{$this->server->uuid}";
}
public function deduplicateFor(): int
{
return 1800;
}
public function toMail(): MailMessage
{
$mail = new MailMessage;
@@ -24,6 +24,16 @@ class ServerPatchCheck extends CustomEmailNotification
return $notifiable->getEnabledChannels('server_patch');
}
public function deduplicationKey(object $notifiable, string $channel): ?string
{
return "server-patch-check:server:{$this->server->uuid}:state:".hash('sha256', json_encode($this->patchData));
}
public function deduplicateFor(): int
{
return 86400;
}
public function toMail($notifiable = null): MailMessage
{
$mail = new MailMessage;
@@ -38,6 +38,18 @@ class TraefikVersionOutdated extends CustomEmailNotification
return $this->formatVersion($info['latest'] ?? 'unknown');
}
public function deduplicationKey(object $notifiable, string $channel): ?string
{
$serverUuids = $this->servers->pluck('uuid')->sort()->values()->join('|');
return 'traefik-version-outdated:servers:'.hash('sha256', $serverUuids);
}
public function deduplicateFor(): int
{
return 86400;
}
public function toMail($notifiable = null): MailMessage
{
$mail = new MailMessage;
+10
View File
@@ -30,6 +30,16 @@ class Unreachable extends CustomEmailNotification
return $notifiable->getEnabledChannels('server_unreachable');
}
public function deduplicationKey(object $notifiable, string $channel): ?string
{
return "server-unreachable:{$this->server->uuid}";
}
public function deduplicateFor(): int
{
return 3600;
}
public function toMail(): ?MailMessage
{
$mail = new MailMessage;