Add Login module
This commit is contained in:
parent
5653489fa6
commit
cc1c12d9f1
app
config
database/factories
modules/Auth
resources
js
lang/de
views/components/layouts
routes
|
@ -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