From 76584651a6c8cb89bd966d62f402f510093a12f3 Mon Sep 17 00:00:00 2001 From: ghassan Date: Wed, 21 Oct 2026 00:36:45 +0300 Subject: [PATCH] feat(cli): app:resend-verification command Resend the verification link to one user (--user=) or every unverified user (--all-unverified), with a --dry-run preview. Uses User::sendEmailVerificationNotification() which is already Throwable- guarded, so SMTP hiccups during a bulk flush log instead of aborting. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Commands/ResendVerificationCommand.php | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 app/Console/Commands/ResendVerificationCommand.php diff --git a/app/Console/Commands/ResendVerificationCommand.php b/app/Console/Commands/ResendVerificationCommand.php new file mode 100644 index 0000000..6b3fe17 --- /dev/null +++ b/app/Console/Commands/ResendVerificationCommand.php @@ -0,0 +1,74 @@ +option('user'); + $all = (bool) $this->option('all-unverified'); + $dryRun = (bool) $this->option('dry-run'); + + if ($one === '' && ! $all) { + $this->error('Pass either --user= or --all-unverified.'); + return self::FAILURE; + } + + if ($one !== '') { + $query = ctype_digit($one) + ? User::where('id', (int) $one) + : User::where('email', $one); + $users = $query->get(); + } else { + $users = User::whereNull('email_verified_at')->get(); + } + + if ($users->isEmpty()) { + $this->warn('No matching users.'); + return self::SUCCESS; + } + + $sent = 0; + $skipped = 0; + foreach ($users as $user) { + if ($user->hasVerifiedEmail()) { + $skipped++; + $this->line(" skip #{$user->id} <{$user->email}> — already verified"); + continue; + } + if ($dryRun) { + $this->line(" would #{$user->id} <{$user->email}>"); + continue; + } + // sendEmailVerificationNotification is already SafeNotifier-wrapped + // and will log instead of throwing on SMTP errors. + $user->sendEmailVerificationNotification(); + $sent++; + $this->line(" sent #{$user->id} <{$user->email}>"); + } + + $this->info($dryRun + ? "Dry run complete — {$users->count()} candidates, {$skipped} already verified." + : "Done — {$sent} sent, {$skipped} skipped."); + + return self::SUCCESS; + } +}