diff --git a/app/Livewire/Actions/Logout.php b/app/Livewire/Actions/Logout.php deleted file mode 100644 index 3ef481d..0000000 --- a/app/Livewire/Actions/Logout.php +++ /dev/null @@ -1,20 +0,0 @@ -logout(); - - Session::invalidate(); - Session::regenerateToken(); - } -} diff --git a/app/Livewire/Auth/ConfirmPassword.php b/app/Livewire/Auth/ConfirmPassword.php new file mode 100644 index 0000000..94ce792 --- /dev/null +++ b/app/Livewire/Auth/ConfirmPassword.php @@ -0,0 +1,40 @@ +validate([ + 'password' => ['required', 'string'], + ]); + + if (! Auth::guard('web')->validate([ + 'email' => Auth::user()->email, + 'password' => $this->password, + ])) { + throw ValidationException::withMessages([ + 'password' => __('auth.password'), + ]); + } + + session(['auth.password_confirmed_at' => time()]); + + $this->redirectIntended(default: route('dashboard', absolute: false), navigate: true); + } +} diff --git a/app/Livewire/Auth/ForgotPassword.php b/app/Livewire/Auth/ForgotPassword.php new file mode 100644 index 0000000..a513938 --- /dev/null +++ b/app/Livewire/Auth/ForgotPassword.php @@ -0,0 +1,43 @@ +validate([ + 'email' => ['required', 'string', '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( + $this->only('email') + ); + + if ($status != Password::RESET_LINK_SENT) { + $this->addError('email', __($status)); + + return; + } + + $this->reset('email'); + + session()->flash('status', __($status)); + } +} diff --git a/app/Livewire/Auth/Login.php b/app/Livewire/Auth/Login.php new file mode 100644 index 0000000..00f19ff --- /dev/null +++ b/app/Livewire/Auth/Login.php @@ -0,0 +1,28 @@ +validate(); + + $this->form->authenticate(); + + Session::regenerate(); + + $this->redirectIntended(default: route('dashboard', absolute: false), navigate: true); + } +} diff --git a/app/Livewire/Auth/ResetPassword.php b/app/Livewire/Auth/ResetPassword.php new file mode 100644 index 0000000..d1c310f --- /dev/null +++ b/app/Livewire/Auth/ResetPassword.php @@ -0,0 +1,77 @@ +token = $token; + + $this->email = request()->string('email'); + } + + /** + * Reset the password for the given user. + */ + public function resetPassword(): void + { + $this->validate([ + 'token' => ['required'], + 'email' => ['required', 'string', 'email'], + 'password' => ['required', 'string', '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( + $this->only('email', 'password', 'password_confirmation', 'token'), + function ($user) { + $user->forceFill([ + 'password' => Hash::make($this->password), + 'remember_token' => Str::random(60), + ])->save(); + + event(new PasswordReset($user)); + } + ); + + // If the password was successfully reset, we will redirect the user back to + // the application's home authenticated view. If there is an error we can + // redirect them back to where they came from with their error message. + if ($status != Password::PASSWORD_RESET) { + $this->addError('email', __($status)); + + return; + } + + Session::flash('status', __($status)); + + $this->redirectRoute('login', navigate: true); + } +} diff --git a/app/Livewire/Navigation.php b/app/Livewire/Navigation.php new file mode 100644 index 0000000..ec17816 --- /dev/null +++ b/app/Livewire/Navigation.php @@ -0,0 +1,31 @@ + Asset::where('type_id', Asset::RESUME)->first()?->slug + ]); + } + + /** + * Log the current user out of the application. + */ + public function logout(): void + { + Auth::guard('web')->logout(); + + Session::invalidate(); + Session::regenerateToken(); + + $this->redirect('/', navigate: true); + } +} diff --git a/app/Providers/VoltServiceProvider.php b/app/Providers/VoltServiceProvider.php deleted file mode 100644 index e61d984..0000000 --- a/app/Providers/VoltServiceProvider.php +++ /dev/null @@ -1,28 +0,0 @@ - 'App\\Livewire', + 'component_locations' => [ + resource_path('views/components'), + resource_path('views/livewire'), + ], /* |--------------------------------------------------------------------------- - | View Path + | Component Namespaces |--------------------------------------------------------------------------- | - | This value is used to specify where Livewire component Blade templates are - | stored when running file creation commands like `artisan make:livewire`. - | It is also used if you choose to omit a component's render() method. + | This value sets default namespaces that will be used to resolve view-based + | components like single-file and multi-file components. These folders'll + | also be referenced when creating new components via the make command. | */ - 'view_path' => resource_path('views/livewire'), + 'component_namespaces' => [ + 'layouts' => resource_path('views/layouts'), + 'pages' => resource_path('views/pages'), + ], /* |--------------------------------------------------------------------------- - | Layout + | Page Layout |--------------------------------------------------------------------------- - | The view that will be used as the layout when rendering a single component - | as an entire page via `Route::get('/post/create', CreatePost::class);`. - | In this case, the view returned by CreatePost will render into $slot. + | The view that will be used as the layout when rendering a single component as + | an entire page via `Route::livewire('/post/create', 'pages::create-post')`. + | In this case, the content of pages::create-post will render into $slot. | */ - 'layout' => 'layouts.app', + 'component_layout' => 'layouts::app', /* |--------------------------------------------------------------------------- @@ -50,7 +56,61 @@ | */ - 'lazy_placeholder' => null, + 'component_placeholder' => null, // Example: 'placeholders::skeleton' + + /* + |--------------------------------------------------------------------------- + | Make Command + |--------------------------------------------------------------------------- + | This value determines the default configuration for the artisan make command + | You can configure the component type (sfc, mfc, class) and whether to use + | the high-voltage (⚡) emoji as a prefix in the sfc|mfc component names. + | + */ + + 'make_command' => [ + 'type' => 'class', // Options: 'sfc', 'mfc', 'class' + 'emoji' => false, // Options: true, false + ], + + /* + |--------------------------------------------------------------------------- + | Class Namespace + |--------------------------------------------------------------------------- + | + | This value sets the root class namespace for Livewire component classes in + | your application. This value will change where component auto-discovery + | finds components. It's also referenced by the file creation commands. + | + */ + + 'class_namespace' => 'App\\Livewire', + + /* + |--------------------------------------------------------------------------- + | Class Path + |--------------------------------------------------------------------------- + | + | This value is used to specify the path where Livewire component class files + | are created when running creation commands like `artisan make:livewire`. + | This path is customizable to match your projects directory structure. + | + */ + + 'class_path' => app_path('Livewire'), + + /* + |--------------------------------------------------------------------------- + | View Path + |--------------------------------------------------------------------------- + | + | This value is used to specify where Livewire component Blade templates are + | stored when running file creation commands like `artisan make:livewire`. + | It is also used if you choose to omit a component's render() method. + | + */ + + 'view_path' => resource_path('views/livewire'), /* |--------------------------------------------------------------------------- @@ -64,11 +124,11 @@ */ 'temporary_file_upload' => [ - 'disk' => null, // Example: 'local', 's3' | Default: 'default' - 'rules' => null, // Example: ['file', 'mimes:png,jpg'] | Default: ['required', 'file', 'max:12288'] (12MB) - 'directory' => null, // Example: 'tmp' | Default: 'livewire-tmp' - 'middleware' => null, // Example: 'throttle:5,1' | Default: 'throttle:60,1' - 'preview_mimes' => [ // Supported file types for temporary pre-signed file URLs... + 'disk' => env('LIVEWIRE_TEMPORARY_FILE_UPLOAD_DISK'), // Example: 'local', 's3' | Default: 'default' + 'rules' => null, // Example: ['file', 'mimes:png,jpg'] | Default: ['required', 'file', 'max:12288'] (12MB) + 'directory' => null, // Example: 'tmp' | Default: 'livewire-tmp' + 'middleware' => null, // Example: 'throttle:5,1' | Default: 'throttle:60,1' + 'preview_mimes' => [ // Supported file types for temporary pre-signed file URLs... 'png', 'gif', 'bmp', 'svg', 'wav', 'mp4', 'mov', 'avi', 'wmv', 'mp3', 'm4a', 'jpg', 'jpeg', 'mpga', 'webp', 'wma', @@ -145,6 +205,19 @@ 'inject_morph_markers' => true, + /* + |--------------------------------------------------------------------------- + | Smart Wire Keys + |--------------------------------------------------------------------------- + | + | Livewire uses loops and keys used within loops to generate smart keys that + | are applied to nested components that don't have them. This makes using + | nested components more reliable by ensuring that they all have keys. + | + */ + + 'smart_wire_keys' => true, + /* |--------------------------------------------------------------------------- | Pagination Theme @@ -157,4 +230,48 @@ */ 'pagination_theme' => 'tailwind', -]; + + /* + |--------------------------------------------------------------------------- + | Release Token + |--------------------------------------------------------------------------- + | + | This token is stored client-side and sent along with each request to check + | a users session to see if a new release has invalidated it. If there is + | a mismatch it will throw an error and prompt for a browser refresh. + | + */ + + 'release_token' => 'a', + + /* + |--------------------------------------------------------------------------- + | CSP Safe + |--------------------------------------------------------------------------- + | + | This config is used to determine if Livewire will use the CSP-safe version + | of Alpine in its bundle. This is useful for applications that are using + | strict Content Security Policy (CSP) to protect against XSS attacks. + | + */ + + 'csp_safe' => false, + + /* + |--------------------------------------------------------------------------- + | Payload Guards + |--------------------------------------------------------------------------- + | + | These settings protect against malicious or oversized payloads that could + | cause denial of service. The default values should feel reasonable for + | most web applications. Each can be set to null to disable the limit. + | + */ + + 'payload' => [ + 'max_size' => 1024 * 1024 * 8, // 8MB - maximum request payload size in bytes + 'max_nesting_depth' => 10, // Maximum depth of dot-notation property paths + 'max_calls' => 50, // Maximum method calls per request + 'max_components' => 20, // Maximum components per batch request + ], +]; \ No newline at end of file diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index e8d480d..7d77126 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -9,12 +9,10 @@ class DatabaseSeeder extends Seeder { public function run(): void { - if (env('APP_ENV') != 'production') { - User::factory()->create([ - 'name' => 'kyledoesdev', - 'email' => 'kyledoesdev@gmail.com', - 'password' => bcrypt('password'), - ]); - } + User::factory()->create([ + 'name' => 'kyledoesdev', + 'email' => 'kyledoesdev@gmail.com', + 'password' => bcrypt('password'), + ]); } } diff --git a/resources/views/flux/icon/audio-waveform.blade.php b/resources/views/flux/icon/audio-waveform.blade.php index 8b428e6..0df177b 100644 --- a/resources/views/flux/icon/audio-waveform.blade.php +++ b/resources/views/flux/icon/audio-waveform.blade.php @@ -1,4 +1,5 @@ {{-- Credit: Lucide (https://lucide.dev) --}} +@blaze @props([ 'variant' => 'outline', diff --git a/resources/views/flux/icon/dices.blade.php b/resources/views/flux/icon/dices.blade.php index c946364..3c6d458 100644 --- a/resources/views/flux/icon/dices.blade.php +++ b/resources/views/flux/icon/dices.blade.php @@ -1,4 +1,5 @@ {{-- Credit: Lucide (https://lucide.dev) --}} +@blaze @props([ 'variant' => 'outline', diff --git a/resources/views/flux/icon/film.blade.php b/resources/views/flux/icon/film.blade.php index 26fe15a..d1dab08 100644 --- a/resources/views/flux/icon/film.blade.php +++ b/resources/views/flux/icon/film.blade.php @@ -1,4 +1,5 @@ {{-- Credit: Lucide (https://lucide.dev) --}} +@blaze @props([ 'variant' => 'outline', diff --git a/resources/views/flux/icon/gamepad-2.blade.php b/resources/views/flux/icon/gamepad-2.blade.php index 267d33e..947d35e 100644 --- a/resources/views/flux/icon/gamepad-2.blade.php +++ b/resources/views/flux/icon/gamepad-2.blade.php @@ -1,4 +1,5 @@ {{-- Credit: Lucide (https://lucide.dev) --}} +@blaze @props([ 'variant' => 'outline', diff --git a/resources/views/flux/icon/heart-pulse.blade.php b/resources/views/flux/icon/heart-pulse.blade.php index 192cad4..3703a83 100644 --- a/resources/views/flux/icon/heart-pulse.blade.php +++ b/resources/views/flux/icon/heart-pulse.blade.php @@ -1,4 +1,5 @@ {{-- Credit: Lucide (https://lucide.dev) --}} +@blaze @props([ 'variant' => 'outline', diff --git a/resources/views/flux/icon/stethoscope.blade.php b/resources/views/flux/icon/stethoscope.blade.php index 04b01b8..1b0e08e 100644 --- a/resources/views/flux/icon/stethoscope.blade.php +++ b/resources/views/flux/icon/stethoscope.blade.php @@ -1,4 +1,5 @@ {{-- Credit: Lucide (https://lucide.dev) --}} +@blaze @props([ 'variant' => 'outline', diff --git a/resources/views/flux/icon/tv.blade.php b/resources/views/flux/icon/tv.blade.php index df7de03..c1cb610 100644 --- a/resources/views/flux/icon/tv.blade.php +++ b/resources/views/flux/icon/tv.blade.php @@ -1,4 +1,5 @@ {{-- Credit: Lucide (https://lucide.dev) --}} +@blaze @props([ 'variant' => 'outline', diff --git a/resources/views/flux/icon/twitch.blade.php b/resources/views/flux/icon/twitch.blade.php index 3cb4eea..ae43f57 100644 --- a/resources/views/flux/icon/twitch.blade.php +++ b/resources/views/flux/icon/twitch.blade.php @@ -1,4 +1,5 @@ {{-- Credit: Lucide (https://lucide.dev) --}} +@blaze @props([ 'variant' => 'outline', diff --git a/resources/views/flux/main.blade.php b/resources/views/flux/main.blade.php index 4bac25d..71e4153 100644 --- a/resources/views/flux/main.blade.php +++ b/resources/views/flux/main.blade.php @@ -1,3 +1,5 @@ +@blaze + @props([ 'container' => null, ]) diff --git a/resources/views/flux/tab/panel.blade.php b/resources/views/flux/tab/panel.blade.php index 72707b0..e085489 100644 --- a/resources/views/flux/tab/panel.blade.php +++ b/resources/views/flux/tab/panel.blade.php @@ -1,3 +1,5 @@ +@blaze + @props([ 'name' => null, ]) diff --git a/resources/views/livewire/connection.blade.php b/resources/views/livewire/connection.blade.php index 3c081be..145e0a8 100644 --- a/resources/views/livewire/connection.blade.php +++ b/resources/views/livewire/connection.blade.php @@ -38,7 +38,7 @@ diff --git a/resources/views/livewire/navigation.blade.php b/resources/views/livewire/navigation.blade.php index 70991dc..897b65c 100644 --- a/resources/views/livewire/navigation.blade.php +++ b/resources/views/livewire/navigation.blade.php @@ -1,27 +1,3 @@ -redirect('/', navigate: true); - } -}; ?> - -@php - use App\Models\Asset; - - $resume = Asset::where('type_id', Asset::RESUME)->first()?->slug; -@endphp - diff --git a/resources/views/livewire/pages/auth/confirm-password.blade.php b/resources/views/livewire/pages/auth/confirm-password.blade.php index a64faac..99fc8ba 100644 --- a/resources/views/livewire/pages/auth/confirm-password.blade.php +++ b/resources/views/livewire/pages/auth/confirm-password.blade.php @@ -1,38 +1,3 @@ -validate([ - 'password' => ['required', 'string'], - ]); - - if (! Auth::guard('web')->validate([ - 'email' => Auth::user()->email, - 'password' => $this->password, - ])) { - throw ValidationException::withMessages([ - 'password' => __('auth.password'), - ]); - } - - session(['auth.password_confirmed_at' => time()]); - - $this->redirectIntended(default: route('dashboard', absolute: false), navigate: true); - } -}; ?> -
{{ __('This is a secure area of the application. Please confirm your password before continuing.') }} diff --git a/resources/views/livewire/pages/auth/forgot-password.blade.php b/resources/views/livewire/pages/auth/forgot-password.blade.php index cb7eafc..7f45a9b 100644 --- a/resources/views/livewire/pages/auth/forgot-password.blade.php +++ b/resources/views/livewire/pages/auth/forgot-password.blade.php @@ -1,41 +1,3 @@ -validate([ - 'email' => ['required', 'string', '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( - $this->only('email') - ); - - if ($status != Password::RESET_LINK_SENT) { - $this->addError('email', __($status)); - - return; - } - - $this->reset('email'); - - session()->flash('status', __($status)); - } -}; ?> -
{{ __('Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.') }} diff --git a/resources/views/livewire/pages/auth/login.blade.php b/resources/views/livewire/pages/auth/login.blade.php index d5fd47e..1916ca9 100644 --- a/resources/views/livewire/pages/auth/login.blade.php +++ b/resources/views/livewire/pages/auth/login.blade.php @@ -1,29 +1,3 @@ -validate(); - - $this->form->authenticate(); - - Session::regenerate(); - - $this->redirectIntended(default: route('dashboard', absolute: false), navigate: true); - } -}; ?> -
diff --git a/resources/views/livewire/pages/auth/reset-password.blade.php b/resources/views/livewire/pages/auth/reset-password.blade.php index 5a7e769..b0d2e69 100644 --- a/resources/views/livewire/pages/auth/reset-password.blade.php +++ b/resources/views/livewire/pages/auth/reset-password.blade.php @@ -1,99 +1,21 @@ -token = $token; - - $this->email = request()->string('email'); - } - - /** - * Reset the password for the given user. - */ - public function resetPassword(): void - { - $this->validate([ - 'token' => ['required'], - 'email' => ['required', 'string', 'email'], - 'password' => ['required', 'string', '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( - $this->only('email', 'password', 'password_confirmation', 'token'), - function ($user) { - $user->forceFill([ - 'password' => Hash::make($this->password), - 'remember_token' => Str::random(60), - ])->save(); - - event(new PasswordReset($user)); - } - ); - - // If the password was successfully reset, we will redirect the user back to - // the application's home authenticated view. If there is an error we can - // redirect them back to where they came from with their error message. - if ($status != Password::PASSWORD_RESET) { - $this->addError('email', __($status)); - - return; - } - - Session::flash('status', __($status)); - - $this->redirectRoute('login', navigate: true); - } -}; ?> -
-
- + Email - +
-
- + Password - +
-
- - - - - + Confirm Password + +
@@ -102,4 +24,4 @@ function ($user) {
-
+
\ No newline at end of file diff --git a/resources/views/livewire/profile/delete-user-form.blade.php b/resources/views/livewire/profile/delete-user-form.blade.php index 99a7f49..3b4c780 100644 --- a/resources/views/livewire/profile/delete-user-form.blade.php +++ b/resources/views/livewire/profile/delete-user-form.blade.php @@ -1,7 +1,7 @@ group(function () { - Volt::route('admin', 'pages.auth.login') + Route::livewire('admin', Login::class) ->name('login'); - Volt::route('forgot-password', 'pages.auth.forgot-password') + Route::livewire('forgot-password', ForgotPassword::class) ->name('password.request'); - Volt::route('reset-password/{token}', 'pages.auth.reset-password') + Route::livewire('reset-password/{token}', ResetPassword::class) ->name('password.reset'); }); Route::middleware('auth')->group(function () { - Volt::route('confirm-password', 'pages.auth.confirm-password') + Route::livewire('confirm-password', ConfirmPassword::class) ->name('password.confirm'); }); diff --git a/routes/web.php b/routes/web.php index 0de87ae..838e456 100644 --- a/routes/web.php +++ b/routes/web.php @@ -30,41 +30,41 @@ Route::view('/', 'welcome')->name('welcome'); /* Career Views */ -Route::get('/education', Education::class)->name('education'); -Route::get('/projects', Projects::class)->name('projects'); -Route::get('/technology', Technologies::class)->name('technologies'); -Route::get('/work_history', WorkHistory::class)->name('work_history'); +Route::livewire('/education', Education::class)->name('education'); +Route::livewire('/projects', Projects::class)->name('projects'); +Route::livewire('/technology', Technologies::class)->name('technologies'); +Route::livewire('/work_history', WorkHistory::class)->name('work_history'); /* Hobby Views */ -Route::get('/board_games', BoardGames::class)->name('board_games'); -Route::get('/movies', ShowMovies::class)->name('movies'); -Route::get('/music', ShowMusic::class)->name('music'); -Route::get('/tv', ShowTv::class)->name('tv_shows'); -Route::get('/video_games', ShowGames::class)->name('video_games'); -Route::get('/gallery', Gallery::class)->name('gallery'); -Route::get('/3d_printing', Printing::class)->name('3d_printing'); +Route::livewire('/board_games', BoardGames::class)->name('board_games'); +Route::livewire('/movies', ShowMovies::class)->name('movies'); +Route::livewire('/music', ShowMusic::class)->name('music'); +Route::livewire('/tv', ShowTv::class)->name('tv_shows'); +Route::livewire('/video_games', ShowGames::class)->name('video_games'); +Route::livewire('/gallery', Gallery::class)->name('gallery'); +Route::livewire('/3d_printing', Printing::class)->name('3d_printing'); Route::get('/asset/{slug}', AssetController::class)->name('asset'); Route::middleware(['auth'])->group(function () { - Route::get('/dashboard', Dashboard::class)->name('dashboard'); + Route::livewire('/dashboard', Dashboard::class)->name('dashboard'); Route::get('/connect/{type}', [ConnectionController::class, 'connect'])->name('connect'); Route::get('/connection/callback', [ConnectionController::class, 'processConnection'])->name('process_connection'); Route::get('/connections/edit', Connection::class)->name('connections.edit'); - Route::get('/blog/edit', Blog::class)->name('blog.edit'); - Route::get('/blog/{post}/views', BlogViews::class)->name('blog.post_views'); + Route::livewire('/blog/edit', Blog::class)->name('blog.edit'); + Route::livewire('/blog/{post}/views', BlogViews::class)->name('blog.post_views'); - Route::get('/panels', Panels::class)->name('panels'); + Route::livewire('/panels', Panels::class)->name('panels'); - Route::get('/gallery/edit', Uploader::class)->name('gallery.edit'); - Route::get('/resumes/edit', Resume::class)->name('resume.edit'); + Route::livewire('/gallery/edit', Uploader::class)->name('gallery.edit'); + Route::livewire('/resumes/edit', Resume::class)->name('resume.edit'); - Route::get('/movies/edit', EditMovies::class)->name('movies.edit'); - Route::get('/tv/edit', EditTv::class)->name('tv.edit'); - Route::get('/music/edit', EditMusic::class)->name('music.edit'); - Route::get('/video_games/edit', EditGames::class)->name('video_games.edit'); + Route::livewire('/movies/edit', EditMovies::class)->name('movies.edit'); + Route::livewire('/tv/edit', EditTv::class)->name('tv.edit'); + Route::livewire('/music/edit', EditMusic::class)->name('music.edit'); + Route::livewire('/video_games/edit', EditGames::class)->name('video_games.edit'); Route::get('/health', HealthCheckResultsController::class)->name('health.index'); });