Add Login module
This commit is contained in:
		
							parent
							
								
									ae4a9616b4
								
							
						
					
					
						commit
						6aeab2fd93
					
				|  | @ -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\Mailgateway\MailgatewayServiceProvider::class, | ||||
|         Modules\Nami\NamiServiceProvider::class, | ||||
|         Modules\Auth\AuthServiceProvider::class, | ||||
|     ], | ||||
| 
 | ||||
|     /* | ||||
|  |  | |||
|  | @ -27,4 +27,9 @@ class UserFactory extends Factory | |||
|             '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.', | ||||
| ]; | ||||
|  |  | |||
|  | @ -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\Payment\SubscriptionController; | ||||
| 
 | ||||
| Route::group(['namespace' => 'App\\Http\\Controllers'], function (): void { | ||||
|     Auth::routes(['register' => false]); | ||||
| }); | ||||
| 
 | ||||
| 
 | ||||
| Route::group(['middleware' => 'auth:web'], function (): void { | ||||
|     Route::post('/nami/login-check', NamiLoginCheckAction::class)->name('nami.login-check'); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue