Commit e6d602adb39e0ab8962a402881288a927758b036
1 parent
898ae29b23
Exists in
master
reset password
Showing 9 changed files with 216 additions and 47 deletions Side-by-side Diff
- app/Http/Controllers/MainController.php
- resources/views/emails/RepairPassword.blade.php
- resources/views/layout/frontend.blade.php
- resources/views/modals/repair_password.blade.php
- resources/views/modals/reset_password.blade.php
- resources/views/modals/send_login.blade.php
- resources/views/modals/successful_repair_password_sent.blade.php
- resources/views/modals/successful_reset_password.blade.php
- routes/web.php
app/Http/Controllers/MainController.php
... | ... | @@ -19,6 +19,7 @@ use App\Models\Positions; |
19 | 19 | use App\Models\reclame; |
20 | 20 | use App\Models\User; |
21 | 21 | use Illuminate\Http\Request; |
22 | +use Illuminate\Support\Carbon; | |
22 | 23 | use Illuminate\Support\Facades\Auth; |
23 | 24 | use Illuminate\Support\Facades\DB; |
24 | 25 | use Illuminate\Support\Facades\Hash; |
... | ... | @@ -26,6 +27,7 @@ use Illuminate\Support\Facades\Mail; |
26 | 27 | use Illuminate\Support\Facades\Validator; |
27 | 28 | use App\Models\PageContent; |
28 | 29 | use App\Enums\MainPageCounters; |
30 | +use Illuminate\Support\Str; | |
29 | 31 | |
30 | 32 | class MainController extends Controller |
31 | 33 | { |
... | ... | @@ -372,7 +374,8 @@ class MainController extends Controller |
372 | 374 | ], 422); |
373 | 375 | } else { |
374 | 376 | try { |
375 | - $user = User::query()->where('email', $request->get('email'))->first(); | |
377 | + $email = $request->input('email'); | |
378 | + $user = User::query()->where('email', $email)->first(); | |
376 | 379 | if (!$user) { |
377 | 380 | return response()->json([ |
378 | 381 | 'errors' => [ |
... | ... | @@ -383,22 +386,37 @@ class MainController extends Controller |
383 | 386 | ], 422); |
384 | 387 | } |
385 | 388 | |
386 | - $new_password = Tools::generator_id(10); | |
387 | - $hash_password = Hash::make($new_password); | |
388 | - | |
389 | - $user->update([ | |
390 | - 'password' => $hash_password, | |
391 | - 'pubpassword' => base64_encode($new_password) | |
392 | - ]); | |
393 | - | |
394 | - Mail::to($user)->send(new MailRepair($new_password)); | |
389 | + $token = Str::random(60); | |
390 | + DB::table('password_resets')->updateOrInsert( | |
391 | + [ | |
392 | + 'email' => $email, | |
393 | + ], | |
394 | + [ | |
395 | + 'email' => $email, | |
396 | + 'token' => $token, | |
397 | + 'created_at' => Carbon::now() | |
398 | + ] | |
399 | + ); | |
400 | + | |
401 | + $link = url('/') . '?' . http_build_query([ | |
402 | + 'token' => $token, | |
403 | + 'email' => $email, | |
404 | + ]) . '#reset'; | |
405 | + | |
406 | + Mail::send('emails.RepairPassword', | |
407 | + ['link' => $link], | |
408 | + function($message) use ($email) { | |
409 | + $message->to($email); | |
410 | + $message->subject('Repair password'); | |
411 | + } | |
412 | + ); | |
395 | 413 | |
396 | 414 | return response()->json([ |
397 | 415 | 'success' => true |
398 | 416 | ], 200); |
399 | 417 | } catch (\Exception $exception) { |
400 | 418 | return response()->json([ |
401 | - 'success' => true | |
419 | + 'success' => false | |
402 | 420 | ], 400); |
403 | 421 | } |
404 | 422 | |
... | ... | @@ -406,6 +424,62 @@ class MainController extends Controller |
406 | 424 | |
407 | 425 | } |
408 | 426 | |
427 | + public function reset_password(Request $request) { | |
428 | + $rules = [ | |
429 | + 'token' => ['required', 'string', 'max:255'], | |
430 | + 'email' => ['required', 'email', 'max:255'], | |
431 | + 'password' => ['required', 'string', 'min:6'], | |
432 | + 'password_confirmation' => ['required', 'same:password'], | |
433 | + ]; | |
434 | + | |
435 | + $messages = [ | |
436 | + 'required' => 'Укажите обязательное поле', | |
437 | + 'email' => 'Неорректный email', | |
438 | + 'same' => 'Пароль и подтверждение пароля не совпадают', | |
439 | + 'min' => [ | |
440 | + 'string' => 'Поле должно быть не менее :min символов', | |
441 | + ], | |
442 | + ]; | |
443 | + | |
444 | + $validator = Validator::make($request->all(), $rules, $messages); | |
445 | + | |
446 | + if ($validator->fails()) { | |
447 | + return response()->json(['ERRORS' => $validator->errors()], 422); | |
448 | + } | |
449 | + | |
450 | + $passwordReset = DB::table('password_resets') | |
451 | + ->where('email', $request->input('email')) | |
452 | + ->where('token', $request->input('token')) | |
453 | + ->first(); | |
454 | + | |
455 | + if (!$passwordReset) { | |
456 | + return response()->json([ | |
457 | + 'success' => false, | |
458 | + 'errors' => [ | |
459 | + 'token' => ['Неверный токен'], | |
460 | + ], | |
461 | + ], 400); | |
462 | + } | |
463 | + | |
464 | + if (Carbon::parse($passwordReset->created_at)->addHours(1)->isPast()) { | |
465 | + return response()->json([ | |
466 | + 'success' => false, | |
467 | + 'errors' => [ | |
468 | + 'token' => ['Токен устарел'], | |
469 | + ], | |
470 | + ], 400); | |
471 | + } | |
472 | + | |
473 | + User::where('email', $request->input('email'))->update([ | |
474 | + 'password' => Hash::make($request->input('password')), | |
475 | + 'pubpassword' => base64_encode($request->input('password')), | |
476 | + ]); | |
477 | + | |
478 | + DB::table('password_resets')->where('email', $request->input('email'))->delete(); | |
479 | + | |
480 | + return response()->json(['success' => true], 200); | |
481 | + } | |
482 | + | |
409 | 483 | // Вывод новостей |
410 | 484 | public function news(Request $request) { |
411 | 485 | $Query = News::query(); |
resources/views/emails/RepairPassword.blade.php
... | ... | @@ -12,15 +12,15 @@ |
12 | 12 | <img src="{{asset('images/emails/20.png')}}" alt="" style="width: 182px; height: 54px; display: block; margin: 0 auto;"> |
13 | 13 | </a> |
14 | 14 | <b style="font-size: 24px; line-height: 32px; display: block; margin: 32px 0;"> |
15 | - Ваш новый пароль. | |
15 | + Ссылка на воостановление пароля. | |
16 | 16 | </b> |
17 | 17 | <b style="font-size: 24px; line-height: 32px; display: block; margin: 32px 0;"> |
18 | - {{$password}} | |
18 | + <a href="{{$link}}" target="_blank" | |
19 | + style="word-wrap: break-word; word-break: break-all; color: #1a55a6; font-size: 16px; line-height: 24px; display: inline-block; text-align: left; max-width: 90%; margin: 0 auto;"> | |
20 | + {{$link}} | |
21 | + </a> | |
22 | + | |
19 | 23 | </b> |
20 | - <a href="{{ config('app.url') }}" target="_blank" | |
21 | - style="word-wrap: break-word; word-break: break-all; color: #1a55a6; font-size: 16px; line-height: 24px; display: inline-block; text-align: left; max-width: 90%; margin: 0 auto;"> | |
22 | - {{ config('app.url') . '#sign' }} | |
23 | - </a> | |
24 | 24 | <div style="text-align:center;margin-top: 32px;font-size: 0;"> |
25 | 25 | <a href="https://t.me/rekamore_su" target="_blank" |
26 | 26 | style="background: #20A0E1;font-size: 16px;font-weight: 700;margin:0 16px 32px 16px;border-radius: 8px;height: 44px;line-height: 44px;text-decoration: none;color:#fff;display: inline-block;vertical-align: top;padding: 0 32px;"> |
resources/views/layout/frontend.blade.php
resources/views/modals/repair_password.blade.php
... | ... | @@ -0,0 +1,60 @@ |
1 | +<div id="repair-password" class="modal"> | |
2 | + <div class="modal__body"> | |
3 | + <div class="modal__title left">Сбросить пароль</div> | |
4 | + <div class="modal__text left">Пожалуйста, введите адрес электронной почты</div> | |
5 | + <form id="repair-password-form" class="modal__sign" action="{{ route('repair_password') }}"> | |
6 | + <div class="modal__sign-item"> | |
7 | + <input type="text" class="input" name="email" id="email" | |
8 | + placeholder="Введите свой email для восстановления" required> | |
9 | + <div id="email-error" class="error-message"></div> | |
10 | + </div> | |
11 | + | |
12 | + <div class="modal__sign-item"> | |
13 | + <button id="repair-password-btn" type="submit" class="button">Отправить</button> | |
14 | + </div> | |
15 | + </form> | |
16 | + <div class="modal__text"> | |
17 | + <span>Вспомнили пароль?</span> | |
18 | + | |
19 | + <a data-fancybox data-src="#sign" data-options='{"touch":false,"autoFocus":false}'>Войти</a> | |
20 | + </div> | |
21 | + </div> | |
22 | +</div> | |
23 | +@include('modals.successful_repair_password_sent') | |
24 | +<script> | |
25 | + $(document).on('click', '#repair-password-btn', function (e) { | |
26 | + e.preventDefault(); | |
27 | + const btnElm = $(this) | |
28 | + const form = $('#repair-password-form'); | |
29 | + $.ajax({ | |
30 | + url: form.attr('action'), | |
31 | + method: form.attr('method'), | |
32 | + data: form.serialize(), | |
33 | + beforeSend() { | |
34 | + btnElm.attr('disabled', true) | |
35 | + }, | |
36 | + success: function (response) { | |
37 | + btnElm.attr('disabled', false) | |
38 | + $.fancybox.close(true); | |
39 | + $.fancybox.open({ | |
40 | + src: '#repair-password-sent', | |
41 | + type: 'inline', | |
42 | + opts: {touch: false} | |
43 | + }); | |
44 | + }, | |
45 | + error: function (jqXHR) { | |
46 | + btnElm.attr('disabled', false) | |
47 | + if (jqXHR.status === 422) { | |
48 | + let errors = jqXHR.responseJSON.errors; | |
49 | + for (let field in errors) { | |
50 | + if (errors.hasOwnProperty(field)) { | |
51 | + $(`#${field}-error`).text(errors[field][0]); | |
52 | + } | |
53 | + } | |
54 | + } else { | |
55 | + alert('Произошла ошибка. Попробуйте снова.'); | |
56 | + } | |
57 | + } | |
58 | + }); | |
59 | + }) | |
60 | +</script> |
resources/views/modals/reset_password.blade.php
1 | -<div id="reset" class="modal"> | |
1 | +<div id="reset-password" class="modal"> | |
2 | 2 | <div class="modal__body"> |
3 | 3 | <div class="modal__title left">Сбросить пароль</div> |
4 | - <div class="modal__text left">Пожалуйста, введите имя пользователя или адрес электронной почты</div> | |
5 | - <form id="reset-password-form" class="modal__sign" action="{{ route('repair_password') }}"> | |
6 | - <div class="modal__sign-item"> | |
7 | - <input type="text" class="input" name="email" id="email" placeholder="Введите свой email для восстановления" required> | |
8 | - <div id="email-error" class="error-message"></div> | |
4 | + <div class="modal__text left">Введите новый пароль</div> | |
5 | + <form id="reset-password-form" class="modal__sign" action="{{ route('reset_password') }}"> | |
6 | + <input type="hidden" name="email" value="{{request()->get('email')}}"> | |
7 | + <input type="hidden" name="token" value="{{request()->get('token')}}"> | |
8 | + | |
9 | + <div class="modal__reg-item form-group"> | |
10 | + <label class="form-group__label">Пароль *</label> | |
11 | + <div class="form-group__item"> | |
12 | + <input type="password" name="password" class="input" | |
13 | + placeholder="**********"> | |
14 | + <button type="button" class="eye"> | |
15 | + <svg class="js-password-show"> | |
16 | + <use xlink:href="{{ asset('images/sprite.svg#eye') }}"></use> | |
17 | + </svg> | |
18 | + <svg class="js-password-hide"> | |
19 | + <use xlink:href="{{ asset('images/sprite.svg#eye-2') }}"></use> | |
20 | + </svg> | |
21 | + </button> | |
22 | + <span id="reset_password_error" class="employer_error-message"></span> | |
23 | + </div> | |
24 | + </div> | |
25 | + <div class="modal__reg-item form-group"> | |
26 | + <label class="form-group__label">Подтвердить пароль *</label> | |
27 | + <div class="form-group__item"> | |
28 | + <input type="password" name="password_confirmation" | |
29 | + class="input" placeholder="**********"> | |
30 | + <button type="button" class="eye"> | |
31 | + <svg class="js-password-show"> | |
32 | + <use xlink:href="{{ asset('images/sprite.svg#eye') }}"></use> | |
33 | + </svg> | |
34 | + <svg class="js-password-hide"> | |
35 | + <use xlink:href="{{ asset('images/sprite.svg#eye-2') }}"></use> | |
36 | + </svg> | |
37 | + </button> | |
38 | + </div> | |
39 | + <span id="reset_password_confirmation_error" class="employer_error-message"></span> | |
9 | 40 | </div> |
41 | + | |
10 | 42 | <div class="modal__sign-item"> |
11 | - <button id="reset-password-btn" type="submit" class="button">Получить новый пароль</button> | |
43 | + <button id="reset-password-btn" type="submit" class="button">Сохранить</button> | |
12 | 44 | </div> |
13 | 45 | </form> |
14 | - <div class="modal__text"> | |
15 | - <span>Вспомнили пароль?</span> | |
16 | - | |
17 | - <a data-fancybox data-src="#sign" data-options='{"touch":false,"autoFocus":false}'>Войти</a> | |
18 | - </div> | |
19 | 46 | </div> |
20 | 47 | </div> |
21 | 48 | @include('modals.successful_reset_password') |
... | ... | @@ -35,10 +62,13 @@ |
35 | 62 | btnElm.attr('disabled', false) |
36 | 63 | $.fancybox.close(true); |
37 | 64 | $.fancybox.open({ |
38 | - src: '#reset-password-sent', | |
65 | + src: '#reset-password-success', | |
39 | 66 | type: 'inline', |
40 | 67 | opts: {touch: false} |
41 | 68 | }); |
69 | + | |
70 | + const url = window.location.origin + window.location.pathname; | |
71 | + window.history.replaceState({}, document.title, url); | |
42 | 72 | }, |
43 | 73 | error: function (jqXHR) { |
44 | 74 | btnElm.attr('disabled', false) |
... | ... | @@ -46,7 +76,7 @@ |
46 | 76 | let errors = jqXHR.responseJSON.errors; |
47 | 77 | for (let field in errors) { |
48 | 78 | if (errors.hasOwnProperty(field)) { |
49 | - $(`#${field}-error`).text(errors[field][0]); | |
79 | + $(`#reset_${field}_error`).text(errors[field][0]); | |
50 | 80 | } |
51 | 81 | } |
52 | 82 | } else { |
... | ... | @@ -54,5 +84,15 @@ |
54 | 84 | } |
55 | 85 | } |
56 | 86 | }); |
87 | + }).ready(function () { | |
88 | + if (window.location.hash === '#reset') { | |
89 | + $.fancybox.open({ | |
90 | + src: '#reset-password', | |
91 | + type: 'inline', | |
92 | + opts: { | |
93 | + touch: false | |
94 | + } | |
95 | + }); | |
96 | + } | |
57 | 97 | }) |
58 | 98 | </script> |
resources/views/modals/send_login.blade.php
... | ... | @@ -36,7 +36,7 @@ |
36 | 36 | </label> |
37 | 37 | </div> |
38 | 38 | <div> |
39 | - <a data-fancybox data-src="#reset" data-options='{"touch":false,"autoFocus":false}' class="modal__sign-bottom-link">Забыли пароль?</a> | |
39 | + <a data-fancybox data-src="#repair-password" data-options='{"touch":false,"autoFocus":false}' class="modal__sign-bottom-link">Забыли пароль?</a> | |
40 | 40 | </div> |
41 | 41 | </div> |
42 | 42 | </div> |
... | ... | @@ -51,16 +51,3 @@ |
51 | 51 | </div> |
52 | 52 | </div> |
53 | 53 | </div> |
54 | -<script> | |
55 | - $(document).ready(function () { | |
56 | - if (window.location.hash === '#sign') { | |
57 | - $.fancybox.open({ | |
58 | - src: '#sign', | |
59 | - type: 'inline', | |
60 | - opts: { | |
61 | - touch: false | |
62 | - } | |
63 | - }); | |
64 | - } | |
65 | - }); | |
66 | -</script> |
resources/views/modals/successful_repair_password_sent.blade.php
... | ... | @@ -0,0 +1,6 @@ |
1 | +<div id="repair-password-sent" class="modal modal_bg" style="display: none;"> | |
2 | + <div class="modal__body"> | |
3 | + <div class="modal__title">Успешно!</div> | |
4 | + <div class="modal__text">На вашу электронную почту отправлено ссылка на воостановление пароля.</div> | |
5 | + </div> | |
6 | +</div> |
resources/views/modals/successful_reset_password.blade.php
1 | -<div id="reset-password-sent" class="modal modal_bg" style="display: none;"> | |
1 | +<div id="reset-password-success" class="modal modal_bg" style="display: none;"> | |
2 | 2 | <div class="modal__body"> |
3 | 3 | <div class="modal__title">Успешно!</div> |
4 | - <div class="modal__text">На вашу электронную почту отправлено новый пароль.</div> | |
4 | + <div class="modal__text">Ваш пароль успешно воостановлена.</div> | |
5 | 5 | </div> |
6 | 6 | </div> |
routes/web.php
... | ... | @@ -446,6 +446,7 @@ Route::post('register_employer', [EmployerController::class, 'register_employer' |
446 | 446 | |
447 | 447 | //восстановление пароля |
448 | 448 | Route::get('repair-password', [MainController::class, 'repair_password'])->name('repair_password'); |
449 | +Route::get('reset-password', [MainController::class, 'reset_password'])->name('reset_password'); | |
449 | 450 | // Звезда сообщения |
450 | 451 | Route::post('stars-answer', [WorkerController::class, 'stars_answer'])->name('stars_answer'); |
451 | 452 |