diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index c878d51..e2b9b50 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -16,10 +16,10 @@ jobs: strategy: fail-fast: true matrix: - os: [ubuntu-latest, windows-latest] + os: [ubuntu-latest] php: [8.3, 8.2] laravel: [11.*, 10.*] - stability: [prefer-lowest, prefer-stable] + stability: [prefer-stable] include: - laravel: 11.* testbench: 9.* diff --git a/composer.json b/composer.json index 20f1e56..d47764d 100644 --- a/composer.json +++ b/composer.json @@ -32,8 +32,7 @@ "pestphp/pest-plugin-laravel": "^2.3", "phpstan/extension-installer": "^1.3", "phpstan/phpstan-deprecation-rules": "^1.1", - "phpstan/phpstan-phpunit": "^1.3", - "spatie/laravel-ray": "^1.35" + "phpstan/phpstan-phpunit": "^1.3" }, "autoload": { "psr-4": { diff --git a/config/users.php b/config/users.php index 310bede..b0ce898 100644 --- a/config/users.php +++ b/config/users.php @@ -1,5 +1,11 @@ [ 'user' => [ - 'model' => \Backstage\Laravel\Users\Eloquent\Models\User::class, + 'model' => User::class, 'table' => 'users', - 'observer' => \Backstage\Laravel\Users\Eloquent\Observers\UserObserver::class, + 'observer' => UserObserver::class, ], 'user_login' => [ - 'model' => \Backstage\Laravel\Users\Eloquent\Models\UserLogin::class, + 'model' => UserLogin::class, 'table' => 'user_logins', ], 'user_notification_preferences' => [ - 'model' => \Backstage\Laravel\Users\Eloquent\Models\UserNotificationPreference::class, + 'model' => UserNotificationPreference::class, 'table' => 'user_notification_preferences', ], ], @@ -27,7 +33,7 @@ 'auth' => [ 'user_created' => [ // Or set Backstage\Filament\Users\Notifications\UserInvitationNotification - 'invitation_notification' => \Backstage\Laravel\Users\Notifications\Invitation::class, + 'invitation_notification' => Invitation::class, 'notification_delivery_channels' => [ 'mail', ], diff --git a/helpers.php b/helpers.php index 5dc6c72..58db9b3 100644 --- a/helpers.php +++ b/helpers.php @@ -1,10 +1,13 @@ ip() . '?key=' . config('services.ip-api.key'))); + $geo = json_decode(@file_get_contents('https://pro.ip-api.com/json/'.request()->ip().'?key='.config('services.ip-api.key'))); session()->put('geo', $geo); } else { @@ -18,13 +21,13 @@ function geo($attribute = '') if (! function_exists('generate_password')) { function generate_password(...$args) { - return \Backstage\Laravel\Users\Domain\Password\Actions\GeneratePassword::run(...$args); + return GeneratePassword::run(...$args); } } if (! function_exists('validate_email')) { function validate_email(...$args) { - return \Backstage\Laravel\Users\Domain\Email\Actions\ValidateEmail::run(...$args); + return ValidateEmail::run(...$args); } } diff --git a/src/Console/Commands/DeleteUser.php b/src/Console/Commands/DeleteUser.php index 4272297..881fffb 100644 --- a/src/Console/Commands/DeleteUser.php +++ b/src/Console/Commands/DeleteUser.php @@ -18,7 +18,7 @@ class DeleteUser extends Command public function handle() { if ($this->option('force-delete') && posix_geteuid() !== 0) { - error('This command must be run as root. Try: sudo php artisan ' . str($this->signature)->replace(['{', '}'], '')->toString()); + error('This command must be run as root. Try: sudo php artisan '.str($this->signature)->replace(['{', '}'], '')->toString()); return Command::FAILURE; } @@ -33,7 +33,7 @@ public function handle() $users = multiselect( label: 'Select the user(s) to delete', - options: $userCollection->pluck('name', 'id')->map(fn ($name, $id) => $name . ' (ID: ' . $id . ')')->toArray(), + options: $userCollection->pluck('name', 'id')->map(fn ($name, $id) => $name.' (ID: '.$id.')')->toArray(), required: true, ); @@ -59,7 +59,7 @@ public function handle() $user->delete(); } - $this->info($this->option('force-delete') ? 'Force deleted' : 'Deleted' . ' user: ' . $user->name); + $this->info($this->option('force-delete') ? 'Force deleted' : 'Deleted'.' user: '.$user->name); } } } diff --git a/src/Console/Commands/ListUsersCommand.php b/src/Console/Commands/ListUsersCommand.php index 63b0449..6bc41b0 100644 --- a/src/Console/Commands/ListUsersCommand.php +++ b/src/Console/Commands/ListUsersCommand.php @@ -31,7 +31,7 @@ public function handle(): void return; } - info('Found ' . $users->count() . ' user(s):'); + info('Found '.$users->count().' user(s):'); $this->renderTable($users); @@ -46,7 +46,7 @@ public function handle(): void $userId = search( label: 'Search for the user that should receive the mail', options: fn (string $value) => strlen($value) > 0 - ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->map(fn ($name, $id) => $name . ' (ID: ' . $id . ')')->toArray() + ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->map(fn ($name, $id) => $name.' (ID: '.$id.')')->toArray() : [] ); @@ -169,7 +169,7 @@ protected function renderTable(Collection $users) $user->email, $user->hasVerifiedEmail() ? 'Yes' : 'No', $user->getRoleNames()->implode(', ') ?: 'No roles', - $user->created_at->format('Y-m-d H:i:s') . ' (' . $user->created_at->diffForHumans() . ')', + $user->created_at->format('Y-m-d H:i:s').' ('.$user->created_at->diffForHumans().')', ])->toArray()); } diff --git a/src/Console/Commands/MakeUserCommand.php b/src/Console/Commands/MakeUserCommand.php index 2809329..7450349 100644 --- a/src/Console/Commands/MakeUserCommand.php +++ b/src/Console/Commands/MakeUserCommand.php @@ -106,7 +106,7 @@ public function handle(): int $this->line("Name: {$user->name}"); $this->line("Email: {$user->email}"); if ($selectedRole) { - $this->line('Role: ' . ($selectedRole instanceof Role ? $selectedRole->name : $selectedRole)); + $this->line('Role: '.($selectedRole instanceof Role ? $selectedRole->name : $selectedRole)); } $this->line("Password: {$password}"); diff --git a/src/Domain/Email/Actions/ValidateEmail.php b/src/Domain/Email/Actions/ValidateEmail.php index c8e0f25..31dff41 100644 --- a/src/Domain/Email/Actions/ValidateEmail.php +++ b/src/Domain/Email/Actions/ValidateEmail.php @@ -8,7 +8,7 @@ class ValidateEmail { use AsAction; - public function handle(string | array $email): bool | array + public function handle(string|array $email): bool|array { if (is_array($email)) { return $this->validateMultipleEmails($email); diff --git a/src/Eloquent/Observers/UserObserver.php b/src/Eloquent/Observers/UserObserver.php index e9477de..4135245 100644 --- a/src/Eloquent/Observers/UserObserver.php +++ b/src/Eloquent/Observers/UserObserver.php @@ -2,6 +2,7 @@ namespace Backstage\Laravel\Users\Eloquent\Observers; +use Backstage\Laravel\Users\Eloquent\Models\User; use Backstage\Laravel\Users\Events\Auth\UserCreated; class UserObserver @@ -9,7 +10,7 @@ class UserObserver /** * Handle the User "created" event. * - * @param \Backstage\Laravel\Users\Eloquent\Models\User $user + * @param User $user * @return void */ public function created($user) diff --git a/src/Events/Auth/UserCreated.php b/src/Events/Auth/UserCreated.php index 4acf159..bdd49d7 100644 --- a/src/Events/Auth/UserCreated.php +++ b/src/Events/Auth/UserCreated.php @@ -16,7 +16,7 @@ class UserCreated /** * The user instance. * - * @var \Backstage\Laravel\Users\Eloquent\Models\User + * @var User */ public function __construct(public User $user) {} } diff --git a/src/Jobs/RecordUserLogin.php b/src/Jobs/RecordUserLogin.php new file mode 100644 index 0000000..84618e5 --- /dev/null +++ b/src/Jobs/RecordUserLogin.php @@ -0,0 +1,50 @@ +|null $inputs + */ + public function __construct( + public int $userId, + public string $type, + public ?string $url, + public ?string $referrer, + public ?array $inputs, + public ?string $userAgent, + public ?string $ipAddress, + ) {} + + public function handle(): void + { + $userModel = config('users.eloquent.user.model'); + + if (! $userModel || ! class_exists($userModel)) { + return; + } + + $user = $userModel::find($this->userId); + + if (! $user) { + return; + } + + $user->logins()->create([ + 'user_id' => $this->userId, + 'type' => $this->type, + 'url' => $this->url, + 'referrer' => $this->referrer, + 'inputs' => $this->inputs ? json_encode($this->inputs) : null, + 'user_agent' => $this->userAgent, + 'ip_address' => $this->ipAddress, + 'hostname' => $this->ipAddress ? gethostbyaddr($this->ipAddress) : null, + ]); + } +} diff --git a/src/LaravelUsersServiceProvider.php b/src/LaravelUsersServiceProvider.php index 667fed9..c373859 100644 --- a/src/LaravelUsersServiceProvider.php +++ b/src/LaravelUsersServiceProvider.php @@ -3,8 +3,16 @@ namespace Backstage\Laravel\Users; use Backstage\Laravel\Users\Console\Commands; +use Backstage\Laravel\Users\Eloquent\Models\User; +use Backstage\Laravel\Users\Eloquent\Observers\UserObserver; use Backstage\Laravel\Users\Events\Auth\UserCreated; use Backstage\Laravel\Users\Facades\UserManager; +use Backstage\Laravel\Users\Listeners\Auth\HandleUserLogin; +use Backstage\Laravel\Users\Listeners\Auth\HandleUserLogout; +use Backstage\Laravel\Users\Listeners\Auth\SendInvitationMail; +use Illuminate\Auth\Events\Login; +use Illuminate\Auth\Events\Logout; +use Illuminate\Contracts\Http\Kernel; use Illuminate\Support\Facades\File; use Spatie\LaravelPackageTools\Package; use Spatie\LaravelPackageTools\PackageServiceProvider; @@ -31,7 +39,7 @@ public function configurePackage(Package $package): void protected function getMigrations(): array { - $migrationPath = __DIR__ . '/../database/migrations/'; + $migrationPath = __DIR__.'/../database/migrations/'; $files = File::allFiles($migrationPath); @@ -61,30 +69,30 @@ public function packageBooted() /** * @var Illuminate\Foundation\Http\Kernel $kernel */ - $kernel = $this->app->make(\Illuminate\Contracts\Http\Kernel::class); + $kernel = $this->app->make(Kernel::class); }); - if (config('users.eloquent.user.observer', \Backstage\Laravel\Users\Eloquent\Observers\UserObserver::class)) { - config('auth.providers.users.model', \Backstage\Laravel\Users\Eloquent\Models\User::class)::observe(config('users.eloquent.user.observer', \Backstage\Laravel\Users\Eloquent\Observers\UserObserver::class)); + if (config('users.eloquent.user.observer', UserObserver::class)) { + config('auth.providers.users.model', User::class)::observe(config('users.eloquent.user.observer', UserObserver::class)); } } protected function getEvents() { $this->app['events']->listen( - \Illuminate\Auth\Events\Login::class, - \Backstage\Laravel\Users\Listeners\Auth\HandleUserLogin::class + Login::class, + HandleUserLogin::class ); $this->app['events']->listen( - \Illuminate\Auth\Events\Logout::class, - \Backstage\Laravel\Users\Listeners\Auth\HandleUserLogout::class + Logout::class, + HandleUserLogout::class ); if (config('users.events.auth.user_created.enabled', true)) { $this->app['events']->listen( UserCreated::class, - \Backstage\Laravel\Users\Listeners\Auth\SendInvitationMail::class + SendInvitationMail::class ); } } diff --git a/src/Listeners/Auth/HandleUserLogin.php b/src/Listeners/Auth/HandleUserLogin.php index 3bbb348..b7e3a35 100644 --- a/src/Listeners/Auth/HandleUserLogin.php +++ b/src/Listeners/Auth/HandleUserLogin.php @@ -2,28 +2,27 @@ namespace Backstage\Laravel\Users\Listeners\Auth; +use Backstage\Laravel\Users\Eloquent\Models\User; +use Backstage\Laravel\Users\Jobs\RecordUserLogin; use Illuminate\Auth\Events\Login; class HandleUserLogin { - public function handle(Login $event) + public function handle(Login $event): void { - /** - * @var \Backstage\Laravel\Users\Eloquent\Models\User $user - */ + /** @var User $user */ $user = $event->user; $inputs = request()->except('_method', '_token', 'password'); - $user->logins()->create([ - 'user_id' => $user->id, - 'type' => 'login', - 'url' => request()->url(), - 'referrer' => request()->server('HTTP_REFERER'), - 'inputs' => count($inputs) ? json_encode($inputs) : null, - 'user_agent' => request()->server('HTTP_USER_AGENT'), - 'ip_address' => request()->ip(), - 'hostname' => gethostbyaddr(request()->ip()), - ]); + RecordUserLogin::dispatch( + userId: $user->id, + type: 'login', + url: request()->url(), + referrer: request()->server('HTTP_REFERER'), + inputs: count($inputs) ? $inputs : null, + userAgent: request()->server('HTTP_USER_AGENT'), + ipAddress: request()->ip(), + ); } } diff --git a/src/Listeners/Auth/HandleUserLogout.php b/src/Listeners/Auth/HandleUserLogout.php index 47f5144..5a565ff 100644 --- a/src/Listeners/Auth/HandleUserLogout.php +++ b/src/Listeners/Auth/HandleUserLogout.php @@ -2,28 +2,31 @@ namespace Backstage\Laravel\Users\Listeners\Auth; +use Backstage\Laravel\Users\Eloquent\Models\User; +use Backstage\Laravel\Users\Jobs\RecordUserLogin; use Illuminate\Auth\Events\Logout; class HandleUserLogout { - public function handle(Logout $event) + public function handle(Logout $event): void { - /** - * @var \Backstage\Laravel\Users\Eloquent\Models\User $user - */ + /** @var User|null $user */ $user = $event->user; + if (! $user) { + return; + } + $inputs = request()->except('_method', '_token', 'password'); - $user->logins()->create([ - 'user_id' => $user->id, - 'type' => 'logout', - 'url' => request()->url(), - 'referrer' => request()->server('HTTP_REFERER'), - 'inputs' => count($inputs) ? json_encode($inputs) : null, - 'user_agent' => request()->server('HTTP_USER_AGENT'), - 'ip_address' => request()->ip(), - 'hostname' => gethostbyaddr(request()->ip()), - ]); + RecordUserLogin::dispatch( + userId: $user->id, + type: 'logout', + url: request()->url(), + referrer: request()->server('HTTP_REFERER'), + inputs: count($inputs) ? $inputs : null, + userAgent: request()->server('HTTP_USER_AGENT'), + ipAddress: request()->ip(), + ); } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 6d7fda3..35852ea 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -13,7 +13,7 @@ protected function setUp(): void parent::setUp(); Factory::guessFactoryNamesUsing( - fn (string $modelName) => 'Backstage\\Laravel\\Users\\Database\\Factories\\' . class_basename($modelName) . 'Factory' + fn (string $modelName) => 'Backstage\\Laravel\\Users\\Database\\Factories\\'.class_basename($modelName).'Factory' ); }