Add audit logging functionality with database schema and event handling
Some checks failed
🧪✨ Tests Workflow / 🛡️ 🔒 License Check (push) Successful in 2m23s
🧪✨ Tests Workflow / 🛡️ 🔒 Library Audit (push) Successful in 2m35s
🧪✨ Tests Workflow / 📝 ✨ Code Lint (push) Successful in 2m25s
🧪✨ Tests Workflow / 🧪 ✨ Database Migrations (push) Successful in 2m39s
🧪✨ Tests Workflow / 🐙 🔍 Code Sniffer (push) Failing after 2m26s
🧪✨ Tests Workflow / 🧪 ✅ Unit Tests (push) Failing after 1m5s

This commit is contained in:
2026-01-10 09:51:35 -05:00
parent 7c70cb245d
commit a1d7512ebc
27 changed files with 428 additions and 65 deletions

View File

@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Events\AccessToken;
use Nyholm\Psr7\Response;
readonly class Issued
{
public function __construct(private Response $response)
{
}
public function getResponse(): Response
{
return $this->response;
}
}

View File

@@ -37,7 +37,7 @@ class Dispatcher implements DispatcherContract, Arrayable
/**
* @var string LISTENERS_NAMESPACE The namespace where listeners are located
*/
private const string LISTENERS_NAMESPACE = 'Siteworxpro\\App\\Events\\Listeners\\';
private const string LISTENERS_NAMESPACE = 'Siteworxpro\\App\\EventListeners\\';
public function __construct()
{
@@ -63,7 +63,7 @@ class Dispatcher implements DispatcherContract, Arrayable
private function registerListeners(): void
{
// traverse the Listeners directory and register all listeners
$listenersPath = __DIR__ . '/Listeners';
$listenersPath = __DIR__ . '/../EventListeners';
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($listenersPath));
foreach ($iterator as $file) {
@@ -199,6 +199,11 @@ class Dispatcher implements DispatcherContract, Arrayable
*/
public function push($event, $payload = []): void
{
if (!is_string($event)) {
$payload = [$event];
$event = get_class($event);
}
$this->pushed->put($event, $payload);
}

View File

@@ -1,33 +0,0 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Events\Listeners\Database;
use Illuminate\Database\Events\ConnectionEstablished;
use Illuminate\Database\Events\ConnectionEvent;
use Siteworxpro\App\Attributes\Events\ListensFor;
use Siteworxpro\App\Events\Listeners\Listener;
use Siteworxpro\App\Services\Facades\Logger;
/**
* Class Connected
* @package Siteworxpro\App\Events\Listeners\Database
*/
#[ListensFor(ConnectionEstablished::class)]
class Connected extends Listener
{
/**
* @param mixed $event
* @param array $payload
* @return null
*/
public function __invoke(mixed $event, array $payload = []): null
{
if (!($event instanceof ConnectionEvent)) {
throw new \TypeError("Invalid event type passed to listener " . static::class);
}
return null;
}
}

View File

@@ -1,14 +0,0 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Events\Listeners;
/**
* Class Listener
*
* @package Siteworxpro\App\Events\Listeners
*/
abstract class Listener implements ListenerInterface
{
}

View File

@@ -1,19 +0,0 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Events\Listeners;
/**
* Interface ListenerInterface
* @package Siteworxpro\App\Events\Listeners
*/
interface ListenerInterface
{
/**
* @param mixed $event
* @param array $payload
* @return mixed
*/
public function __invoke(mixed $event, array $payload = []): mixed;
}

View File

@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Events\Login;
use Nyholm\Psr7\ServerRequest;
readonly class LoginAttempt
{
public function __construct(
private ServerRequest $request,
) {
}
public function getEmail(): string
{
return $this->request->getParsedBody()['email'] ?? '';
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Events\Login;
use Nyholm\Psr7\ServerRequest;
use Siteworxpro\App\Models\User;
readonly class LoginFailed
{
public function __construct(
private ServerRequest $request,
private ?User $user = null,
) {
}
public function getRequestIp(): string
{
if ($this->request->getHeader('X-Forwarded-For')) {
$ipAddresses = explode(',', $this->request->getHeaderLine('X-Forwarded-For'));
return trim($ipAddresses[0]);
}
if ($this->request->getHeader('X-Real-IP')) {
return $this->request->getHeaderLine('X-Real-IP');
}
if ($this->request->getServerParams()['HTTP_CLIENT_IP'] ?? false) {
return $this->request->getServerParams()['HTTP_CLIENT_IP'];
}
if ($this->request->getServerParams()['HTTP_X_FORWARDED_FOR'] ?? false) {
$ipAddresses = explode(',', $this->request->getServerParams()['HTTP_X_FORWARDED_FOR']);
return trim($ipAddresses[0]);
}
return $this->request->getServerParams()['REMOTE_ADDR'] ?? 'unknown';
}
public function getUsernameAttempted(): string
{
return $this->request->getParsedBody()['email'] ?? '';
}
public function getUser(): ?User
{
return $this->user;
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Events\Login;
use Nyholm\Psr7\ServerRequest;
use Siteworxpro\App\Models\User;
readonly class LoginSuccess
{
public function __construct(
private ServerRequest $serverRequest,
private User $user
) {
}
public function getRequest(): ServerRequest
{
return $this->serverRequest;
}
public function getUser(): User
{
return $this->user;
}
}