Skip to content

Comments

Add admin activity logging for CRUD operations#2189

Draft
lancepioch wants to merge 3 commits intomainfrom
lance/1585
Draft

Add admin activity logging for CRUD operations#2189
lancepioch wants to merge 3 commits intomainfrom
lance/1585

Conversation

@lancepioch
Copy link
Member

Log create, update, and delete actions performed in the admin panel using Filament's RecordCreated/RecordUpdated events and a DeleteAction before() hook. Sensitive fields (passwords, tokens) are redacted from stored properties.

Resolves #1585

Log create, update, and delete actions performed in the admin panel
using Filament's RecordCreated/RecordUpdated events and a DeleteAction
before() hook. Sensitive fields (passwords, tokens) are redacted from
stored properties.
@coderabbitai
Copy link

coderabbitai bot commented Feb 6, 2026

📝 Walkthrough

Walkthrough

Adds an admin activity logging flow for the Filament admin panel: a new listener captures RecordCreated/RecordUpdated events (and delete actions via Filament service provider), redacts sensitive fields, and dispatches activity log entries with derived resource slugs and subjects.

Changes

Cohort / File(s) Summary
Admin Activity Listener
app/Listeners/AdminActivityListener.php
New listener class handling RecordCreated and RecordUpdated events; derives resource slug from model class, redacts sensitive fields recursively (REDACTED_FIELDS constant), and dispatches activity logging with subject and redacted properties.
Event Registration
app/Providers/EventServiceProvider.php
Registers AdminActivityListener for Filament\Resources\Events\RecordCreated and RecordUpdated events; adds corresponding use imports.
Delete Action Hook
app/Providers/Filament/FilamentServiceProvider.php
Adds a before hook on Filament DeleteAction to log admin delete events using same slug derivation and activity dispatching.
Translations
lang/en/activity.php
Adds top-level admin translations for multiple resource types (user, server, node, egg, role, database-host, mount, webhook-configuration, api-key) with create/update/delete messages and placeholders.
Tests
tests/Filament/Admin/AdminActivityListenerTest.php
New Pest/PHPUnit tests covering create/update logging, delete access control, subject assignment, redaction (including nested arrays), kebab-case event naming, and test helpers (pageInstance, createEvent, updateEvent).

Sequence Diagram(s)

sequenceDiagram
    actor Admin as Admin User
    participant Filament
    participant Listener as AdminActivityListener
    participant Redactor as redactSensitiveFields
    participant Activity as Activity Logger
    participant DB as Database

    Admin->>Filament: Perform Create/Update/Delete action
    activate Filament

    Filament->>Listener: Dispatch RecordCreated / RecordUpdated / DeleteAction event
    deactivate Filament

    activate Listener
    Listener->>Listener: Derive resource slug from model class
    Listener->>Redactor: Extract event data / properties
    activate Redactor
    Redactor->>Redactor: Recursively redact sensitive fields
    Redactor-->>Listener: Return redacted properties
    deactivate Redactor

    Listener->>Activity: Dispatch ActivityLogged with slug, action, subject, properties
    deactivate Listener

    activate Activity
    Activity->>DB: Persist activity record with redacted properties
    deactivate Activity
Loading
🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR implements create/update/delete logging (#1585), redacts sensitive fields, and provides activity event infrastructure. However, it does not fully address all requirements: missing role/permission implementation for viewing logs, no purge/retention mechanism, field-level change metadata, and detailed human-readable messages. Implement role/permission for activity log access, add data retention/purge logic for 30-60 days, include field-level old->new changes in activity properties, and generate detailed human-readable activity messages using translation keys.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'Add admin activity logging for CRUD operations' clearly and concisely summarizes the primary change: implementing admin action logging for create, update, and delete operations.
Description check ✅ Passed The PR description is directly related to the changeset, explaining the logging mechanism for admin panel actions, field redaction approach, and referencing the resolved issue.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing admin activity logging: AdminActivityListener handles RecordCreated/RecordUpdated events, FilamentServiceProvider logs delete actions, EventServiceProvider registers listeners, activity translations are added, and comprehensive tests are included.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
app/Listeners/AdminActivityListener.php (1)

30-36: Chained static calls to derive the slug could throw if $page lacks the expected static methods.

pageInstance() in tests uses newInstanceWithoutConstructor(), which works. But in production, if $page::getResource() or $resourceClass::getModel() returns something unexpected (e.g., a non-Filament page dispatches the event), this chain will fail with a cryptic error. Consider adding a guard or try/catch if this listener is wired to a broad event.

tests/Filament/Admin/AdminActivityListenerTest.php (3)

96-123: Redaction test doesn't cover all fields in REDACTED_FIELDS.

The listener defines 8 redacted fields (password, password_confirmation, current_password, token, secret, api_key, daemon_token, _token), but this test only verifies 5 of them. current_password, daemon_token, and _token are untested.

🧪 Proposed fix to cover all redacted fields
     $data = [
         'name' => 'Visible',
         'password' => 'should-be-redacted',
         'password_confirmation' => 'should-be-redacted',
+        'current_password' => 'should-be-redacted',
         'token' => 'should-be-redacted',
         'secret' => 'should-be-redacted',
         'api_key' => 'should-be-redacted',
+        'daemon_token' => 'should-be-redacted',
+        '_token' => 'should-be-redacted',
     ];
 
     $listener = new AdminActivityListener();
     $listener->handle(updateEvent($egg, $data, pageInstance(EditEgg::class)));
 
     Event::assertDispatched(ActivityLogged::class, function (ActivityLogged $event) {
         $properties = $event->model->properties;
 
         expect($properties)->toHaveKey('name', 'Visible')
             ->toHaveKey('password', '[REDACTED]')
             ->toHaveKey('password_confirmation', '[REDACTED]')
+            ->toHaveKey('current_password', '[REDACTED]')
             ->toHaveKey('token', '[REDACTED]')
             ->toHaveKey('secret', '[REDACTED]')
-            ->toHaveKey('api_key', '[REDACTED]');
+            ->toHaveKey('api_key', '[REDACTED]')
+            ->toHaveKey('daemon_token', '[REDACTED]')
+            ->toHaveKey('_token', '[REDACTED]');
 
         return true;
     });

151-158: This test is effectively a duplicate of the "logs create activity for a node" test (lines 58-65).

Both tests create a node, call createEvent, and assert admin:node.create. If the intent is to test kebab-case conversion specifically, consider using a model whose class name has multiple words (e.g., DatabaseHostdatabase-host) to actually exercise the kebab-case transformation.


17-20: pageInstance via newInstanceWithoutConstructor is fragile.

This bypasses all constructor logic and may silently produce objects in an invalid state. It works today because only static methods (getResource()) are called, but it could break if the page class adds initialization logic. Consider documenting this assumption with a comment.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@app/Providers/EventServiceProvider.php`:
- Around line 21-22: The EventServiceProvider registers non-existent events
RecordCreated and RecordUpdated for AdminActivityListener so the listener is
never invoked; either remove those registrations from EventServiceProvider and
instead register a model observer for the Eloquent events (created/updated) that
points to your AdminActivityListener (or create a dedicated
AdminActivityObserver and register it via the EventServiceProvider or in a
service provider), or implement and dispatch custom RecordCreated and
RecordUpdated events where records are created/updated (e.g., from your Filament
resource pages) or move the logging logic into Filament resource lifecycle hooks
like afterCreate() / afterSave() on the relevant Resource/Page classes to call
the AdminActivityListener functionality.
🧹 Nitpick comments (4)
lang/en/activity.php (1)

166-170: API key translations lack identifiers.

Unlike other admin action translations that include identifying information (:username, :name, :description), the api-key entries provide no context about which API key was affected. Consider adding an identifier placeholder for better audit trail clarity.

 'api-key' => [
-    'create' => 'Created API key',
-    'update' => 'Updated API key',
-    'delete' => 'Deleted API key',
+    'create' => 'Created API key <b>:identifier</b>',
+    'update' => 'Updated API key <b>:identifier</b>',
+    'delete' => 'Deleted API key <b>:identifier</b>',
 ],
app/Listeners/AdminActivityListener.php (3)

13-19: Consider expanding the sensitive fields list.

The current list may miss other sensitive fields commonly found in admin panels. Consider adding fields like daemon_token, daemon_token_id, client_secret, private_key, or pattern-based matching for fields containing token, secret, or key in their names.

Additionally, the matching is case-sensitive, so Password or API_KEY would not be redacted.

🛡️ Suggested improvement
 protected const REDACTED_FIELDS = [
     'password',
     'password_confirmation',
     'token',
     'secret',
     'api_key',
+    'daemon_token',
+    'daemon_token_id', 
+    'client_secret',
+    'private_key',
 ];

Or consider pattern-based matching in redactSensitiveFields:

$sensitivePatterns = ['password', 'token', 'secret', 'key'];
$shouldRedact = false;
foreach ($sensitivePatterns as $pattern) {
    if (stripos($key, $pattern) !== false) {
        $shouldRedact = true;
        break;
    }
}

50-53: Sensitive fields are silently dropped rather than marked as redacted.

Currently, sensitive fields are completely omitted from the logged properties. This makes it impossible to distinguish between "field was not submitted" vs "field was redacted for security." Consider replacing the value with a placeholder like '[REDACTED]' to maintain audit visibility.

♻️ Suggested change
 foreach ($data as $key => $value) {
     if (in_array($key, self::REDACTED_FIELDS, true)) {
-        continue;
+        $redacted[$key] = '[REDACTED]';
+        continue;
     }

32-32: Action determination assumes binary create/update.

The logic assumes any page that isn't CreateRecord is an update. If Filament introduces other page types or if custom pages exist, this could misclassify actions. Consider being more explicit or logging the actual page type.

♻️ Suggested defensive improvement
-$action = $page instanceof \Filament\Resources\Pages\CreateRecord ? 'create' : 'update';
+$action = match (true) {
+    $page instanceof \Filament\Resources\Pages\CreateRecord => 'create',
+    $page instanceof \Filament\Resources\Pages\EditRecord => 'update',
+    default => 'update', // fallback for compatibility
+};

Comment on lines +21 to +22
RecordCreated::class => [AdminActivityListener::class],
RecordUpdated::class => [AdminActivityListener::class],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

# Find AdminActivityListener and check its handle method
echo "=== AdminActivityListener location and handle signature ==="
find . -name "*AdminActivityListener*" -type f

echo ""
echo "=== AdminActivityListener handle method signature ==="
rg -n "class AdminActivityListener|function handle|public function handle" app/ --type php -A 3 | grep -A 10 "AdminActivityListener"

Repository: pelican-dev/panel

Length of output: 1470


🏁 Script executed:

#!/bin/bash

# Search for RecordCreated and RecordUpdated event definitions
echo "=== Searching for RecordCreated event definition ==="
rg -n "class RecordCreated" --type php -A 10

echo ""
echo "=== Searching for RecordUpdated event definition ==="
rg -n "class RecordUpdated" --type php -A 10

Repository: pelican-dev/panel

Length of output: 167


🏁 Script executed:

#!/bin/bash

# Check how RecordCreated/RecordUpdated are dispatched to see actual parameters
echo "=== How RecordCreated is dispatched ==="
rg -n "RecordCreated::dispatch|dispatch.*RecordCreated" --type php -B 2 -A 2

echo ""
echo "=== How RecordUpdated is dispatched ==="
rg -n "RecordUpdated::dispatch|dispatch.*RecordUpdated" --type php -B 2 -A 2

Repository: pelican-dev/panel

Length of output: 141


🌐 Web query:

Filament RecordCreated RecordUpdated event listener signature parameters

💡 Result:

Filament doesn’t ship Laravel events named RecordCreated / RecordUpdated for resource CRUD. Instead, you typically hook into:

1) Filament resource page hooks (recommended)

Create page (CreateRecord)

After a record is created, you can use the page’s $record property (the created Eloquent model) and $data (form state). (filamentphp.com)

use Filament\Resources\Pages\CreateRecord;

class CreateUser extends CreateRecord
{
    protected function afterCreate(): void
    {
        $record = $this->record; // Eloquent model
        $data   = $this->data;   // validated form data array
    }
}

Edit page (EditRecord)

Updates happen via handleRecordUpdate(Model $record, array $data) (and you can use $this->record as well). (filamentphp.com)

use Filament\Resources\Pages\EditRecord;
use Illuminate\Database\Eloquent\Model;

class EditUser extends EditRecord
{
    protected function afterSave(): void
    {
        $record = $this->record; // updated model
    }

    protected function handleRecordUpdate(Model $record, array $data): Model
    {
        // $record = model being updated, $data = form data
        return parent::handleRecordUpdate($record, $data);
    }
}

2) Laravel / Eloquent model events (framework-level)

If you want true “created/updated” listeners, use Eloquent events/observers (created(Model $model), updated(Model $model), etc.). (laravel.com)

If you meant a third-party Filament plugin that defines RecordCreated / RecordUpdated, tell me the package name/namespace and Filament major version (v2/v3/v4), and I’ll pull the exact event class + listener method signature.

Citations:


Remove or implement RecordCreated and RecordUpdated events—they do not exist in Filament core or this codebase.

The listener registrations on lines 21-22 reference RecordCreated and RecordUpdated events that are not defined anywhere in the codebase or Filament's core library. These events are never dispatched, so the AdminActivityListener will never be triggered. Either:

  1. Define and dispatch these custom events explicitly in the appropriate resource pages, or
  2. Use Laravel's Eloquent model events (created, updated) with an observer/listener, or
  3. Hook into Filament's page lifecycle methods (afterCreate(), afterSave()) directly on resource pages.

Without dispatching these events, admin activity logging will not function.

🤖 Prompt for AI Agents
In `@app/Providers/EventServiceProvider.php` around lines 21 - 22, The
EventServiceProvider registers non-existent events RecordCreated and
RecordUpdated for AdminActivityListener so the listener is never invoked; either
remove those registrations from EventServiceProvider and instead register a
model observer for the Eloquent events (created/updated) that points to your
AdminActivityListener (or create a dedicated AdminActivityObserver and register
it via the EventServiceProvider or in a service provider), or implement and
dispatch custom RecordCreated and RecordUpdated events where records are
created/updated (e.g., from your Filament resource pages) or move the logging
logic into Filament resource lifecycle hooks like afterCreate() / afterSave() on
the relevant Resource/Page classes to call the AdminActivityListener
functionality.

@lancepioch lancepioch marked this pull request as ready for review February 7, 2026 14:23
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@app/Listeners/AdminActivityListener.php`:
- Around line 22-39: The AdminActivityListener is registered for Filament
RecordCreated/RecordUpdated events which are not dispatched in v4.5, so it never
runs in production; update wiring by either invoking AdminActivityListener from
Resource page lifecycle hooks (call or dispatch from CreateRecord::afterCreate()
and EditRecord::afterSave()) or attach it to Eloquent model events
(created/updated) instead; modify EventServiceProvider to stop registering
non-existent Filament events and instead dispatch a custom event or call
AdminActivityListener::handle(...) from the Resource pages (or register
listeners for the model class events) so the listener actually executes in real
requests.
🧹 Nitpick comments (3)
app/Listeners/AdminActivityListener.php (2)

46-63: Sensitive fields are silently dropped instead of replaced with a redaction marker.

Currently, redacted fields are completely omitted from the logged properties. This makes it impossible to distinguish "the field wasn't submitted" from "the field was submitted but redacted." Consider replacing with a placeholder value instead of skipping.

♻️ Proposed change
         foreach ($data as $key => $value) {
             if (in_array($key, self::REDACTED_FIELDS, true)) {
-                continue;
+                $redacted[$key] = '[REDACTED]';
+                continue;
             }

13-19: Consider extending the redaction list with current_password and _token.

Fields like current_password (used in password change flows) and _token (CSRF token sometimes present in form data) may also contain sensitive values.

tests/Filament/Admin/AdminActivityListenerTest.php (1)

15-18: pageInstance bypasses constructor — verify getResource() works on the resulting object.

newInstanceWithoutConstructor() skips all property initialization. Since AdminActivityListener::handle() calls $page::getResource() (a static method), this should work as long as getResource() doesn't rely on instance state. However, this is fragile — any future change to the page class could break these tests silently.

Comment on lines 22 to 39
public function handle(Model $record, array $data, Page $page): void
{
if (Filament::getCurrentPanel()?->getId() !== 'admin') {
return;
}

$resourceClass = $page::getResource();
$modelClass = $resourceClass::getModel();
$slug = Str::kebab(class_basename($modelClass));

$action = $page instanceof \Filament\Resources\Pages\CreateRecord ? 'create' : 'update';

$properties = $this->redactSensitiveFields($data);

Activity::event("admin:$slug.$action")
->subject($record)
->property($properties)
->log();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how RecordCreated/RecordUpdated are defined and what constructor params they take
echo "=== RecordCreated definition ==="
rg -n "class RecordCreated" --type php -A 20

echo ""
echo "=== RecordUpdated definition ==="
rg -n "class RecordUpdated" --type php -A 20

echo ""
echo "=== How these events are dispatched ==="
rg -n "RecordCreated\b" --type php -C 3 | head -80
echo ""
rg -n "RecordUpdated\b" --type php -C 3 | head -80

Repository: pelican-dev/panel

Length of output: 2420


🏁 Script executed:

#!/bin/bash
# Check if Filament events exist in vendor
echo "=== Checking vendor directory for Filament events ==="
find vendor -path "*Filament/Resources/Events*" -name "*.php" 2>/dev/null | head -20

echo ""
echo "=== Check the full AdminActivityListener implementation ==="
cat -n app/Listeners/AdminActivityListener.php

Repository: pelican-dev/panel

Length of output: 2246


🏁 Script executed:

#!/bin/bash
# Check for other listeners and their signatures
echo "=== Other listener implementations ==="
find app/Listeners -name "*.php" -exec echo "File: {}" \; -exec head -30 {} \;

echo ""
echo "=== Check test files for event dispatching patterns ==="
find tests -name "*Event*" -o -name "*Listener*" 2>/dev/null | head -10

echo ""
echo "=== Search for any Filament event usage in tests ==="
rg "RecordCreated|RecordUpdated" --type php -B 5 -A 5 tests/ 2>/dev/null | head -100

Repository: pelican-dev/panel

Length of output: 6736


🏁 Script executed:

#!/bin/bash
cat -n tests/Filament/Admin/AdminActivityListenerTest.php

Repository: pelican-dev/panel

Length of output: 5485


🏁 Script executed:

#!/bin/bash
# Search for event dispatch calls
echo "=== Search for RecordCreated/RecordUpdated event instantiation ==="
rg "RecordCreated|RecordUpdated" --type php -B 3 -A 3 | grep -v "tests/" | grep -v ".phpt" | head -100

echo ""
echo "=== Search for event dispatching ==="
rg "event\(" --type php | grep -i "record" | head -20

echo ""
echo "=== Check for any Filament event handling documentation ==="
rg "handle.*RecordCreated|handle.*RecordUpdated" --type php -B 5 -A 5

Repository: pelican-dev/panel

Length of output: 1430


🌐 Web query:

Filament RecordCreated RecordUpdated event listener signature

💡 Result:

Filament doesn’t expose Laravel events named RecordCreated / RecordUpdated that you can register listeners for. Instead, record creation/update in a Resource page is meant to be customized via lifecycle hook methods (no parameters, void return):

Create page (CreateRecord)

protected function afterCreate(): void
{
    // Runs after the record is created.
}

(Also available: beforeFill, afterFill, beforeValidate, afterValidate, beforeCreate, afterCreate.) [1]

The created model is stored on the page as $record (type Model|null). [3]

Edit page (EditRecord)

protected function afterSave(): void
{
    // Runs after the record is saved (updated).
}

(Also available: beforeFill, afterFill, beforeValidate, afterValidate, beforeSave, afterSave.) [2]

If you truly need Laravel-style events, dispatch your own Laravel events from afterCreate() / afterSave() and then use the normal listener signature public function handle(YourEvent $event): void.

Sources: [1] [2] [3]


🏁 Script executed:

#!/bin/bash
echo "=== Check composer.json for Filament version ==="
grep -A 2 "filament" composer.json | head -10

echo ""
echo "=== Search for any dispatch() calls ==="
rg "dispatch.*Record" --type php

echo ""
echo "=== Search for all imports of Filament events ==="
rg "use Filament.*Events" --type php

echo ""
echo "=== Check if there's any custom event handling ==="
rg "Event::" --type php | head -20

Repository: pelican-dev/panel

Length of output: 2262


🌐 Web query:

Filament 4.5 Events RecordCreated RecordUpdated Laravel event listener

💡 Result:

Filament v4.x (including 4.5) does not expose global Laravel events named RecordCreated, RecordUpdated, etc. for Resource page CRUD in a way you can listen to via EventServiceProvider.

What to use instead

1) Filament Resource page lifecycle hooks (recommended for “only when done via Filament UI”)

On Create pages you have hooks like beforeCreate() / afterCreate(); on Edit pages you have beforeSave() / afterSave(). Use these hooks, and if you need a Laravel event, dispatch your own from there. [1] [2]

// app/Filament/Resources/PostResource/Pages/CreatePost.php
use App\Events\PostCreatedFromFilament;
use Filament\Resources\Pages\CreateRecord;

class CreatePost extends CreateRecord
{
    protected function afterCreate(): void
    {
        PostCreatedFromFilament::dispatch($this->record);
    }
}
// app/Filament/Resources/PostResource/Pages/EditPost.php
use App\Events\PostUpdatedFromFilament;
use Filament\Resources\Pages\EditRecord;

class EditPost extends EditRecord
{
    protected function afterSave(): void
    {
        PostUpdatedFromFilament::dispatch($this->record);
    }
}

Then register normal Laravel listeners for PostCreatedFromFilament / PostUpdatedFromFilament.

2) Eloquent model events / observers (recommended for “no matter where it’s created/updated”)

If your goal is “whenever the model changes (Filament or not)”, use Eloquent created / updated (Observer) instead of Filament-specific hooks.


Sources: Filament Resource Create lifecycle hooks (includes afterCreate). [1] Filament Resource Edit lifecycle hooks (includes afterSave). [2]

[1] https://docs.laravel-filament.cn/docs/panels/resources/creating-records/
[2] https://docs.laravel-filament.cn/docs/4.x/resources/editing-records


This listener will never be triggered in production — Filament v4.5 does not expose RecordCreated/RecordUpdated as dispatchable Laravel events.

The code registers AdminActivityListener for these non-existent events in EventServiceProvider. Filament 4.5 uses lifecycle hook methods (afterCreate(), afterSave()) on Resource pages instead. If you need to log record creation/updates:

  1. Add hooks to your Resource pages:
// In CreateRecord page
protected function afterCreate(): void
{
    // dispatch custom event or call listener directly
}

// In EditRecord page  
protected function afterSave(): void
{
    // dispatch custom event or call listener directly
}
  1. Or use Eloquent model events (created, updated) if activity should be logged regardless of how records are created.

The listener tests pass only because they manually invoke handle() directly—they don't validate that the listener is actually called through the event system.

🤖 Prompt for AI Agents
In `@app/Listeners/AdminActivityListener.php` around lines 22 - 39, The
AdminActivityListener is registered for Filament RecordCreated/RecordUpdated
events which are not dispatched in v4.5, so it never runs in production; update
wiring by either invoking AdminActivityListener from Resource page lifecycle
hooks (call or dispatch from CreateRecord::afterCreate() and
EditRecord::afterSave()) or attach it to Eloquent model events (created/updated)
instead; modify EventServiceProvider to stop registering non-existent Filament
events and instead dispatch a custom event or call
AdminActivityListener::handle(...) from the Resource pages (or register
listeners for the model class events) so the listener actually executes in real
requests.

- Change handle() signature to accept RecordCreated|RecordUpdated event objects
  instead of separate params (Laravel passes the event, not constructor args)
- Use [REDACTED] placeholder instead of silently dropping sensitive fields
- Expand redacted fields list with current_password, daemon_token, _token
- Update tests to construct proper event objects
@notAreYouScared
Copy link
Member

From what I can see, nothing is logged in the actor's activity logs.

@notAreYouScared notAreYouScared marked this pull request as draft February 17, 2026 11:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Admin Action Logging

2 participants