Стилизация для страницы логина, мидла для проверки роли, маршрут получения списка пользователей.
This commit is contained in:
parent
a33555abe2
commit
1eaf2fa744
73
src/app/Domain/Shared/Exceptions/AppException.php
Normal file
73
src/app/Domain/Shared/Exceptions/AppException.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\Shared\Exceptions;
|
||||
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Throwable;
|
||||
|
||||
class AppException extends Exception implements Arrayable
|
||||
{
|
||||
protected string $errorSlug;
|
||||
|
||||
public function __construct(
|
||||
string $errorSlug,
|
||||
string $message = "",
|
||||
int $code = 0,
|
||||
Throwable|null $previous = null
|
||||
)
|
||||
{
|
||||
$this->errorSlug = $errorSlug;
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
public function getSlug(): string
|
||||
{
|
||||
return $this->errorSlug;
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
$data = [
|
||||
'timestamp' => Carbon::now(),
|
||||
'error' => $this->getSlug(),
|
||||
'status' => $this->getCode(),
|
||||
'message' => $this->getMessage(),
|
||||
];
|
||||
|
||||
if (!app()->environment('production') || config('app.debug')) {
|
||||
$data['trace'] = $this->getTrace();
|
||||
|
||||
if ($prev = $this->getPrevious()) {
|
||||
$data['previous'] = [
|
||||
'code' => $prev->getCode(),
|
||||
'message' => $prev->getMessage(),
|
||||
'trace' => $prev->getTrace()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function render(): JsonResponse
|
||||
{
|
||||
return response()->json(
|
||||
$this->toArray(),
|
||||
$this->getCode() ?: 500
|
||||
);
|
||||
}
|
||||
|
||||
public static function new(
|
||||
string $slug,
|
||||
string $message,
|
||||
int $code = 500,
|
||||
Throwable|null $previous = null
|
||||
): never {
|
||||
throw new self($slug, $message, $code, $previous);
|
||||
}
|
||||
}
|
||||
@ -6,5 +6,8 @@ namespace App\Http\Controllers;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
//
|
||||
public function index()
|
||||
{
|
||||
return view('user.index', []);
|
||||
}
|
||||
}
|
||||
|
||||
45
src/app/Http/Middleware/CheckRoleMiddleware.php
Normal file
45
src/app/Http/Middleware/CheckRoleMiddleware.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Domain\Shared\Exceptions\AppException;
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Auth\Guard;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class CheckRoleMiddleware
|
||||
{
|
||||
public function __construct(
|
||||
private readonly Guard $auth
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Closure $next
|
||||
* @param string ...$roles
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, string ...$roles): Response
|
||||
{
|
||||
/** @var User|null $user */
|
||||
$user = $this->auth->user();
|
||||
|
||||
if (!$user) {
|
||||
AppException::new('UNAUTHORIZED', 'UNAUTHORIZED', Response::HTTP_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$userRole = $user->load('role')->role();
|
||||
|
||||
$hasRole = $userRole->whereIn('code', $roles)
|
||||
->count();
|
||||
|
||||
if (!$hasRole) {
|
||||
AppException::new('forbidden', 'Недостаточно прав');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@ -12,6 +12,16 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
/**
|
||||
* @property string $uuid
|
||||
* @property string $name
|
||||
* @property string $email
|
||||
* @property string $password
|
||||
* @property string $role_uuid
|
||||
* @property Carbon $created_at
|
||||
* @property Carbon $updated_at
|
||||
* @property-read Role $role
|
||||
*/
|
||||
class User extends Authenticatable
|
||||
{
|
||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Http\Middleware\CheckRoleMiddleware;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Foundation\Configuration\Exceptions;
|
||||
use Illuminate\Foundation\Configuration\Middleware;
|
||||
@ -13,7 +14,9 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware): void {
|
||||
//
|
||||
$middleware->alias([
|
||||
'role' => CheckRoleMiddleware::class,
|
||||
]);
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions): void {
|
||||
//
|
||||
|
||||
@ -1,12 +1,18 @@
|
||||
@extends('layouts.default')
|
||||
|
||||
@section('content')
|
||||
<form method="POST" action="{{ route('login') }}">
|
||||
<div class="max-w-md w-full space-y-8 bg-white p-8 rounded-lg shadow-md">
|
||||
<div>
|
||||
<h2 class="mt-6 text-center text-3xl font-extrabold text-gray-900">
|
||||
Вход в систему
|
||||
</h2>
|
||||
</div>
|
||||
<form class="mt-8 space-y-6" method="POST" action="{{ route('login') }}">
|
||||
@csrf
|
||||
|
||||
<!-- Поле Email -->
|
||||
<div class="sm:col-span-1">
|
||||
<label for="email">Email:</label>
|
||||
<div>
|
||||
<label for="email" class="block text-sm font-medium text-gray-700">Email:</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
@ -15,15 +21,16 @@
|
||||
placeholder="Enter your email"
|
||||
required
|
||||
autocomplete="username"
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
|
||||
/>
|
||||
@error('email')
|
||||
<p class="w-full text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Поле Пароль -->
|
||||
<div class="sm:col-span-1 mt-4">
|
||||
<label for="password">Password:</label>
|
||||
<div class="mt-4">
|
||||
<label for="password" class="block text-sm font-medium text-gray-700">Password:</label>
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
@ -31,17 +38,19 @@
|
||||
placeholder="Enter your password"
|
||||
required
|
||||
autocomplete="current-password"
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
|
||||
/>
|
||||
@error('password')
|
||||
<p class="w-full text-red-500">{{ $message }}</p>
|
||||
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Кнопка входа -->
|
||||
<div class="mt-6">
|
||||
<button type="submit">
|
||||
<button type="submit" class="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||
Войти
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@endsection
|
||||
@ -1,10 +1,5 @@
|
||||
@extends ('layouts.default')
|
||||
@extends ('layouts.app')
|
||||
|
||||
@section ('content')
|
||||
<h1>dashboard</h1>
|
||||
|
||||
<form method="POST" action="{{ route('logout')}}">
|
||||
@csrf
|
||||
<button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Выйти</button>
|
||||
</form>
|
||||
@endsection
|
||||
16
src/resources/views/layouts/app.blade.php
Normal file
16
src/resources/views/layouts/app.blade.php
Normal file
@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
<head>
|
||||
@include('layouts.partials.head')
|
||||
</head>
|
||||
<body>
|
||||
<div class="min-h-screen xl:flex">
|
||||
<div class="flex-1 transition-all duration-300 ease-in-out">
|
||||
@include('layouts.partials.app-header')
|
||||
<div class="p-4 mx-auto max-w-(--breakpoint-2xl) md:p-6">
|
||||
@yield('content')
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -3,6 +3,8 @@
|
||||
<head>
|
||||
@include('layouts.partials.head')
|
||||
</head>
|
||||
<body>
|
||||
<body class="bg-gray-50">
|
||||
<div class="min-h-screen flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
|
||||
@yield('content')
|
||||
</div>
|
||||
</body>
|
||||
11
src/resources/views/layouts/partials/app-header.blade.php
Normal file
11
src/resources/views/layouts/partials/app-header.blade.php
Normal file
@ -0,0 +1,11 @@
|
||||
<header class="flex items-center justify-between px-4 py-3">
|
||||
<nav class="flex-1">
|
||||
<ul class="flex justify-center space-x-4">
|
||||
<li><a href="{{ route('user.index') }}">Пользователи</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<form method="POST" action="{{ route('logout')}}">
|
||||
@csrf
|
||||
<button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Выйти</button>
|
||||
</form>
|
||||
</header>
|
||||
5
src/resources/views/user/index.blade.php
Normal file
5
src/resources/views/user/index.blade.php
Normal file
@ -0,0 +1,5 @@
|
||||
@extends ('layouts.app')
|
||||
|
||||
@section ('content')
|
||||
<h1>Users index</h1>
|
||||
@endsection
|
||||
@ -3,6 +3,7 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Http\Controllers\DashboardController;
|
||||
use App\Http\Controllers\UserController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::get('/', function () {
|
||||
@ -10,5 +11,9 @@ Route::get('/', function () {
|
||||
});
|
||||
|
||||
Route::middleware(['auth', 'verified'])->group(function () {
|
||||
Route::get('/dashboard', [DashboardController::class, 'index']);
|
||||
Route::get('/dashboard', [DashboardController::class, 'index'])
|
||||
->name('dashboard');
|
||||
Route::get('/users', [UserController::class, 'index'])
|
||||
->name('user.index')
|
||||
->middleware('role:admin');
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user