diff --git a/app/Jobs/ApiTokenExpirationWarningJob.php b/app/Jobs/ApiTokenExpirationWarningJob.php index f59b1a0b17..c32e52af0d 100644 --- a/app/Jobs/ApiTokenExpirationWarningJob.php +++ b/app/Jobs/ApiTokenExpirationWarningJob.php @@ -22,17 +22,19 @@ class ApiTokenExpirationWarningJob implements ShouldBeEncrypted, ShouldQueue, Si public $timeout = 120; + private const WARNING_DAYS = 7; + public function handle(): void { PersonalAccessToken::query() ->whereNotNull('expires_at') ->where('expires_at', '>', now()) - ->where('expires_at', '<=', now()->addDay()) + ->where('expires_at', '<=', now()->addDays(self::WARNING_DAYS)) ->whereNull('api_token_expiration_warning_sent_at') ->where('tokenable_type', User::class) ->chunkById(100, function ($tokens) { foreach ($tokens as $token) { - if (! $token->team_id) { + if (! $token->team_id || ! $this->isDue($token)) { continue; } @@ -53,7 +55,7 @@ class ApiTokenExpirationWarningJob implements ShouldBeEncrypted, ShouldQueue, Si ->whereKey($token->getKey()) ->whereNotNull('expires_at') ->where('expires_at', '>', now()) - ->where('expires_at', '<=', now()->addDay()) + ->where('expires_at', '<=', now()->addDays(self::WARNING_DAYS)) ->whereNull('api_token_expiration_warning_sent_at') ->update(['api_token_expiration_warning_sent_at' => $warningSentAt]); @@ -65,4 +67,17 @@ class ApiTokenExpirationWarningJob implements ShouldBeEncrypted, ShouldQueue, Si } }); } + + /** + * Warn 7 days before expiry. A token that lives 7 days or less gets the warning 24 hours before expiry, + * so it is not warned right after it is created. + */ + private function isDue(PersonalAccessToken $token): bool + { + if ($token->created_at->diffInDays($token->expires_at) > self::WARNING_DAYS) { + return true; + } + + return $token->expires_at->lte(now()->addDay()); + } } diff --git a/app/Notifications/ApiTokenExpiringNotification.php b/app/Notifications/ApiTokenExpiringNotification.php index bb89d11637..8461a81fbf 100644 --- a/app/Notifications/ApiTokenExpiringNotification.php +++ b/app/Notifications/ApiTokenExpiringNotification.php @@ -32,7 +32,7 @@ class ApiTokenExpiringNotification extends CustomEmailNotification public function toMail(): MailMessage { $mail = new MailMessage; - $mail->subject("Coolify: API token '{$this->tokenName}' expires in 24 hours"); + $mail->subject("Coolify: API token '{$this->tokenName}' expires on {$this->expiresAt}"); $mail->view('emails.api-token-expiring', [ 'tokenName' => $this->tokenName, 'expiresAt' => $this->expiresAt, diff --git a/resources/views/components/status-badge.blade.php b/resources/views/components/status-badge.blade.php index a8f49e8bf5..1f1b3089b0 100644 --- a/resources/views/components/status-badge.blade.php +++ b/resources/views/components/status-badge.blade.php @@ -5,6 +5,7 @@ 'as' => 'span', 'dynamic' => false, 'pulse' => false, // ping the dot for a state that is still in progress + 'size' => 'md', // sm fits inline next to 12px table text without growing the row ]) @php @@ -15,7 +16,12 @@ 'error' => 'bg-red-500', ]; - $baseClasses = 'inline-flex h-6 max-w-full items-center gap-1.5 whitespace-nowrap rounded-full border border-neutral-200 bg-neutral-100 px-2 text-xs font-medium leading-none text-neutral-700 dark:border-white/[0.12] dark:bg-white/[0.07] dark:text-white'; + $sizeClasses = [ + 'sm' => 'h-4 gap-1 px-1.5 text-[10px]', + 'md' => 'h-6 gap-1.5 px-2 text-xs', + ]; + + $baseClasses = 'inline-flex max-w-full items-center whitespace-nowrap rounded-full border border-neutral-200 bg-neutral-100 font-medium leading-none text-neutral-700 dark:border-white/[0.12] dark:bg-white/[0.07] dark:text-white ' . ($sizeClasses[$size] ?? $sizeClasses['md']); @endphp @if ($as === 'button') diff --git a/resources/views/livewire/security/api-tokens.blade.php b/resources/views/livewire/security/api-tokens.blade.php index 964f57068b..df909b9b07 100644 --- a/resources/views/livewire/security/api-tokens.blade.php +++ b/resources/views/livewire/security/api-tokens.blade.php @@ -234,7 +234,7 @@