mirror of
https://github.com/devcode-it/openstamanager.git
synced 2025-02-23 06:47:40 +01:00
feat: ✨ Aggiunto reset password
This commit is contained in:
parent
3020cf0293
commit
f8bab49852
@ -2,9 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Auth\Events\PasswordReset;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Support\Facades\Password;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
use JetBrains\PhpStorm\ArrayShape;
|
use JetBrains\PhpStorm\ArrayShape;
|
||||||
|
|
||||||
@ -39,21 +44,41 @@ class AuthController extends Controller
|
|||||||
], Response::HTTP_BAD_REQUEST);
|
], Response::HTTP_BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[ArrayShape(['username' => 'string', 'password' => 'string', 'remember' => 'string'])]
|
public function forgotPassword(Request $request): Response|JsonResponse
|
||||||
private function rules(Request $request): array
|
|
||||||
{
|
{
|
||||||
$additional_validation = '';
|
$request->validate([
|
||||||
$db_field = 'username';
|
'email' => 'required|email|exists:users,email',
|
||||||
if (filter_var($request->input('username'), FILTER_VALIDATE_EMAIL)) {
|
]);
|
||||||
$additional_validation = '|email';
|
|
||||||
$db_field = 'email';
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
$response = Password::broker()->sendResetLink($request->input('email'));
|
||||||
'username' => "required|string|exists:users,$db_field|$additional_validation",
|
|
||||||
'password' => 'required|string',
|
return $response === Password::RESET_LINK_SENT
|
||||||
'remember' => 'boolean',
|
? response()->noContent()
|
||||||
];
|
: \response()->json(['errors' => ['email' => [__($response)]]], Response::HTTP_BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resetPassword(Request $request): JsonResponse|Response
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'token' => 'required|string',
|
||||||
|
'email' => 'required|email|exists:users,email',
|
||||||
|
'password' => ['required|string|confirmed', \Illuminate\Validation\Rules\Password::defaults()],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = Password::broker()->reset(
|
||||||
|
$request->only(['email', 'password', 'password_confirmation', 'token']),
|
||||||
|
function (User $user, string $password) {
|
||||||
|
$user->password = Hash::make($password);
|
||||||
|
$user->setRememberToken(Str::random(60));
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
event(new PasswordReset($user));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return $response === Password::PASSWORD_RESET
|
||||||
|
? response()->noContent()
|
||||||
|
: response()->json(['errors' => ['email' => [__($response)]]], Response::HTTP_BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,4 +95,21 @@ class AuthController extends Controller
|
|||||||
|
|
||||||
return response()->noContent();
|
return response()->noContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[ArrayShape(['username' => 'string', 'password' => 'string', 'remember' => 'string'])]
|
||||||
|
private function rules(Request $request): array
|
||||||
|
{
|
||||||
|
$additional_validation = '';
|
||||||
|
$db_field = 'username';
|
||||||
|
if (filter_var($request->input('username'), FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$additional_validation = '|email';
|
||||||
|
$db_field = 'email';
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'username' => "required|string|exists:users,$db_field|$additional_validation",
|
||||||
|
'password' => 'required|string',
|
||||||
|
'remember' => 'boolean',
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ class User extends Authenticatable
|
|||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'name',
|
'username',
|
||||||
'email',
|
'email',
|
||||||
'password',
|
'password',
|
||||||
];
|
];
|
||||||
|
@ -21,6 +21,7 @@ import {
|
|||||||
|
|
||||||
export default class LoginPage extends Page {
|
export default class LoginPage extends Page {
|
||||||
loading: Cash;
|
loading: Cash;
|
||||||
|
forgotPasswordLoading: Cash;
|
||||||
|
|
||||||
view(vnode) {
|
view(vnode) {
|
||||||
return (
|
return (
|
||||||
@ -46,9 +47,14 @@ export default class LoginPage extends Page {
|
|||||||
style="float: right;"
|
style="float: right;"
|
||||||
onclick={this.onLoginButtonClicked.bind(this)}
|
onclick={this.onLoginButtonClicked.bind(this)}
|
||||||
/>
|
/>
|
||||||
<mwc-button dense label="Password dimenticata" style="margin-top: 16px;">
|
<LoadingButton
|
||||||
<Mdi icon="lock-question" slot="icon"/>
|
dense
|
||||||
</mwc-button>
|
id="forgot-password-button"
|
||||||
|
label="Password dimenticata"
|
||||||
|
icon="lock-question"
|
||||||
|
style="margin-top: 16px;"
|
||||||
|
onclick={this.onForgotPasswordButtonClicked.bind(this)}
|
||||||
|
/>
|
||||||
</form>
|
</form>
|
||||||
</mwc-card>
|
</mwc-card>
|
||||||
);
|
);
|
||||||
@ -58,6 +64,7 @@ export default class LoginPage extends Page {
|
|||||||
super.oncreate(vnode);
|
super.oncreate(vnode);
|
||||||
|
|
||||||
this.loading = $(this.element).find('#login-button mwc-circular-progress');
|
this.loading = $(this.element).find('#login-button mwc-circular-progress');
|
||||||
|
this.forgotPasswordLoading = $(this.element).find('#forgot-password-button mwc-circular-progress');
|
||||||
}
|
}
|
||||||
|
|
||||||
async onLoginButtonClicked(event: PointerEvent) {
|
async onLoginButtonClicked(event: PointerEvent) {
|
||||||
@ -81,6 +88,7 @@ export default class LoginPage extends Page {
|
|||||||
data: formData
|
data: formData
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
// noinspection ES6MissingAwait
|
||||||
showSnackbar(Object.values(error.data.errors).join(' '), false);
|
showSnackbar(Object.values(error.data.errors).join(' '), false);
|
||||||
this.loading.hide();
|
this.loading.hide();
|
||||||
return;
|
return;
|
||||||
@ -88,4 +96,31 @@ export default class LoginPage extends Page {
|
|||||||
|
|
||||||
window.location.href = window.route('dashboard');
|
window.location.href = window.route('dashboard');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async onForgotPasswordButtonClicked() {
|
||||||
|
this.forgotPasswordLoading.show();
|
||||||
|
const field: HTMLFormElement = document.querySelector('#username');
|
||||||
|
field.type = 'email';
|
||||||
|
if (!field.reportValidity()) {
|
||||||
|
field.type = 'text';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
field.type = 'text';
|
||||||
|
|
||||||
|
try {
|
||||||
|
await redaxios.post(window.route('password.forgot'), {
|
||||||
|
email: field.value,
|
||||||
|
_token: $('meta[name="csrf-token"]').attr('content')
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// noinspection ES6MissingAwait
|
||||||
|
showSnackbar(Object.values(error.data.errors).join(' '), false);
|
||||||
|
this.loading.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// noinspection ES6MissingAwait
|
||||||
|
showSnackbar(__('La password è stata inviata alla tua email'));
|
||||||
|
this.loading.hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
103
resources/js/Views/ResetPasswordPage.jsx
Normal file
103
resources/js/Views/ResetPasswordPage.jsx
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// noinspection DuplicatedCode
|
||||||
|
|
||||||
|
import '@maicol07/mwc-card';
|
||||||
|
import '@maicol07/mwc-layout-grid';
|
||||||
|
import '../WebComponents/TextField';
|
||||||
|
|
||||||
|
import {Inertia} from '@inertiajs/inertia';
|
||||||
|
import type {TextField} from '@material/mwc-textfield';
|
||||||
|
import type {Cash} from 'cash-dom';
|
||||||
|
import redaxios from 'redaxios';
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-absolute-path
|
||||||
|
import logoUrl from '/images/logo_completo.png';
|
||||||
|
|
||||||
|
import LoadingButton from '../Components/LoadingButton.jsx';
|
||||||
|
import Mdi from '../Components/Mdi.jsx';
|
||||||
|
import Page from '../Components/Page.jsx';
|
||||||
|
import {
|
||||||
|
getFormData,
|
||||||
|
isFormValid,
|
||||||
|
showSnackbar
|
||||||
|
} from '../utils';
|
||||||
|
|
||||||
|
export default class ResetPasswordPage extends Page {
|
||||||
|
loading: Cash;
|
||||||
|
parameters: URLSearchParams;
|
||||||
|
|
||||||
|
oninit(vnode) {
|
||||||
|
super.oninit(vnode);
|
||||||
|
|
||||||
|
this.parameters = new URLSearchParams(window.location.search);
|
||||||
|
}
|
||||||
|
|
||||||
|
view(vnode) {
|
||||||
|
return (
|
||||||
|
<mwc-card outlined className="center ext-container ext-container-small">
|
||||||
|
<img src={logoUrl} className="center stretch" alt={__('OpenSTAManager')}/>
|
||||||
|
<form id="reset-password" style="padding: 16px; text-align: center;">
|
||||||
|
<h3 style="margin-top: 0;">{__('Reimposta password')}</h3>
|
||||||
|
<input hidden id="email" name="email" value={this.parameters.get('email')}/>
|
||||||
|
<input hidden id="token" name="token" value={this.parameters.get('token')}/>
|
||||||
|
<text-field label={__('Password')} id="password" name="password" required type="password">
|
||||||
|
<Mdi icon="lock-outline" slot="icon"/>
|
||||||
|
</text-field>
|
||||||
|
<text-field label={__('Conferma password')} id="password_confirm" name="password_confirm" type="password" required style="margin-top: 16px;">
|
||||||
|
<Mdi icon="repeat-variant" slot="icon"/>
|
||||||
|
</text-field>
|
||||||
|
<LoadingButton
|
||||||
|
type="submit"
|
||||||
|
raised
|
||||||
|
id="reset-password-button"
|
||||||
|
label={__('Resetta password')}
|
||||||
|
icon="lock-reset"
|
||||||
|
style="float: right;"
|
||||||
|
onclick={this.onResetPasswordButtonClicked.bind(this)}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</mwc-card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
oncreate(vnode) {
|
||||||
|
super.oncreate(vnode);
|
||||||
|
|
||||||
|
this.loading = $(this.element).find('#reset-password mwc-circular-progress');
|
||||||
|
}
|
||||||
|
|
||||||
|
async onResetPasswordButtonClicked(event: PointerEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.loading.show();
|
||||||
|
|
||||||
|
const form = $(this.element).find('#reset-password');
|
||||||
|
const password: TextField = form.find('#password').get(0);
|
||||||
|
const passwordConfirm: TextField = form.find('#password_confirm').get(0);
|
||||||
|
|
||||||
|
passwordConfirm.setCustomValidity(
|
||||||
|
password.value !== passwordConfirm.value ? __('Le password non corrispondono') : ''
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isFormValid(form)) {
|
||||||
|
this.loading.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = getFormData(form);
|
||||||
|
|
||||||
|
formData._token = $('meta[name="csrf-token"]').attr('content');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await redaxios.put(window.route('password.resetPassword'), formData);
|
||||||
|
} catch (error) {
|
||||||
|
// noinspection ES6MissingAwait
|
||||||
|
showSnackbar(Object.values(error.data.errors).join(' '), false);
|
||||||
|
this.loading.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Inertia.visit('/');
|
||||||
|
|
||||||
|
// noinspection ES6MissingAwait
|
||||||
|
showSnackbar(__('Reset della password effettuato con successo. Puoi ora accedere.'));
|
||||||
|
}
|
||||||
|
}
|
@ -37,16 +37,27 @@ Route::name('auth.')
|
|||||||
->middleware('guest')
|
->middleware('guest')
|
||||||
->group(static function () {
|
->group(static function () {
|
||||||
Route::inertia('login', 'LoginPage')
|
Route::inertia('login', 'LoginPage')
|
||||||
->name('login');
|
->name('login');
|
||||||
/*Route::inertia('password-request', '')
|
|
||||||
->name('password-request');*/
|
|
||||||
|
|
||||||
Route::post('login', [AuthController::class, 'authenticate'])
|
Route::post('login', [AuthController::class, 'authenticate'])
|
||||||
->name('authenticate');
|
->name('authenticate');
|
||||||
|
|
||||||
/*Route::post('logout', 'Auth\LoginController@logout')
|
/*Route::post('logout', 'Auth\LoginController@logout')
|
||||||
->name('auth.logout');*/
|
->name('auth.logout');*/
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Route::name('password.')
|
||||||
|
->middleware('guest')
|
||||||
|
->group(static function () {
|
||||||
|
Route::post('forgot', [AuthController::class, 'forgot'])
|
||||||
|
->name('forgot');
|
||||||
|
|
||||||
|
Route::inertia('reset', 'ResetPasswordPage')
|
||||||
|
->name('reset');
|
||||||
|
|
||||||
|
Route::post('reset', [AuthController::class, 'resetPassword'])
|
||||||
|
->name('resetPassword');
|
||||||
|
});
|
||||||
|
|
||||||
Route::name('setup.')->group(static function () {
|
Route::name('setup.')->group(static function () {
|
||||||
Route::inertia('setup', 'SetupPage', [
|
Route::inertia('setup', 'SetupPage', [
|
||||||
'languages' => cache()->rememberForever('app.languages', fn () => array_map(
|
'languages' => cache()->rememberForever('app.languages', fn () => array_map(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user