From 7d94630fd68e037fcc1bd3710be2c73a5534655f Mon Sep 17 00:00:00 2001 From: Nicholas Ciechanowski Date: Thu, 12 Oct 2023 22:51:26 +1100 Subject: [PATCH] feat: swap over to spatie logger --- app/Enums/LogAction.php | 22 --- .../Controllers/Api/WebHookController.php | 25 +-- app/Models/Log.php | 35 ---- app/Models/Quote.php | 58 ++----- app/Models/RequestedQuote.php | 38 ++--- app/Models/User.php | 34 +--- app/Providers/AppServiceProvider.php | 6 +- composer.json | 3 +- composer.lock | 155 +++++++++++++++++- config/activitylog.php | 52 ++++++ .../2023_10_10_200426_create_logs_table.php | 30 ---- ...10_12_080056_create_activity_log_table.php | 27 +++ ...add_event_column_to_activity_log_table.php | 22 +++ ...atch_uuid_column_to_activity_log_table.php | 22 +++ .../views/components/paginator.blade.php | 106 ++++++++++++ .../views/livewire/guest/navigation.blade.php | 2 +- .../livewire/layout/navigation.blade.php | 2 +- .../views/livewire/pages/admin/logs.blade.php | 30 ++-- .../views/livewire/pages/admin/send.blade.php | 14 +- .../views/livewire/pages/auth/login.blade.php | 13 +- .../pages/auth/reset-password.blade.php | 37 ++--- .../profile/update-password-form.blade.php | 21 +-- 22 files changed, 485 insertions(+), 269 deletions(-) delete mode 100644 app/Enums/LogAction.php delete mode 100644 app/Models/Log.php create mode 100644 config/activitylog.php delete mode 100644 database/migrations/2023_10_10_200426_create_logs_table.php create mode 100644 database/migrations/2023_10_12_080056_create_activity_log_table.php create mode 100644 database/migrations/2023_10_12_080057_add_event_column_to_activity_log_table.php create mode 100644 database/migrations/2023_10_12_080058_add_batch_uuid_column_to_activity_log_table.php create mode 100644 resources/views/components/paginator.blade.php diff --git a/app/Enums/LogAction.php b/app/Enums/LogAction.php deleted file mode 100644 index 73d0968..0000000 --- a/app/Enums/LogAction.php +++ /dev/null @@ -1,22 +0,0 @@ - auth()?->user()?->id ?? 1, - 'loggable_type' => Quote::class, - 'loggable_id' => null, - 'action' => LogAction::SEND, - 'content' => $quote, - 'ip' => request()->ip(), - ]); + activity() + ->event('send') + ->log("Manually sent quote: $quote"); $this->webHookSend($quote); } @@ -42,14 +35,10 @@ class WebHookController extends Controller { $quote = Quote::inRandomOrder()->first(); - Log::create([ - 'user_id' => auth()?->user()?->id ?? 1, - 'loggable_type' => Quote::class, - 'loggable_id' => $quote->id, - 'action' => LogAction::REQUEST, - 'content' => 'Random quote requested', - 'ip' => request()->ip(), - ]); + activity() + ->performedOn($quote) + ->event('send') + ->log("Requested quote: $quote->quote"); $this->webHookSend($quote->quote); } diff --git a/app/Models/Log.php b/app/Models/Log.php deleted file mode 100644 index 3e1be21..0000000 --- a/app/Models/Log.php +++ /dev/null @@ -1,35 +0,0 @@ -belongsTo(User::class); - } - - /** - * Get the parent loggable model (user, quote or requested quote). - */ - public function loggable(): MorphTo - { - return $this->morphTo(); - } -} diff --git a/app/Models/Quote.php b/app/Models/Quote.php index e904578..5387dea 100644 --- a/app/Models/Quote.php +++ b/app/Models/Quote.php @@ -2,67 +2,37 @@ namespace App\Models; -use App\Enums\LogAction; use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\Relations\MorphOne; use Illuminate\Database\Eloquent\SoftDeletes; +use Spatie\Activitylog\LogOptions; +use Spatie\Activitylog\Traits\LogsActivity; /** * @mixin IdeHelperQuote */ class Quote extends Model { - use SoftDeletes; - - public function request(): void - { - // Send the quote - // If success, add it to the transactions - - Log::create([ - 'user_id' => auth()?->user()?->id, - 'loggable_type' => self::class, - 'loggable_id' => $this->id, - 'action' => LogAction::REQUEST, - 'content' => $this->quote, - 'ip' => request()->ip(), - ]); - } + use LogsActivity, SoftDeletes; protected $fillable = [ 'user_id', 'quote', ]; - public function logs(): MorphOne + public function request(): void { - return $this->morphOne(Log::class, 'loggable'); + // Send the quote + // If success, add it to the transactions + + activity() + ->performedOn($this) + ->event('send') + ->log("Requested quote: $this->quote"); } - public static function boot(): void + public function getActivityLogOptions(): LogOptions { - parent::boot(); - - self::created(function (Quote $model) { - Log::create([ - 'user_id' => auth()?->user()?->id, - 'loggable_type' => self::class, - 'loggable_id' => $model->id, - 'action' => LogAction::CREATE, - 'content' => $model->quote, - 'ip' => request()->ip(), - ]); - }); - - self::deleted(function (Quote $model) { - Log::create([ - 'user_id' => auth()?->user()?->id, - 'loggable_type' => self::class, - 'loggable_id' => $model->id, - 'action' => LogAction::DELETE, - 'content' => $model->quote, - 'ip' => request()->ip(), - ]); - }); + return LogOptions::defaults() + ->logOnly(['quote']); } } diff --git a/app/Models/RequestedQuote.php b/app/Models/RequestedQuote.php index e9fa709..4a3b2cc 100644 --- a/app/Models/RequestedQuote.php +++ b/app/Models/RequestedQuote.php @@ -2,56 +2,44 @@ namespace App\Models; -use App\Enums\LogAction; use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\Relations\MorphOne; use Illuminate\Database\Eloquent\SoftDeletes; +use Spatie\Activitylog\Facades\LogBatch; +use Spatie\Activitylog\LogOptions; +use Spatie\Activitylog\Traits\LogsActivity; /** * @mixin IdeHelperRequestedQuote */ class RequestedQuote extends Model { - use SoftDeletes; + use LogsActivity, SoftDeletes; protected $fillable = [ 'quote', ]; - public function logs(): MorphOne - { - return $this->morphOne(Log::class, 'loggable'); - } - public function approve(): void { - Log::create([ - 'user_id' => auth()?->user()?->id, - 'loggable_type' => self::class, - 'loggable_id' => $this->id, - 'action' => LogAction::APPROVE, - 'content' => $this->quote, - 'ip' => request()->ip(), - ]); + LogBatch::startBatch(); Quote::create([ 'quote' => $this->quote, ]); $this->delete(); + + LogBatch::endBatch(); } public function reject(): void { - Log::create([ - 'user_id' => auth()?->user()?->id, - 'loggable_type' => self::class, - 'loggable_id' => $this->id, - 'action' => LogAction::REJECT, - 'content' => $this->quote, - 'ip' => request()->ip(), - ]); - $this->delete(); } + + public function getActivityLogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['quote']); + } } diff --git a/app/Models/User.php b/app/Models/User.php index 49ba7d0..54c6a75 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2,20 +2,18 @@ namespace App\Models; -use App\Enums\LogAction; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\MorphOne; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Spatie\Activitylog\LogOptions; +use Spatie\Activitylog\Traits\LogsActivity; /** * @mixin IdeHelperUser */ class User extends Authenticatable { - use HasFactory, Notifiable; + use LogsActivity, HasFactory, Notifiable; /** * The model's default values for attributes. @@ -64,34 +62,14 @@ class User extends Authenticatable 'password' => 'hashed', ]; - public function logAction(): HasMany - { - return $this->hasMany(Log::class); - } - - public function logs(): MorphOne - { - return $this->morphOne(Log::class, 'loggable'); - } - public function getFullNameAttribute(): string { return "$this->firstname $this->lastname"; } - public static function boot(): void + public function getActivityLogOptions(): LogOptions { - parent::boot(); - - self::created(function ($model) { - Log::create([ - 'user_id' => auth()?->user()?->id ?? 1, - 'loggable_type' => self::class, - 'loggable_id' => $model->id, - 'action' => LogAction::CREATE, - 'content' => $model->full_name, - 'ip' => request()->ip(), - ]); - }); + return LogOptions::defaults() + ->logExcept(['password']); } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index a8daa55..fc7759b 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -3,6 +3,7 @@ namespace App\Providers; use Illuminate\Support\ServiceProvider; +use Spatie\Activitylog\Models\Activity; class AppServiceProvider extends ServiceProvider { @@ -15,6 +16,7 @@ class AppServiceProvider extends ServiceProvider $this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class); } + // This is so we can access alpine without livewire on the page. \Livewire\Livewire::forceAssetInjection(); } @@ -23,6 +25,8 @@ class AppServiceProvider extends ServiceProvider */ public function boot(): void { - // + Activity::saving(function (Activity $activity) { + $activity->properties = $activity->properties->put('ip', request()->ip()); + }); } } diff --git a/composer.json b/composer.json index ba0c6a7..e84244c 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,8 @@ "laravel/sanctum": "^3.2", "laravel/tinker": "^2.8", "livewire/livewire": "^3.0", - "livewire/volt": "^1.0" + "livewire/volt": "^1.0", + "spatie/laravel-activitylog": "^4.7" }, "require-dev": { "barryvdh/laravel-ide-helper": "^2.13", diff --git a/composer.lock b/composer.lock index 9dfa19f..388f2da 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "feae295eec753591e7cf70bf21f57924", + "content-hash": "da777baec27a8032a972c70cc19d114a", "packages": [ { "name": "brick/math", @@ -3251,6 +3251,157 @@ ], "time": "2023-04-15T23:01:58+00:00" }, + { + "name": "spatie/laravel-activitylog", + "version": "4.7.3", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-activitylog.git", + "reference": "ec65a478a909b8df1b4f0c3c45de2592ca7639e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-activitylog/zipball/ec65a478a909b8df1b4f0c3c45de2592ca7639e5", + "reference": "ec65a478a909b8df1b4f0c3c45de2592ca7639e5", + "shasum": "" + }, + "require": { + "illuminate/config": "^8.0 || ^9.0 || ^10.0", + "illuminate/database": "^8.69 || ^9.27 || ^10.0", + "illuminate/support": "^8.0 || ^9.0 || ^10.0", + "php": "^8.0", + "spatie/laravel-package-tools": "^1.6.3" + }, + "require-dev": { + "ext-json": "*", + "orchestra/testbench": "^6.23 || ^7.0 || ^8.0", + "pestphp/pest": "^1.20" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\Activitylog\\ActivitylogServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Spatie\\Activitylog\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + }, + { + "name": "Sebastian De Deyne", + "email": "sebastian@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + }, + { + "name": "Tom Witkowski", + "email": "dev.gummibeer@gmail.com", + "homepage": "https://gummibeer.de", + "role": "Developer" + } + ], + "description": "A very simple activity logger to monitor the users of your website or application", + "homepage": "https://github.com/spatie/activitylog", + "keywords": [ + "activity", + "laravel", + "log", + "spatie", + "user" + ], + "support": { + "issues": "https://github.com/spatie/laravel-activitylog/issues", + "source": "https://github.com/spatie/laravel-activitylog/tree/4.7.3" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2023-01-25T17:04:51+00:00" + }, + { + "name": "spatie/laravel-package-tools", + "version": "1.16.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-package-tools.git", + "reference": "cc7c991555a37f9fa6b814aa03af73f88026a83d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/cc7c991555a37f9fa6b814aa03af73f88026a83d", + "reference": "cc7c991555a37f9fa6b814aa03af73f88026a83d", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^9.28|^10.0", + "php": "^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "orchestra/testbench": "^7.7|^8.0", + "pestphp/pest": "^1.22", + "phpunit/phpunit": "^9.5.24", + "spatie/pest-plugin-test-time": "^1.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\LaravelPackageTools\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "role": "Developer" + } + ], + "description": "Tools for creating Laravel packages", + "homepage": "https://github.com/spatie/laravel-package-tools", + "keywords": [ + "laravel-package-tools", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-package-tools/issues", + "source": "https://github.com/spatie/laravel-package-tools/tree/1.16.1" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2023-08-23T09:04:39+00:00" + }, { "name": "symfony/console", "version": "v6.3.4", @@ -9758,5 +9909,5 @@ "php": "^8.1" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/config/activitylog.php b/config/activitylog.php new file mode 100644 index 0000000..29d6f1a --- /dev/null +++ b/config/activitylog.php @@ -0,0 +1,52 @@ + env('ACTIVITY_LOGGER_ENABLED', true), + + /* + * When the clean-command is executed, all recording activities older than + * the number of days specified here will be deleted. + */ + 'delete_records_older_than_days' => 365, + + /* + * If no log name is passed to the activity() helper + * we use this default log name. + */ + 'default_log_name' => 'default', + + /* + * You can specify an auth driver here that gets user models. + * If this is null we'll use the current Laravel auth driver. + */ + 'default_auth_driver' => null, + + /* + * If set to true, the subject returns soft deleted models. + */ + 'subject_returns_soft_deleted_models' => true, + + /* + * This model will be used to log activity. + * It should implement the Spatie\Activitylog\Contracts\Activity interface + * and extend Illuminate\Database\Eloquent\Model. + */ + 'activity_model' => \Spatie\Activitylog\Models\Activity::class, + + /* + * This is the name of the table that will be created by the migration and + * used by the Activity model shipped with this package. + */ + 'table_name' => 'activity_log', + + /* + * This is the database connection that will be used by the migration and + * the Activity model shipped with this package. In case it's not set + * Laravel's database.default will be used instead. + */ + 'database_connection' => env('ACTIVITY_LOGGER_DB_CONNECTION'), +]; diff --git a/database/migrations/2023_10_10_200426_create_logs_table.php b/database/migrations/2023_10_10_200426_create_logs_table.php deleted file mode 100644 index a804593..0000000 --- a/database/migrations/2023_10_10_200426_create_logs_table.php +++ /dev/null @@ -1,30 +0,0 @@ -id(); - $table->foreignIdFor(User::class)->nullable(); - $table->string('loggable_type'); - $table->integer('loggable_id'); - $table->string('action'); - $table->text('content'); - $table->string('ip'); - $table->timestamps(); - }); - } - - public function down(): void - { - Schema::dropIfExists('logs'); - } -}; diff --git a/database/migrations/2023_10_12_080056_create_activity_log_table.php b/database/migrations/2023_10_12_080056_create_activity_log_table.php new file mode 100644 index 0000000..7c05bc8 --- /dev/null +++ b/database/migrations/2023_10_12_080056_create_activity_log_table.php @@ -0,0 +1,27 @@ +create(config('activitylog.table_name'), function (Blueprint $table) { + $table->bigIncrements('id'); + $table->string('log_name')->nullable(); + $table->text('description'); + $table->nullableMorphs('subject', 'subject'); + $table->nullableMorphs('causer', 'causer'); + $table->json('properties')->nullable(); + $table->timestamps(); + $table->index('log_name'); + }); + } + + public function down() + { + Schema::connection(config('activitylog.database_connection'))->dropIfExists(config('activitylog.table_name')); + } +} diff --git a/database/migrations/2023_10_12_080057_add_event_column_to_activity_log_table.php b/database/migrations/2023_10_12_080057_add_event_column_to_activity_log_table.php new file mode 100644 index 0000000..7b797fd --- /dev/null +++ b/database/migrations/2023_10_12_080057_add_event_column_to_activity_log_table.php @@ -0,0 +1,22 @@ +table(config('activitylog.table_name'), function (Blueprint $table) { + $table->string('event')->nullable()->after('subject_type'); + }); + } + + public function down() + { + Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) { + $table->dropColumn('event'); + }); + } +} diff --git a/database/migrations/2023_10_12_080058_add_batch_uuid_column_to_activity_log_table.php b/database/migrations/2023_10_12_080058_add_batch_uuid_column_to_activity_log_table.php new file mode 100644 index 0000000..8f7db66 --- /dev/null +++ b/database/migrations/2023_10_12_080058_add_batch_uuid_column_to_activity_log_table.php @@ -0,0 +1,22 @@ +table(config('activitylog.table_name'), function (Blueprint $table) { + $table->uuid('batch_uuid')->nullable()->after('properties'); + }); + } + + public function down() + { + Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) { + $table->dropColumn('batch_uuid'); + }); + } +} diff --git a/resources/views/components/paginator.blade.php b/resources/views/components/paginator.blade.php new file mode 100644 index 0000000..2fb0392 --- /dev/null +++ b/resources/views/components/paginator.blade.php @@ -0,0 +1,106 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/resources/views/livewire/guest/navigation.blade.php b/resources/views/livewire/guest/navigation.blade.php index 6de4550..240c1e1 100644 --- a/resources/views/livewire/guest/navigation.blade.php +++ b/resources/views/livewire/guest/navigation.blade.php @@ -1,4 +1,4 @@ -
+
@auth diff --git a/resources/views/livewire/pages/admin/send.blade.php b/resources/views/livewire/pages/admin/send.blade.php index 67603a0..337def7 100644 --- a/resources/views/livewire/pages/admin/send.blade.php +++ b/resources/views/livewire/pages/admin/send.blade.php @@ -1,6 +1,5 @@ $validated['quote']]); - Log::create([ - 'user_id' => auth()->user()->id, - 'loggable_type' => Log::class, - 'loggable_id' => null, - 'action' => LogAction::SEND, - 'content' => $validated['quote'], - 'ip' => request()->ip(), - ]); + activity() + ->event('send') + ->log("Manually sent quote: {$validated['quote']}"); $this->quote = ''; } @@ -37,7 +31,7 @@ new #[Layout('layouts.app')] class extends Component diff --git a/resources/views/livewire/pages/auth/login.blade.php b/resources/views/livewire/pages/auth/login.blade.php index 9e788c2..74d5354 100644 --- a/resources/views/livewire/pages/auth/login.blade.php +++ b/resources/views/livewire/pages/auth/login.blade.php @@ -1,6 +1,5 @@ regenerate(); - Log::create([ - 'user_id' => auth()->user()->id, - 'loggable_type' => User::class, - 'loggable_id' => auth()->user()->id, - 'action' => LogAction::ACCESS, - 'content' => 'User logged in', - 'ip' => request()->ip(), - ]); + activity() + ->performedOn(auth()->user()) + ->event('access') + ->log('login'); $this->redirect( session('url.intended', RouteServiceProvider::HOME), diff --git a/resources/views/livewire/pages/auth/reset-password.blade.php b/resources/views/livewire/pages/auth/reset-password.blade.php index 4603b51..8ccb2cf 100644 --- a/resources/views/livewire/pages/auth/reset-password.blade.php +++ b/resources/views/livewire/pages/auth/reset-password.blade.php @@ -1,6 +1,5 @@ only('email', 'password', 'password_confirmation', 'token'), - function ($user) { - $user->forceFill([ - 'password' => Hash::make($this->password), - 'remember_token' => Str::random(60), - ])->save(); - event(new PasswordReset($user)); - } - ); + auth()->user()->update([ + $status = Password::reset( + $this->only('email', 'password', 'password_confirmation', 'token'), + function ($user) { + $user->forceFill([ + 'password' => Hash::make($this->password), + 'remember_token' => Str::random(60), + ])->save(); + + event(new PasswordReset($user)); + } + ); + ]); // If the password was successfully reset, we will redirect the user back to // the application's home authenticated view. If there is an error we can @@ -59,16 +61,11 @@ new #[Layout('layouts.guest')] class extends Component return; } - session()->flash('status', __($status)); + activity() + ->event('updated') + ->log('password reset'); - Log::create([ - 'user_id' => auth()->user()->id, - 'loggable_type' => User::class, - 'loggable_id' => auth()?->user()?->id, - 'action' => LogAction::UPDATE, - 'content' => 'User reset password via reset', - 'ip' => request()->ip(), - ]); + session()->flash('status', __($status)); $this->redirectRoute('login', navigate: true); } diff --git a/resources/views/livewire/profile/update-password-form.blade.php b/resources/views/livewire/profile/update-password-form.blade.php index c9c03bb..185e3b7 100644 --- a/resources/views/livewire/profile/update-password-form.blade.php +++ b/resources/views/livewire/profile/update-password-form.blade.php @@ -1,6 +1,5 @@ user()->update([ - 'password' => Hash::make($validated['password']), - ]); + // We don't want to have a complete log, just something for updating password + activity()->withoutLogs(function () { + auth()->user()->update([ + 'password' => Hash::make($validated['password']), + ]); + }); - Log::create([ - 'user_id' => auth()->user()->id, - 'loggable_type' => User::class, - 'loggable_id' => auth()?->user()?->id, - 'action' => LogAction::UPDATE, - 'content' => 'User updated password', - 'ip' => request()->ip(), - ]); + activity() + ->event('updated') + ->log('password updated'); $this->reset('current_password', 'password', 'password_confirmation');