From a1be6ff62bead1903e3f0c27d0e969a77454780e Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 7 Apr 2024 16:12:41 +0200 Subject: [PATCH] Limit the number of error messages Firefly III will send. --- app/Jobs/MailError.php | 67 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/app/Jobs/MailError.php b/app/Jobs/MailError.php index c06a617d03..4e170923a7 100644 --- a/app/Jobs/MailError.php +++ b/app/Jobs/MailError.php @@ -27,6 +27,7 @@ use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Message; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\Log; use Symfony\Component\Mailer\Exception\TransportException; /** @@ -68,6 +69,14 @@ class MailError extends Job implements ShouldQueue $args['user'] = $this->userData; $args['ip'] = $this->ipAddress; $args['token'] = config('firefly.ipinfo_token'); + + // limit number of error mails that can be sent. + if ($this->reachedLimit()) { + Log::info('MailError: reached limit, not sending email.'); + return; + } + + if ($this->attempts() < 3 && '' !== $email) { try { \Mail::send( @@ -79,7 +88,7 @@ class MailError extends Job implements ShouldQueue } } ); - } catch (\Exception|TransportException $e) { // @phpstan-ignore-line + } catch (\Exception | TransportException $e) { // @phpstan-ignore-line $message = $e->getMessage(); if (str_contains($message, 'Bcc')) { app('log')->warning('[Bcc] Could not email or log the error. Please validate your email settings, use the .env.example file as a guide.'); @@ -96,4 +105,60 @@ class MailError extends Job implements ShouldQueue } } } + + /** + * @return bool + */ + private function reachedLimit(): bool + { + Log::debug('reachedLimit()'); + $types = [ + '5m' => ['limit' => 5, 'reset' => 5 * 60], + '1h' => ['limit' => 15, 'reset' => 60 * 60], + '24h' => ['limit' => 15, 'reset' => 24 * 60 * 60], + ]; + $file = storage_path('framework/cache/error-count.json'); + $limits = []; + if(!is_writable($file)) { + Log::error(sprintf('MailError: cannot write to "%s", cannot rate limit errors!', $file)); + return false; + } + if (!file_exists($file)) { + Log::debug(sprintf('Wrote new file in "%s"', $file)); + file_put_contents($file, json_encode($limits, JSON_PRETTY_PRINT)); + } + if (file_exists($file)) { + Log::debug(sprintf('Read file in "%s"', $file)); + $limits = json_decode(file_get_contents($file), true); + } + // limit reached? + foreach ($types as $type => $info) { + Log::debug(sprintf('Now checking limit "%s"', $type), $info); + if (!isset($limits[$type])) { + Log::debug(sprintf('Limit "%s" reset to zero, did not exist yet.', $type)); + $limits[$type] = [ + 'time' => time(), + 'sent' => 0, + ]; + } + + if (time() - $limits[$type]['time'] > $info['reset']) { + Log::debug(sprintf('Time past for this limit is %d seconds, exceeding %d seconds. Reset to zero.', (time() - $limits[$type]['time']), $info['reset'])); + $limits[$type] = [ + 'time' => time(), + 'sent' => 0, + ]; + } + + if ($limits[$type]['sent'] > $info['limit']) { + Log::warning(sprintf('Sent %d emails in %s, return true.', $limits[$type]['sent'], $type)); + return true; + } + $limits[$type]['sent']++; + } + file_put_contents($file, json_encode($limits, JSON_PRETTY_PRINT)); + Log::debug('No limits reached, return FALSE.'); + return false; + + } }