Set up a fresh Laravel app

This commit is contained in:
Bob Molitor
2025-10-16 14:22:46 +02:00
commit 81a98dd9f4
112 changed files with 17024 additions and 0 deletions
@@ -0,0 +1,28 @@
<x-layouts.auth>
<div class="flex flex-col gap-6">
<x-auth-header
:title="__('Confirm password')"
:description="__('This is a secure area of the application. Please confirm your password before continuing.')"
/>
<x-auth-session-status class="text-center" :status="session('status')" />
<form method="POST" action="{{ route('password.confirm.store') }}" class="flex flex-col gap-6">
@csrf
<flux:input
name="password"
:label="__('Password')"
type="password"
required
autocomplete="current-password"
:placeholder="__('Password')"
viewable
/>
<flux:button variant="primary" type="submit" class="w-full" data-test="confirm-password-button">
{{ __('Confirm') }}
</flux:button>
</form>
</div>
</x-layouts.auth>
@@ -0,0 +1,51 @@
<?php
use Illuminate\Support\Facades\Password;
use Livewire\Attributes\Layout;
use Livewire\Volt\Component;
new #[Layout('components.layouts.auth')] class extends Component {
public string $email = '';
/**
* Send a password reset link to the provided email address.
*/
public function sendPasswordResetLink(): void
{
$this->validate([
'email' => ['required', 'string', 'email'],
]);
Password::sendResetLink($this->only('email'));
session()->flash('status', __('A reset link will be sent if the account exists.'));
}
}; ?>
<div class="flex flex-col gap-6">
<x-auth-header :title="__('Forgot password')" :description="__('Enter your email to receive a password reset link')" />
<!-- Session Status -->
<x-auth-session-status class="text-center" :status="session('status')" />
<form method="POST" wire:submit="sendPasswordResetLink" class="flex flex-col gap-6">
<!-- Email Address -->
<flux:input
wire:model="email"
:label="__('Email Address')"
type="email"
required
autofocus
placeholder="email@example.com"
/>
<flux:button variant="primary" type="submit" class="w-full" data-test="email-password-reset-link-button">
{{ __('Email password reset link') }}
</flux:button>
</form>
<div class="space-x-1 rtl:space-x-reverse text-center text-sm text-zinc-400">
<span>{{ __('Or, return to') }}</span>
<flux:link :href="route('login')" wire:navigate>{{ __('log in') }}</flux:link>
</div>
</div>
@@ -0,0 +1,156 @@
<?php
use App\Models\User;
use Illuminate\Auth\Events\Lockout;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Laravel\Fortify\Features;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Validate;
use Livewire\Volt\Component;
new #[Layout('components.layouts.auth')] class extends Component {
#[Validate('required|string|email')]
public string $email = '';
#[Validate('required|string')]
public string $password = '';
public bool $remember = false;
/**
* Handle an incoming authentication request.
*/
public function login(): void
{
$this->validate();
$this->ensureIsNotRateLimited();
$user = $this->validateCredentials();
if (Features::canManageTwoFactorAuthentication() && $user->hasEnabledTwoFactorAuthentication()) {
Session::put([
'login.id' => $user->getKey(),
'login.remember' => $this->remember,
]);
$this->redirect(route('two-factor.login'), navigate: true);
return;
}
Auth::login($user, $this->remember);
RateLimiter::clear($this->throttleKey());
Session::regenerate();
$this->redirectIntended(default: route('dashboard', absolute: false), navigate: true);
}
/**
* Validate the user's credentials.
*/
protected function validateCredentials(): User
{
$user = Auth::getProvider()->retrieveByCredentials(['email' => $this->email, 'password' => $this->password]);
if (! $user || ! Auth::getProvider()->validateCredentials($user, ['password' => $this->password])) {
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
'email' => __('auth.failed'),
]);
}
return $user;
}
/**
* Ensure the authentication request is not rate limited.
*/
protected function ensureIsNotRateLimited(): void
{
if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
return;
}
event(new Lockout(request()));
$seconds = RateLimiter::availableIn($this->throttleKey());
throw ValidationException::withMessages([
'email' => __('auth.throttle', [
'seconds' => $seconds,
'minutes' => ceil($seconds / 60),
]),
]);
}
/**
* Get the authentication rate limiting throttle key.
*/
protected function throttleKey(): string
{
return Str::transliterate(Str::lower($this->email).'|'.request()->ip());
}
}; ?>
<div class="flex flex-col gap-6">
<x-auth-header :title="__('Log in to your account')" :description="__('Enter your email and password below to log in')" />
<!-- Session Status -->
<x-auth-session-status class="text-center" :status="session('status')" />
<form method="POST" wire:submit="login" class="flex flex-col gap-6">
<!-- Email Address -->
<flux:input
wire:model="email"
:label="__('Email address')"
type="email"
required
autofocus
autocomplete="email"
placeholder="email@example.com"
/>
<!-- Password -->
<div class="relative">
<flux:input
wire:model="password"
:label="__('Password')"
type="password"
required
autocomplete="current-password"
:placeholder="__('Password')"
viewable
/>
@if (Route::has('password.request'))
<flux:link class="absolute top-0 text-sm end-0" :href="route('password.request')" wire:navigate>
{{ __('Forgot your password?') }}
</flux:link>
@endif
</div>
<!-- Remember Me -->
<flux:checkbox wire:model="remember" :label="__('Remember me')" />
<div class="flex items-center justify-end">
<flux:button variant="primary" type="submit" class="w-full" data-test="login-button">
{{ __('Log in') }}
</flux:button>
</div>
</form>
@if (Route::has('register'))
<div class="space-x-1 text-sm text-center rtl:space-x-reverse text-zinc-600 dark:text-zinc-400">
<span>{{ __('Don\'t have an account?') }}</span>
<flux:link :href="route('register')" wire:navigate>{{ __('Sign up') }}</flux:link>
</div>
@endif
</div>
@@ -0,0 +1,99 @@
<?php
use App\Models\User;
use Illuminate\Auth\Events\Registered;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
use Illuminate\Validation\Rules;
use Livewire\Attributes\Layout;
use Livewire\Volt\Component;
new #[Layout('components.layouts.auth')] class extends Component {
public string $name = '';
public string $email = '';
public string $password = '';
public string $password_confirmation = '';
/**
* Handle an incoming registration request.
*/
public function register(): void
{
$validated = $this->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:' . User::class],
'password' => ['required', 'string', 'confirmed', Rules\Password::defaults()],
]);
event(new Registered(($user = User::create($validated))));
Auth::login($user);
Session::regenerate();
$this->redirectIntended(route('dashboard', absolute: false), navigate: true);
}
}; ?>
<div class="flex flex-col gap-6">
<x-auth-header :title="__('Create an account')" :description="__('Enter your details below to create your account')" />
<!-- Session Status -->
<x-auth-session-status class="text-center" :status="session('status')" />
<form method="POST" wire:submit="register" class="flex flex-col gap-6">
<!-- Name -->
<flux:input
wire:model="name"
:label="__('Name')"
type="text"
required
autofocus
autocomplete="name"
:placeholder="__('Full name')"
/>
<!-- Email Address -->
<flux:input
wire:model="email"
:label="__('Email address')"
type="email"
required
autocomplete="email"
placeholder="email@example.com"
/>
<!-- Password -->
<flux:input
wire:model="password"
:label="__('Password')"
type="password"
required
autocomplete="new-password"
:placeholder="__('Password')"
viewable
/>
<!-- Confirm Password -->
<flux:input
wire:model="password_confirmation"
:label="__('Confirm password')"
type="password"
required
autocomplete="new-password"
:placeholder="__('Confirm password')"
viewable
/>
<div class="flex items-center justify-end">
<flux:button type="submit" variant="primary" class="w-full" data-test="register-user-button">
{{ __('Create account') }}
</flux:button>
</div>
</form>
<div class="space-x-1 rtl:space-x-reverse text-center text-sm text-zinc-600 dark:text-zinc-400">
<span>{{ __('Already have an account?') }}</span>
<flux:link :href="route('login')" wire:navigate>{{ __('Log in') }}</flux:link>
</div>
</div>
@@ -0,0 +1,114 @@
<?php
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
use Illuminate\Validation\Rules;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Locked;
use Livewire\Volt\Component;
new #[Layout('components.layouts.auth')] class extends Component {
#[Locked]
public string $token = '';
public string $email = '';
public string $password = '';
public string $password_confirmation = '';
/**
* Mount the component.
*/
public function mount(string $token): void
{
$this->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' => $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::PasswordReset) {
$this->addError('email', __($status));
return;
}
Session::flash('status', __($status));
$this->redirectRoute('login', navigate: true);
}
}; ?>
<div class="flex flex-col gap-6">
<x-auth-header :title="__('Reset password')" :description="__('Please enter your new password below')" />
<!-- Session Status -->
<x-auth-session-status class="text-center" :status="session('status')" />
<form method="POST" wire:submit="resetPassword" class="flex flex-col gap-6">
<!-- Email Address -->
<flux:input
wire:model="email"
:label="__('Email')"
type="email"
required
autocomplete="email"
/>
<!-- Password -->
<flux:input
wire:model="password"
:label="__('Password')"
type="password"
required
autocomplete="new-password"
:placeholder="__('Password')"
viewable
/>
<!-- Confirm Password -->
<flux:input
wire:model="password_confirmation"
:label="__('Confirm password')"
type="password"
required
autocomplete="new-password"
:placeholder="__('Confirm password')"
viewable
/>
<div class="flex items-center justify-end">
<flux:button type="submit" variant="primary" class="w-full" data-test="reset-password-button">
{{ __('Reset password') }}
</flux:button>
</div>
</form>
</div>
@@ -0,0 +1,99 @@
<x-layouts.auth>
<div class="flex flex-col gap-6">
<div
class="relative w-full h-auto"
x-cloak
x-data="{
showRecoveryInput: @js($errors->has('recovery_code')),
code: '',
recovery_code: '',
toggleInput() {
this.showRecoveryInput = !this.showRecoveryInput;
this.code = '';
this.recovery_code = '';
$dispatch('clear-2fa-auth-code');
$nextTick(() => {
this.showRecoveryInput
? this.$refs.recovery_code?.focus()
: $dispatch('focus-2fa-auth-code');
});
},
}"
>
<div x-show="!showRecoveryInput">
<x-auth-header
:title="__('Authentication Code')"
:description="__('Enter the authentication code provided by your authenticator application.')"
/>
</div>
<div x-show="showRecoveryInput">
<x-auth-header
:title="__('Recovery Code')"
:description="__('Please confirm access to your account by entering one of your emergency recovery codes.')"
/>
</div>
<form method="POST" action="{{ route('two-factor.login.store') }}">
@csrf
<div class="space-y-5 text-center">
<div x-show="!showRecoveryInput">
<div class="flex items-center justify-center my-5">
<x-input-otp
name="code"
digits="6"
autocomplete="one-time-code"
x-model="code"
/>
</div>
@error('code')
<flux:text color="red">
{{ $message }}
</flux:text>
@enderror
</div>
<div x-show="showRecoveryInput">
<div class="my-5">
<flux:input
type="text"
name="recovery_code"
x-ref="recovery_code"
x-bind:required="showRecoveryInput"
autocomplete="one-time-code"
x-model="recovery_code"
/>
</div>
@error('recovery_code')
<flux:text color="red">
{{ $message }}
</flux:text>
@enderror
</div>
<flux:button
variant="primary"
type="submit"
class="w-full"
>
{{ __('Continue') }}
</flux:button>
</div>
<div class="mt-5 space-x-0.5 text-sm leading-5 text-center">
<span class="opacity-50">{{ __('or you can') }}</span>
<div class="inline font-medium underline cursor-pointer opacity-80">
<span x-show="!showRecoveryInput" @click="toggleInput()">{{ __('login using a recovery code') }}</span>
<span x-show="showRecoveryInput" @click="toggleInput()">{{ __('login using an authentication code') }}</span>
</div>
</div>
</form>
</div>
</div>
</x-layouts.auth>
@@ -0,0 +1,64 @@
<?php
use App\Livewire\Actions\Logout;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
use Illuminate\View\View;
use Livewire\Attributes\Layout;
use Livewire\Volt\Component;
new #[Layout('components.layouts.auth')] class extends Component {
/**
* Send an email verification notification to the user.
*/
public function sendVerification(): void
{
Auth::user()->sendEmailVerificationNotification();
Session::flash('status', 'verification-link-sent');
}
/**
* Log the current user out of the application.
*/
public function logout(Logout $logout): void
{
$logout();
$this->redirect('/', navigate: true);
}
/**
* Handle the component's rendering hook.
*/
public function rendering(View $view): void
{
if (Auth::user()->hasVerifiedEmail()) {
$this->redirectIntended(default: route('dashboard', absolute: false), navigate: true);
return;
}
}
}; ?>
<div class="mt-4 flex flex-col gap-6">
<flux:text class="text-center">
{{ __('Please verify your email address by clicking on the link we just emailed to you.') }}
</flux:text>
@if (session('status') == 'verification-link-sent')
<flux:text class="text-center font-medium !dark:text-green-400 !text-green-600">
{{ __('A new verification link has been sent to the email address you provided during registration.') }}
</flux:text>
@endif
<div class="flex flex-col items-center justify-between space-y-3">
<flux:button wire:click="sendVerification" variant="primary" class="w-full">
{{ __('Resend verification email') }}
</flux:button>
<flux:link class="text-sm cursor-pointer" wire:click="logout" data-test="logout-button">
{{ __('Log out') }}
</flux:link>
</div>
</div>