diff --git a/app/Http/Middleware/TrackLastActivity.php b/app/Http/Middleware/TrackLastActivity.php new file mode 100644 index 00000000..449114d2 --- /dev/null +++ b/app/Http/Middleware/TrackLastActivity.php @@ -0,0 +1,28 @@ +user()) { + $lastActive = $user->last_active_at; + + if (! $lastActive || $lastActive->diffInMinutes(Carbon::now()) >= 3) { + $user->update([ + 'last_active_at' => now(), + ]); + } + } + + return $next($request); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index eabfdec8..450914ee 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -54,6 +54,7 @@ * @property Carbon | null $banned_at * @property Carbon $created_at * @property Carbon $updated_at + * @property Carbon | null $last_active_at * @property Collection | Activity[] $activities * @property Collection | Article[] $articles * @property Collection | Thread[] $threads @@ -96,6 +97,7 @@ final class User extends Authenticatable implements FilamentUser, HasAvatar, Has 'banned_at', 'banned_reason', 'opt_in', + 'last_active_at', ]; protected $hidden = [ @@ -103,6 +105,7 @@ final class User extends Authenticatable implements FilamentUser, HasAvatar, Has 'remember_token', 'two_factor_recovery_codes', 'two_factor_secret', + 'last_active_at', ]; protected $casts = [ @@ -110,6 +113,7 @@ final class User extends Authenticatable implements FilamentUser, HasAvatar, Has 'last_login_at' => 'datetime', 'banned_at' => 'datetime', 'settings' => 'array', + 'last_active_at' => 'datetime', ]; public function hasProvider(string $provider): bool diff --git a/bootstrap/app.php b/bootstrap/app.php index 3f6d2e38..2522bac8 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -20,6 +20,7 @@ ]); $middleware->web(append: [ LocaleMiddleware::class, + \App\Http\Middleware\TrackLastActivity::class, ]); }) ->withExceptions(function (Exceptions $exceptions): void { diff --git a/composer.lock b/composer.lock index 2c2fce0f..def99c66 100644 --- a/composer.lock +++ b/composer.lock @@ -6676,10 +6676,6 @@ ], "type": "library", "extra": { - "branch-alias": { - "dev-master": "3.x-dev", - "dev-2.x": "2.x-dev" - }, "laravel": { "providers": [ "Carbon\\Laravel\\ServiceProvider" @@ -6689,6 +6685,10 @@ "includes": [ "extension.neon" ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-master": "3.x-dev" } }, "autoload": { diff --git a/database/migrations/2024_12_19_234550_add_last_active_at_to_users_table.php b/database/migrations/2024_12_19_234550_add_last_active_at_to_users_table.php new file mode 100644 index 00000000..4a6ca0eb --- /dev/null +++ b/database/migrations/2024_12_19_234550_add_last_active_at_to_users_table.php @@ -0,0 +1,17 @@ +timestamp('last_active_at')->nullable()->after('last_login_ip'); + }); + } +}; diff --git a/tests/Feature/UserActivitiesTest.php b/tests/Feature/UserActivitiesTest.php index a518252f..61a87e0a 100644 --- a/tests/Feature/UserActivitiesTest.php +++ b/tests/Feature/UserActivitiesTest.php @@ -5,6 +5,7 @@ use App\Models\Activity; use App\Models\Article; use Carbon\Carbon; +use Illuminate\Support\Facades\Route; it('records activity when an article is created', function (): void { $user = $this->login(); @@ -39,3 +40,12 @@ Carbon::now()->subWeek()->format('Y-m-d') )); }); + +it('does not update the last activity for unauthenticated users', function (): void { + Route::middleware(\App\Http\Middleware\TrackLastActivity::class) + ->get('/activity-user', fn () => 'ok'); + + $this->get('/activity-user')->assertOk(); + + $this->assertDatabaseMissing('users', ['last_active_at' => now()]); +});