Add Login module
This commit is contained in:
		
							parent
							
								
									b594e92cda
								
							
						
					
					
						commit
						6acab6888b
					
				|  | @ -1,60 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Http\Controllers\Auth; |  | ||||||
| 
 |  | ||||||
| use App\Http\Controllers\Controller; |  | ||||||
| use App\Providers\RouteServiceProvider; |  | ||||||
| use Illuminate\Foundation\Auth\AuthenticatesUsers; |  | ||||||
| use Illuminate\Http\Request; |  | ||||||
| use Inertia\Response; |  | ||||||
| 
 |  | ||||||
| class LoginController extends Controller |  | ||||||
| { |  | ||||||
|     /* |  | ||||||
|     |-------------------------------------------------------------------------- |  | ||||||
|     | Login Controller |  | ||||||
|     |-------------------------------------------------------------------------- |  | ||||||
|     | |  | ||||||
|     | This controller handles authenticating users for the application and |  | ||||||
|     | redirecting them to your home screen. The controller uses a trait |  | ||||||
|     | to conveniently provide its functionality to your applications. |  | ||||||
|     | |  | ||||||
|     */ |  | ||||||
| 
 |  | ||||||
|     use AuthenticatesUsers; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Where to redirect users after login. |  | ||||||
|      * |  | ||||||
|      * @var string |  | ||||||
|      */ |  | ||||||
|     protected $redirectTo = RouteServiceProvider::HOME; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Create a new controller instance. |  | ||||||
|      * |  | ||||||
|      * @return void |  | ||||||
|      */ |  | ||||||
|     public function __construct() |  | ||||||
|     { |  | ||||||
|         $this->middleware('guest')->except('logout'); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function showLoginForm(): Response |  | ||||||
|     { |  | ||||||
|         session()->put('title', 'Anmelden'); |  | ||||||
| 
 |  | ||||||
|         return \Inertia::render('authentication/VLogin'); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Validate the user login request. |  | ||||||
|      */ |  | ||||||
|     protected function validateLogin(Request $request): void |  | ||||||
|     { |  | ||||||
|         $request->validate([ |  | ||||||
|             $this->username() => 'required|max:255|string|email', |  | ||||||
|             'password' => 'required|string', |  | ||||||
|         ]); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,71 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Http\Controllers\Auth; |  | ||||||
| 
 |  | ||||||
| use App\Http\Controllers\Controller; |  | ||||||
| use App\Providers\RouteServiceProvider; |  | ||||||
| use App\User; |  | ||||||
| use Illuminate\Foundation\Auth\RegistersUsers; |  | ||||||
| use Illuminate\Support\Facades\Hash; |  | ||||||
| use Illuminate\Support\Facades\Validator; |  | ||||||
| 
 |  | ||||||
| class RegisterController extends Controller |  | ||||||
| { |  | ||||||
|     /* |  | ||||||
|     |-------------------------------------------------------------------------- |  | ||||||
|     | Register Controller |  | ||||||
|     |-------------------------------------------------------------------------- |  | ||||||
|     | |  | ||||||
|     | This controller handles the registration of new users as well as their |  | ||||||
|     | validation and creation. By default this controller uses a trait to |  | ||||||
|     | provide this functionality without requiring any additional code. |  | ||||||
|     | |  | ||||||
|     */ |  | ||||||
| 
 |  | ||||||
|     use RegistersUsers; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Where to redirect users after registration. |  | ||||||
|      * |  | ||||||
|      * @var string |  | ||||||
|      */ |  | ||||||
|     protected $redirectTo = RouteServiceProvider::HOME; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Create a new controller instance. |  | ||||||
|      * |  | ||||||
|      * @return void |  | ||||||
|      */ |  | ||||||
|     public function __construct() |  | ||||||
|     { |  | ||||||
|         $this->middleware('guest'); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Get a validator for an incoming registration request. |  | ||||||
|      * |  | ||||||
|      * @return \Illuminate\Contracts\Validation\Validator |  | ||||||
|      */ |  | ||||||
|     protected function validator(array $data) |  | ||||||
|     { |  | ||||||
|         return Validator::make($data, [ |  | ||||||
|             'name' => ['required', 'string', 'max:255'], |  | ||||||
|             'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], |  | ||||||
|             'password' => ['required', 'string', 'min:8', 'confirmed'], |  | ||||||
|         ]); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Create a new user instance after a valid registration. |  | ||||||
|      * |  | ||||||
|      * @return \App\User |  | ||||||
|      */ |  | ||||||
|     protected function create(array $data) |  | ||||||
|     { |  | ||||||
|         return User::create([ |  | ||||||
|             'name' => $data['name'], |  | ||||||
|             'email' => $data['email'], |  | ||||||
|             'password' => Hash::make($data['password']), |  | ||||||
|         ]); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -0,0 +1,32 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace App\View\Page; | ||||||
|  | 
 | ||||||
|  | use Illuminate\View\Component; | ||||||
|  | 
 | ||||||
|  | class Full extends Component | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     public function __construct(public string $title = '', public ?string $heading = null) | ||||||
|  |     { | ||||||
|  |         session()->put('title', $title); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function render() | ||||||
|  |     { | ||||||
|  |         return <<<'HTML' | ||||||
|  |             <div class="min-w-[16rem] sm:min-w-[18rem] md:min-w-[24rem] bg-gray-800 rounded-xl overflow-hidden shadow-lg @if($heading === null) p-6 md:p-10 @endif"> | ||||||
|  |                 @if ($heading) | ||||||
|  |                 <div class="h-24 p-6 md:px-10 bg-primary-800 flex justify-between items-center w-full"> | ||||||
|  |                     <span class="text-primary-500 text-xl">{{$heading}}</span> | ||||||
|  |                     <img src="{{asset('img/dpsg.gif')}}" class="w-24" /> | ||||||
|  |                 </div> | ||||||
|  |                 @endif | ||||||
|  | 
 | ||||||
|  |                 <div @if($heading !== null) class="p-6 md:p-10" @endif> | ||||||
|  |                     {{ $slot }} | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |         HTML; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,22 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace App\View\Ui; | ||||||
|  | 
 | ||||||
|  | use Illuminate\View\Component; | ||||||
|  | 
 | ||||||
|  | class Button extends Component | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     public function __construct(public string $type = 'button') | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function render() | ||||||
|  |     { | ||||||
|  |         return <<<'HTML' | ||||||
|  |             <button type="{{$type}}" class="px-3 py-2 uppercase no-underline text-sm items-center justify-center bg-primary-700 rounded text-primary-300"> | ||||||
|  |                 {{$slot}} | ||||||
|  |             </button> | ||||||
|  |         HTML; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -183,6 +183,7 @@ return [ | ||||||
|         Modules\Invoice\InvoiceServiceProvider::class, |         Modules\Invoice\InvoiceServiceProvider::class, | ||||||
|         Modules\Mailgateway\MailgatewayServiceProvider::class, |         Modules\Mailgateway\MailgatewayServiceProvider::class, | ||||||
|         Modules\Nami\NamiServiceProvider::class, |         Modules\Nami\NamiServiceProvider::class, | ||||||
|  |         Modules\Auth\AuthServiceProvider::class, | ||||||
|     ], |     ], | ||||||
| 
 | 
 | ||||||
|     /* |     /* | ||||||
|  |  | ||||||
|  | @ -27,4 +27,9 @@ class UserFactory extends Factory | ||||||
|             'lastname' => $this->faker->lastName, |             'lastname' => $this->faker->lastName, | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     public function loginData(string $email, string $password): self | ||||||
|  |     { | ||||||
|  |         return $this->state(['email' => $email, 'password' => Hash::make($password)]); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,31 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace Modules\Auth; | ||||||
|  | 
 | ||||||
|  | use Illuminate\Routing\Router; | ||||||
|  | use Illuminate\Support\ServiceProvider; | ||||||
|  | use Modules\Auth\Components\LoginForm; | ||||||
|  | 
 | ||||||
|  | class AuthServiceProvider extends ServiceProvider | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * Register services. | ||||||
|  |      * | ||||||
|  |      * @return void | ||||||
|  |      */ | ||||||
|  |     public function register() | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Bootstrap services. | ||||||
|  |      * | ||||||
|  |      * @return void | ||||||
|  |      */ | ||||||
|  |     public function boot() | ||||||
|  |     { | ||||||
|  |         app(Router::class)->middleware(['web', 'guest'])->group(function ($router) { | ||||||
|  |             $router->get('/login', LoginForm::class)->name('login'); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,62 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace Modules\Auth\Components; | ||||||
|  | 
 | ||||||
|  | use Illuminate\Foundation\Auth\AuthenticatesUsers; | ||||||
|  | use Illuminate\Foundation\Auth\ThrottlesLogins; | ||||||
|  | use Illuminate\Http\Request; | ||||||
|  | use Illuminate\Validation\ValidationException; | ||||||
|  | use Livewire\Attributes\Layout; | ||||||
|  | use Livewire\Component; | ||||||
|  | 
 | ||||||
|  | class LoginForm extends Component | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     use AuthenticatesUsers; | ||||||
|  |     use ThrottlesLogins; | ||||||
|  | 
 | ||||||
|  |     public string $email = ''; | ||||||
|  |     public string $password = ''; | ||||||
|  | 
 | ||||||
|  |     public function validateLogin(Request $request) | ||||||
|  |     { | ||||||
|  |         $this->validate([ | ||||||
|  |             'email' => 'required|max:255|string|email', | ||||||
|  |             'password' => 'required|string', | ||||||
|  |         ]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     protected function credentials(Request $request) | ||||||
|  |     { | ||||||
|  |         return ['email' => $this->email, 'password' => $this->password]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     protected function sendLoginResponse(Request $request) | ||||||
|  |     { | ||||||
|  |         return redirect()->intended('/'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function submit() | ||||||
|  |     { | ||||||
|  |         return $this->login(request()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[Layout('components.layouts.full')]
 | ||||||
|  |     public function render(): string | ||||||
|  |     { | ||||||
|  |         return <<<'HTML' | ||||||
|  |             <x-page::full heading="Login" title="Login"> | ||||||
|  |                 <form wire:submit="submit"> | ||||||
|  |                     <div class="grid gap-5"> | ||||||
|  |                         <x-form::text name="email" wire:model="email" label="E-Mail-Adresse"></x-form::text> | ||||||
|  |                         <x-form::text name="password" wire:model="password" type="password" label="Passwort"></x-form::text> | ||||||
|  |                         <x-ui::button type="submit">Login</x-ui::button> | ||||||
|  |                         <div class="flex justify-center"> | ||||||
|  |                             <button type="button" class="text-gray-500 text-sm hover:text-gray-300" @click.prevent="$inertia.visit('/password/reset')">Passwort vergessen?</button> | ||||||
|  |                         </div> | ||||||
|  |                     </div> | ||||||
|  |                 </form> | ||||||
|  |             </x-page::full> | ||||||
|  |         HTML; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,69 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace Modules\Auth\Components; | ||||||
|  | 
 | ||||||
|  | use App\User; | ||||||
|  | use Illuminate\Auth\Events\Lockout; | ||||||
|  | use Tests\TestCase; | ||||||
|  | use Illuminate\Foundation\Testing\DatabaseTransactions; | ||||||
|  | use Illuminate\Support\Facades\Event; | ||||||
|  | use Livewire\Livewire; | ||||||
|  | 
 | ||||||
|  | uses(TestCase::class); | ||||||
|  | uses(DatabaseTransactions::class); | ||||||
|  | 
 | ||||||
|  | it('redirects to login', function () { | ||||||
|  |     test()->get('/')->assertRedirect('/login'); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | it('displays component', function () { | ||||||
|  |     test()->get('/login')->assertSeeLivewire(LoginForm::class)->assertDontSee('Dashboard'); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | it('displays form', function () { | ||||||
|  |     Livewire::test(LoginForm::class) | ||||||
|  |         ->assertSee('Login') | ||||||
|  |         ->assertSee('Passwort vergessen'); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | it('loggs in', function () { | ||||||
|  |     User::factory()->loginData('admin@example.com', 'secret')->create(); | ||||||
|  |     Livewire::test(LoginForm::class) | ||||||
|  |         ->set('email', 'admin@example.com') | ||||||
|  |         ->set('password', 'secret') | ||||||
|  |         ->call('submit') | ||||||
|  |         ->assertRedirect('/'); | ||||||
|  | 
 | ||||||
|  |     $this->assertEquals('admin@example.com', auth()->user()->email); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | it('displays failed login response', function () { | ||||||
|  |     Livewire::test(LoginForm::class) | ||||||
|  |         ->set('email', 'admin@example.com') | ||||||
|  |         ->set('password', 'secret') | ||||||
|  |         ->call('submit') | ||||||
|  |         ->assertHasErrors(['email' => 'Login fehlgeschlagen.']); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | it('increments login attempts', function () { | ||||||
|  |     Event::fake([Lockout::class]); | ||||||
|  |     Livewire::test(LoginForm::class) | ||||||
|  |         ->set('email', 'admin@example.com') | ||||||
|  |         ->set('password', 'secret') | ||||||
|  |         ->call('submit') | ||||||
|  |         ->call('submit') | ||||||
|  |         ->call('submit') | ||||||
|  |         ->call('submit') | ||||||
|  |         ->call('submit') | ||||||
|  |         ->call('submit'); | ||||||
|  |     Event::assertDispatchedTimes(Lockout::class); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | it('requires email and password', function () { | ||||||
|  |     User::factory()->loginData('admin@example.com', 'secret')->create(); | ||||||
|  |     Livewire::test(LoginForm::class) | ||||||
|  |         ->set('email', '') | ||||||
|  |         ->set('password', '') | ||||||
|  |         ->call('submit') | ||||||
|  |         ->assertHasErrors(['email', 'password']); | ||||||
|  | }); | ||||||
|  | @ -1,10 +0,0 @@ | ||||||
| <template> |  | ||||||
|     <div class="h-24 p-6 md:px-10 bg-primary-800 flex justify-between items-center w-full"> |  | ||||||
|         <span class="text-primary-500 text-xl"><slot></slot></span> |  | ||||||
|         <img src="../../../img/dpsg.gif" class="w-24" /> |  | ||||||
|     </div> |  | ||||||
| </template> |  | ||||||
| 
 |  | ||||||
| <script> |  | ||||||
| export default {}; |  | ||||||
| </script> |  | ||||||
|  | @ -1,36 +0,0 @@ | ||||||
| <template> |  | ||||||
|     <page-full-layout banner> |  | ||||||
|         <template #heading> |  | ||||||
|             <page-full-heading-banner>Login</page-full-heading-banner> |  | ||||||
|         </template> |  | ||||||
|         <form @submit.prevent="submit"> |  | ||||||
|             <div class="grid gap-5"> |  | ||||||
|                 <f-text id="email" v-model="values.email" label="E-Mail-Adresse"></f-text> |  | ||||||
|                 <f-text id="password" v-model="values.password" type="password" label="Passwort"></f-text> |  | ||||||
|                 <button type="submit" class="btn btn-primary">Login</button> |  | ||||||
|             </div> |  | ||||||
|         </form> |  | ||||||
|     </page-full-layout> |  | ||||||
| </template> |  | ||||||
| 
 |  | ||||||
| <script> |  | ||||||
| import FullLayout from '../layouts/FullLayout.vue'; |  | ||||||
| 
 |  | ||||||
| export default { |  | ||||||
|     layout: FullLayout, |  | ||||||
| 
 |  | ||||||
|     data: function () { |  | ||||||
|         return { |  | ||||||
|             values: { |  | ||||||
|                 email: '', |  | ||||||
|                 password: '', |  | ||||||
|             }, |  | ||||||
|         }; |  | ||||||
|     }, |  | ||||||
|     methods: { |  | ||||||
|         submit() { |  | ||||||
|             this.$inertia.post('/login', this.values); |  | ||||||
|         }, |  | ||||||
|     }, |  | ||||||
| }; |  | ||||||
| </script> |  | ||||||
|  | @ -1,39 +0,0 @@ | ||||||
| <template> |  | ||||||
|     <page-full-layout banner> |  | ||||||
|         <template #heading> |  | ||||||
|             <page-full-heading-banner>Login</page-full-heading-banner> |  | ||||||
|         </template> |  | ||||||
|         <form @submit.prevent="submit"> |  | ||||||
|             <div class="grid gap-5"> |  | ||||||
|                 <f-text id="email" v-model="values.email" label="E-Mail-Adresse"></f-text> |  | ||||||
|                 <f-text id="password" v-model="values.password" type="password" label="Passwort"></f-text> |  | ||||||
|                 <button type="submit" class="btn btn-primary">Login</button> |  | ||||||
|                 <div class="flex justify-center"> |  | ||||||
|                     <button type="button" class="text-gray-500 text-sm hover:text-gray-300" @click.prevent="$inertia.visit('/password/reset')">Passwort vergessen?</button> |  | ||||||
|                 </div> |  | ||||||
|             </div> |  | ||||||
|         </form> |  | ||||||
|     </page-full-layout> |  | ||||||
| </template> |  | ||||||
| 
 |  | ||||||
| <script> |  | ||||||
| import FullLayout from '../../layouts/FullLayout.vue'; |  | ||||||
| 
 |  | ||||||
| export default { |  | ||||||
|     layout: FullLayout, |  | ||||||
| 
 |  | ||||||
|     data: function () { |  | ||||||
|         return { |  | ||||||
|             values: { |  | ||||||
|                 email: '', |  | ||||||
|                 password: '', |  | ||||||
|             }, |  | ||||||
|         }; |  | ||||||
|     }, |  | ||||||
|     methods: { |  | ||||||
|         submit() { |  | ||||||
|             this.$inertia.post('/login', this.values); |  | ||||||
|         }, |  | ||||||
|     }, |  | ||||||
| }; |  | ||||||
| </script> |  | ||||||
|  | @ -12,6 +12,6 @@ return [ | ||||||
|     | |     | | ||||||
|     */ |     */ | ||||||
| 
 | 
 | ||||||
|     'failed'   => 'Diese Kombination aus Zugangsdaten wurde nicht in unserer Datenbank gefunden.', |     'failed'   => 'Login fehlgeschlagen.', | ||||||
|     'throttle' => 'Zu viele Loginversuche. Versuchen Sie es bitte in :seconds Sekunden nochmal.', |     'throttle' => 'Zu viele Loginversuche. Versuchen Sie es bitte in :seconds Sekunden nochmal.', | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | <!DOCTYPE html> | ||||||
|  | <html class="h-full" lang="de"> | ||||||
|  |     <x-head></x-head> | ||||||
|  |     <body class="min-h-full flex justify-center items-center bg-gray-900"> | ||||||
|  |         {{ $slot }} | ||||||
|  |         @livewireScriptConfig | ||||||
|  |     </body> | ||||||
|  | </html> | ||||||
|  | @ -76,9 +76,7 @@ use App\Membership\Actions\MembershipStoreAction; | ||||||
| use App\Membership\Actions\MembershipUpdateAction; | use App\Membership\Actions\MembershipUpdateAction; | ||||||
| use App\Payment\SubscriptionController; | use App\Payment\SubscriptionController; | ||||||
| 
 | 
 | ||||||
| Route::group(['namespace' => 'App\\Http\\Controllers'], function (): void { | 
 | ||||||
|     Auth::routes(['register' => false]); |  | ||||||
| }); |  | ||||||
| 
 | 
 | ||||||
| Route::group(['middleware' => 'auth:web'], function (): void { | Route::group(['middleware' => 'auth:web'], function (): void { | ||||||
|     Route::post('/nami/login-check', NamiLoginCheckAction::class)->name('nami.login-check'); |     Route::post('/nami/login-check', NamiLoginCheckAction::class)->name('nami.login-check'); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue