Files
newznab-tmux-NNTmux/app/Observers/UserActivityObserver.php
T
2026-06-17 09:49:39 +02:00

175 lines
5.4 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Observers;
use App\Jobs\SendAccountChangedEmail;
use App\Models\User;
use App\Models\UserActivity;
use Spatie\Permission\Models\Role;
class UserActivityObserver
{
/**
* Handle the User "created" event.
*/
public function created(User $user): void
{
UserActivity::create([
'user_id' => $user->id,
'username' => $user->username,
'activity_type' => 'registered',
'description' => "New user registered: {$user->username}",
'metadata' => array_filter([
'email' => $user->email,
'ip_address' => config('nntmux_settings.store_user_ip') ? request()->ip() : null,
], static fn ($value): bool => $value !== null),
]);
}
/**
* Handle the User "updated" event.
*/
public function updated(User $user): void
{
// Check if the role was changed
if ($user->isDirty('roles_id')) {
$oldRoleId = $user->getOriginal('roles_id');
$newRoleId = $user->roles_id;
// Get role names
$oldRoleName = $this->getRoleName($oldRoleId);
$newRoleName = $this->getRoleName($newRoleId);
UserActivity::create([
'user_id' => $user->id,
'username' => $user->username,
'activity_type' => 'role_updated',
'description' => "User role updated: {$user->username}{$newRoleName}",
'metadata' => [
'old_role_id' => $oldRoleId,
'new_role_id' => $newRoleId,
'old_role_name' => $oldRoleName,
'new_role_name' => $newRoleName,
'updated_by' => auth()->user()?->username ?? 'System', // @phpstan-ignore nullsafe.neverNull
],
]);
// Send email notification to user about role change
SendAccountChangedEmail::dispatch($user);
}
}
/**
* Handle the User "deleting" event (before soft delete).
* Sets the deleted_by field to track who deleted the account.
*/
public function deleting(User $user): void
{
$deletedBy = auth()->user()?->username ?? 'System'; // @phpstan-ignore nullsafe.neverNull
// Check if user is deleting their own account
if (auth()->check() && auth()->id() === $user->id) {
$deletedBy = 'Self';
}
// Update the deleted_by field directly in the database
// We use a direct query to avoid triggering update events
User::withoutEvents(function () use ($user, $deletedBy) {
User::withTrashed()
->where('id', $user->id)
->update(['deleted_by' => $deletedBy]);
});
// Store the deleted_by value on the model for the deleted event
$user->deleted_by = $deletedBy;
}
/**
* Handle the User "deleted" event (soft delete).
*/
public function deleted(User $user): void
{
// Only log if it's a soft delete (not force delete)
if ($user->trashed()) {
UserActivity::create([
'user_id' => $user->id,
'username' => $user->username,
'activity_type' => 'deleted',
'description' => "User deleted: {$user->username}",
'metadata' => [
'email' => $user->email,
'deleted_by' => $user->deleted_by ?? 'System',
],
]);
}
}
/**
* Handle the User "restored" event.
*/
public function restored(User $user): void
{
// Clear the deleted_by field when user is restored
User::withoutEvents(function () use ($user) {
User::where('id', $user->id)->update(['deleted_by' => null]);
});
UserActivity::create([
'user_id' => $user->id,
'username' => $user->username,
'activity_type' => 'registered',
'description' => "User restored: {$user->username}",
'metadata' => [
'restored_by' => auth()->user()?->username ?? 'System', // @phpstan-ignore nullsafe.neverNull
],
]);
}
/**
* Handle the User "force deleted" event.
*/
public function forceDeleted(User $user): void
{
$deletedBy = auth()->user()?->username ?? 'System'; // @phpstan-ignore nullsafe.neverNull
// Check if user is deleting their own account
if (auth()->check() && auth()->id() === $user->id) {
$deletedBy = 'Self';
}
// Log permanent deletion
UserActivity::create([
'user_id' => null, // User no longer exists
'username' => $user->username,
'activity_type' => 'deleted',
'description' => "User permanently deleted: {$user->username}",
'is_permanent' => true,
'metadata' => [
'email' => $user->email,
'deleted_by' => $deletedBy,
'permanent' => true,
],
]);
}
/**
* Get role name by ID
*/
private function getRoleName(?int $roleId): string
{
if (! $roleId) {
return 'None';
}
try {
$role = Role::find($roleId);
return $role ? $role->name : "Role #{$roleId}";
} catch (\Exception $e) {
return "Role #{$roleId}";
}
}
}