diff --git a/app/Filament/Resources/Users/Pages/CreateUser.php b/app/Filament/Resources/Users/Pages/CreateUser.php new file mode 100644 index 0000000..125b3ff --- /dev/null +++ b/app/Filament/Resources/Users/Pages/CreateUser.php @@ -0,0 +1,11 @@ +components([ + TextInput::make('name') + ->label('Name') + ->required(), + TextInput::make('email') + ->label('E-Mail') + ->email() + ->required() + ->unique(ignoreRecord: true), + TextInput::make('password') + ->label('Passwort') + ->password() + ->revealable() + ->required(fn (string $operation) => $operation === 'create') + ->dehydrateStateUsing(fn ($state) => filled($state) ? bcrypt($state) : null) + ->dehydrated(fn ($state) => filled($state)) + ->minLength(8), + Checkbox::make('is_admin') + ->label('Administrator'), + ]); + } +} diff --git a/app/Filament/Resources/Users/Tables/UsersTable.php b/app/Filament/Resources/Users/Tables/UsersTable.php new file mode 100644 index 0000000..b279605 --- /dev/null +++ b/app/Filament/Resources/Users/Tables/UsersTable.php @@ -0,0 +1,44 @@ +columns([ + TextColumn::make('name') + ->label('Name') + ->searchable() + ->sortable(), + TextColumn::make('email') + ->label('E-Mail') + ->searchable() + ->sortable(), + IconColumn::make('is_admin') + ->label('Administrator'), + TextColumn::make('created_at') + ->label('Erstellt') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([]) + ->recordActions([ + EditAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + ]), + ]); + } +} diff --git a/app/Filament/Resources/Users/UserResource.php b/app/Filament/Resources/Users/UserResource.php new file mode 100644 index 0000000..182b4a8 --- /dev/null +++ b/app/Filament/Resources/Users/UserResource.php @@ -0,0 +1,54 @@ + ListUsers::route('/'), + 'create' => CreateUser::route('/create'), + 'edit' => EditUser::route('/{record}/edit'), + ]; + } +} diff --git a/app/Http/Controllers/LoginController.php b/app/Http/Controllers/LoginController.php new file mode 100644 index 0000000..2119bac --- /dev/null +++ b/app/Http/Controllers/LoginController.php @@ -0,0 +1,37 @@ +validate([ + 'email' => ['required', 'email'], + 'password' => ['required'], + ]); + + if (Auth::attempt($credentials, $request->boolean('remember'))) { + $request->session()->regenerate(); + return redirect()->intended('/'); + } + + return back()->withErrors(['email' => 'Die eingegebenen Daten stimmen nicht überein.'])->onlyInput('email'); + } + + public function logout(Request $request) + { + Auth::logout(); + $request->session()->invalidate(); + $request->session()->regenerateToken(); + return redirect('/login'); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index f6ba1d2..037253e 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2,31 +2,33 @@ namespace App\Models; -// use Illuminate\Contracts\Auth\MustVerifyEmail; use Database\Factories\UserFactory; +use Filament\Models\Contracts\FilamentUser; +use Filament\Panel; use Illuminate\Database\Eloquent\Attributes\Fillable; use Illuminate\Database\Eloquent\Attributes\Hidden; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; -#[Fillable(['name', 'email', 'password'])] +#[Fillable(['name', 'email', 'password', 'is_admin'])] #[Hidden(['password', 'remember_token'])] -class User extends Authenticatable +class User extends Authenticatable implements FilamentUser { /** @use HasFactory */ use HasFactory, Notifiable; - /** - * Get the attributes that should be cast. - * - * @return array - */ + public function canAccessPanel(Panel $panel): bool + { + return $this->is_admin; + } + protected function casts(): array { return [ 'email_verified_at' => 'datetime', - 'password' => 'hashed', + 'password' => 'hashed', + 'is_admin' => 'boolean', ]; } } diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index c4ceb07..370a066 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -25,11 +25,12 @@ class UserFactory extends Factory public function definition(): array { return [ - 'name' => fake()->name(), - 'email' => fake()->unique()->safeEmail(), - 'email_verified_at' => now(), - 'password' => static::$password ??= Hash::make('password'), - 'remember_token' => Str::random(10), + 'name' => fake()->name(), + 'email' => fake()->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => static::$password ??= Hash::make('password'), + 'remember_token' => Str::random(10), + 'is_admin' => false, ]; } diff --git a/database/migrations/2026_06_28_155733_add_is_admin_to_users_table.php b/database/migrations/2026_06_28_155733_add_is_admin_to_users_table.php new file mode 100644 index 0000000..312e980 --- /dev/null +++ b/database/migrations/2026_06_28_155733_add_is_admin_to_users_table.php @@ -0,0 +1,28 @@ +boolean('is_admin')->default(false)->after('remember_token'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('is_admin'); + }); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index e00b4dd..9874a69 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -12,6 +12,7 @@ class DatabaseSeeder extends Seeder public function run(): void { $this->call([ + UserSeeder::class, PhotoprofileSeeder::class, ]); } diff --git a/database/seeders/UserSeeder.php b/database/seeders/UserSeeder.php new file mode 100644 index 0000000..46fb67a --- /dev/null +++ b/database/seeders/UserSeeder.php @@ -0,0 +1,31 @@ + 'admin@fotobox.local'], + [ + 'name' => 'Admin', + 'password' => Hash::make('admin'), + 'is_admin' => true, + ] + ); + + User::firstOrCreate( + ['email' => 'user@fotobox.local'], + [ + 'name' => 'User', + 'password' => Hash::make('user'), + 'is_admin' => false, + ] + ); + } +} diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php new file mode 100644 index 0000000..cf7a4f2 --- /dev/null +++ b/resources/views/auth/login.blade.php @@ -0,0 +1,45 @@ + + + + + + Fotobox – Anmelden + @if (file_exists(public_path('build/manifest.json')) || file_exists(public_path('hot'))) + @vite(['resources/css/app.css', 'resources/js/app.js']) + @endif + + + +

Fotobox

+ +
+ @csrf + +
+ + + @error('email') + {{ $message }} + @enderror +
+ +
+ + +
+ + + + +
+ + + diff --git a/routes/web.php b/routes/web.php index 1f56b05..1923fd4 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,8 +1,16 @@ group(function () { +Route::middleware('guest')->group(function () { + Route::get('/login', [LoginController::class, 'show'])->name('login'); + Route::post('/login', [LoginController::class, 'login']); +}); + +Route::post('/logout', [LoginController::class, 'logout'])->name('logout')->middleware('auth'); + +Route::middleware('auth')->group(function () { Route::get('/', [PhotoController::class, 'index'])->name('camera'); });