Applied laravel breeze api

main
TZGyn 3 years ago
parent c9c2c709a0
commit b6554e19a9
Signed by: TZGyn
GPG Key ID: 122EAF77AE81FD4A

@ -0,0 +1,38 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Http\Requests\Auth\LoginRequest;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
class AuthenticatedSessionController extends Controller
{
/**
* Handle an incoming authentication request.
*/
public function store(LoginRequest $request): Response
{
$request->authenticate();
$request->session()->regenerate();
return response()->noContent();
}
/**
* Destroy an authenticated session.
*/
public function destroy(Request $request): Response
{
Auth::guard('web')->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return response()->noContent();
}
}

@ -0,0 +1,26 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class EmailVerificationNotificationController extends Controller
{
/**
* Send a new email verification notification.
*/
public function store(Request $request): JsonResponse|RedirectResponse
{
if ($request->user()->hasVerifiedEmail()) {
return redirect()->intended(RouteServiceProvider::HOME);
}
$request->user()->sendEmailVerificationNotification();
return response()->json(['status' => 'verification-link-sent']);
}
}

@ -0,0 +1,53 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
use Illuminate\Validation\Rules;
use Illuminate\Validation\ValidationException;
class NewPasswordController extends Controller
{
/**
* Handle an incoming new password request.
*
* @throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request): JsonResponse
{
$request->validate([
'token' => ['required'],
'email' => ['required', 'email'],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
// Here we will attempt to reset the user's password. If it is successful we
// will update the password on an actual user model and persist it to the
// database. Otherwise we will parse the error and return the response.
$status = Password::reset(
$request->only('email', 'password', 'password_confirmation', 'token'),
function ($user) use ($request) {
$user->forceFill([
'password' => Hash::make($request->password),
'remember_token' => Str::random(60),
])->save();
event(new PasswordReset($user));
}
);
if ($status != Password::PASSWORD_RESET) {
throw ValidationException::withMessages([
'email' => [__($status)],
]);
}
return response()->json(['status' => __($status)]);
}
}

@ -0,0 +1,39 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
use Illuminate\Validation\ValidationException;
class PasswordResetLinkController extends Controller
{
/**
* Handle an incoming password reset link request.
*
* @throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request): JsonResponse
{
$request->validate([
'email' => ['required', 'email'],
]);
// We will send the password reset link to this user. Once we have attempted
// to send the link, we will examine the response then see the message we
// need to show to the user. Finally, we'll send out a proper response.
$status = Password::sendResetLink(
$request->only('email')
);
if ($status != Password::RESET_LINK_SENT) {
throw ValidationException::withMessages([
'email' => [__($status)],
]);
}
return response()->json(['status' => __($status)]);
}
}

@ -0,0 +1,41 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules;
class RegisteredUserController extends Controller
{
/**
* Handle an incoming registration request.
*
* @throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request): Response
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:'.User::class],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
event(new Registered($user));
Auth::login($user);
return response()->noContent();
}
}

@ -0,0 +1,32 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Auth\EmailVerificationRequest;
use Illuminate\Http\RedirectResponse;
class VerifyEmailController extends Controller
{
/**
* Mark the authenticated user's email address as verified.
*/
public function __invoke(EmailVerificationRequest $request): RedirectResponse
{
if ($request->user()->hasVerifiedEmail()) {
return redirect()->intended(
config('app.frontend_url').RouteServiceProvider::HOME.'?verified=1'
);
}
if ($request->user()->markEmailAsVerified()) {
event(new Verified($request->user()));
}
return redirect()->intended(
config('app.frontend_url').RouteServiceProvider::HOME.'?verified=1'
);
}
}

@ -39,7 +39,7 @@ class Kernel extends HttpKernel
], ],
'api' => [ 'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api', \Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class, \Illuminate\Routing\Middleware\SubstituteBindings::class,
], ],
@ -62,6 +62,6 @@ class Kernel extends HttpKernel
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \App\Http\Middleware\ValidateSignature::class, 'signed' => \App\Http\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, 'verified' => \App\Http\Middleware\EnsureEmailIsVerified::class,
]; ];
} }

@ -0,0 +1,27 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureEmailIsVerified
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if (! $request->user() ||
($request->user() instanceof MustVerifyEmail &&
! $request->user()->hasVerifiedEmail())) {
return response()->json(['message' => 'Your email address is not verified.'], 409);
}
return $next($request);
}
}

@ -0,0 +1,85 @@
<?php
namespace App\Http\Requests\Auth;
use Illuminate\Auth\Events\Lockout;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
class LoginRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
*/
public function rules(): array
{
return [
'email' => ['required', 'string', 'email'],
'password' => ['required', 'string'],
];
}
/**
* Attempt to authenticate the request's credentials.
*
* @throws \Illuminate\Validation\ValidationException
*/
public function authenticate(): void
{
$this->ensureIsNotRateLimited();
if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
'email' => __('auth.failed'),
]);
}
RateLimiter::clear($this->throttleKey());
}
/**
* Ensure the login request is not rate limited.
*
* @throws \Illuminate\Validation\ValidationException
*/
public function ensureIsNotRateLimited(): void
{
if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
return;
}
event(new Lockout($this));
$seconds = RateLimiter::availableIn($this->throttleKey());
throw ValidationException::withMessages([
'email' => trans('auth.throttle', [
'seconds' => $seconds,
'minutes' => ceil($seconds / 60),
]),
]);
}
/**
* Get the rate limiting throttle key for the request.
*/
public function throttleKey(): string
{
return Str::transliterate(Str::lower($this->input('email')).'|'.$this->ip());
}
}

@ -2,18 +2,18 @@
namespace App\Providers; namespace App\Providers;
// use Illuminate\Support\Facades\Gate; use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider class AuthServiceProvider extends ServiceProvider
{ {
/** /**
* The model to policy mappings for the application. * The policy mappings for the application.
* *
* @var array<class-string, class-string> * @var array<class-string, class-string>
*/ */
protected $policies = [ protected $policies = [
// // 'App\Models\Model' => 'App\Policies\ModelPolicy',
]; ];
/** /**
@ -21,6 +21,12 @@ class AuthServiceProvider extends ServiceProvider
*/ */
public function boot(): void public function boot(): void
{ {
$this->registerPolicies();
ResetPassword::createUrlUsing(function (object $notifiable, string $token) {
return config('app.frontend_url')."/password-reset/$token?email={$notifiable->getEmailForPasswordReset()}";
});
// //
} }
} }

@ -17,7 +17,7 @@ class RouteServiceProvider extends ServiceProvider
* *
* @var string * @var string
*/ */
public const HOME = '/home'; public const HOME = '/dashboard';
/** /**
* Define your route model bindings, pattern filters, and other route configuration. * Define your route model bindings, pattern filters, and other route configuration.

@ -8,6 +8,7 @@
"php": "^8.1", "php": "^8.1",
"doctrine/dbal": "^3.6", "doctrine/dbal": "^3.6",
"guzzlehttp/guzzle": "^7.2", "guzzlehttp/guzzle": "^7.2",
"laravel/breeze": "^1.21",
"laravel/framework": "^10.10", "laravel/framework": "^10.10",
"laravel/sanctum": "^3.2", "laravel/sanctum": "^3.2",
"laravel/telescope": "^4.14", "laravel/telescope": "^4.14",

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "01f69d10e6a795f735b06b9498ef24b3", "content-hash": "ee957195d42c63a6861ba52824e5af26",
"packages": [ "packages": [
{ {
"name": "brick/math", "name": "brick/math",
@ -1317,6 +1317,68 @@
], ],
"time": "2021-10-07T12:57:01+00:00" "time": "2021-10-07T12:57:01+00:00"
}, },
{
"name": "laravel/breeze",
"version": "v1.21.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/breeze.git",
"reference": "a7e7e2acfb2fd332183aae41c445be7a2329e93e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/breeze/zipball/a7e7e2acfb2fd332183aae41c445be7a2329e93e",
"reference": "a7e7e2acfb2fd332183aae41c445be7a2329e93e",
"shasum": ""
},
"require": {
"illuminate/console": "^10.0",
"illuminate/filesystem": "^10.0",
"illuminate/support": "^10.0",
"illuminate/validation": "^10.0",
"php": "^8.1.0"
},
"require-dev": {
"orchestra/testbench": "^8.0",
"phpstan/phpstan": "^1.10"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
},
"laravel": {
"providers": [
"Laravel\\Breeze\\BreezeServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Laravel\\Breeze\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"description": "Minimal Laravel authentication scaffolding with Blade and Tailwind.",
"keywords": [
"auth",
"laravel"
],
"support": {
"issues": "https://github.com/laravel/breeze/issues",
"source": "https://github.com/laravel/breeze"
},
"time": "2023-05-04T15:02:53+00:00"
},
{ {
"name": "laravel/framework", "name": "laravel/framework",
"version": "v10.13.1", "version": "v10.13.1",

@ -57,6 +57,8 @@ return [
'url' => env('APP_URL', 'http://localhost'), 'url' => env('APP_URL', 'http://localhost'),
'frontend_url' => env('FRONTEND_URL', 'http://localhost:3000'),
'asset_url' => env('ASSET_URL'), 'asset_url' => env('ASSET_URL'),
/* /*

@ -15,11 +15,11 @@ return [
| |
*/ */
'paths' => ['api/*', 'sanctum/csrf-cookie'], 'paths' => ['*'],
'allowed_methods' => ['*'], 'allowed_methods' => ['*'],
'allowed_origins' => ['*'], 'allowed_origins' => [env('FRONTEND_URL', 'http://localhost:3000')],
'allowed_origins_patterns' => [], 'allowed_origins_patterns' => [],
@ -29,6 +29,6 @@ return [
'max_age' => 0, 'max_age' => 0,
'supports_credentials' => false, 'supports_credentials' => true,
]; ];

@ -1,7 +1,5 @@
<?php <?php
use Laravel\Sanctum\Sanctum;
return [ return [
/* /*
@ -16,9 +14,10 @@ return [
*/ */
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( 'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
'%s%s', '%s%s%s',
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
Sanctum::currentApplicationUrlWithPort() env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : '',
env('FRONTEND_URL') ? ','.parse_url(env('FRONTEND_URL'), PHP_URL_HOST) : ''
))), ))),
/* /*
@ -41,8 +40,8 @@ return [
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| This value controls the number of minutes until an issued token will be | This value controls the number of minutes until an issued token will be
| considered expired. If this value is null, personal access tokens do | considered expired. This will override any values set in the token's
| not expire. This won't tweak the lifetime of first-party sessions. | "expires_at" attribute, but first-party sessions are not affected.
| |
*/ */

@ -1,13 +0,0 @@
{
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"devDependencies": {
"axios": "^1.1.2",
"laravel-vite-plugin": "^0.7.5",
"vite": "^4.0.0"
}
}

@ -1 +0,0 @@
import './bootstrap';

@ -1,32 +0,0 @@
/**
* We'll load the axios HTTP library which allows us to easily issue requests
* to our Laravel back-end. This library automatically handles sending the
* CSRF token as a header based on the value of the "XSRF" token cookie.
*/
import axios from 'axios';
window.axios = axios;
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
/**
* Echo exposes an expressive API for subscribing to channels and listening
* for events that are broadcast by Laravel. Echo and event broadcasting
* allows your team to easily build robust real-time web applications.
*/
// import Echo from 'laravel-echo';
// import Pusher from 'pusher-js';
// window.Pusher = Pusher;
// window.Echo = new Echo({
// broadcaster: 'pusher',
// key: import.meta.env.VITE_PUSHER_APP_KEY,
// cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER ?? 'mt1',
// wsHost: import.meta.env.VITE_PUSHER_HOST ? import.meta.env.VITE_PUSHER_HOST : `ws-${import.meta.env.VITE_PUSHER_APP_CLUSTER}.pusher.com`,
// wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80,
// wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443,
// forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https',
// enabledTransports: ['ws', 'wss'],
// });

File diff suppressed because one or more lines are too long

@ -9,11 +9,11 @@ use Illuminate\Support\Facades\Route;
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| Here is where you can register API routes for your application. These | Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will | routes are loaded by the RouteServiceProvider within a group which
| be assigned to the "api" middleware group. Make something great! | is assigned the "api" middleware group. Enjoy building your API!
| |
*/ */
Route::middleware('auth:sanctum')->get('/user', function (Request $request) { Route::middleware(['auth:sanctum'])->get('/user', function (Request $request) {
return $request->user(); return $request->user();
}); });

@ -0,0 +1,37 @@
<?php
use App\Http\Controllers\Auth\AuthenticatedSessionController;
use App\Http\Controllers\Auth\EmailVerificationNotificationController;
use App\Http\Controllers\Auth\NewPasswordController;
use App\Http\Controllers\Auth\PasswordResetLinkController;
use App\Http\Controllers\Auth\RegisteredUserController;
use App\Http\Controllers\Auth\VerifyEmailController;
use Illuminate\Support\Facades\Route;
Route::post('/register', [RegisteredUserController::class, 'store'])
->middleware('guest')
->name('register');
Route::post('/login', [AuthenticatedSessionController::class, 'store'])
->middleware('guest')
->name('login');
Route::post('/forgot-password', [PasswordResetLinkController::class, 'store'])
->middleware('guest')
->name('password.email');
Route::post('/reset-password', [NewPasswordController::class, 'store'])
->middleware('guest')
->name('password.store');
Route::get('/verify-email/{id}/{hash}', VerifyEmailController::class)
->middleware(['auth', 'signed', 'throttle:6,1'])
->name('verification.verify');
Route::post('/email/verification-notification', [EmailVerificationNotificationController::class, 'store'])
->middleware(['auth', 'throttle:6,1'])
->name('verification.send');
Route::post('/logout', [AuthenticatedSessionController::class, 'destroy'])
->middleware('auth')
->name('logout');

@ -1,6 +1,5 @@
<?php <?php
use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
/* /*
@ -9,17 +8,13 @@ use Illuminate\Support\Facades\Route;
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| Here is where you can register web routes for your application. These | Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will | routes are loaded by the RouteServiceProvider within a group which
| be assigned to the "web" middleware group. Make something great! | contains the "web" middleware group. Now create something great!
| |
*/ */
Route::get('/', function () { Route::get('/', function () {
return view('welcome'); return ['Laravel' => app()->version()];
}); });
Route::prefix('api')->group(function () { require __DIR__.'/auth.php';
Route::resources([
'/posts' => PostController::class,
]);
});

@ -0,0 +1,37 @@
<?php
namespace Tests\Feature\Auth;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class AuthenticationTest extends TestCase
{
use RefreshDatabase;
public function test_users_can_authenticate_using_the_login_screen(): void
{
$user = User::factory()->create();
$response = $this->post('/login', [
'email' => $user->email,
'password' => 'password',
]);
$this->assertAuthenticated();
$response->assertNoContent();
}
public function test_users_can_not_authenticate_with_invalid_password(): void
{
$user = User::factory()->create();
$this->post('/login', [
'email' => $user->email,
'password' => 'wrong-password',
]);
$this->assertGuest();
}
}

@ -0,0 +1,54 @@
<?php
namespace Tests\Feature\Auth;
use App\Models\User;
use App\Providers\RouteServiceProvider;
use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\URL;
use Tests\TestCase;
class EmailVerificationTest extends TestCase
{
use RefreshDatabase;
public function test_email_can_be_verified(): void
{
$user = User::factory()->create([
'email_verified_at' => null,
]);
Event::fake();
$verificationUrl = URL::temporarySignedRoute(
'verification.verify',
now()->addMinutes(60),
['id' => $user->id, 'hash' => sha1($user->email)]
);
$response = $this->actingAs($user)->get($verificationUrl);
Event::assertDispatched(Verified::class);
$this->assertTrue($user->fresh()->hasVerifiedEmail());
$response->assertRedirect(config('app.frontend_url').RouteServiceProvider::HOME.'?verified=1');
}
public function test_email_is_not_verified_with_invalid_hash(): void
{
$user = User::factory()->create([
'email_verified_at' => null,
]);
$verificationUrl = URL::temporarySignedRoute(
'verification.verify',
now()->addMinutes(60),
['id' => $user->id, 'hash' => sha1('wrong-email')]
);
$this->actingAs($user)->get($verificationUrl);
$this->assertFalse($user->fresh()->hasVerifiedEmail());
}
}

@ -0,0 +1,47 @@
<?php
namespace Tests\Feature\Auth;
use App\Models\User;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Notification;
use Tests\TestCase;
class PasswordResetTest extends TestCase
{
use RefreshDatabase;
public function test_reset_password_link_can_be_requested(): void
{
Notification::fake();
$user = User::factory()->create();
$this->post('/forgot-password', ['email' => $user->email]);
Notification::assertSentTo($user, ResetPassword::class);
}
public function test_password_can_be_reset_with_valid_token(): void
{
Notification::fake();
$user = User::factory()->create();
$this->post('/forgot-password', ['email' => $user->email]);
Notification::assertSentTo($user, ResetPassword::class, function (object $notification) use ($user) {
$response = $this->post('/reset-password', [
'token' => $notification->token,
'email' => $user->email,
'password' => 'password',
'password_confirmation' => 'password',
]);
$response->assertSessionHasNoErrors();
return true;
});
}
}

@ -0,0 +1,24 @@
<?php
namespace Tests\Feature\Auth;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class RegistrationTest extends TestCase
{
use RefreshDatabase;
public function test_new_users_can_register(): void
{
$response = $this->post('/register', [
'name' => 'Test User',
'email' => 'test@example.com',
'password' => 'password',
'password_confirmation' => 'password',
]);
$this->assertAuthenticated();
$response->assertNoContent();
}
}

@ -1,11 +0,0 @@
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
refresh: true,
}),
],
});
Loading…
Cancel
Save