Add Filament admin plugin stack

This commit is contained in:
fatihalp 2026-03-03 13:38:08 +03:00
parent be1341b3e9
commit 024a21306c
4 changed files with 267 additions and 0 deletions

View File

@ -3,6 +3,10 @@ namespace Modules\Partner\Providers;
use A909M\FilamentStateFusion\FilamentStateFusionPlugin;
use App\Models\User;
use App\Settings\GeneralSettings;
use DutchCodingCompany\FilamentDeveloperLogins\FilamentDeveloperLoginsPlugin;
use DutchCodingCompany\FilamentSocialite\FilamentSocialitePlugin;
use DutchCodingCompany\FilamentSocialite\Provider;
use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\AuthenticateSession;
use Filament\Http\Middleware\DisableBladeIconComponents;
@ -16,8 +20,13 @@ use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Illuminate\View\Middleware\ShareErrorsFromSession;
use Jeffgreco13\FilamentBreezy\BreezyCore;
use Laravel\Socialite\Contracts\User as SocialiteUserContract;
use Spatie\Permission\Models\Role;
use Throwable;
class PartnerPanelProvider extends PanelProvider
{
@ -43,6 +52,13 @@ class PartnerPanelProvider extends PanelProvider
)
->enableTwoFactorAuthentication()
->enableSanctumTokens(),
FilamentDeveloperLoginsPlugin::make()
->enabled(fn (): bool => app()->environment('local'))
->users([
'Partner (Add Listing)' => 'b@b.com',
])
->redirectTo(fn (): ?string => self::partnerCreateListingUrl()),
self::socialitePlugin(),
])
->pages([Dashboard::class])
->middleware([
@ -58,4 +74,104 @@ class PartnerPanelProvider extends PanelProvider
])
->authMiddleware([Authenticate::class]);
}
private static function socialitePlugin(): FilamentSocialitePlugin
{
return FilamentSocialitePlugin::make()
->providers(self::socialiteProviders())
->registration(true)
->resolveUserUsing(function (string $provider, SocialiteUserContract $oauthUser): ?User {
if (! filled($oauthUser->getEmail())) {
return null;
}
return User::query()->where('email', strtolower(trim((string) $oauthUser->getEmail())))->first();
})
->createUserUsing(function (string $provider, SocialiteUserContract $oauthUser): User {
$email = filled($oauthUser->getEmail())
? strtolower(trim((string) $oauthUser->getEmail()))
: sprintf('%s_%s@social.local', $provider, $oauthUser->getId());
$user = User::query()->firstOrCreate(
['email' => $email],
[
'name' => trim((string) ($oauthUser->getName() ?: $oauthUser->getNickname() ?: ucfirst($provider).' User')),
'password' => Hash::make(Str::random(40)),
'status' => 'active',
],
);
if (class_exists(Role::class)) {
$partnerRole = Role::firstOrCreate(['name' => 'partner', 'guard_name' => 'web']);
$user->syncRoles([$partnerRole->name]);
}
return $user;
});
}
/**
* @return array<int, Provider>
*/
private static function socialiteProviders(): array
{
$providers = [];
if (self::providerEnabled('google')) {
$providers[] = Provider::make('google')
->label('Google')
->icon('heroicon-o-globe-alt')
->color(Color::hex('#4285F4'));
}
if (self::providerEnabled('facebook')) {
$providers[] = Provider::make('facebook')
->label('Facebook')
->icon('heroicon-o-users')
->color(Color::hex('#1877F2'));
}
if (self::providerEnabled('apple')) {
$providers[] = Provider::make('apple')
->label('Apple')
->icon('heroicon-o-device-phone-mobile')
->color(Color::Gray)
->stateless(true);
}
return $providers;
}
private static function providerEnabled(string $provider): bool
{
try {
$settings = app(GeneralSettings::class);
$enabled = match ($provider) {
'google' => (bool) $settings->enable_google_login,
'facebook' => (bool) $settings->enable_facebook_login,
'apple' => (bool) $settings->enable_apple_login,
default => false,
};
return $enabled
&& filled(config("services.{$provider}.client_id"))
&& filled(config("services.{$provider}.client_secret"));
} catch (Throwable) {
return (bool) config("services.{$provider}.enabled", false)
&& filled(config("services.{$provider}.client_id"))
&& filled(config("services.{$provider}.client_secret"));
}
}
private static function partnerCreateListingUrl(): ?string
{
$partner = User::query()->where('email', 'b@b.com')->first();
if (! $partner) {
return null;
}
return route('filament.partner.resources.listings.create', ['tenant' => $partner->getKey()]);
}
}

View File

@ -0,0 +1,116 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Laravel\Socialite\Facades\Socialite;
use Throwable;
class SocialAuthController extends Controller
{
/**
* @var array<int, string>
*/
private array $allowedProviders = ['google', 'facebook', 'apple'];
public function redirect(string $provider): RedirectResponse
{
abort_unless($this->isProviderAllowed($provider), 404);
abort_unless($this->isProviderEnabled($provider), 404);
return $this->driver($provider)->redirect();
}
public function callback(Request $request, string $provider): RedirectResponse
{
abort_unless($this->isProviderAllowed($provider), 404);
abort_unless($this->isProviderEnabled($provider), 404);
try {
$oauthUser = $this->driver($provider)->user();
} catch (Throwable) {
return redirect()->route('login')
->withErrors(['email' => __('Social login failed. Please try again.')]);
}
if (! filled($oauthUser->getId())) {
return redirect()->route('login')
->withErrors(['email' => __('Unable to read social account identity.')]);
}
$socialiteUser = DB::table('socialite_users')
->where('provider', $provider)
->where('provider_id', (string) $oauthUser->getId())
->first();
$user = null;
if ($socialiteUser?->user_id) {
$user = User::query()->find($socialiteUser->user_id);
}
if (! $user) {
$email = filled($oauthUser->getEmail())
? strtolower(trim((string) $oauthUser->getEmail()))
: sprintf('%s_%s@social.local', $provider, $oauthUser->getId());
$user = User::query()->firstOrCreate(
['email' => $email],
[
'name' => trim((string) ($oauthUser->getName() ?: $oauthUser->getNickname() ?: ucfirst($provider).' User')),
'password' => Hash::make(Str::random(40)),
'status' => 'active',
'email_verified_at' => now(),
],
);
}
DB::table('socialite_users')->updateOrInsert(
[
'provider' => $provider,
'provider_id' => (string) $oauthUser->getId(),
],
[
'user_id' => $user->getKey(),
'updated_at' => now(),
'created_at' => $socialiteUser?->created_at ?? now(),
],
);
Auth::guard('web')->login($user, true);
$request->session()->regenerate();
return redirect()->intended(route('dashboard', absolute: false));
}
private function driver(string $provider)
{
$driver = Socialite::driver($provider)
->redirectUrl(route('auth.social.callback', ['provider' => $provider], absolute: true));
if ($provider === 'apple' || (bool) config("services.{$provider}.stateless", false)) {
return $driver->stateless();
}
return $driver;
}
private function isProviderAllowed(string $provider): bool
{
return in_array($provider, $this->allowedProviders, true);
}
private function isProviderEnabled(string $provider): bool
{
return (bool) config("services.{$provider}.enabled", false)
&& filled(config("services.{$provider}.client_id"))
&& filled(config("services.{$provider}.client_secret"));
}
}

View File

@ -1,6 +1,17 @@
<x-guest-layout>
<!-- Session Status -->
<x-auth-session-status class="mb-4" :status="session('status')" />
@php
$socialProviders = collect([
'google' => 'Google',
'facebook' => 'Facebook',
'apple' => 'Apple',
])->filter(
fn ($label, $provider) => (bool) config("services.{$provider}.enabled")
&& filled(config("services.{$provider}.client_id"))
&& filled(config("services.{$provider}.client_secret"))
);
@endphp
<form method="POST" action="{{ route('login') }}">
@csrf
@ -43,5 +54,20 @@
{{ __('Log in') }}
</x-primary-button>
</div>
@if($socialProviders->isNotEmpty())
<div class="mt-6 border-t pt-4">
<p class="text-sm text-gray-600 mb-3">{{ __('Or continue with') }}</p>
<div class="grid gap-2">
@foreach($socialProviders as $provider => $label)
<a href="{{ route('auth.social.redirect', ['provider' => $provider]) }}"
class="inline-flex items-center justify-center rounded-md border border-gray-300 px-3 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50">
{{ $label }}
</a>
@endforeach
</div>
</div>
@endif
</form>
</x-guest-layout>

View File

@ -8,6 +8,7 @@ use App\Http\Controllers\Auth\NewPasswordController;
use App\Http\Controllers\Auth\PasswordController;
use App\Http\Controllers\Auth\PasswordResetLinkController;
use App\Http\Controllers\Auth\RegisteredUserController;
use App\Http\Controllers\Auth\SocialAuthController;
use App\Http\Controllers\Auth\VerifyEmailController;
use Illuminate\Support\Facades\Route;
@ -22,6 +23,14 @@ Route::middleware('guest')->group(function () {
Route::post('login', [AuthenticatedSessionController::class, 'store']);
Route::get('auth/{provider}/redirect', [SocialAuthController::class, 'redirect'])
->whereIn('provider', ['google', 'facebook', 'apple'])
->name('auth.social.redirect');
Route::get('auth/{provider}/callback', [SocialAuthController::class, 'callback'])
->whereIn('provider', ['google', 'facebook', 'apple'])
->name('auth.social.callback');
Route::get('forgot-password', [PasswordResetLinkController::class, 'create'])
->name('password.request');