Certificate manager built on Laravel 13, Livewire 4, and Flux. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
97 lines
3.1 KiB
PHP
97 lines
3.1 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Models\Certificate;
|
|
use App\Models\User;
|
|
use App\Notifications\CertificateExpiredNotification;
|
|
use App\Notifications\CertificateExpiringNotification;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Support\Carbon;
|
|
|
|
class SendCertificateReminders extends Command
|
|
{
|
|
protected $signature = 'app:send-certificate-reminders';
|
|
|
|
protected $description = 'Notify users about certificates that are approaching or reaching their expiry date';
|
|
|
|
public function handle(): int
|
|
{
|
|
$today = Carbon::today();
|
|
|
|
$advanceCount = $this->sendAdvanceReminders($today);
|
|
$urgentCount = $this->sendExpiryReminders($today);
|
|
|
|
$this->info("Sent {$advanceCount} advance reminder(s) and {$urgentCount} expiry reminder(s).");
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Part 1: for each user, notify about certificates expiring exactly on
|
|
* their personal "months before" threshold.
|
|
*/
|
|
private function sendAdvanceReminders(Carbon $today): int
|
|
{
|
|
$sent = 0;
|
|
|
|
User::query()
|
|
->whereHas('certificates')
|
|
->with('certificates')
|
|
->chunkById(100, function ($users) use ($today, &$sent) {
|
|
foreach ($users as $user) {
|
|
$targetDate = $today->copy()->addMonths($user->reminder_months_before);
|
|
|
|
$certificates = $user->certificates()
|
|
->whereDate('expires_at', $targetDate)
|
|
->tap($this->notRemindedToday($today))
|
|
->get();
|
|
|
|
foreach ($certificates as $certificate) {
|
|
$user->notify(new CertificateExpiringNotification($certificate));
|
|
$certificate->forceFill(['last_reminded_at' => now()])->save();
|
|
$sent++;
|
|
}
|
|
}
|
|
});
|
|
|
|
return $sent;
|
|
}
|
|
|
|
/**
|
|
* Part 2: notify about certificates expiring today (across all users).
|
|
*/
|
|
private function sendExpiryReminders(Carbon $today): int
|
|
{
|
|
$sent = 0;
|
|
|
|
Certificate::query()
|
|
->whereDate('expires_at', $today)
|
|
->tap($this->notRemindedToday($today))
|
|
->with('user')
|
|
->chunkById(100, function ($certificates) use (&$sent) {
|
|
foreach ($certificates as $certificate) {
|
|
$certificate->user?->notify(new CertificateExpiredNotification($certificate));
|
|
$certificate->forceFill(['last_reminded_at' => now()])->save();
|
|
$sent++;
|
|
}
|
|
});
|
|
|
|
return $sent;
|
|
}
|
|
|
|
/**
|
|
* Skip certificates already reminded today, so a re-run (or a missed day
|
|
* followed by a catch-up run) never double-sends.
|
|
*/
|
|
private function notRemindedToday(Carbon $today): callable
|
|
{
|
|
return function (Builder $query) use ($today) {
|
|
$query->where(function (Builder $query) use ($today) {
|
|
$query->whereNull('last_reminded_at')
|
|
->orWhereDate('last_reminded_at', '<', $today);
|
|
});
|
|
};
|
|
}
|
|
}
|