| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 |
- <?php
- declare(strict_types=1);
- namespace App\Controllers;
- use App\ApiClient\AdminClient;
- use App\ApiClient\ApiException;
- use App\Auth\SessionManager;
- use Psr\Http\Message\ResponseInterface;
- use Psr\Http\Message\ServerRequestInterface;
- use Slim\Views\Twig;
- /**
- * `/app/audit` — filterable audit log list. Filters are passed
- * through to the api as-is; pagination is done page+page_size in
- * the query string. The page tolerates a partial outage by
- * rendering an empty state with the api error message.
- */
- final class AuditController
- {
- use CrudControllerSupport;
- private const ALLOWED_ACTIONS = [
- 'reporter.created', 'reporter.updated', 'reporter.deleted',
- 'consumer.created', 'consumer.updated', 'consumer.deleted',
- 'token.created', 'token.revoked',
- 'policy.created', 'policy.updated', 'policy.deleted',
- 'category.created', 'category.updated', 'category.deleted',
- 'manual_block.created', 'manual_block.deleted',
- 'allowlist.created', 'allowlist.deleted',
- 'job.triggered',
- ];
- private const ALLOWED_KINDS = ['user', 'admin-token', 'reporter', 'consumer', 'system'];
- public function __construct(
- private readonly Twig $twigEngine,
- private readonly SessionManager $sessionManager,
- private readonly AdminClient $admin,
- ) {
- }
- protected function twig(): Twig
- {
- return $this->twigEngine;
- }
- protected function sessions(): SessionManager
- {
- return $this->sessionManager;
- }
- public function index(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
- {
- if (($redirect = $this->requireUser($request, $response)) !== null) {
- return $redirect;
- }
- $user = $this->sessions()->getUser();
- // Guard for static analysis — requireUser bounced if null already.
- if ($user === null) {
- return $response->withStatus(302)->withHeader('Location', '/login');
- }
- $params = $request->getQueryParams();
- $filters = [
- 'actor_kind' => self::clean($params['actor_kind'] ?? null),
- 'actor_id' => self::clean($params['actor_id'] ?? null),
- 'action' => self::clean($params['action'] ?? null),
- 'entity_type' => self::clean($params['entity_type'] ?? null),
- 'entity_id' => self::clean($params['entity_id'] ?? null),
- 'subject_kind' => self::clean($params['subject_kind'] ?? null),
- 'subject_id' => self::clean($params['subject_id'] ?? null),
- 'from' => self::clean($params['from'] ?? null),
- 'to' => self::clean($params['to'] ?? null),
- ];
- $page = isset($params['page']) && ctype_digit((string) $params['page']) ? max(1, (int) $params['page']) : 1;
- $pageSize = 50;
- $list = null;
- $error = null;
- try {
- $list = $this->admin->listAuditLog($user->userId, $filters, $page, $pageSize);
- } catch (ApiException $e) {
- $error = 'API error: ' . $e->getMessage();
- }
- return $this->twigEngine->render($response, 'pages/audit/index.twig', [
- 'active_section' => 'audit',
- 'list' => $list,
- 'page' => $page,
- 'page_size' => $pageSize,
- 'filters' => $filters,
- 'allowed_actions' => self::ALLOWED_ACTIONS,
- 'allowed_kinds' => self::ALLOWED_KINDS,
- 'error' => $error,
- ]);
- }
- private static function clean(mixed $v): ?string
- {
- if (!is_string($v)) {
- return null;
- }
- $trimmed = trim($v);
- return $trimmed === '' ? null : $trimmed;
- }
- }
|