page($request, 'forgot'); } public function sendResetLink(AccountEmailRequest $request): RedirectResponse { try { Password::broker('web_users')->sendResetLink($request->safe()->only('email')); } catch (Throwable $exception) { report($exception); return back()->with('error', __('Не удалось отправить письмо. Попробуй ещё раз через минуту.'))->onlyInput('email'); } return back()->with('status', __('Если аккаунт с такой почтой существует, письмо со ссылкой для восстановления придёт в течение нескольких минут. Проверь также папку «Спам».'))->onlyInput('email'); } public function reset(Request $request, string $token): Response { return $this->page($request, 'reset', ['token' => $token]); } public function updatePassword(ResetAccountPasswordRequest $request): RedirectResponse { // Keep token validation, password update and token consumption atomic. $status = DB::transaction(function () use ($request): string { User::where('email', $request->string('email')->toString())->lockForUpdate()->first(); return Password::broker('web_users')->reset( $request->safe()->only(['email', 'password', 'password_confirmation', 'token']), function (User $user, string $password): void { $user->forceFill(['password' => $password, 'remember_token' => Str::random(60)])->save(); event(new PasswordReset($user)); }, ); }); if ($status !== Password::PASSWORD_RESET) { return back()->withErrors(['token' => __('Ссылка недействительна или срок её действия истёк. Запроси новое письмо.')])->onlyInput('email'); } return redirect()->route('login')->with('status', __('Пароль обновлён. Войди в аккаунт с новым паролем.')); } public function notice(Request $request): Response { return $this->page($request, 'verification'); } public function resend(AccountEmailRequest $request, AccountEmailService $emails): RedirectResponse { $user = User::whereRaw('LOWER(email) = ?', [$request->string('email')->toString()])->first(); if ($user !== null) { $emails->sendVerification($user); } return back()->with('status', __('Если этот email ожидает подтверждения, мы отправим письмо. Повторный запрос доступен через минуту. Проверь также папку «Спам».'))->onlyInput('email'); } public function verify(Request $request, string $id, string $hash): Response { if (! $request->hasValidSignature()) { return $this->page($request, 'invalid'); } $user = User::find($id); if ($user === null || ! hash_equals(sha1($user->getEmailForVerification()), $hash)) { return $this->page($request, 'invalid'); } if (! $user->hasVerifiedEmail() && $user->markEmailAsVerified()) { event(new Verified($user)); } return $this->page($request, 'verified'); } private function page(Request $request, string $mode, array $extra = []): Response { $email = $mode === 'reset' ? $request->query('email', '') : $request->old('email', $mode === 'verification' ? $request->session()->get('verification_email', '') : ''); return Inertia::render('Auth/AccountAccess', [ 'site' => [ 'name' => config('moonwell.realm.server_name'), 'tagline' => config('moonwell.realm.tagline'), ], 'auth' => [ 'authenticated' => $request->user() !== null, 'username' => $request->user()?->username, 'access_label' => $request->user()?->accessLabel(), 'is_admin' => $request->user()?->canAccessAdminPanel() ?? false, ], 'urls' => [ 'home' => route('landing.index'), 'login' => route('login'), 'cabinet' => route('cabinet.index'), 'logout' => route('logout'), 'forgot' => route('password.request'), 'send_reset' => route('password.email'), 'reset' => route('password.update'), 'verification' => route('verification.notice'), 'resend' => route('verification.send'), 'offer' => config('moonwell.legal.offer_published') ? route('legal.offer') : null, 'privacy' => config('moonwell.legal.privacy_published') ? route('legal.privacy') : null, 'rules' => config('moonwell.legal.rules_published') ? route('legal.rules') : null, ], 'flash' => [ 'status' => $request->session()->get('status'), 'error' => $request->session()->get('error'), ], 'mode' => $mode, 'email' => is_string($email) ? $email : '', ...$extra, ]); } }