refactor(audit): extract audit domain into self-contained module
Move the entire audit subsystem (system audit, API audit, import audit, user lifecycle audit, frontend telemetry) from core into modules/audit/. Core decoupling via interface-based injection: - AuditRecorderInterface replaces SystemAuditService in 10+ core services - UserLifecycleAuditInterface / ImportAuditInterface for specialized flows - NullAuditRecorder fallback when audit module is disabled - ApiBootstrap/ApiResponse use null-safe callable resolvers Module structure (modules/audit/): - Manifest with routes, permissions, scheduler jobs, authorization policy - 9 services, 8 repositories, 6 domain enums, 4 job handlers - 33 page files, 4 JS files, 8 test files, migration scripts, i18n Core cleanup: - OperationsAuthorizationPolicy, UiCapabilityMap, PermissionService surgically cleaned of audit-specific constants - Sidebar template cleared of hardcoded audit navigation - AuditModuleIsolationContractTest ensures no future core→module coupling All quality gates pass: 1346 tests (19,276 assertions), PHPStan level 5 clean, architecture contracts verified. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,42 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Service\Scheduler\Handler;
|
||||
|
||||
use MintyPHP\Service\Audit\SystemAuditService;
|
||||
|
||||
class SystemAuditPurgeJobHandler implements ScheduledJobHandlerInterface
|
||||
{
|
||||
public function __construct(private readonly SystemAuditService $systemAuditService)
|
||||
{
|
||||
}
|
||||
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'label' => 'System audit purge',
|
||||
'description' => 'Purges system audit entries by retention policy',
|
||||
'default_enabled' => 1,
|
||||
'default_timezone' => defined('APP_TIMEZONE') ? (string) APP_TIMEZONE : 'UTC',
|
||||
'default_schedule_type' => 'daily',
|
||||
'default_schedule_interval' => 1,
|
||||
'default_schedule_time' => '03:00',
|
||||
'default_schedule_weekdays_csv' => null,
|
||||
'default_catchup_once' => 1,
|
||||
'allowed_schedule_types' => ['hourly', 'daily', 'weekly'],
|
||||
];
|
||||
}
|
||||
|
||||
public function execute(?int $actorUserId): array
|
||||
{
|
||||
$deleted = $this->systemAuditService->purgeExpired();
|
||||
|
||||
return [
|
||||
'status' => 'success',
|
||||
'error_code' => null,
|
||||
'error_message' => null,
|
||||
'result' => [
|
||||
'deleted_count' => $deleted,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,7 @@ namespace MintyPHP\Service\Scheduler;
|
||||
|
||||
use MintyPHP\App\AppContainer;
|
||||
use MintyPHP\App\Module\ModuleRegistry;
|
||||
use MintyPHP\Service\Audit\SystemAuditService;
|
||||
use MintyPHP\Service\Scheduler\Handler\ScheduledJobHandlerInterface;
|
||||
use MintyPHP\Service\Scheduler\Handler\SystemAuditPurgeJobHandler;
|
||||
use MintyPHP\Service\Scheduler\Handler\UserLifecycleJobHandler;
|
||||
use MintyPHP\Service\User\UserLifecycleService;
|
||||
use RuntimeException;
|
||||
@@ -14,7 +12,6 @@ use RuntimeException;
|
||||
class ScheduledJobRegistry
|
||||
{
|
||||
public const USER_LIFECYCLE_RUN = 'user_lifecycle_run';
|
||||
public const SYSTEM_AUDIT_PURGE = 'system_audit_purge';
|
||||
|
||||
/** @var array<string, ScheduledJobHandlerInterface> */
|
||||
private array $handlers;
|
||||
@@ -38,12 +35,10 @@ class ScheduledJobRegistry
|
||||
|
||||
public function __construct(
|
||||
UserLifecycleService $userLifecycleService,
|
||||
SystemAuditService $systemAuditService,
|
||||
private readonly AppContainer $container
|
||||
) {
|
||||
$this->handlers = [
|
||||
self::USER_LIFECYCLE_RUN => new UserLifecycleJobHandler($userLifecycleService),
|
||||
self::SYSTEM_AUDIT_PURGE => new SystemAuditPurgeJobHandler($systemAuditService),
|
||||
];
|
||||
|
||||
$this->loadModuleJobs();
|
||||
|
||||
@@ -5,13 +5,12 @@ namespace MintyPHP\Service\Scheduler;
|
||||
use MintyPHP\Domain\Taxonomy\ScheduledJobRunStatus;
|
||||
use MintyPHP\Domain\Taxonomy\ScheduledJobTriggerType;
|
||||
use MintyPHP\Domain\Taxonomy\SchedulerRuntimeResult;
|
||||
use MintyPHP\Domain\Taxonomy\SystemAuditOutcome;
|
||||
use MintyPHP\Repository\Scheduler\ScheduledJobRepositoryInterface;
|
||||
use MintyPHP\Repository\Scheduler\ScheduledJobRunRepositoryInterface;
|
||||
use MintyPHP\Repository\Scheduler\SchedulerRuntimeRepositoryInterface;
|
||||
use MintyPHP\Repository\Support\DatabaseSessionRepository;
|
||||
use MintyPHP\Repository\Support\RepoQuery;
|
||||
use MintyPHP\Service\Audit\SystemAuditService;
|
||||
use MintyPHP\Service\Audit\AuditRecorderInterface;
|
||||
|
||||
/**
|
||||
* Orchestrates the execution of scheduled jobs.
|
||||
@@ -33,7 +32,7 @@ class SchedulerRunService
|
||||
private readonly ScheduledJobRegistry $scheduledJobRegistry,
|
||||
private readonly ScheduleCalculator $scheduleCalculator,
|
||||
private readonly DatabaseSessionRepository $databaseSessionRepository,
|
||||
private readonly SystemAuditService $systemAuditService
|
||||
private readonly AuditRecorderInterface $systemAuditService
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -423,13 +422,13 @@ class SchedulerRunService
|
||||
private function auditOutcomeForRunStatus(string $status): string
|
||||
{
|
||||
if ($status === ScheduledJobRunStatus::Success->value) {
|
||||
return SystemAuditOutcome::Success->value;
|
||||
return 'success';
|
||||
}
|
||||
if ($status === ScheduledJobRunStatus::Failed->value) {
|
||||
return SystemAuditOutcome::Failed->value;
|
||||
return 'failed';
|
||||
}
|
||||
// "skipped" is an operational non-error state (e.g. already running),
|
||||
// not an authorization denial.
|
||||
return SystemAuditOutcome::Success->value;
|
||||
return 'success';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,7 @@ use MintyPHP\Repository\Scheduler\ScheduledJobRepositoryInterface;
|
||||
use MintyPHP\Repository\Scheduler\ScheduledJobRunRepositoryInterface;
|
||||
use MintyPHP\Repository\Scheduler\SchedulerRuntimeRepositoryInterface;
|
||||
use MintyPHP\Repository\Support\DatabaseSessionRepository;
|
||||
use MintyPHP\Service\Audit\AuditServicesFactory;
|
||||
use MintyPHP\Service\Audit\SystemAuditService;
|
||||
use MintyPHP\Service\Audit\AuditRecorderInterface;
|
||||
use MintyPHP\Service\User\UserLifecycleService;
|
||||
use MintyPHP\Service\User\UserServicesFactory;
|
||||
|
||||
@@ -21,7 +20,7 @@ class SchedulerServicesFactory
|
||||
|
||||
public function __construct(
|
||||
private readonly UserServicesFactory $userServicesFactory,
|
||||
private readonly AuditServicesFactory $auditServicesFactory,
|
||||
private readonly AuditRecorderInterface $auditRecorder,
|
||||
private readonly DatabaseSessionRepository $databaseSessionRepository,
|
||||
private readonly SchedulerRepositoryFactory $schedulerRepositoryFactory,
|
||||
private readonly AppContainer $appContainer
|
||||
@@ -48,7 +47,7 @@ class SchedulerServicesFactory
|
||||
$this->getScheduledJobRegistry(),
|
||||
$this->getScheduleCalculator(),
|
||||
$this->getDatabaseSessionRepository(),
|
||||
$this->getSystemAuditService()
|
||||
$this->getAuditRecorder()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -86,7 +85,6 @@ class SchedulerServicesFactory
|
||||
{
|
||||
return $this->scheduledJobRegistry ??= new ScheduledJobRegistry(
|
||||
$this->getUserLifecycleService(),
|
||||
$this->getSystemAuditService(),
|
||||
$this->appContainer
|
||||
);
|
||||
}
|
||||
@@ -96,9 +94,9 @@ class SchedulerServicesFactory
|
||||
return $this->databaseSessionRepository;
|
||||
}
|
||||
|
||||
private function getSystemAuditService(): SystemAuditService
|
||||
private function getAuditRecorder(): AuditRecorderInterface
|
||||
{
|
||||
return $this->auditServicesFactory->createSystemAuditService();
|
||||
return $this->auditRecorder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user