Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions resources/lang/en/general.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,9 @@
'last_updated' => 'Last updated :time',
'external_link' => 'External Link',
'external_link_disclaimer' => 'You are about to leave '.config('app.name').' to an external website. '.config('app.name').' has no control over the content of this site. Are you sure you wish to continue?',

// Throttling
'amount_hours' => ':amount hour|:amount hours',
'amount_minutes' => ':amount minute|:amount minutes',
'amount_seconds' => ':amount second|:amount seconds',
];
74 changes: 74 additions & 0 deletions src/UserInterface/Components/ThrottledComponent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

declare(strict_types=1);

namespace ARKEcosystem\Foundation\UserInterface\Components;

use ARKEcosystem\Foundation\UserInterface\Components\Concerns\HandleToast;
use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException;
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
use Illuminate\Support\Facades\RateLimiter;
use Livewire\Component;

abstract class ThrottledComponent extends Component
{
use WithRateLimiting;
use HandleToast;

protected string $throttledType = 'warning';

abstract protected function getThrottlingMaxAttempts(): int;

abstract protected function getThrottlingTime(): int;

abstract protected function getThrottlingKey(): string;

abstract protected function getThrottlingMessage(string $availableIn): string;

final protected function getThrottlingAvailableIn(): string
{
$throttlingKey = $this->getThrottlingKey();

$secondsUntilAvailable = RateLimiter::availableIn($this->getRateLimitKey($throttlingKey));

$parts = explode(':', gmdate('H:i', $secondsUntilAvailable));

$hours = (int) $parts[0];
$minutes = (int) $parts[1];

if ($hours > 0) {
if ($minutes > 0) {
return sprintf(
'%s, %s',
trans_choice('ui::general.amount_hours', $hours, ['amount' => $hours]),
trans_choice('ui::general.amount_minutes', $minutes, ['amount' => $minutes]),
);
}

return trans_choice('ui::general.amount_hours', $hours, ['amount' => $hours]);
}

if ($minutes > 0) {
return trans_choice('ui::general.amount_minutes', $minutes, ['amount' => $minutes]);
}

return trans_choice('ui::general.amount_seconds', $secondsUntilAvailable, ['amount' => $secondsUntilAvailable]);
}

protected function isThrottled(): bool
{
try {
$this->rateLimit(
$this->getThrottlingMaxAttempts(),
$this->getThrottlingTime(),
$this->getThrottlingKey()
);
} catch (TooManyRequestsException) {
$this->toast($this->getThrottlingMessage($this->getThrottlingAvailableIn()), $this->throttledType);

return true;
}

return false;
}
}