133 lines
4.5 KiB
PHP
133 lines
4.5 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Auth;
|
|
|
|
use App\Models\Customer;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\RateLimiter;
|
|
use Livewire\Attributes\Layout;
|
|
use Livewire\Attributes\On;
|
|
use Livewire\Component;
|
|
|
|
/**
|
|
* Where somebody waits between registering and confirming.
|
|
*
|
|
* Signed in but not through: the account exists, and nothing in the portal
|
|
* opens until the address is confirmed. The page offers the two things that can
|
|
* move somebody forward — send it again, or throw the account away and start
|
|
* over with the address they meant.
|
|
*
|
|
* "Abmelden und neu registrieren" used to be the second one, and it was wrong
|
|
* twice: signing out does not free the address, so registering again with the
|
|
* same one fails — and every abandoned attempt stays in `users` for ever. The
|
|
* button deletes the account instead, and anything nobody comes back to is
|
|
* removed after PruneUnverifiedAccounts::AFTER_DAYS days.
|
|
*
|
|
* Fortify would own this page, but 'views' => false: the app renders its own
|
|
* screens (R1/R2) and Fortify keeps only the POST actions.
|
|
*/
|
|
#[Layout('layouts.portal')]
|
|
class VerifyEmail extends Component
|
|
{
|
|
/**
|
|
* Six a minute is generous for a person and useless for anything else.
|
|
* Without it this is a button that makes the server send mail to an
|
|
* arbitrary address as fast as it can be clicked.
|
|
*/
|
|
private const PER_MINUTE = 6;
|
|
|
|
/**
|
|
* Somebody already through has no step left, and telling them they do is
|
|
* how a confirmed account goes looking for a mail it does not need. The
|
|
* `verified` middleware only guards the other direction — it keeps the
|
|
* unconfirmed out of the portal and has nothing to say about this page.
|
|
*/
|
|
public function mount()
|
|
{
|
|
if (Auth::user()?->hasVerifiedEmail()) {
|
|
return $this->redirectRoute('dashboard', navigate: false);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function resend(): void
|
|
{
|
|
$user = Auth::user();
|
|
|
|
// Already through — a stale tab, or the link opened in another window.
|
|
// Sending another confirmation for an address that no longer needs one
|
|
// is how somebody ends up chasing a mail they do not need.
|
|
if ($user === null || $user->hasVerifiedEmail()) {
|
|
$this->redirectRoute('dashboard', navigate: true);
|
|
|
|
return;
|
|
}
|
|
|
|
$key = 'verify-resend:'.$user->getKey();
|
|
|
|
if (RateLimiter::tooManyAttempts($key, self::PER_MINUTE)) {
|
|
$this->dispatch('notify', message: __('verify_email.notice_hint'));
|
|
|
|
return;
|
|
}
|
|
|
|
RateLimiter::hit($key, 60);
|
|
$user->sendEmailVerificationNotification();
|
|
|
|
$this->dispatch('notify', message: __('verify_email.notice_sent'));
|
|
}
|
|
|
|
/**
|
|
* Throw this registration away.
|
|
*
|
|
* Only an UNCONFIRMED account, and only one nothing is attached to. The
|
|
* webhook creates a customer's login from a paid checkout, and such a user
|
|
* can sit here unconfirmed while already having a contract — deleting that
|
|
* would be data loss dressed up as a convenience. It refuses and says where
|
|
* to go instead.
|
|
*
|
|
* R23: the confirmation is a modal, not a browser dialog. This method is
|
|
* what the modal's event reaches, and it re-checks everything itself
|
|
* because a modal is reachable without this page's own guards.
|
|
*/
|
|
#[On('own-account-delete-confirmed')]
|
|
public function deleteAccount()
|
|
{
|
|
$user = Auth::user();
|
|
|
|
if ($user === null) {
|
|
return $this->redirectRoute('login', navigate: false);
|
|
}
|
|
|
|
if ($user->hasVerifiedEmail()) {
|
|
// Confirmed accounts are closed, not deleted — that is a different
|
|
// action, with an invoice history behind it (ConfirmCloseAccount).
|
|
return $this->redirectRoute('dashboard', navigate: false);
|
|
}
|
|
|
|
if (Customer::query()->where('user_id', $user->getKey())->exists()
|
|
|| Customer::query()->where('email', $user->email)->exists()) {
|
|
$this->dispatch('notify', message: __('verify_email.delete_has_contract'));
|
|
|
|
return null;
|
|
}
|
|
|
|
Auth::guard('web')->logout();
|
|
$user->delete();
|
|
|
|
session()->invalidate();
|
|
session()->regenerateToken();
|
|
session()->flash('status', __('verify_email.deleted'));
|
|
|
|
return $this->redirectRoute('register', navigate: false);
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
return view('livewire.auth.verify-email', [
|
|
'email' => Auth::user()?->getEmailForVerification() ?? '',
|
|
]);
|
|
}
|
|
}
|